r92102 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r92101‎ | r92102 | r92103 >
Date:22:01, 13 July 2011
Author:tparscal
Status:deferred
Tags:
Comment:
Made flowing iterative, only doing 3 lines at a time, allowing input to be taken more frequently and rendering to be aborted. Also made rendering re-use existing lines.
Modified paths:
  • /trunk/parsers/wikidom/demos/es/index.html (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.Document.js (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.ParagraphBlock.js (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.Surface.css (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.Surface.js (modified) (history)
  • /trunk/parsers/wikidom/lib/es/es.TextFlow.js (modified) (history)

Diff [purge]

Index: trunk/parsers/wikidom/lib/es/es.Document.js
@@ -76,17 +76,17 @@
7777 block.document = null;
7878 };
7979
80 -Document.prototype.renderBlocks = function() {
 80+Document.prototype.renderBlocks = function( offset, callback ) {
8181 // Remember width, to avoid updates when without width changes
8282 this.width = this.$.innerWidth();
8383 // Render blocks
8484 for ( var i = 0; i < this.blocks.length; i++ ) {
8585 this.$.append( this.blocks[i].$ );
86 - this.blocks[i].renderContent();
 86+ this.blocks[i].renderContent( offset, callback );
8787 }
8888 };
8989
90 -Document.prototype.updateBlocks = function() {
 90+Document.prototype.updateBlocks = function( offset, callback ) {
9191 // Bypass rendering when width has not changed
9292 var width = this.$.innerWidth();
9393 if ( this.width === width ) {
@@ -96,6 +96,6 @@
9797 // Render blocks
9898 var doc;
9999 this.$.children( '.editSurface-block' ).each( function( i ) {
100 - $(this).data( 'block' ).renderContent();
 100+ $(this).data( 'block' ).renderContent( offset, callback );
101101 } );
102102 };
Index: trunk/parsers/wikidom/lib/es/es.ParagraphBlock.js
@@ -48,8 +48,8 @@
4949 *
5050 * @param $container {jQuery Selection} Container to render into
5151 */
52 -ParagraphBlock.prototype.renderContent = function() {
53 - this.flow.render();
 52+ParagraphBlock.prototype.renderContent = function( offset, callback ) {
 53+ this.flow.render( offset, callback );
5454 };
5555
5656 /**
Index: trunk/parsers/wikidom/lib/es/es.Surface.css
@@ -10,24 +10,38 @@
1111 outline: none;
1212 }
1313
 14+.editSurface {
 15+ overflow: hidden;
 16+}
 17+
1418 .editSurface-document {
1519 cursor: text;
1620 margin-top: 1em;
 21+ overflow: hidden;
1722 }
1823
1924 .editSurface-block {
2025 margin: 1em;
2126 margin-top: 0;
 27+ position: relative;
2228 }
2329
24 -.editSurface-line {
25 - display: inline-block;
 30+.editSurface-line,
 31+.editSurface-ruler {
2632 line-height: 1.5em;
2733 cursor: text;
2834 white-space: nowrap;
2935 color: #000000;
3036 }
3137
 38+.editSurface-ruler {
 39+ position: absolute;
 40+ top: 0;
 41+ left: 0;
 42+ display: inline-block;
 43+ z-index: -1000;
 44+}
 45+
3246 .editSurface-line.empty {
3347 display: block;
3448 width: 0px;
Index: trunk/parsers/wikidom/lib/es/es.Surface.js
@@ -1,17 +1,3 @@
2 -/*@cc_on
3 -(function(f) {
4 - window.setTimeout = f(window.setTimeout); // overwrites the global function!
5 - window.setInterval = f(window.setInterval); // overwrites the global function!
6 -})(function(f) {
7 - return function(c, t) {
8 - var a = [].slice.call(arguments, 2); // gathers the extra args
9 - return f(function() {
10 - c.apply(this, a); // passes them to your function
11 - }, t);
12 - };
13 - });
14 -@*/
15 -
162 /**
173 *
184 * @param $container
@@ -45,8 +31,8 @@
4632
4733 // Cursor
4834 this.cursor = new Cursor();
49 - this.$.after( this.cursor.$ );
50 -
 35+ this.$.append( this.cursor.$ );
 36+
5137 // Hidden input
5238 this.$input = $( '<input class="editSurface-input" />' )
5339 .prependTo( this.$ )
@@ -76,6 +62,12 @@
7763 }
7864 });
7965
 66+ $(window).resize( function() {
 67+ surface.render( 0, function() {
 68+ surface.drawSelection();
 69+ } );
 70+ } );
 71+
8072 // First render
8173 this.render();
8274 }
@@ -131,7 +123,8 @@
132124 if ( this.keydownTimeout ) {
133125 clearTimeout( this.keydownTimeout );
134126 }
135 - this.keydownTimeout = setTimeout( function ( surface ) {
 127+ var surface = this;
 128+ this.keydownTimeout = setTimeout( function () {
136129 var val = surface.$input.val();
137130 surface.$input.val( '' );
138131 if ( val.length > 0 ) {
@@ -139,7 +132,7 @@
140133 location.block.insertContent( location.offset, val.split(''));
141134 location.offset += val.length;
142135 }
143 - }, 0, this );
 136+ }, 10 );
144137 break;
145138 }
146139 return true;
@@ -434,15 +427,15 @@
435428 /**
436429 * Updates the rendered view.
437430 *
438 - * @param from Location: Where to start re-flowing from (optional)
 431+ * @param offset Location: Where to start re-flowing from (optional)
439432 */
440 -Surface.prototype.render = function( from ) {
 433+Surface.prototype.render = function( offset, callback ) {
441434 if ( !this.rendered ) {
442435 this.rendered = true;
443436 this.$.append( this.doc.$ );
444 - this.doc.renderBlocks();
 437+ this.doc.renderBlocks( offset, callback );
445438 } else {
446 - this.doc.updateBlocks();
 439+ this.doc.updateBlocks( offset, callback );
447440 }
448441 };
449442
@@ -458,6 +451,7 @@
459452 if ( selection === undefined ) {
460453 selection = this.selection;
461454 }
 455+ var surface = this;
462456 if ( selection.from && selection.to ) {
463457 selection.normalize();
464458 var from = selection.start,
@@ -465,7 +459,9 @@
466460 if ( from.block === to.block ) {
467461 // Single block annotation
468462 from.block.annotateContent( annotation, from.offset, to.offset );
469 - from.block.renderContent();
 463+ from.block.renderContent( function() {
 464+ surface.drawSelection();
 465+ } );
470466 } else {
471467 // Multiple block annotation
472468 for ( var i = from.block.getIndex(), end = to.block.getIndex(); i <= end; i++ ) {
@@ -473,18 +469,23 @@
474470 if ( block === from.block ) {
475471 // From offset to length
476472 block.annotateContent( annotation, from.offset, block.getLength() );
477 - block.renderContent();
 473+ block.renderContent( function() {
 474+ surface.drawSelection();
 475+ } );
478476 } else if ( block === to.block ) {
479477 // From 0 to offset
480478 block.annotateContent( annotation, 0, to.offset );
481 - block.renderContent();
 479+ block.renderContent( function() {
 480+ surface.drawSelection();
 481+ } );
482482 } else {
483483 // Full coverage
484484 block.annotateContent( annotation, 0, block.getLength() );
485 - block.renderContent();
 485+ block.renderContent( function() {
 486+ surface.drawSelection();
 487+ } );
486488 }
487489 }
488490 }
489491 }
490 - this.drawSelection();
491492 };
Index: trunk/parsers/wikidom/lib/es/es.TextFlow.js
@@ -12,6 +12,7 @@
1313 this.width = null;
1414 this.boundaryTest = /([ \-\t\r\n\f])/g;
1515 this.widthCache = {};
 16+ this.renderState = {};
1617 }
1718
1819 /**
@@ -55,7 +56,7 @@
5657 var virtual = line < this.lines.length - 1
5758 && this.boundaryTest.exec( this.lines[line].text.substr( -1 ) ) ? -1 : 0;
5859 line = Math.min( line, this.lines.length - 1 );
59 - var $ruler = $( '<div class="editSurface-line"></div>' ).appendTo( this.$ )
 60+ var $ruler = $( '<div class="editSurface-ruler"></div>' ).appendTo( this.$ )
6061 ruler = $ruler[0],
6162 fit = this.fitCharacters(
6263 this.lines[line].start, this.lines[line].end, ruler, position.left
@@ -138,7 +139,7 @@
139140 * measuring for those cases.
140141 */
141142 if ( this.lines[line].start < offset ) {
142 - var $ruler = $( '<div class="editSurface-line"></div>' ).appendTo( this.$ ),
 143+ var $ruler = $( '<div class="editSurface-ruler"></div>' ).appendTo( this.$ ),
143144 ruler = $ruler[0];
144145 ruler.innerHTML = this.content.render( this.lines[line].start, offset );
145146 position.left = ruler.clientWidth;
@@ -182,6 +183,60 @@
183184 }
184185 };
185186
 187+TextFlow.prototype.renderIteration = function() {
 188+ var rs = this.renderState,
 189+ iteration = 0;
 190+ while ( ++iteration <= rs.iterationLimit && rs.wordOffset < rs.wordCount - 1 ) {
 191+ rs.wordFit = this.fitWords( rs.wordOffset, rs.wordCount - 1, rs.ruler, rs.width );
 192+ if ( rs.wordFit.width > rs.width ) {
 193+ // The first word didn't fit, we need to split it up
 194+ rs.charOffset = rs.lineStart;
 195+ rs.wordOffset++;
 196+ rs.lineEnd = this.boundaries[rs.wordOffset];
 197+ do {
 198+ rs.charFit = this.fitCharacters( rs.charOffset, rs.lineEnd, rs.ruler, rs.width );
 199+ // If we were able to get the rest of the characters on the line OK
 200+ if ( rs.charFit.end === rs.lineEnd) {
 201+ // Try to fit more words on the line
 202+ rs.wordFit = this.fitWords(
 203+ rs.wordOffset, rs.wordCount - 1, rs.ruler, rs.width - rs.charFit.width
 204+ );
 205+ if ( rs.wordFit.end > rs.wordOffset ) {
 206+ rs.wordOffset = rs.wordFit.end;
 207+ rs.charFit.end = rs.lineEnd = this.boundaries[rs.wordOffset];
 208+ }
 209+ }
 210+ this.appendLine( rs.charOffset, rs.charFit.end );
 211+ // Move on to another line
 212+ rs.charOffset = rs.charFit.end;
 213+ } while ( rs.charOffset < rs.lineEnd );
 214+ } else {
 215+ rs.wordOffset = rs.wordFit.end;
 216+ rs.lineEnd = this.boundaries[rs.wordOffset];
 217+ this.appendLine( rs.lineStart, rs.lineEnd );
 218+ }
 219+ rs.lineStart = rs.lineEnd;
 220+ }
 221+ // Only perform on actual last iteration
 222+ if ( rs.wordOffset >= rs.wordCount - 1 ) {
 223+ // Cleanup
 224+ rs.$ruler.remove();
 225+ this.$.find( '.editSurface-line[line-index=' + ( this.lines.length - 1 ) + ']' )
 226+ .nextAll()
 227+ .remove();
 228+ rs.timeout = undefined;
 229+ if ( $.isFunction( rs.callback ) ) {
 230+ rs.callback();
 231+ }
 232+ } else {
 233+ rs.ruler.innerHTML = '';
 234+ var flow = this;
 235+ rs.timeout = setTimeout( function() {
 236+ flow.renderIteration();
 237+ }, 0 );
 238+ }
 239+};
 240+
186241 /**
187242 * Renders text into a series of HTML elements, each a single line of wrapped text.
188243 *
@@ -193,13 +248,18 @@
194249 * text, but will be automatically ignored if the text or width of the container has changed.
195250 *
196251 * @param offset {Integer} Offset to re-render from, if possible (not yet implemented)
197 - * @param callback {Function} Function to execute when flowing is complete
198252 */
199253 TextFlow.prototype.render = function( offset, callback ) {
 254+ var rs = this.renderState;
 255+
 256+ // Stop iterating from last render
 257+ if ( rs.timeout !== undefined ) {
 258+ clearTimeout( rs.timeout );
 259+ // Cleanup
 260+ rs.$ruler.remove();
 261+ }
 262+
200263 this.widthCache = {};
201 -
202 - // Reset lines in the DOM and the "lines" array
203 - this.$.empty();
204264
205265 this.scanBoundaries();
206266
@@ -210,8 +270,8 @@
211271 * inconsistencies between browsers and box models, we can just create an element inside the
212272 * container and measure it.
213273 */
214 - var $ruler = $( '<div>&nbsp;</div>' ).appendTo( this.$ ),
215 - width = $ruler.innerWidth()
 274+ rs.$ruler = $( '<div>&nbsp;</div>' ).appendTo( this.$ );
 275+ rs.width = rs.$ruler.innerWidth()
216276
217277 // TODO: Take offset into account
218278 // Ignore offset optimization if the width has changed or the text has never been flowed before
@@ -223,51 +283,19 @@
224284
225285 this.lines = [];
226286 // Iterate over each word that will fit in a line, appending them to the DOM as we go
227 - var wordOffset = 0,
228 - lineStart = 0,
229 - lineEnd,
230 - wordFit,
231 - charOffset,
232 - charFit,
233 - wordCount = this.boundaries.length,
234 - ruler = $ruler.addClass('editSurface-line')[0];
235 - while ( wordOffset < wordCount - 1 ) {
236 - wordFit = this.fitWords( wordOffset, wordCount - 1, ruler, width );
237 - if ( wordFit.width > width ) {
238 - // The first word didn't fit, we need to split it up
239 - charOffset = lineStart;
240 - wordOffset++;
241 - lineEnd = this.boundaries[wordOffset];
242 - do {
243 - charFit = this.fitCharacters( charOffset, lineEnd, ruler, width );
244 - // If we were able to get the rest of the characters on the line OK
245 - if (charFit.end === lineEnd) {
246 - // Try to fit more words on the line
247 - wordFit = this.fitWords(
248 - wordOffset, wordCount - 1, ruler, width - charFit.width
249 - );
250 - if ( wordFit.end > wordOffset ) {
251 - wordOffset = wordFit.end;
252 - charFit.end = lineEnd = this.boundaries[wordOffset];
253 - }
254 - }
255 - this.appendLine( charOffset, charFit.end );
256 - // Move on to another line
257 - charOffset = charFit.end;
258 - } while ( charOffset < lineEnd );
259 - } else {
260 - wordOffset = wordFit.end;
261 - lineEnd = this.boundaries[wordOffset];
262 - this.appendLine( lineStart, lineEnd );
263 - }
264 - lineStart = lineEnd;
265 - }
266 - // Cleanup
267 - $ruler.remove();
268287
269 - if ( $.isFunction( callback ) ) {
270 - callback();
271 - }
 288+ rs.wordOffset = 0;
 289+ rs.lineStart = 0;
 290+ rs.lineEnd = 0;
 291+ rs.wordFit = null;
 292+ rs.charOffset = 0;
 293+ rs.charFit = null;
 294+ rs.wordCount = this.boundaries.length;
 295+ rs.ruler = rs.$ruler.addClass('editSurface-ruler')[0];
 296+ rs.iterationLimit = 3;
 297+ rs.callback = callback;
 298+
 299+ this.renderIteration();
272300 };
273301
274302 /**
@@ -277,9 +305,12 @@
278306 * @param end {Integer} Ending of text range for line
279307 */
280308 TextFlow.prototype.appendLine = function( start, end ) {
281 - $line = $( '<div class="editSurface-line" line-index="'
282 - + this.lines.length + '">' + this.content.render( start, end ) + '</div>' )
283 - .appendTo( this.$ );
 309+ $line = this.$.find( '.editSurface-line[line-index=' + this.lines.length + ']' );
 310+ if ( !$line.length ) {
 311+ $line = $( '<div class="editSurface-line" line-index="' + this.lines.length + '"></div>' )
 312+ .appendTo( this.$ );
 313+ }
 314+ $line[0].innerHTML = this.content.render( start, end );
284315 // Collect line information
285316 this.lines.push({
286317 'text': this.content.substring( start, end ),
Index: trunk/parsers/wikidom/demos/es/index.html
@@ -155,10 +155,6 @@
156156 'type': 'all'
157157 } );
158158 } );
159 -
160 - $(window).resize( function() {
161 - surface.render();
162 - } );
163159 } );
164160 </script>
165161 </body>

Follow-up revisions

RevisionCommit summaryAuthorDate
r92165redraw selection after we have applied annotation...hashar16:27, 14 July 2011

Status & tagging log