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 @@
+