diff --git a/modules/es/bases/es.DocumentNode.js b/modules/es/bases/es.DocumentNode.js index 78c647057c..b9876da90b 100644 --- a/modules/es/bases/es.DocumentNode.js +++ b/modules/es/bases/es.DocumentNode.js @@ -69,6 +69,40 @@ es.DocumentNode.traverseUpstream = function( node, callback ) { } }; +/** + * Find the common ancestor of two equal-depth nodes, and return the + * path from each node to the common ancestor. + * @param {es.DocumentNode} node1 + * @param {es.DocumentNode} node2 + * @returns {Object|Boolean} Object with keys 'commonAncestor', 'node1Path' and 'node2Path', + * or false if there is no common ancestor or if the nodes have unequal depth + */ +es.DocumentNode.getCommonAncestorPaths = function( node1, node2 ) { + var path1 = [], + path2 = [], + n1 = node1, + n2 = node2; + + // Move up from n1 and n2 simultaneously until we find the + // common ancestor + while ( n1 !== n2 ) { + // Add these nodes to their respective paths + path1.push( n1 ); + path2.push( n2 ); + // Move up + n1 = n1.getParent(); + n2 = n2.getParent(); + if ( n1 === null || n2 === null ) { + // Reached a root, so no common ancestor or unequal depth + return false; + } + } + + // If we got here, we've found the common ancestor, and because we did + // simultaneous traversal we also know node1 and node2 have the same depth. + return { 'commonAncestor': n1, 'node1Path': path1, 'node2Path': path2 }; +}; + /* Inheritance */ es.extendClass( es.DocumentNode, es.EventEmitter ); diff --git a/tests/es/es.DocumentNode.test.js b/tests/es/es.DocumentNode.test.js new file mode 100644 index 0000000000..8a6f9baf78 --- /dev/null +++ b/tests/es/es.DocumentNode.test.js @@ -0,0 +1,46 @@ +module( 'es/bases' ); + +test( 'es.DocumentNode.getCommonAncestorPaths', 14, function() { + var documentModel = es.DocumentModel.newFromPlainObject( esTest.obj ), result; + + var list = documentModel.children[1].children[0].children[0].children[1]; + result = es.DocumentNode.getCommonAncestorPaths( list, list ); + // Test 1 + ok( result.commonAncestor == list, 'same nodes (commonAncestor)' ); + // Test 2 + ok( es.compareArrays( result.node1Path, [] ), 'adjacent list items (node1Path)' ); + // Test 3 + ok( es.compareArrays( result.node2Path, [] ), 'adjacent list items (node2Path)' ); + + result = es.DocumentNode.getCommonAncestorPaths( list.children[0], list.children[1] ); + // Test 4 + ok( result.commonAncestor == list, 'adjacent list items (commonAncestor)' ); + // Test 5 + ok( es.compareArrays( result.node1Path, [ list.children[0] ] ), 'adjacent list items (node1Path)' ); + // Test 6 + ok( es.compareArrays( result.node2Path, [ list.children[1] ] ), 'adjacent list items (node2Path)' ); + + result = es.DocumentNode.getCommonAncestorPaths( list.children[0], list.children[2] ); + // Test 7 + ok( result.commonAncestor == list, 'non-adjacent sibling list items (commonAncestor)' ); + // Test 8 + ok( es.compareArrays( result.node1Path, [ list.children[0] ] ), 'non-adjacent sibling list items (node1Path)' ); + // Test 9 + ok( es.compareArrays( result.node2Path, [ list.children[2] ] ), 'non-adjacent sibling list items (node2Path)' ); + + result = es.DocumentNode.getCommonAncestorPaths( list.children[0].children[0], list.children[2].children[0] ); + // Test 10 + ok( result.commonAncestor == list, 'paragraphs inside list items (commonAncestor)' ); + // Test 11 + ok( es.compareArrays( result.node1Path, [ list.children[0].children[0], list.children[0] ] ), 'paragraphs inside list items (node1Path)' ); + // Test 12 + ok( es.compareArrays( result.node2Path, [ list.children[2].children[0], list.children[2] ] ), 'paragraphs inside list items (node2Path)' ); + + result = es.DocumentNode.getCommonAncestorPaths( list.children[0].children[0], list.children[2] ); + // Test 13 + equal( result, false, 'nodes of unequal depth' ); + + result = es.DocumentNode.getCommonAncestorPaths( list, es.DocumentModel.newFromPlainObject( esTest.obj ).children[1] ); + // Test 14 + equal( result, false, 'nodes in different trees' ); +} ); \ No newline at end of file diff --git a/tests/es/index.html b/tests/es/index.html index f230e722b4..96f1c938d4 100644 --- a/tests/es/index.html +++ b/tests/es/index.html @@ -46,5 +46,6 @@ +