Added test for getOffset map

And fixed ve.dm.DocumentFragment constructor to generate a correct offset map which creates references to branch nodes only

Change-Id: If9e515be0c63d272bfed9bf4da625a48edd36f48
This commit is contained in:
Trevor Parscal 2012-04-27 17:16:29 -07:00
parent fb9f6e0a3b
commit a774b3dbf6
5 changed files with 60 additions and 39 deletions

View file

@ -31,32 +31,36 @@ ve.dm.DocumentFragment = function( data, parentDocument ) {
var node,
textLength = 0,
inTextNode = false,
// TODO document this stack of stacks
// Stack of stacks, each containing a
stack = [[this.rootNode], []],
children,
openingIndex,
currentStack = stack[1],
parentStack = stack[0],
currentNode = this.rootNode;
for ( var i = 0, length = this.data.length; i < length; i++ ) {
// Set the node reference for this offset in the offset cache
// This looks simple, but there are three cases that result in the same thing:
// 1. data[i] is an opening, so offset i is before the opening, so we
// need to point to the parent of the opened element. currentNode
// will be set to the opened element later, but right now it's
// still set to the parent of the opened element.
// 2. data[i] is a closing, so offset i is before the closing, so we
// need to point to the closed element. currentNode will be set to
// the parent of the closed element later, but right now it's still
// set to the closed element
// 3. data[i] is content, so offset i is in the middle of an element,
// so obviously we need currentNode, which won't be changed by this
// iteration
this.offsetMap[i] = currentNode;
/*
* Set the node reference for this offset in the offset cache.
*
* This looks simple, but there are three cases that result in the same thing:
*
* 1. data[i] is an opening, so offset i is before the opening, so we need to point to the
* parent of the opened element. currentNode will be set to the opened element later,
* but right now its still set to the parent of the opened element.
* 2. data[i] is a closing, so offset i is before the closing, so we need to point to the
* closed element. currentNode will be set to the parent of the closed element later,
* but right now it's still set to the closed element.
* 3. data[i] is content, so offset i is in the middle of an element, so obviously we need
* currentNode, which won't be changed by this iteration.
*
* We want to populate the offsetMap with branches only, but we've just written the actual
* node that lives at this offset. So if it's a leaf node, change it to its parent.
*/
this.offsetMap[i] = currentNode.canHaveChildren() ?
currentNode : parentStack[parentStack.length - 1];
// Infer that if an item in the linear model has a type attribute than it must be an element
if ( this.data[i].type === undefined ) {
// Text node
// Text node opening
if ( !inTextNode ) {
// Create a lengthless text node
node = new ve.dm.TextNode();
@ -71,15 +75,16 @@ ve.dm.DocumentFragment = function( data, parentDocument ) {
// Track the length
textLength++;
} else {
// Text node closing
if ( inTextNode ) {
// Finish the text node by setting the length
currentNode.setLength( textLength );
// But the state variables back as they were
// Put the state variables back as they were
currentNode = parentStack[parentStack.length - 1];
inTextNode = false;
textLength = 0;
}
// Element open/close
if ( this.data[i].type.charAt( 0 ) != '/' ) {
// Branch or leaf node opening
// Create a childless node
@ -88,20 +93,22 @@ ve.dm.DocumentFragment = function( data, parentDocument ) {
node.setRoot( root );
// Put the childless node on the current inner stack
currentStack.push( node );
// Create a new inner stack for this node
parentStack = currentStack;
currentStack = [];
stack.push( currentStack );
if ( node.canHaveChildren() ) {
// Create a new inner stack for this node
parentStack = currentStack;
currentStack = [];
stack.push( currentStack );
}
currentNode = node;
} else {
// Branch or leaf node closing
// Pop this node's inner stack from the outer stack. It'll have all of the node's
// child nodes fully constructed
children = stack.pop();
currentStack = parentStack;
parentStack = stack[stack.length - 2];
// Attach the children to the node (but don't try and splice 0 children into a leaf)
if ( children.length ) {
if ( currentNode.canHaveChildren() ) {
// Pop this node's inner stack from the outer stack. It'll have all of the node's
// child nodes fully constructed
children = stack.pop();
currentStack = parentStack;
parentStack = stack[stack.length - 2];
// Attach the children to the node
ve.batchSplice( currentNode, 0, 0, children );
}
currentNode = parentStack[parentStack.length - 1];

View file

@ -34,3 +34,7 @@ ve.BranchNode.prototype.getChildren = function() {
ve.BranchNode.prototype.indexOf = function( node ) {
return ve.inArray( node, this.children );
};
ve.BranchNode.prototype.canHaveChildren = function() {
return true;
};

View file

@ -8,3 +8,7 @@
ve.LeafNode = function() {
//
};
ve.LeafNode.prototype.canHaveChildren = function() {
return false;
};

View file

@ -3,16 +3,22 @@ module( 've.dm.DocumentFragment' );
/* Tests */
test( 'getData', 1, function() {
var parentDocument = new ve.dm.Document(),
fragment = new ve.dm.DocumentFragment( ve.dm.example.data, parentDocument );
var fragment = new ve.dm.DocumentFragment( ve.dm.example.data );
deepEqual( fragment.getData(), ve.dm.example.data );
} );
/*
test( 'getOffsetMap', 1, function() {
//
test( 'getOffsetMap', 43, function() {
var fragment = new ve.dm.DocumentFragment( ve.dm.example.data ),
actual = fragment.getOffsetMap(),
expected = ve.dm.example.getOffsetMap( fragment.getRootNode() );
ok( actual.length === expected.length, 'offset map lengths match' );
for ( var i = 0; i < actual.length; i++ ) {
ok( actual[i] === expected[i], 'reference at offset ' + i );
}
} );
/*
test( 'getRootNode', 1, function() {
//
} );
@ -20,4 +26,4 @@ test( 'getRootNode', 1, function() {
test( 'getNodeFromOffset', 1, function() {
//
} );
*/
*/

View file

@ -188,7 +188,7 @@ ve.dm.example.tree = [
* @method
* @param {ve.dm.DocumentNode} root Document node to reference
*/
ve.dm.example.getOffsets = function( root ) {
ve.dm.example.getOffsetMap = function( root ) {
/**
* Looks up a value in a node tree.
*
@ -238,7 +238,7 @@ ve.dm.example.getOffsets = function( root ) {
// </p>
lookup( 1, 0, 0, 1, 0 ), // 16 - listItem
// <ul>
lookup( 1, 0, 0, 1 ), // 17 - list
lookup( 1, 0, 0, 1, 0, 1 ), // 17 - list
// <li>
lookup( 1, 0, 0, 1, 0, 1, 0 ), // 18 - listItem
// <p>