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( '&', '&' )
+ .replace( ' ', ' ' )
+ .replace( '<', '<' )
+ .replace( '>', '>' )
+ .replace( '\'', ''' )
+ .replace( '"', '"' );
+ // 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( ' ' );
+ $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