| Index: trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js |
| — | — | @@ -821,132 +821,14 @@ |
| 822 | 822 | * @param {es.Range} range |
| 823 | 823 | * @returns {es.Transaction} |
| 824 | 824 | */ |
| 825 | | -es.DocumentModel.prototype.prepareRemoval = function( range ) { |
| 826 | | - var doc = this; |
| 827 | | - |
| 828 | | - /** |
| 829 | | - * Remove content only, and completely covered droppable nodes drop the nodes entirely. |
| 830 | | - * |
| 831 | | - * @param {es.DocumentModelNode} node Node to strip from |
| 832 | | - * @param {es.Range} range Range of data to strip |
| 833 | | - */ |
| 834 | | - function strip( tx, node, range, offset ) { |
| 835 | | - var childNodes = node.getChildren(), |
| 836 | | - selectedNodes = node.selectNodes( range, true ), |
| 837 | | - rules = es.DocumentModel.nodeRules, |
| 838 | | - left = offset || 0, |
| 839 | | - right, |
| 840 | | - elementLength, |
| 841 | | - selectedNode; |
| 842 | | - offset = offset || 0; |
| 843 | | - for ( var i = 0; i < childNodes.length; i++ ) { |
| 844 | | - if ( selectedNodes.length && childNodes[i] === selectedNodes[0].node ) { |
| 845 | | - for ( var j = 0; j < selectedNodes.length; j++ ) { |
| 846 | | - selectedNode = selectedNodes[j]; |
| 847 | | - elementLength = selectedNode.node.getElementLength(); |
| 848 | | - right = left + elementLength; |
| 849 | | - // Handle selected nodes |
| 850 | | - if ( !selectedNode.range ) { |
| 851 | | - // Drop whole nodes |
| 852 | | - if ( /*rules[selectedNode.node.getElementType()].droppable*/ true ) { |
| 853 | | - tx.pushRemove( doc.data.slice( left, right ) ); |
| 854 | | - } else { |
| 855 | | - tx.pushRetain( 1 ); |
| 856 | | - strip( |
| 857 | | - tx, selectedNode.node, new es.Range( 0, elementLength ), left + 1 |
| 858 | | - ); |
| 859 | | - tx.pushRetain( 1 ); |
| 860 | | - } |
| 861 | | - } else { |
| 862 | | - if ( selectedNode.node.hasChildren() ) { |
| 863 | | - tx.pushRetain( 1 ); |
| 864 | | - strip( tx, selectedNode.node, selectedNode.range, left + 1 ); |
| 865 | | - tx.pushRetain( 1 ); |
| 866 | | - } else { |
| 867 | | - // Strip content |
| 868 | | - tx.pushRetain( 1 + selectedNode.range.start ); |
| 869 | | - if ( selectedNode.globalRange.getLength() ) { |
| 870 | | - tx.pushRemove( |
| 871 | | - doc.data.slice( |
| 872 | | - selectedNode.globalRange.start + offset, |
| 873 | | - selectedNode.globalRange.end + offset |
| 874 | | - ) |
| 875 | | - ); |
| 876 | | - } |
| 877 | | - tx.pushRetain( elementLength - selectedNode.range.end - 1 ); |
| 878 | | - } |
| 879 | | - } |
| 880 | | - left = right; |
| 881 | | - } |
| 882 | | - i += selectedNodes.length - 1; |
| 883 | | - } else { |
| 884 | | - elementLength = childNodes[i].getElementLength(); |
| 885 | | - right = left + elementLength; |
| 886 | | - // Handle non-selected nodes |
| 887 | | - tx.pushRetain( elementLength ); |
| 888 | | - } |
| 889 | | - left = right; |
| 890 | | - } |
| 891 | | - } |
| 892 | 825 | |
| 893 | | - var tx = new es.Transaction(); |
| 894 | | - range.normalize(); |
| 895 | | - |
| 896 | | - var node1 = this.getNodeFromOffset( range.start ); |
| 897 | | - var node2 = this.getNodeFromOffset( range.end ); |
| 898 | | - |
| 899 | | - // If a selection is painted across two paragraphs, and then the text is deleted, the two |
| 900 | | - // paragraphs can become one paragraph. However, if the selection crosses into a table, those |
| 901 | | - // cannot be merged. To make this simple, we are follow a basic rule: |
| 902 | | - // can merge = ( same type ) && ( same parent ) |
| 903 | | - // So you can merge adjacent paragraphs, or listitems. And you can't merge a paragraph into |
| 904 | | - // a table row. There may be other rules we will want in here later, for instance, special |
| 905 | | - // casing merging a listitem into a paragraph. |
| 906 | | - if ( node1 && node2 && |
| 907 | | - node1.getElementType() === node2.getElementType() && |
| 908 | | - node1.getParent() === node2.getParent() |
| 909 | | - ) { |
| 910 | | - // Retain to the start of the range |
| 911 | | - if ( range.start > 0 ) { |
| 912 | | - tx.pushRetain( range.start ); |
| 913 | | - } |
| 914 | | - // Remove all data in a given range. |
| 915 | | - tx.pushRemove( this.data.slice( range.start, range.end ) ); |
| 916 | | - // Retain up to the end of the document. Why do we do this? Because Trevor said so! |
| 917 | | - if ( range.end < this.data.length ) { |
| 918 | | - tx.pushRetain( this.data.length - range.end ); |
| 919 | | - } |
| 920 | | - } else { |
| 921 | | - strip( tx, this, range ); |
| 922 | | - } |
| 923 | | - |
| 924 | | - tx.optimize(); |
| 925 | | - return tx; |
| 926 | | -}; |
| 927 | | - |
| 928 | | -/** |
| 929 | | - * Generates a transaction which removes data from a given range. |
| 930 | | - * |
| 931 | | - * When removing data inside an element, the data is simply discarded and the node's length is |
| 932 | | - * adjusted accordingly. When removing data across elements, there are two situations that can cause |
| 933 | | - * added complexity: |
| 934 | | - * 1. A range spans between nodes of different levels or types |
| 935 | | - * 2. A range only partially covers one or two nodes |
| 936 | | - * |
| 937 | | - * To resolve these issues in a predictable way the following rules must be obeyed: |
| 938 | | - * 1. Structural elements are retained unless the range being removed covers the entire element |
| 939 | | - * 2. Elements can only be merged if they are of the same time and share a common parent |
| 940 | | - * |
| 941 | | - * @method |
| 942 | | - * @param {es.Range} range |
| 943 | | - * @returns {es.Transaction} |
| 944 | | - */ |
| 945 | | -/* |
| 946 | | -es.DocumentModel.prototype.prepareRemovalRoan = function( range ) { |
| | 826 | +es.DocumentModel.prototype.prepareRemoval = function( range ) { |
| 947 | 827 | var tx = new es.Transaction(), selectedNodes, selectedNode, startNode, endNode, i; |
| 948 | 828 | range.normalize(); |
| 949 | 829 | if ( range.start === range.end ) { |
| 950 | | - // Empty range, return empty transaction |
| | 830 | + // Empty range, nothing to do |
| | 831 | + // Retain up to the end of the document. Why do we do this? Because Trevor said so! |
| | 832 | + tx.pushRetain( this.data.length ); |
| 951 | 833 | return tx; |
| 952 | 834 | } |
| 953 | 835 | |
| — | — | @@ -968,7 +850,6 @@ |
| 969 | 851 | // This is the simple case. node1 and node2 are either the same node, or can be merged |
| 970 | 852 | // So we can just remove all the data in the range and call it a day, no fancy |
| 971 | 853 | // processing necessary |
| 972 | | - // FIXME we're not accounting for droppability here, should we? |
| 973 | 854 | |
| 974 | 855 | // Retain to the start of the range |
| 975 | 856 | if ( range.start > 0 ) { |
| — | — | @@ -981,43 +862,35 @@ |
| 982 | 863 | tx.pushRetain( this.data.length - range.end ); |
| 983 | 864 | } |
| 984 | 865 | } else { |
| 985 | | - //if ( range.start > 0 ) { |
| 986 | | - // tx.pushRetain( range.start ); |
| 987 | | - //} |
| 988 | | - |
| | 866 | + var index = 0; |
| 989 | 867 | for ( i = 0; i < selectedNodes.length; i++ ) { |
| 990 | 868 | selectedNode = selectedNodes[i]; |
| 991 | | - if ( !selectedNode.range ) { |
| 992 | | - // Remove the entire node |
| 993 | | - // TODO accounting for droppability will suck |
| 994 | | - tx.pushRemove( this.data.slice( selectedNode.globalRange.start, selectedNode.globalRange.end ) ); |
| 995 | | - } else { |
| 996 | | - // Remove part of the node |
| 997 | | - // TODO account for droppability |
| 998 | | - // TODO need to descend, rawr |
| 999 | | - tx.pushRetain( 1 + selectedNode.range.start ); |
| 1000 | | - if ( selectedNode.globalRange.getLength() ) { |
| 1001 | | - tx.pushRemove( |
| 1002 | | - this.data.slice( |
| 1003 | | - selectedNode.globalRange.start, |
| 1004 | | - selectedNode.globalRange.end |
| 1005 | | - ) |
| 1006 | | - ); |
| 1007 | | - } |
| 1008 | | - tx.pushRetain( selectedNode.node.getElementLength() - selectedNode.range.end - 1 ); |
| | 869 | + // Retain up to where the next removal starts |
| | 870 | + if ( selectedNode.globalRange.start > index ) { |
| | 871 | + tx.pushRetain( selectedNode.globalRange.start - index ); |
| 1009 | 872 | } |
| | 873 | + |
| | 874 | + // Remove stuff |
| | 875 | + if ( selectedNode.globalRange.getLength() ) { |
| | 876 | + tx.pushRemove( |
| | 877 | + this.data.slice( |
| | 878 | + selectedNode.globalRange.start, |
| | 879 | + selectedNode.globalRange.end |
| | 880 | + ) |
| | 881 | + ); |
| | 882 | + } |
| | 883 | + index = selectedNode.globalRange.end; |
| 1010 | 884 | } |
| 1011 | 885 | |
| 1012 | 886 | // Retain up to the end of the document. Why do we do this? Because Trevor said so! |
| 1013 | | - if ( range.end < this.data.length ) { |
| 1014 | | - tx.pushRetain( this.data.length - range.end ); |
| | 887 | + if ( index < this.data.length ) { |
| | 888 | + tx.pushRetain( this.data.length - index ); |
| 1015 | 889 | } |
| 1016 | 890 | } |
| 1017 | 891 | |
| 1018 | 892 | tx.optimize(); |
| 1019 | 893 | return tx; |
| 1020 | 894 | }; |
| 1021 | | -*/ |
| 1022 | 895 | |
| 1023 | 896 | /** |
| 1024 | 897 | * Generates a transaction which annotates content within a given range. |