https://www.mediawiki.org/wiki/Special:Code/MediaWiki/103498
Revision: 103498
Author: catrope
Date: 2011-11-17 19:23:15 +0000 (Thu, 17 Nov 2011)
Log Message:
-----------
Improve the merging logic in prepareRemoval() to also allow merging nested
nodes, e.g. by deleting </p></li><li><p>
Modified Paths:
--------------
trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js
trunk/extensions/VisualEditor/tests/es/es.DocumentBranchNode.test.js
trunk/extensions/VisualEditor/tests/es/es.DocumentModel.test.js
Modified: trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js
===================================================================
--- trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js
2011-11-17 19:22:11 UTC (rev 103497)
+++ trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js
2011-11-17 19:23:15 UTC (rev 103498)
@@ -827,6 +827,36 @@
*/
es.DocumentModel.prototype.prepareRemoval = function( range ) {
+ // If a selection is painted across two paragraphs, and then the text
is deleted, the two
+ // paragraphs can become one paragraph. However, if the selection
crosses into a table, those
+ // cannot be merged. To make this simple, we follow rule #2 in the
comment above for deciding
+ // whether two elements can be merged.
+ // So you can merge adjacent paragraphs, or list items. And you can't
merge a paragraph into
+ // a table row. There may be other rules we will want in here later,
for instance, special
+ // casing merging a listitem into a paragraph.
+ function canMerge( node1, node2 ) {
+ var n1 = node1, n2 = node2;
+ // Simultaneously walk upwards from node1 and node2
+ // until we reach the common ancestor.
+ while ( n1 !== n2 ) {
+ if ( n1.getElementType() !== n2.getElementType() ) {
+ // Not the same type
+ return false;
+ }
+ n1 = n1.getParent();
+ n2 = n2.getParent();
+ if ( n1 === null || n2 === null ) {
+ // Reached a root, so no common ancestor
+ // or different depth
+ return false;
+ }
+ }
+ // We've reached the common ancestor using simultaneous
traversal,
+ // so we know node1 and node2 have the same depth. We also
haven't
+ // seen any nodes with mismatching types along the way, so
we're good.
+ return true;
+ }
+
var tx = new es.Transaction(), selectedNodes, selectedNode, startNode,
endNode, i;
range.normalize();
if ( range.start === range.end ) {
@@ -840,17 +870,7 @@
startNode = selectedNodes[0].node;
endNode = selectedNodes[selectedNodes.length - 1].node;
- // If a selection is painted across two paragraphs, and then the text
is deleted, the two
- // paragraphs can become one paragraph. However, if the selection
crosses into a table, those
- // cannot be merged. To make this simple, we are follow a basic rule:
- // can merge = ( same type ) && ( same parent )
- // So you can merge adjacent paragraphs, or listitems. And you can't
merge a paragraph into
- // a table row. There may be other rules we will want in here later,
for instance, special
- // casing merging a listitem into a paragraph.
- if ( startNode && endNode &&
- startNode.getParent() === endNode.getParent() &&
- startNode.getElementType() === endNode.getElementType()
- ) {
+ if ( startNode && endNode && canMerge( startNode, endNode ) ) {
// This is the simple case. node1 and node2 are either the same
node, or can be merged
// So we can just remove all the data in the range and call it
a day, no fancy
// processing necessary
Modified: trunk/extensions/VisualEditor/tests/es/es.DocumentBranchNode.test.js
===================================================================
--- trunk/extensions/VisualEditor/tests/es/es.DocumentBranchNode.test.js
2011-11-17 19:22:11 UTC (rev 103497)
+++ trunk/extensions/VisualEditor/tests/es/es.DocumentBranchNode.test.js
2011-11-17 19:23:15 UTC (rev 103498)
@@ -948,7 +948,7 @@
' to ' + selectNodesTests[i].input.end
+ ')'
);
if ( console && console.log && !compare( result,
selectNodesTests[i].output ) ) {
- console.log("Test " + (i+1) + " FAILED");
+ console.log( "Test " + (i+1) + " FAILED" );
console.log( result );
console.log( selectNodesTests[i].output );
}
Modified: trunk/extensions/VisualEditor/tests/es/es.DocumentModel.test.js
===================================================================
--- trunk/extensions/VisualEditor/tests/es/es.DocumentModel.test.js
2011-11-17 19:22:11 UTC (rev 103497)
+++ trunk/extensions/VisualEditor/tests/es/es.DocumentModel.test.js
2011-11-17 19:23:15 UTC (rev 103498)
@@ -301,7 +301,7 @@
);
} );
-test( 'es.DocumentModel.prepareRemoval', 8, function() {
+test( 'es.DocumentModel.prepareRemoval', 11, function() {
var documentModel = es.DocumentModel.newFromPlainObject( esTest.obj );
// Test 1
@@ -341,26 +341,9 @@
],
'prepareRemoval removes entire elements'
);
-
+
// Test 3
deepEqual(
- documentModel.prepareRemoval( new es.Range( 21, 23 )
).getOperations(),
- [
- { 'type': 'retain', 'length': 21 },
- {
- 'type': 'remove',
- 'data': [
- { 'type': '/listItem' },
- { 'type': 'listItem', 'attributes': {
'styles': ['number'] } }
- ]
- },
- { 'type': 'retain', 'length': 11 }
- ],
- 'prepareRemoval merges two list items'
- );
-
- // Test 4
- deepEqual(
documentModel.prepareRemoval( new es.Range( 3, 9 )
).getOperations(),
[
{ 'type': 'retain', 'length': 3 },
@@ -375,7 +358,7 @@
'prepareRemoval works across structural nodes'
);
- // Test 5
+ // Test 4
deepEqual(
documentModel.prepareRemoval( new es.Range( 3, 24 )
).getOperations(),
[
@@ -410,7 +393,7 @@
'prepareRemoval strips and drops correctly when working across
structural nodes'
);
- // Test 6
+ // Test 5
deepEqual(
documentModel.prepareRemoval( new es.Range( 3, 25 )
).getOperations(),
[
@@ -450,7 +433,7 @@
'prepareRemoval strips and drops correctly when working across
structural nodes (2)'
);
- // Test 7
+ // Test 6
deepEqual(
documentModel.prepareRemoval( new es.Range( 9, 17 )
).getOperations(),
[
@@ -475,7 +458,7 @@
'prepareRemoval will not merge items of unequal types'
);
- // Test 8
+ // Test 7
deepEqual(
documentModel.prepareRemoval( new es.Range( 9, 27 )
).getOperations(),
[
@@ -509,6 +492,76 @@
],
'prepareRemoval blanks a paragraph and a list'
);
+
+ // Test 8
+ deepEqual(
+ documentModel.prepareRemoval( new es.Range( 21, 23 )
).getOperations(),
+ [
+ { 'type': 'retain', 'length': 21 },
+ {
+ 'type': 'remove',
+ 'data': [
+ { 'type': '/listItem' },
+ { 'type': 'listItem', 'attributes': {
'styles': ['number'] } }
+ ]
+ },
+ { 'type': 'retain', 'length': 11 }
+ ],
+ 'prepareRemoval merges two list items'
+ );
+
+ // Test 9
+ deepEqual(
+ documentModel.prepareRemoval( new es.Range( 20, 24 )
).getOperations(),
+ [
+ { 'type': 'retain', 'length': 20 },
+ {
+ 'type': 'remove',
+ 'data': [
+ { 'type': '/paragraph' },
+ { 'type': '/listItem' },
+ { 'type': 'listItem', 'attributes': {
'styles': ['number'] } },
+ { 'type': 'paragraph' }
+ ]
+ },
+ { 'type': 'retain', 'length': 10 }
+ ],
+ 'prepareRemoval merges two list items and the paragraphs inside
them'
+ );
+
+ // Test 10
+ deepEqual(
+ documentModel.prepareRemoval( new es.Range( 20, 23 )
).getOperations(),
+ [
+ { 'type': 'retain', 'length': 34 }
+ ],
+ 'prepareRemoval returns a null transaction when attempting an
unbalanced merge'
+ );
+
+ // Test 11
+ deepEqual(
+ documentModel.prepareRemoval( new es.Range( 15, 24 )
).getOperations(),
+ [
+ { 'type': 'retain', 'length': 15 },
+ {
+ 'type': 'remove',
+ 'data': [
+ { 'type': '/paragraph' },
+ { 'type': '/listItem' },
+ { 'type': 'listItem', 'attributes': {
'styles': ['bullet', 'bullet'] } },
+ { 'type': 'paragraph' },
+ 'f',
+ { 'type': '/paragraph' },
+ { 'type': '/listItem' },
+ { 'type': 'listItem', 'attributes': {
'styles': ['number'] } },
+ { 'type': 'paragraph' }
+ ]
+ },
+ { 'type': 'retain', 'length': 10 }
+ ],
+ 'prepareRemoval merges two list items and the paragraphs inside
them'
+ );
+
} );
test( 'es.DocumentModel.prepareInsertion', 11, function() {
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs