Merge branch 'dmrewrite' of ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor into dmrewrite

This commit is contained in:
Christian Williams 2012-05-10 16:29:37 -07:00
commit 6e7c02a126
6 changed files with 161 additions and 43 deletions

View file

@ -21,8 +21,10 @@ ve.ce.BranchNode = function( type, model, $element ) {
// Events
this.model.addListenerMethod( this, 'splice', 'onSplice' );
this.$.addClass("ve-ce-branch");
// DOM Changes
this.$.addClass( 've-ce-branchNode' );
// Initialization
if ( model.getChildren().length ) {
this.onSplice.apply( this, [0, 0].concat( model.getChildren() ) );
}

View file

@ -15,7 +15,10 @@ ve.ce.LeafNode = function( type, model, $element ) {
ve.LeafNode.call( this );
ve.ce.Node.call( this, type, model, $element );
this.$.addClass("ve-ce-leaf");
// DOM Changes
if ( model.isWrapped() ) {
this.$.addClass( 've-ce-leafNode' );
}
};
/* Inheritance */

View file

@ -34,6 +34,8 @@ ve.Document.prototype.getDocumentNode = function() {
* @returns {Array} List of objects describing nodes in the selection and the ranges therein
* 'node': Reference to a ve.dm.Node
* 'range': ve.Range, missing if the entire node is covered
* 'index': Index of the node in its parent
* 'nodeRange': Range covering the inside of the entire node
* @throws 'Invalid start offset' if range.start is out of range
* @throws 'Invalid end offset' if range.end is out of range
*/
@ -42,13 +44,18 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
retval = [],
start = range.start,
end = range.end,
stack = [ { 'node': doc, 'index': 0 } ],
stack = [ {
'node': doc,
'index': 0,
'startOffset': 0
} ],
node,
prevNode,
nextNode,
left,
right,
currentFrame = stack[0],
parentFrame,
startInside,
endInside,
startBetween,
@ -68,9 +75,15 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
}
if ( !doc.children || doc.children.length === 0 ) {
return [];
// Document has no children. This is weird
return [ {
'node': doc,
'range': new ve.Range( start, end ),
'index': 0,
'parentRange': new ve.Range( 0, doc.getLength() )
} ];
}
// TODO we could find the start more efficiently using the offset map
// TODO maybe we could find the start more efficiently using the offset map
left = doc.children[0].isWrapped() ? 1 : 0;
while ( end >= left ) {
@ -89,9 +102,14 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
if ( start == end && ( startBetween || endBetween ) ) {
// Empty range in the parent, outside of any child
parentFrame = stack[stack.length - 2];
return [ {
'node': currentFrame.node,
'range': new ve.Range( start, end )
'range': new ve.Range( start, end ),
'index': parentFrame.index,
'nodeRange': new ve.Range( parentFrame.startOffset,
parentFrame.startOffset + currentFrame.node.getLength()
)
} ];
} else if ( startBetween ) {
// start is between the previous sibling and node
@ -99,7 +117,11 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
if ( mode == 'leaves' && node.children && node.children.length ) {
// Descend into node
currentFrame = { 'node': node, 'index': 0 };
currentFrame = {
'node': node,
'index': 0,
'startOffset': left
};
stack.push( currentFrame );
if ( node.children[0].isWrapped() ) {
left++;
@ -108,14 +130,22 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
continue;
} else {
// All of node is covered
// TODO should this have a range or not?
retval.push( { 'node': node } );
retval.push( {
'node': node,
// no 'range' because the entire node is covered
'index': currentFrame.index,
'nodeRange': new ve.Range( left, right )
} );
startFound = true;
}
} else if ( startInside && endInside ) {
if ( node.children && node.children.length ) {
// Descend into node
currentFrame = { 'node': node, 'index': 0 };
currentFrame = {
'node': node,
'index': 0,
'startOffset': left
};
stack.push( currentFrame );
// If the first child of node has an opening, skip over it
if ( node.children[0].isWrapped() ) {
@ -126,14 +156,20 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
// node is a leaf node and the range is entirely inside it
return [ {
'node': node,
'range': new ve.Range( left, right )
'range': new ve.Range( left, right ),
'index': currentFrame.index,
'nodeRange': new ve.Range( left, right )
} ];
}
} else if ( startInside ) {
if ( mode == 'leaves' && node.children && node.children.length ) {
// node is a branch node and the start is inside it
// Descend into it
currentFrame = { 'node': node, 'index': 0 };
currentFrame = {
'node': node,
'index': 0,
'startOffset': left
};
stack.push( currentFrame );
if ( node.children[0].isWrapped() ) {
left++;
@ -144,7 +180,9 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
// Add to retval and keep going
retval.push( {
'node': node,
'range': new ve.Range( start, right )
'range': new ve.Range( start, right ),
'index': currentFrame.index,
'nodeRange': new ve.Range( left, right )
} );
startFound = true;
}
@ -156,7 +194,11 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
if ( mode == 'leaves' && node.children && node.children.length ) {
// Descend into node
currentFrame = { 'node': node, 'index': 0 };
currentFrame = {
'node': node,
'index': 0,
'startOffset': left
};
stack.push( currentFrame );
if ( node.children[0].isWrapped() ) {
left++;
@ -164,15 +206,23 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
continue;
} else {
// All of node is covered
// TODO should this have a range or not?
retval.push( { 'node': node } );
retval.push( {
'node': node,
// no 'range' because the entire node is covered
'index': currentFrame.index,
'nodeRange': new ve.Range( left, right )
} );
return retval;
}
} else if ( endInside ) {
if ( mode == 'leaves' && node.children && node.children.length ) {
// node is a branch node and the end is inside it
// Descend into it
currentFrame = { 'node': node, 'index': 0 };
currentFrame = {
'node': node,
'index': 0,
'startOffset': left
};
stack.push( currentFrame );
if ( node.children[0].isWrapped() ) {
left++;
@ -183,7 +233,9 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
// Add to retval and return
retval.push( {
'node': node,
'range': new ve.Range( left, end )
'range': new ve.Range( left, end ),
'index': currentFrame.index,
'nodeRange': new ve.Range( left, right )
} );
return retval;
}
@ -195,7 +247,11 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
if ( mode == 'leaves' && node.children && node.children.length ) {
// Descend into node
currentFrame = { 'node': node, 'index': 0 };
currentFrame = {
'node': node,
'index': 0,
'startOffset': left
};
stack.push( currentFrame );
if ( node.children[0].isWrapped() ) {
left++;
@ -203,8 +259,12 @@ ve.Document.prototype.selectNodes = function( range, mode ) {
continue;
} else {
// All of node is covered
// TODO should this have a range or not?
retval.push( { 'node': node } );
retval.push( {
'node': node,
// no 'range' because the entire node is covered
'index': currentFrame.index,
'nodeRange': new ve.Range( left, right )
} );
}
}

View file

@ -37,7 +37,7 @@ test( 'canHaveGrandchildren', 1, function() {
test( 'updateDomWrapper', 3, function() {
var node = new ve.ce.BranchNodeStub( new ve.dm.BranchNodeStub( [], { 'type': 'a' } ) );
// Add classes and content to the node
node.$.addClass( 'test' ).text( 'hello' );
node.$.attr( 'class', 'test' ).text( 'hello' );
// Modify attribute
node.getModel().attributes.type = 'b';
node.updateDomWrapper( 'type' );

View file

@ -7,12 +7,6 @@ ve.ce.LeafNodeStub = function( model ) {
ve.ce.LeafNode.call( this, 'leaf-stub', model );
};
ve.ce.LeafNodeStub.rules = {
'canHaveChildren': false,
'canHaveGrandchildren': false,
'isWrapped': true
};
ve.extendClass( ve.ce.LeafNodeStub, ve.ce.LeafNode );
ve.ce.factory.register( 'leaf-stub', ve.ce.LeafNodeStub );

View file

@ -12,58 +12,115 @@ ve.example.getSelectNodesCases = function( doc ) {
'actual': doc.selectNodes( new ve.Range( 0, 3 ), 'leaves' ),
'expected': [
// heading/text - partial leaf results have ranges with global offsets
{ 'node': lookup( documentNode, 0, 0 ), 'range': new ve.Range( 1, 3 ) }
{
'node': lookup( documentNode, 0, 0 ),
'range': new ve.Range( 1, 3 ),
'index': 0,
'nodeRange': new ve.Range( 1, 4 )
}
]
},
{
'actual': doc.selectNodes( new ve.Range( 0, 10 ), 'leaves' ),
'expected': [
// heading/text - full coverage leaf nodes do not have ranges
{ 'node': lookup( documentNode, 0, 0 ) },
{
'node': lookup( documentNode, 0, 0 ),
'index': 0,
'nodeRange': new ve.Range( 1, 4 )
},
// table/tableRow/tableCell/paragraph/text - leaf nodes from different levels
{ 'node': lookup( documentNode, 1, 0, 0, 0, 0 ) }
{
'node': lookup( documentNode, 1, 0, 0, 0, 0 ),
'index': 0,
'nodeRange': new ve.Range( 9, 10 )
}
]
},
{
'actual': doc.selectNodes( new ve.Range( 28, 41 ), 'leaves' ),
'expected': [
// table/tableRow/tableCell/list/listItem/paragraph/text
{ 'node': lookup( documentNode, 1, 0, 0, 2, 0, 0, 0 ) },
{
'node': lookup( documentNode, 1, 0, 0, 2, 0, 0, 0 ),
'index': 0,
'nodeRange': new ve.Range( 28, 29 )
},
// preformatted/text
{ 'node': lookup( documentNode, 2, 0 ) },
{
'node': lookup( documentNode, 2, 0 ),
'index': 0,
'nodeRange': new ve.Range( 36, 37 )
},
// preformatted/image - leaf nodes that are not text nodes
{ 'node': lookup( documentNode, 2, 1 ) },
{
'node': lookup( documentNode, 2, 1 ),
'index': 1,
'nodeRange': new ve.Range( 38, 38 )
},
// preformatted/text
{ 'node': lookup( documentNode, 2, 2 ) }
{
'node': lookup( documentNode, 2, 2 ),
'index': 2,
'nodeRange': new ve.Range( 39, 40 )
}
]
},
{
'actual': doc.selectNodes( new ve.Range( 2, 15 ), 'siblings' ),
'expected': [
// heading
{ 'node': lookup( documentNode, 0 ), 'range': new ve.Range( 2, 4 ) },
{
'node': lookup( documentNode, 0 ),
'range': new ve.Range( 2, 4 ),
'index': 0,
'nodeRange': new ve.Range( 1, 4 )
},
// table
{ 'node': lookup( documentNode, 1 ), 'range': new ve.Range( 6, 15 ) }
{
'node': lookup( documentNode, 1 ),
'range': new ve.Range( 6, 15 ),
'index': 1,
'nodeRange': new ve.Range( 6, 34 )
}
]
},
{
'actual': doc.selectNodes( new ve.Range( 2, 49 ), 'siblings' ),
'expected': [
// heading
{ 'node': lookup( documentNode, 0 ), 'range': new ve.Range( 2, 4 ) },
{
'node': lookup( documentNode, 0 ),
'range': new ve.Range( 2, 4 ),
'index': 0,
'nodeRange': new ve.Range( 1, 4 )
},
// table
{ 'node': lookup( documentNode, 1 ) },
{
'node': lookup( documentNode, 1 ),
'index': 1,
'nodeRange': new ve.Range( 6, 34 )
},
// preformatted
{ 'node': lookup( documentNode, 2 ) },
{
'node': lookup( documentNode, 2 ),
'index': 2,
'nodeRange': new ve.Range( 36, 40 )
},
// definitionList
{ 'node': lookup( documentNode, 3 ), 'range': new ve.Range( 42, 49 ) }
{
'node': lookup( documentNode, 3 ),
'range': new ve.Range( 42, 49 ),
'index': 3,
'nodeRange': new ve.Range( 42, 52 )
}
]
}
];
};
/**
* Asserts that two node trees are equavilant.
* Asserts that two node trees are equivalent.
*
* This will perform 4 assertions on each branch node and 3 assertions on each leaf node.
*
@ -82,7 +139,7 @@ ve.example.nodeTreeEqual = function( a, b ) {
};
/**
* Asserts that two node selections are equavilant.
* Asserts that two node selections are equivalent.
*
* This will perform 1 assertion to check the number of results in the selection and then 2
* assertions on each result
@ -98,6 +155,8 @@ ve.example.nodeSelectionEqual = function( a, b ) {
} else {
strictEqual( 'range' in a[i], 'range' in b[i], 'range existence match' );
}
deepEqual( a[i].index, b[i].index, 'index match' );
deepEqual( a[i].nodeRange, b[i].nodeRange, 'nodeRange match' );
}
};