Index: trunk/parsers/wikidom/lib/es/es.Content.js |
— | — | @@ -401,26 +401,6 @@ |
402 | 402 | }; |
403 | 403 | |
404 | 404 | /** |
405 | | - * Breaks cross references, which occur when content is copied around. |
406 | | - * |
407 | | - * Because content data is an array of characters, or arrays containing a character and any |
408 | | - * number of references to annotation objects, slicing the array still leaves annotated characters |
409 | | - * as references to shared memory. This should be used only when annotations are going to be |
410 | | - * changed because it is rather expensive. |
411 | | - * |
412 | | - * @method |
413 | | - */ |
414 | | -es.Content.prototype.isolate = function() { |
415 | | - var i = 0, |
416 | | - length = this.data.length; |
417 | | - while ( i < length ) { |
418 | | - // The slice method works for array or string character type |
419 | | - this.data[i] = this.data[i].slice( 0 ); |
420 | | - i++; |
421 | | - } |
422 | | -}; |
423 | | - |
424 | | -/** |
425 | 405 | * Inserts content data at a specific position. |
426 | 406 | * |
427 | 407 | * Inserted content will inherit annotations from neighboring content. |
— | — | @@ -550,12 +530,33 @@ |
551 | 531 | * @emits "change" with type:"annotate" data property |
552 | 532 | */ |
553 | 533 | es.Content.prototype.annotate = function( method, annotation, range ) { |
| 534 | + // Support calling without a range argument, using the full content range as default |
554 | 535 | if ( !range ) { |
555 | 536 | range = new es.Range( 0, this.data.length ); |
556 | 537 | } else { |
557 | 538 | range.normalize(); |
558 | 539 | } |
559 | | - var i; |
| 540 | + /* |
| 541 | + * Because content data is an array of either strings containing a single character each or |
| 542 | + * references to arrays containing a single character string followed by a series of references |
| 543 | + * to annotation objects, making a "copy" by slicing content data will cause references to |
| 544 | + * annotated characters in the content data to be shared between the original and the "copy". To |
| 545 | + * ensure that modifications to annotated characters in the content data do not affect the data |
| 546 | + * of other content objects, annotated characters must be sliced individually. This is too |
| 547 | + * expensive to do on all content on every copy, so we only do it when we are going to modify |
| 548 | + * the annotation information, and on a few annotated characters as possible. |
| 549 | + */ |
| 550 | + for ( var i = range.start; i < range.end; i++ ) { |
| 551 | + if ( typeof this.data[i] !== 'string' ) { |
| 552 | + this.data[i] = this.data[i].slice( 0 ); |
| 553 | + } |
| 554 | + i++; |
| 555 | + } |
| 556 | + /* |
| 557 | + * Support toggle method by automatically choosing add or remove based on the coverage of the |
| 558 | + * content being annotated; if the content is not covered or partially covered add will be used, |
| 559 | + * if the content is completely covered, remove will be used. |
| 560 | + */ |
560 | 561 | if ( method === 'toggle' ) { |
561 | 562 | var coverage = this.coverageOfAnnotation( range, annotation, false ); |
562 | 563 | if ( coverage.length === range.getLength() ) { |
— | — | @@ -567,7 +568,7 @@ |
568 | 569 | } |
569 | 570 | if ( method === 'add' ) { |
570 | 571 | var duplicate; |
571 | | - for ( i = range.start; i < range.end; i++ ) { |
| 572 | + for ( var i = range.start; i < range.end; i++ ) { |
572 | 573 | duplicate = -1; |
573 | 574 | if ( typeof this.data[i] === 'string' ) { |
574 | 575 | // Never annotate new lines |
— | — | @@ -589,7 +590,7 @@ |
590 | 591 | } |
591 | 592 | } |
592 | 593 | } else if ( method === 'remove' ) { |
593 | | - for ( i = range.start; i < range.end; i++ ) { |
| 594 | + for ( var i = range.start; i < range.end; i++ ) { |
594 | 595 | if ( typeof this.data[i] !== 'string' ) { |
595 | 596 | if ( annotation.type === 'all' ) { |
596 | 597 | // Remove all annotations by converting the annotated character to a plain |
Index: trunk/parsers/wikidom/lib/es/es.Transaction.js |
— | — | @@ -1,6 +1,9 @@ |
2 | 2 | /** |
3 | | - * Creates an operation to be applied to a content object. |
| 3 | + * Creates a transaction which can be applied to a content object. |
4 | 4 | * |
| 5 | + * Transactions contain a series of operations, such as retain, insert, remove, start and end. Each |
| 6 | + * operation describes a step that must be taken to construct a new version of a content object. |
| 7 | + * |
5 | 8 | * @class |
6 | 9 | * @constructor |
7 | 10 | * @param content {es.Content} Content to operate on |
— | — | @@ -12,13 +15,10 @@ |
13 | 16 | }; |
14 | 17 | |
15 | 18 | /** |
16 | | - * List of operation implementations. |
| 19 | + * List of operation implementations. |
17 | 20 | */ |
18 | 21 | es.Transaction.operations = ( function() { |
19 | 22 | function annotate( con, add, rem ) { |
20 | | - // Ensure that modifications to annotated characters do not affect other uses of the same |
21 | | - // content by isolating it - performing a deep-slice |
22 | | - con.isolate(); |
23 | 23 | for ( var i = 0; i < add.length; i++ ) { |
24 | 24 | con.annotate( 'add', add[i] ); |
25 | 25 | } |
— | — | @@ -147,8 +147,7 @@ |
148 | 148 | adv; |
149 | 149 | for ( var i = 0; i < this.operations.length; i++ ) { |
150 | 150 | var op = this.operations[i]; |
151 | | - adv = op.model.commit( op.val, cur, src, dst, add, rem ); |
152 | | - cur += adv; |
| 151 | + cur += op.model.commit( op.val, cur, src, dst, add, rem ); |
153 | 152 | } |
154 | 153 | return dst; |
155 | 154 | }; |
— | — | @@ -161,8 +160,7 @@ |
162 | 161 | adv; |
163 | 162 | for ( var i = 0; i < this.operations.length; i++ ) { |
164 | 163 | var op = this.operations[i]; |
165 | | - adv = op.model.rollback( op.val, cur, src, dst, add, rem ); |
166 | | - cur += adv; |
| 164 | + cur += op.model.rollback( op.val, cur, src, dst, add, rem ); |
167 | 165 | } |
168 | 166 | return dst; |
169 | 167 | }; |