mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-27 15:50:29 +00:00
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:
parent
fb9f6e0a3b
commit
a774b3dbf6
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -8,3 +8,7 @@
|
|||
ve.LeafNode = function() {
|
||||
//
|
||||
};
|
||||
|
||||
ve.LeafNode.prototype.canHaveChildren = function() {
|
||||
return false;
|
||||
};
|
||||
|
|
|
@ -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() {
|
||||
//
|
||||
} );
|
||||
*/
|
||||
*/
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue