Index: trunk/parsers/wikidom/lib/es/es.Document.js |
— | — | @@ -76,17 +76,17 @@ |
77 | 77 | block.document = null; |
78 | 78 | }; |
79 | 79 | |
80 | | -Document.prototype.renderBlocks = function() { |
| 80 | +Document.prototype.renderBlocks = function( offset, callback ) { |
81 | 81 | // Remember width, to avoid updates when without width changes |
82 | 82 | this.width = this.$.innerWidth(); |
83 | 83 | // Render blocks |
84 | 84 | for ( var i = 0; i < this.blocks.length; i++ ) { |
85 | 85 | this.$.append( this.blocks[i].$ ); |
86 | | - this.blocks[i].renderContent(); |
| 86 | + this.blocks[i].renderContent( offset, callback ); |
87 | 87 | } |
88 | 88 | }; |
89 | 89 | |
90 | | -Document.prototype.updateBlocks = function() { |
| 90 | +Document.prototype.updateBlocks = function( offset, callback ) { |
91 | 91 | // Bypass rendering when width has not changed |
92 | 92 | var width = this.$.innerWidth(); |
93 | 93 | if ( this.width === width ) { |
— | — | @@ -96,6 +96,6 @@ |
97 | 97 | // Render blocks |
98 | 98 | var doc; |
99 | 99 | this.$.children( '.editSurface-block' ).each( function( i ) { |
100 | | - $(this).data( 'block' ).renderContent(); |
| 100 | + $(this).data( 'block' ).renderContent( offset, callback ); |
101 | 101 | } ); |
102 | 102 | }; |
Index: trunk/parsers/wikidom/lib/es/es.ParagraphBlock.js |
— | — | @@ -48,8 +48,8 @@ |
49 | 49 | * |
50 | 50 | * @param $container {jQuery Selection} Container to render into |
51 | 51 | */ |
52 | | -ParagraphBlock.prototype.renderContent = function() { |
53 | | - this.flow.render(); |
| 52 | +ParagraphBlock.prototype.renderContent = function( offset, callback ) { |
| 53 | + this.flow.render( offset, callback ); |
54 | 54 | }; |
55 | 55 | |
56 | 56 | /** |
Index: trunk/parsers/wikidom/lib/es/es.Surface.css |
— | — | @@ -10,24 +10,38 @@ |
11 | 11 | outline: none; |
12 | 12 | } |
13 | 13 | |
| 14 | +.editSurface { |
| 15 | + overflow: hidden; |
| 16 | +} |
| 17 | + |
14 | 18 | .editSurface-document { |
15 | 19 | cursor: text; |
16 | 20 | margin-top: 1em; |
| 21 | + overflow: hidden; |
17 | 22 | } |
18 | 23 | |
19 | 24 | .editSurface-block { |
20 | 25 | margin: 1em; |
21 | 26 | margin-top: 0; |
| 27 | + position: relative; |
22 | 28 | } |
23 | 29 | |
24 | | -.editSurface-line { |
25 | | - display: inline-block; |
| 30 | +.editSurface-line, |
| 31 | +.editSurface-ruler { |
26 | 32 | line-height: 1.5em; |
27 | 33 | cursor: text; |
28 | 34 | white-space: nowrap; |
29 | 35 | color: #000000; |
30 | 36 | } |
31 | 37 | |
| 38 | +.editSurface-ruler { |
| 39 | + position: absolute; |
| 40 | + top: 0; |
| 41 | + left: 0; |
| 42 | + display: inline-block; |
| 43 | + z-index: -1000; |
| 44 | +} |
| 45 | + |
32 | 46 | .editSurface-line.empty { |
33 | 47 | display: block; |
34 | 48 | 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 | | - |
16 | 2 | /** |
17 | 3 | * |
18 | 4 | * @param $container |
— | — | @@ -45,8 +31,8 @@ |
46 | 32 | |
47 | 33 | // Cursor |
48 | 34 | this.cursor = new Cursor(); |
49 | | - this.$.after( this.cursor.$ ); |
50 | | - |
| 35 | + this.$.append( this.cursor.$ ); |
| 36 | + |
51 | 37 | // Hidden input |
52 | 38 | this.$input = $( '<input class="editSurface-input" />' ) |
53 | 39 | .prependTo( this.$ ) |
— | — | @@ -76,6 +62,12 @@ |
77 | 63 | } |
78 | 64 | }); |
79 | 65 | |
| 66 | + $(window).resize( function() { |
| 67 | + surface.render( 0, function() { |
| 68 | + surface.drawSelection(); |
| 69 | + } ); |
| 70 | + } ); |
| 71 | + |
80 | 72 | // First render |
81 | 73 | this.render(); |
82 | 74 | } |
— | — | @@ -131,7 +123,8 @@ |
132 | 124 | if ( this.keydownTimeout ) { |
133 | 125 | clearTimeout( this.keydownTimeout ); |
134 | 126 | } |
135 | | - this.keydownTimeout = setTimeout( function ( surface ) { |
| 127 | + var surface = this; |
| 128 | + this.keydownTimeout = setTimeout( function () { |
136 | 129 | var val = surface.$input.val(); |
137 | 130 | surface.$input.val( '' ); |
138 | 131 | if ( val.length > 0 ) { |
— | — | @@ -139,7 +132,7 @@ |
140 | 133 | location.block.insertContent( location.offset, val.split('')); |
141 | 134 | location.offset += val.length; |
142 | 135 | } |
143 | | - }, 0, this ); |
| 136 | + }, 10 ); |
144 | 137 | break; |
145 | 138 | } |
146 | 139 | return true; |
— | — | @@ -434,15 +427,15 @@ |
435 | 428 | /** |
436 | 429 | * Updates the rendered view. |
437 | 430 | * |
438 | | - * @param from Location: Where to start re-flowing from (optional) |
| 431 | + * @param offset Location: Where to start re-flowing from (optional) |
439 | 432 | */ |
440 | | -Surface.prototype.render = function( from ) { |
| 433 | +Surface.prototype.render = function( offset, callback ) { |
441 | 434 | if ( !this.rendered ) { |
442 | 435 | this.rendered = true; |
443 | 436 | this.$.append( this.doc.$ ); |
444 | | - this.doc.renderBlocks(); |
| 437 | + this.doc.renderBlocks( offset, callback ); |
445 | 438 | } else { |
446 | | - this.doc.updateBlocks(); |
| 439 | + this.doc.updateBlocks( offset, callback ); |
447 | 440 | } |
448 | 441 | }; |
449 | 442 | |
— | — | @@ -458,6 +451,7 @@ |
459 | 452 | if ( selection === undefined ) { |
460 | 453 | selection = this.selection; |
461 | 454 | } |
| 455 | + var surface = this; |
462 | 456 | if ( selection.from && selection.to ) { |
463 | 457 | selection.normalize(); |
464 | 458 | var from = selection.start, |
— | — | @@ -465,7 +459,9 @@ |
466 | 460 | if ( from.block === to.block ) { |
467 | 461 | // Single block annotation |
468 | 462 | from.block.annotateContent( annotation, from.offset, to.offset ); |
469 | | - from.block.renderContent(); |
| 463 | + from.block.renderContent( function() { |
| 464 | + surface.drawSelection(); |
| 465 | + } ); |
470 | 466 | } else { |
471 | 467 | // Multiple block annotation |
472 | 468 | for ( var i = from.block.getIndex(), end = to.block.getIndex(); i <= end; i++ ) { |
— | — | @@ -473,18 +469,23 @@ |
474 | 470 | if ( block === from.block ) { |
475 | 471 | // From offset to length |
476 | 472 | block.annotateContent( annotation, from.offset, block.getLength() ); |
477 | | - block.renderContent(); |
| 473 | + block.renderContent( function() { |
| 474 | + surface.drawSelection(); |
| 475 | + } ); |
478 | 476 | } else if ( block === to.block ) { |
479 | 477 | // From 0 to offset |
480 | 478 | block.annotateContent( annotation, 0, to.offset ); |
481 | | - block.renderContent(); |
| 479 | + block.renderContent( function() { |
| 480 | + surface.drawSelection(); |
| 481 | + } ); |
482 | 482 | } else { |
483 | 483 | // Full coverage |
484 | 484 | block.annotateContent( annotation, 0, block.getLength() ); |
485 | | - block.renderContent(); |
| 485 | + block.renderContent( function() { |
| 486 | + surface.drawSelection(); |
| 487 | + } ); |
486 | 488 | } |
487 | 489 | } |
488 | 490 | } |
489 | 491 | } |
490 | | - this.drawSelection(); |
491 | 492 | }; |
Index: trunk/parsers/wikidom/lib/es/es.TextFlow.js |
— | — | @@ -12,6 +12,7 @@ |
13 | 13 | this.width = null; |
14 | 14 | this.boundaryTest = /([ \-\t\r\n\f])/g; |
15 | 15 | this.widthCache = {}; |
| 16 | + this.renderState = {}; |
16 | 17 | } |
17 | 18 | |
18 | 19 | /** |
— | — | @@ -55,7 +56,7 @@ |
56 | 57 | var virtual = line < this.lines.length - 1 |
57 | 58 | && this.boundaryTest.exec( this.lines[line].text.substr( -1 ) ) ? -1 : 0; |
58 | 59 | 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.$ ) |
60 | 61 | ruler = $ruler[0], |
61 | 62 | fit = this.fitCharacters( |
62 | 63 | this.lines[line].start, this.lines[line].end, ruler, position.left |
— | — | @@ -138,7 +139,7 @@ |
139 | 140 | * measuring for those cases. |
140 | 141 | */ |
141 | 142 | 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.$ ), |
143 | 144 | ruler = $ruler[0]; |
144 | 145 | ruler.innerHTML = this.content.render( this.lines[line].start, offset ); |
145 | 146 | position.left = ruler.clientWidth; |
— | — | @@ -182,6 +183,60 @@ |
183 | 184 | } |
184 | 185 | }; |
185 | 186 | |
| 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 | + |
186 | 241 | /** |
187 | 242 | * Renders text into a series of HTML elements, each a single line of wrapped text. |
188 | 243 | * |
— | — | @@ -193,13 +248,18 @@ |
194 | 249 | * text, but will be automatically ignored if the text or width of the container has changed. |
195 | 250 | * |
196 | 251 | * @param offset {Integer} Offset to re-render from, if possible (not yet implemented) |
197 | | - * @param callback {Function} Function to execute when flowing is complete |
198 | 252 | */ |
199 | 253 | 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 | + |
200 | 263 | this.widthCache = {}; |
201 | | - |
202 | | - // Reset lines in the DOM and the "lines" array |
203 | | - this.$.empty(); |
204 | 264 | |
205 | 265 | this.scanBoundaries(); |
206 | 266 | |
— | — | @@ -210,8 +270,8 @@ |
211 | 271 | * inconsistencies between browsers and box models, we can just create an element inside the |
212 | 272 | * container and measure it. |
213 | 273 | */ |
214 | | - var $ruler = $( '<div> </div>' ).appendTo( this.$ ), |
215 | | - width = $ruler.innerWidth() |
| 274 | + rs.$ruler = $( '<div> </div>' ).appendTo( this.$ ); |
| 275 | + rs.width = rs.$ruler.innerWidth() |
216 | 276 | |
217 | 277 | // TODO: Take offset into account |
218 | 278 | // Ignore offset optimization if the width has changed or the text has never been flowed before |
— | — | @@ -223,51 +283,19 @@ |
224 | 284 | |
225 | 285 | this.lines = []; |
226 | 286 | // 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(); |
268 | 287 | |
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(); |
272 | 300 | }; |
273 | 301 | |
274 | 302 | /** |
— | — | @@ -277,9 +305,12 @@ |
278 | 306 | * @param end {Integer} Ending of text range for line |
279 | 307 | */ |
280 | 308 | 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 ); |
284 | 315 | // Collect line information |
285 | 316 | this.lines.push({ |
286 | 317 | 'text': this.content.substring( start, end ), |
Index: trunk/parsers/wikidom/demos/es/index.html |
— | — | @@ -155,10 +155,6 @@ |
156 | 156 | 'type': 'all' |
157 | 157 | } ); |
158 | 158 | } ); |
159 | | - |
160 | | - $(window).resize( function() { |
161 | | - surface.render(); |
162 | | - } ); |
163 | 159 | } ); |
164 | 160 | </script> |
165 | 161 | </body> |