From f4fcf9663e003aff469d004a43f20784c37e36ab Mon Sep 17 00:00:00 2001 From: Trevor Parscal Date: Mon, 7 May 2012 15:14:37 -0700 Subject: [PATCH] Added documentation and tests for ve.dm.Document.selectNodes Also: * Broke node lookup method out into ve.dm.example.lookupNode * Added ve.dm.example.nodeSelectionEqual Change-Id: I437cdd3f3154d10782f03f78df6d5c457ecfc845 --- modules/ve2/dm/ve.dm.Document.js | 37 ++++--- tests/ve2/dm/ve.dm.Document.test.js | 17 +++ tests/ve2/dm/ve.dm.example.js | 159 ++++++++++++++++------------ 3 files changed, 132 insertions(+), 81 deletions(-) diff --git a/modules/ve2/dm/ve.dm.Document.js b/modules/ve2/dm/ve.dm.Document.js index 0eb224a9e4..6d78219f6d 100644 --- a/modules/ve2/dm/ve.dm.Document.js +++ b/modules/ve2/dm/ve.dm.Document.js @@ -65,9 +65,16 @@ ve.dm.Document.prototype.rebuildNodes = function( parent, index, numNodes, offse return nodes; }; -// TODO needs docs -// TODO needs more modes, probably. Current implementation is for mode=='leaf' -// TODO needs tests +/** + * Gets a list of nodes and the ranges within them that a selection of the document covers. + * + * @method + * @param {ve.Range} range Range within document to select nodes + * @param {String} [mode='leaf'] Type of selection to perform, currently only 'leaf' is supported + * @returns {Array} List of objects describing nodes in the selection and the ranges therein + * @throws 'Invalid start offset' if range.start is out of range + * @throws 'Invalid end offset' if range.end is out of range + */ ve.dm.Document.prototype.selectNodes = function( range, mode ) { var doc = this.getDocumentNode(), retval = [], @@ -84,19 +91,24 @@ ve.dm.Document.prototype.selectNodes = function( range, mode ) { endInside, startBetween, endBetween; - + + // TODO needs more modes, probably. Current implementation is only for mode == 'leaf' + if ( mode && mode !== 'leaf') { + throw 'Invalid mode: ' + mode; + } + if ( start < 0 || start > this.data.length ) { - throw 'Invalid start offset ' + start; + throw 'Invalid start offset: ' + start; } if ( end < 0 || end > this.data.length ) { - throw 'Invalid end offset ' + end; + throw 'Invalid end offset: ' + end; } - - if ( !doc.children || doc.children.length == 0 ) { + + if ( !doc.children || doc.children.length === 0 ) { return []; } left = doc.children[0].isWrapped() ? 1 : 0; - + while ( end >= left ) { node = currentFrame.node.children[currentFrame.index]; prevNode = currentFrame.node.children[currentFrame.index - 1]; @@ -112,7 +124,7 @@ ve.dm.Document.prototype.selectNodes = function( range, mode ) { // Is the end between node and nextNode or between node and the parent's closing? endBetween = end == right + 1 && node.isWrapped() && ( !nextNode || nextNode.isWrapped() ); - + if ( start == end && ( startBetween || endBetween ) ) { // Empty range in the parent, outside of any child return [ { @@ -120,7 +132,7 @@ ve.dm.Document.prototype.selectNodes = function( range, mode ) { 'range': new ve.Range( start, end ) } ]; } - + if ( startInside && endInside ) { if ( node.children && node.children.length ) { // Descend into node @@ -206,11 +218,10 @@ ve.dm.Document.prototype.selectNodes = function( range, mode ) { ( node.isWrapped() ? 1 : 0 ) + // Skip over nextNode's opening, if present ( nextNode.isWrapped() ? 1 : 0 ); - } else { // There is no next node, move up the stack stack.pop(); - if ( stack.length == 0 ) { + if ( stack.length === 0 ) { // This shouldn't be possible return retval; } diff --git a/tests/ve2/dm/ve.dm.Document.test.js b/tests/ve2/dm/ve.dm.Document.test.js index b44ec9c106..216903c9ac 100644 --- a/tests/ve2/dm/ve.dm.Document.test.js +++ b/tests/ve2/dm/ve.dm.Document.test.js @@ -18,3 +18,20 @@ test( 'rebuildNodes', 114, function() { // Test count: ( ( 4 tests x 21 branch nodes ) + ( 3 tests x 10 leaf nodes ) ) = 114 ve.dm.example.nodeTreeEqual( documentNode, ve.dm.example.tree ); } ); + +test( 'selectNodes', 7, function() { + var doc = new ve.dm.Document( ve.dm.example.data ), + documentNode = doc.getDocumentNode(), + lookup = ve.dm.example.lookupNode; + + // Test count: ( 1 test + ( 2 tests x 2 results ) + ( 2 test + 1 result with range ) ) = 7 + ve.dm.example.nodeSelectionEqual( + doc.selectNodes( new ve.Range( 0, 10 ) ), + [ + // heading + { 'node': lookup( documentNode, 0 ) }, + // table/row/cell/paragraph/text + { 'node': lookup( documentNode, 1, 0, 0, 0, 0 ), 'range': new ve.Range( 9, 10 ) } + ] + ); +} ); diff --git a/tests/ve2/dm/ve.dm.example.js b/tests/ve2/dm/ve.dm.example.js index 47c296d302..0b014fc934 100644 --- a/tests/ve2/dm/ve.dm.example.js +++ b/tests/ve2/dm/ve.dm.example.js @@ -261,6 +261,42 @@ ve.dm.example.nodeTreeEqual = function( a, b ) { } }; +/** + * Asserts that two node selections are equavilant. + * + * This will perform 1 assertion to check the number of results in the selection and then 2 + * assertions on each result plus 2 more for each result with a range. + * + * @method + */ +ve.dm.example.nodeSelectionEqual = function( a, b ) { + equal( a.length, b.length, 'length match' ); + for ( var i = 0; i < a.length; i++ ) { + ok( a[i].node === b[i].node, 'node match' ); + ok( 'range' in a[i] === 'range' in b[i], 'range existence match' ); + if ( a[i].range ) { + strictEqual( a[i].range.from, b[i].range.from, 'range from match' ); + strictEqual( a[i].range.to, b[i].range.to, 'range to match' ); + } + } +}; + +/** + * Looks up a value in a node tree. + * + * @method + * @param {ve.Node} root Root node to lookup from + * @param {Integer} [...] Index path + * @param {ve.Node} Node at given path + */ +ve.dm.example.lookupNode = function( root ) { + var node = root; + for ( var i = 1; i < arguments.length; i++ ) { + node = node.children[arguments[i]]; + } + return node; +}; + /** * Creates an offset map that references a node tree. * @@ -270,127 +306,114 @@ ve.dm.example.nodeTreeEqual = function( a, b ) { * @param {ve.dm.DocumentNode} root Document node to reference */ ve.dm.example.getOffsetMap = function( root ) { - /** - * Looks up a value in a node tree. - * - * @method - * @param {Integer} [...] Index path - * @param {ve.Node} Node at given path - */ - function lookup() { - var node = root; - for ( var i = 0; i < arguments.length; i++ ) { - node = node.children[arguments[i]]; - } - return node; - } + var lookup = ve.dm.example.lookupNode; return [ - lookup(), // 0 - document + lookup( root ), // 0 - document //

- lookup( 0 ), // 1 - heading + lookup( root, 0 ), // 1 - heading // a - lookup( 0 ), // 2 - heading + lookup( root, 0 ), // 2 - heading // b (bold) - lookup( 0 ), // 3 - heading + lookup( root, 0 ), // 3 - heading // c (italic) - lookup( 0 ), // 4 - heading + lookup( root, 0 ), // 4 - heading //

- lookup(), // 5 - document + lookup( root ), // 5 - document // - lookup( 1 ), // 6 - table + lookup( root, 1 ), // 6 - table // - lookup( 1, 0 ), // 7 - tableRow + lookup( root, 1, 0 ), // 7 - tableRow // - lookup( 1, 0 ), // 33 - tableRow + lookup( root, 1, 0 ), // 33 - tableRow // - lookup( 1 ), // 34 - table + lookup( root, 1 ), // 34 - table //
- lookup( 1, 0, 0 ), // 8 - tableCell + lookup( root, 1, 0, 0 ), // 8 - tableCell //

- lookup( 1, 0, 0, 0 ), // 9 - paragraph + lookup( root, 1, 0, 0, 0 ), // 9 - paragraph // d - lookup( 1, 0, 0, 0 ), // 10 - paragraph + lookup( root, 1, 0, 0, 0 ), // 10 - paragraph //

- lookup( 1, 0, 0 ), // 11 - tableCell + lookup( root, 1, 0, 0 ), // 11 - tableCell //
    - lookup( 1, 0, 0, 1 ), // 12 - list + lookup( root, 1, 0, 0, 1 ), // 12 - list //
  • - lookup( 1, 0, 0, 1, 0 ), // 13 - listItem + lookup( root, 1, 0, 0, 1, 0 ), // 13 - listItem //

    - lookup( 1, 0, 0, 1, 0, 0 ), // 14 - paragraph + lookup( root, 1, 0, 0, 1, 0, 0 ), // 14 - paragraph // e - lookup( 1, 0, 0, 1, 0, 0 ), // 15 - paragraph + lookup( root, 1, 0, 0, 1, 0, 0 ), // 15 - paragraph //

    - lookup( 1, 0, 0, 1, 0 ), // 16 - listItem + lookup( root, 1, 0, 0, 1, 0 ), // 16 - listItem //
      - lookup( 1, 0, 0, 1, 0, 1 ), // 17 - list + lookup( root, 1, 0, 0, 1, 0, 1 ), // 17 - list //
    • - lookup( 1, 0, 0, 1, 0, 1, 0 ), // 18 - listItem + lookup( root, 1, 0, 0, 1, 0, 1, 0 ), // 18 - listItem //

      - lookup( 1, 0, 0, 1, 0, 1, 0, 0 ), // 19 - paragraph + lookup( root, 1, 0, 0, 1, 0, 1, 0, 0 ), // 19 - paragraph // f - lookup( 1, 0, 0, 1, 0, 1, 0, 0 ), // 20 - paragraph + lookup( root, 1, 0, 0, 1, 0, 1, 0, 0 ), // 20 - paragraph //

      - lookup( 1, 0, 0, 1, 0, 1, 0 ), // 21 - listItem + lookup( root, 1, 0, 0, 1, 0, 1, 0 ), // 21 - listItem //
    • - lookup( 1, 0, 0, 1, 0, 1 ), // 22 - list + lookup( root, 1, 0, 0, 1, 0, 1 ), // 22 - list //
    - lookup( 1, 0, 0, 1, 0 ), // 23 - listItem + lookup( root, 1, 0, 0, 1, 0 ), // 23 - listItem //
  • - lookup( 1, 0, 0, 1 ), // 24 - list + lookup( root, 1, 0, 0, 1 ), // 24 - list //
- lookup( 1, 0, 0 ), // 25 - tableCell + lookup( root, 1, 0, 0 ), // 25 - tableCell //
    - lookup( 1, 0, 0, 2 ), // 26 - list + lookup( root, 1, 0, 0, 2 ), // 26 - list //
  • - lookup( 1, 0, 0, 2, 0 ), // 27 - listItem + lookup( root, 1, 0, 0, 2, 0 ), // 27 - listItem //

    - lookup( 1, 0, 0, 2, 0, 0 ), // 28 - paragraph + lookup( root, 1, 0, 0, 2, 0, 0 ), // 28 - paragraph // g - lookup( 1, 0, 0, 2, 0, 0 ), // 29 - paragraph + lookup( root, 1, 0, 0, 2, 0, 0 ), // 29 - paragraph //

    - lookup( 1, 0, 0, 2, 0 ), // 30 - listItem + lookup( root, 1, 0, 0, 2, 0 ), // 30 - listItem //
  • - lookup( 1, 0, 0, 2 ), // 31 - list + lookup( root, 1, 0, 0, 2 ), // 31 - list //
- lookup( 1, 0, 0 ), // 32 - tableCell + lookup( root, 1, 0, 0 ), // 32 - tableCell //
- lookup(), // 35- document + lookup( root ), // 35- document //
-		lookup( 2 ), // 36 - preformatted
+		lookup( root, 2 ), // 36 - preformatted
 		// h
-		lookup( 2 ), // 37 - preformatted
+		lookup( root, 2 ), // 37 - preformatted
 		// 
-		lookup( 2 ), // 38 - preformatted
+		lookup( root, 2 ), // 38 - preformatted
 		// 
-		lookup( 2 ), // 39 - preformatted
+		lookup( root, 2 ), // 39 - preformatted
 		// i
-		lookup( 2 ), // 40 - preformatted
+		lookup( root, 2 ), // 40 - preformatted
 		// 
- lookup(), // 41 - document + lookup( root ), // 41 - document //
- lookup( 3 ), // 42 - definitionList + lookup( root, 3 ), // 42 - definitionList //
- lookup( 3, 0 ), // 43 - definitionListItem + lookup( root, 3, 0 ), // 43 - definitionListItem //

- lookup( 3, 0, 0 ), // 44 - paragraph + lookup( root, 3, 0, 0 ), // 44 - paragraph // f - lookup( 3, 0, 0 ), // 45 - paragraph + lookup( root, 3, 0, 0 ), // 45 - paragraph //

- lookup( 3, 0 ), // 46 - definitionListItem + lookup( root, 3, 0 ), // 46 - definitionListItem //
- lookup( 3 ), // 47 - definitionList + lookup( root, 3 ), // 47 - definitionList //
- lookup( 3, 1 ), // 48 - definitionListItem + lookup( root, 3, 1 ), // 48 - definitionListItem //

- lookup( 3, 1, 0 ), // 49 - paragraph + lookup( root, 3, 1, 0 ), // 49 - paragraph // f - lookup( 3, 1, 0 ), // 50 - paragraph + lookup( root, 3, 1, 0 ), // 50 - paragraph //

- lookup( 3, 1 ), // 51 - definitionListItem + lookup( root, 3, 1 ), // 51 - definitionListItem //
- lookup( 3 ), // 52 - definitionList + lookup( root, 3 ), // 52 - definitionList //
- lookup() // 53 - document + lookup( root ) // 53 - document ]; };