http://www.mediawiki.org/wiki/Special:Code/MediaWiki/89657

Revision: 89657
Author:   tparscal
Date:     2011-06-07 17:04:29 +0000 (Tue, 07 Jun 2011)
Log Message:
-----------
Added surface demo and libraries.

Modified Paths:
--------------
    trunk/parsers/wikidom/README

Added Paths:
-----------
    trunk/parsers/wikidom/demos/surface/
    trunk/parsers/wikidom/demos/surface/index.html
    trunk/parsers/wikidom/lib/jquery.editSurface.css
    trunk/parsers/wikidom/lib/jquery.editSurface.js
    trunk/parsers/wikidom/lib/jquery.flow.js

Modified: trunk/parsers/wikidom/README
===================================================================
--- trunk/parsers/wikidom/README        2011-06-07 17:03:25 UTC (rev 89656)
+++ trunk/parsers/wikidom/README        2011-06-07 17:04:29 UTC (rev 89657)
@@ -41,4 +41,30 @@
 ; List
 : A series of item objects.
 ; Table
-: A series of rows, each a series of columns, which contain document objects.
\ No newline at end of file
+: A series of rows, each a series of columns, which contain document objects.
+
+== EditSurface ==
+
+EditSurface is a proof-of-concept for rendering a structured and formatted 
text editor. The document
+structure is from WikiDOM, and should be kept compatible as WikiDOM changes. 
+
+=== Design ===
+
+A document is made up of a series of blocks, each with unique data structures 
and user interfaces.
+These blocks may be paragraphs, lists, tables, etc. Within a block, the most 
common atomic element
+is a line of text. Within a line of text, annotations can be applied which 
will affect it's
+rendering. Additionally, discreet in-line objects can be used within a line. 
These items are treated
+as a single character during selection, and are modified using a supplemental 
user interface such as
+a dialog or inspector.
+
+=== Plans ===
+
+* Re-flow lines without deleting paragraph elements
+* Auto-update selection on resize
+* Draw blinking cursor
+* Handle keyboard input for moving cursor and making selection
+* Handle keyboard input for inserting and deleting
+* Support multiple font styles in the same paragraph
+* Support "black-box" in-line elements, treat them as a single character
+* Add list editor
+* Add table editor

Added: trunk/parsers/wikidom/demos/surface/index.html
===================================================================
--- trunk/parsers/wikidom/demos/surface/index.html                              
(rev 0)
+++ trunk/parsers/wikidom/demos/surface/index.html      2011-06-07 17:04:29 UTC 
(rev 89657)
@@ -0,0 +1,40 @@
+<!doctype html>
+
+<html>
+       <head>
+               <title>EditSurface Demo</title>
+               <link rel="stylesheet" href="../../lib/jquery.editSurface.css">
+       </head>
+       <body>
+               <h1>EditSurface Demo</h1>
+               <div id="selection"><p></p></div>
+               <div id="es"></div>
+               
+               <!-- EditSurface -->
+               <script type="text/javascript" 
src="../../lib/jquery.js"></script>
+               <script type="text/javascript" 
src="../../lib/jquery.flow.js"></script>
+               <script type="text/javascript" 
src="../../lib/jquery.editSurface.js"></script>
+               
+               <!-- Demo -->
+               <script>
+                       var text = [
+                               "In text display, line wrap is the feature of 
continuing on a new line when a line is full, such that each line fits in the 
viewable window, allowing text to be read from top to bottom without any 
horizontal scrolling.",
+                               "Word wrap is the additional feature of most 
text editors, word processors, and web browsers, of breaking lines between and 
not within words, except when a single word is longer than a line.",
+                               "It is usually done on the fly when viewing or 
printing a document, so no line break code is manually entered, or 
stored.[citation needed] If the user changes the margins, the editor will 
either automatically reposition the line breaks to ensure that all the text 
will \"flow\" within the margins and remain visible, or provide the typist some 
convenient way to reposition the line breaks.",
+                               "A soft return is the break resulting from line 
wrap or word wrap, whereas a hard return is an intentional break, creating a 
new paragraph.",
+                               "The soft returns are usually placed after the 
ends of complete words, or after the punctuation that follows complete words. 
However, word wrap may also occur following a hyphen.",
+                               "Word wrap following hyphens is sometimes not 
desired, and can be avoided by using a so-called non-breaking hyphen instead of 
a regular hyphen. On the other hand, when using word processors, invisible 
hyphens, called soft hyphens, can also be inserted inside words so that word 
wrap can occur following the soft hyphens.",
+                               "Sometimes, word wrap is not desirable between 
words. In such cases, word wrap can usually be avoided by using a hard space or 
non-breaking space between the words, instead of regular spaces.",
+                               
"OccasionallyThereAreWordsThatAreSoLongTheyExceedTheWidthOfTheLineAndEndUpWrappingBetweenMultipleLines.",
+                       ].join( ' ' );
+                       $( '#es' ).editSurface( {
+                               'document': { 'blocks': [ {
+                                       'type': 'paragraph',
+                                       'lines': [
+                                               { 'text': text }
+                                       ]
+                               } ] }
+                       } );
+               </script>
+       </body>
+</html>


Property changes on: trunk/parsers/wikidom/demos/surface/index.html
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/parsers/wikidom/lib/jquery.editSurface.css
===================================================================
--- trunk/parsers/wikidom/lib/jquery.editSurface.css                            
(rev 0)
+++ trunk/parsers/wikidom/lib/jquery.editSurface.css    2011-06-07 17:04:29 UTC 
(rev 89657)
@@ -0,0 +1,45 @@
+body {
+       font-family: "Arial";
+       font-size: 0.8em;
+}
+
+#selection {
+       position: absolute;
+       right: 3%;
+       width: 45%;
+       border: solid 1px Highlight;
+       line-height: 1.5em;
+       cursor: text;
+}
+
+#selection p {
+       margin: 0;
+       padding: 1em;
+}
+
+.editSurface-container {
+       position: absolute;
+       left: 3%;
+       width: 45%;
+       border: solid 1px red;
+       cursor: text;
+}
+
+.editSurface-paragraph {
+       padding: 1em;
+}
+
+.editSurface-container .editSurface-line {
+       height: 1.5em;
+       line-height: 1.5em;
+       width: 100%;
+       cursor: text;
+       overflow: hidden;
+}
+
+.editSurface-range {
+       display: none;
+       position: absolute;
+       background-color: Highlight;
+       cursor: text;
+}


Property changes on: trunk/parsers/wikidom/lib/jquery.editSurface.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: trunk/parsers/wikidom/lib/jquery.editSurface.js
===================================================================
--- trunk/parsers/wikidom/lib/jquery.editSurface.js                             
(rev 0)
+++ trunk/parsers/wikidom/lib/jquery.editSurface.js     2011-06-07 17:04:29 UTC 
(rev 89657)
@@ -0,0 +1,205 @@
+$.fn.editSurface = function( options ) {
+       var $this = $(this);
+       
+       options = $.extend( {
+               // Defaults
+               'document': null
+       }, options );
+       
+       $this
+               .addClass( 'editSurface-container' )
+               .append( '<div class="editSurface-document"></div>' )
+               .before( '<div class="editSurface-range"></div>'
+                               + '<div class="editSurface-range"></div>'
+                               + '<div class="editSurface-range"></div>');
+       var ranges = {
+               '$all': $( '.editSurface-range' ),
+               '$first': $( '.editSurface-range:eq(0)' ),
+               '$fill': $( '.editSurface-range:eq(1)' ),
+               '$last': $( '.editSurface-range:eq(2)' )
+       };
+       var $document = $this.find( '.editSurface-document' );
+       var sel = {
+               'active': false,
+               'from': null,
+               'to': null,
+               'start': null,
+               'end': null
+       };
+       function getSelectionText() {
+               var text;
+               if ( sel.from && sel.to ) {
+                       if ( sel.from.line === sel.to.line ) {
+                               text = sel.from.$target.data( 'text' ).substr(
+                                       sel.from.index, sel.to.index - 
sel.from.index
+                               );
+                       } else {
+                               text = sel.from.$target.data( 'text' ).substr( 
sel.from.index );
+                               var $sibling = sel.from.$target.next();
+                               for ( var i = sel.from.line + 1; i < 
sel.to.line; i++ ) {
+                                       text += $sibling.data( 'text' )
+                                       $sibling = $sibling.next();
+                               }
+                               text += sel.to.$target.data( 'text' ).substr( 
0, sel.to.index );
+                       }
+               }
+               return text;
+       }
+       function getSelection( e ) {
+               var $target = $( e.target );
+               var metrics = $target.data( 'metrics' );
+               var text = $target.data( 'text' );
+               var line = $target.data( 'line' );
+               if ( !$.isArray( metrics ) || metrics.length === 0 ) {
+                       return null;
+               }
+               var to = metrics.length - 1;
+               var l = 0;
+               var r = 0;
+               for ( var i = 0; i <= to; i++ ) {
+                       l = r;
+                       r += metrics[i];
+                       if ( ( e.layerX > l && e.layerX <= r ) || ( e.layerX >= 
r && i == to ) ) {
+                               var offset = $target.offset();
+                               var height = $target.height();
+                               return {
+                                               '$target': $target,
+                                               'index': i,
+                                               'line': line,
+                                               'top': offset.top,
+                                               'left': offset.left + l,
+                                               'right': r,
+                                               'bottom': offset.top + height,
+                                               'width': r - l,
+                                               'height': height
+                               };
+                       }
+               }
+               return null;
+       }
+       
+       function drawSelection( $container ) {
+               if ( sel.from && sel.to ) {
+                       if ( sel.from.line === sel.to.line ) {
+                               // 1 line
+                               if ( sel.from.index !== sel.to.index ) {
+                                       ranges.$first.show().css( {
+                                               'left': sel.from.left,
+                                               'top': sel.from.top,
+                                               'width': sel.to.right - 
sel.from.right,
+                                               'height': sel.from.height
+                                       } );
+                                       ranges.$fill.hide();
+                                       ranges.$last.hide();
+                                       // XXX: Demo code!
+                                       $( '#selection p' ).text( 
getSelectionText() );
+                                       return;
+                               }
+                       } else if ( sel.from.line < sel.to.line ) {
+                               // 2+ lines
+                               ranges.$first.show().css( {
+                                       'left': sel.from.left,
+                                       'top': sel.from.top,
+                                       'width': ( $container.innerWidth() - 
sel.from.left )
+                                                       + 
$container.offset().left,
+                                       'height': sel.from.height
+                               } );
+                               if ( sel.from.line < sel.to.line - 1 ) {
+                                       ranges.$fill.show().css( {
+                                               'left': 
$container.offset().left,
+                                               'top': sel.from.bottom,
+                                               'width': 
$container.innerWidth(),
+                                               'height': sel.to.top - 
sel.from.bottom
+                                       } );
+                               } else {
+                                       ranges.$fill.hide();
+                               }
+                               ranges.$last.show().css( {
+                                       'left': $container.offset().left,
+                                       'top': sel.to.top,
+                                       'width': sel.to.left - 
$container.offset().left,
+                                       'height': sel.to.height
+                               } );
+                               // XXX: Demo code!
+                               $( '#selection p' ).text( getSelectionText() );
+                               return;
+                       }
+               }
+               // No selection
+               ranges.$all.hide();
+       }
+       
+       function renderDocument( doc ) {
+               $document.empty();
+               for ( var i = 0; i < doc.blocks.length; i++ ) {
+                       switch ( doc.blocks[i].type ) {
+                               case 'paragraph':
+                                       renderParagraph( doc.blocks[i], 
$document )
+                                       break;
+                       }
+               }
+       }
+       
+       function renderParagraph( paragraph, $container ) {
+               var $paragraph = $( '<div class="editSurface-paragraph"></div>' 
).appendTo( $container );
+               var lines = [];
+               for ( var i = 0; i < paragraph.lines.length; i++ ) {
+                       lines.push( paragraph.lines[i].text );
+               }
+               $paragraph.flow( lines.join( ' ' ) );
+               $paragraph
+                       .mousedown( function( e ) {
+                               // TODO: If the target is not a line, find the 
nearest line to the cursor and use it
+                               if ( $( e.target ).is( '.editSurface-line' ) ) {
+                                       e.preventDefault();
+                                       e.stopPropagation();
+                                       sel = {
+                                               'active': true,
+                                               'from': null,
+                                               'to': null,
+                                               'start': getSelection( e ),
+                                               'end': null
+                                       };
+                                       drawSelection( $paragraph );
+                               }
+                               return false;
+                       } )
+                       .mouseup( function( e ) {
+                               if ( !$( e.target ).is( '.editSurface-line' ) 
|| !sel.from || !sel.to
+                                               || ( sel.from.line === 
sel.to.line && sel.from.index === sel.to.index ) ) {
+                                       sel.from = null;
+                                       sel.to = null;
+                                       sel.start = null;
+                                       sel.end = null;
+                               }
+                               sel.active = false;
+                               drawSelection( $paragraph );
+                       } )
+                       .mousemove( function( e ) {
+                               // TODO: If the target is not a line, find the 
nearest line to the cursor and use it
+                               if ( $( e.target ).is( '.editSurface-line' ) && 
sel.active ) {
+                                       sel.end = getSelection( e );
+                                       if ( sel.start.line < sel.end.line
+                                                       || ( sel.start.line === 
sel.end.line
+                                                                       && 
sel.start.index < sel.end.index ) ) {
+                                               sel.from = sel.start;
+                                               sel.to = sel.end;
+                                       } else {
+                                               sel.from = sel.end;
+                                               sel.to = sel.start;
+                                       }
+                                       drawSelection( $paragraph );
+                               }
+                       } );
+       }
+       
+       function update() {
+               // Render document
+               if ( options.document !== null ) {
+                       renderDocument( options.document, $this );
+               }
+       }
+       
+       $(window).resize( update );
+       update();
+};


Property changes on: trunk/parsers/wikidom/lib/jquery.editSurface.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: trunk/parsers/wikidom/lib/jquery.flow.js
===================================================================
--- trunk/parsers/wikidom/lib/jquery.flow.js                            (rev 0)
+++ trunk/parsers/wikidom/lib/jquery.flow.js    2011-06-07 17:04:29 UTC (rev 
89657)
@@ -0,0 +1,91 @@
+/*
+ * Flow jQuery plugin
+ */
+
+$.flow = { 'widthCache': {} };
+
+$.fn.flow = function( text ) {
+       var lineLimit = $(this).innerWidth();
+       
+       // Wordify
+       var words = [],
+               word = { 'text': '', 'width': 0, 'metrics': [] };
+       for ( var i = 0; i < text.length; i++ ) {
+               var char = text[i];
+               // Boundary detection
+               var boundary = String( ' -\t\r\n\f' ).indexOf( char ) >= 0;
+               // Encoding
+               var charHtml = char
+                       .replace( '&', '&amp;' )
+                       .replace( ' ', '&nbsp;' )
+                       .replace( '<', '&lt;' )
+                       .replace( '>', '&gt;' )
+                       .replace( '\'', '&apos;' )
+                       .replace( '"', '&quot;' );
+        // Measurement
+               var charWidth;
+               if ( typeof $.flow.widthCache[char] === 'undefined' ) {
+                       charWidth = $.flow.widthCache[char] =
+                               $( '<span>' + charHtml + '</span>' ).appendTo( 
$(this) ).width();
+               } else {
+                       charWidth = $.flow.widthCache[char];
+               }
+               // Virtual boundary
+               if ( word.width + charWidth >= lineLimit ) {
+                       words[words.length] = word;
+                       word = { 'text': '', 'width': 0, 'metrics': [] };
+               }
+               // Append
+               if ( boundary ) {
+                       if ( word.text.length ) {
+                               words[words.length] = word;
+                               word = { 'text': '', 'width': 0, 'metrics': [] 
};
+                       }
+                       words[words.length] = { 'text': char, 'width': 
charWidth, 'metrics': [charWidth] };
+               } else {
+                       word.text += char;
+                       word.width += charWidth;
+                       word.metrics[word.metrics.length] = charWidth;
+               }
+       }
+       if ( word.text.length ) {
+               words[words.length] = word;
+       }
+       
+       // Lineify
+       var lines = [],
+               line = { 'text': '', 'width': 0, 'metrics': [] };
+       for ( var i = 0; i < words.length; i++ ) {
+               var hardReturn = String( '\r\n\f' ).indexOf( words[i].text ) >= 
0;
+               if ( line.width + words[i].width > lineLimit || hardReturn ) {
+                       lines[lines.length] = line;
+                       line = { 'text': '', 'width': 0, 'metrics': [] };
+               }
+               if ( !hardReturn && ( line.width > 0 || words[i].text !== ' ' ) 
) {
+                       line.text += words[i].text;
+                       line.width += words[i].width;
+                       line.metrics = line.metrics.concat( words[i].metrics );
+               }
+       }
+       if ( line.text.length ) {
+               lines[lines.length] = line;
+       }
+       
+       // Flow
+       $(this).empty();
+       for ( var i = 0; i < lines.length; i++ ) {
+               var $line = $( '<div class="editSurface-line"></div>' )
+                       .data( 'metrics', lines[i].metrics )
+                       .data( 'text', lines[i].text )
+                       .data( 'line', i );
+               if ( lines[i].text.length ) {
+                       $line.text( lines[i].text );
+               } else {
+                       $line.html( '&nbsp;' );
+                       $line.addClass( 'empty' );
+               }
+               $(this).append( $line );
+       }
+       
+       return $(this);
+};
\ No newline at end of file


Property changes on: trunk/parsers/wikidom/lib/jquery.flow.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to