Index: trunk/parsers/wikidom/lib/hype/models/es.DocumentModel.js |
— | — | @@ -580,7 +580,15 @@ |
581 | 581 | * @returns {es.Transaction} |
582 | 582 | */ |
583 | 583 | es.DocumentModel.prototype.prepareInsertion = function( offset, data ) { |
584 | | - function containsStructuralElements( data ) { |
| 584 | + /** |
| 585 | + * Returns true if data starts with an opening element and ends with a closing element |
| 586 | + */ |
| 587 | + /*function isStructuralData( data ) { |
| 588 | + return data.length >= 2 && |
| 589 | + data[0].type !== undefined && data[0].type.charAt( 0 ) != '/' && |
| 590 | + data[data.length - 1].type !== undefined && data[data.length - 1].type.charAt( 0 ) == '/'; |
| 591 | + }*/ |
| 592 | + function isStructuralData( data ) { |
585 | 593 | var i; |
586 | 594 | for ( i = 0; i < data.length; i++ ) { |
587 | 595 | if ( data[i].type !== undefined ) { |
— | — | @@ -596,20 +604,76 @@ |
597 | 605 | // * The end of the document (offset length-1) |
598 | 606 | // * Any location between elements, i.e. the item before is a closing and the item after is an opening |
599 | 607 | return offset <= 0 || offset >= data.length - 1 || ( |
600 | | - data[offset - 1].type !== undefined && data[offset - 1].type.charAt( 0 ) != '/' && |
601 | | - data[offset].type !== undefined && data[offset].type.charAt( 0 ) == '/' |
| 608 | + data[offset - 1].type !== undefined && data[offset - 1].type.charAt( 0 ) == '/' && |
| 609 | + data[offset].type !== undefined && data[offset].type.charAt( 0 ) != '/' |
602 | 610 | ); |
603 | 611 | } |
| 612 | + |
| 613 | + /** |
| 614 | + * Balances mismatched openings/closings in data |
| 615 | + * @return data itself if nothing was changed, or a clone of data with balancing changes made. data itself is never touched |
| 616 | + */ |
| 617 | + function balance( data ) { |
| 618 | + var i, stack = [], element, workingData = null; |
| 619 | + |
| 620 | + for ( i = 0; i < data.length; i++ ) { |
| 621 | + if ( data[i].type === undefined ) { |
| 622 | + // Not an opening or a closing, skip |
| 623 | + } else if ( data[i].type.charAt( 0 ) != '/' ) { |
| 624 | + // Opening |
| 625 | + stack.push( data[i].type ); |
| 626 | + } else { |
| 627 | + // Closing |
| 628 | + if ( stack.length == 0 ) { |
| 629 | + // The stack is empty, so this is an unopened closing |
| 630 | + // Remove it |
| 631 | + if ( workingData === null ) { |
| 632 | + workingData = data.slice( 0 ); |
| 633 | + } |
| 634 | + workingData.splice( i, 1 ); |
| 635 | + } |
| 636 | + |
| 637 | + element = stack.pop(); |
| 638 | + if ( element != data[i].type.substr( 1 ) ) { |
| 639 | + // Closing doesn't match what's expected |
| 640 | + // This means the input is malformed and cannot possibly |
| 641 | + // have been a fragment taken from well-formed data |
| 642 | + throw 'Input is malformed: expected /' + element + ' but got ' + data[i].type + ' at index ' + i; |
| 643 | + } |
| 644 | + } |
| 645 | + } |
| 646 | + |
| 647 | + // Check whether there are any unclosed tags and close them |
| 648 | + if ( stack.length > 0 && workingData === null ) { |
| 649 | + workingData = data.slice( 0 ); |
| 650 | + } |
| 651 | + while ( stack.length > 0 ) { |
| 652 | + element = stack.pop(); |
| 653 | + workingData.push( { 'type': '/' + element } ); |
| 654 | + } |
| 655 | + |
| 656 | + // TODO |
| 657 | + // Check whether there is any raw unenclosed content and deal with that somehow |
| 658 | + |
| 659 | + return workingData || data; |
| 660 | + } |
604 | 661 | |
605 | | - var tx = new es.Transaction(), insertedData = data; |
| 662 | + var tx = new es.Transaction(), |
| 663 | + insertedData = data, // may be cloned and modified |
| 664 | + isStructuralLoc = isStructuralLocation( offset, this.data ); |
| 665 | + |
606 | 666 | if ( offset > 0 ) { |
607 | 667 | tx.pushRetain( offset ); |
608 | 668 | } |
609 | | - // TODO check for structural changes |
610 | | - if ( containsStructuralElements( insertedData ) ) { |
611 | | - // TODO |
| 669 | + |
| 670 | + if ( isStructuralData( insertedData ) ) { |
| 671 | + if ( isStructuralLoc ) { |
| 672 | + insertedData = balance( insertedData ); |
| 673 | + } else { |
| 674 | + // TODO close and reopen the element the insertion point is in |
| 675 | + } |
612 | 676 | } else { |
613 | | - if ( isStructuralLocation( offset, this.data ) ) { |
| 677 | + if ( isStructuralLoc ) { |
614 | 678 | // We're inserting content into a structural location, |
615 | 679 | // so we need to wrap the inserted content in a paragraph. |
616 | 680 | insertedData = [ { 'type': 'paragraph' }, { 'type': '/paragraph' } ]; |