mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 14:33:59 +00:00
Merge "(bug 45423) Create SurfaceFragment.isolate method"
This commit is contained in:
commit
1ef5c07a93
|
@ -690,3 +690,88 @@ ve.dm.SurfaceFragment.prototype.rewrapAllNodes = function () {
|
|||
// TODO: Implement
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Isolates the nodes in a fragment.
|
||||
*
|
||||
* The node selection is expanded to siblings and then these are isolated such that they are the
|
||||
* sole children of a parent element which can be placed anywhere.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.dm.SurfaceFragment} This fragment
|
||||
*/
|
||||
ve.dm.SurfaceFragment.prototype.isolate = function () {
|
||||
// Handle null fragment
|
||||
if ( !this.surface ) {
|
||||
return this;
|
||||
}
|
||||
var nodes, startSplitNode, endSplitNode, tx,
|
||||
startOffset, endOffset,
|
||||
startSplitRequired = false,
|
||||
endSplitRequired = false,
|
||||
startSplitNodes = [],
|
||||
endSplitNodes = [],
|
||||
fragment = this;
|
||||
|
||||
function createSplits( splitNodes, insertBefore ) {
|
||||
var i, length,
|
||||
startOffsetChange = 0, endOffsetChange = 0, data = [];
|
||||
for ( i = 0, length = splitNodes.length; i < length; i++ ) {
|
||||
data.unshift( { 'type': '/' + splitNodes[i].type } );
|
||||
data.push( splitNodes[i].getClonedElement() );
|
||||
|
||||
if ( insertBefore ) {
|
||||
startOffsetChange += 2;
|
||||
endOffsetChange += 2;
|
||||
}
|
||||
}
|
||||
|
||||
tx = ve.dm.Transaction.newFromInsertion( fragment.document, insertBefore ? startOffset : endOffset, data );
|
||||
fragment.surface.change( tx, !fragment.noAutoSelect && tx.translateRange( fragment.range ) );
|
||||
|
||||
startOffset += startOffsetChange;
|
||||
endOffset += endOffsetChange;
|
||||
}
|
||||
|
||||
nodes = this.document.selectNodes( this.range, 'siblings' );
|
||||
|
||||
// Find start split point, if required
|
||||
startSplitNode = nodes[0].node;
|
||||
startOffset = startSplitNode.getOuterRange().start;
|
||||
while ( startSplitNode.constructor.static.parentNodeTypes !== null ) {
|
||||
if ( startSplitNode.parent.indexOf( startSplitNode ) > 0 ) {
|
||||
startSplitRequired = true;
|
||||
}
|
||||
startSplitNode = startSplitNode.parent;
|
||||
if ( startSplitRequired ) {
|
||||
startSplitNodes.unshift(startSplitNode);
|
||||
} else {
|
||||
startOffset = startSplitNode.getOuterRange().start;
|
||||
}
|
||||
}
|
||||
|
||||
// Find end split point, if required
|
||||
endSplitNode = nodes[nodes.length - 1].node;
|
||||
endOffset = endSplitNode.getOuterRange().end;
|
||||
while ( endSplitNode.constructor.static.parentNodeTypes !== null ) {
|
||||
if ( endSplitNode.parent.indexOf( endSplitNode ) < endSplitNode.parent.getChildren().length - 1 ) {
|
||||
endSplitRequired = true;
|
||||
}
|
||||
endSplitNode = endSplitNode.parent;
|
||||
if ( endSplitRequired ) {
|
||||
endSplitNodes.unshift(endSplitNode);
|
||||
} else {
|
||||
endOffset = endSplitNode.getOuterRange().end;
|
||||
}
|
||||
}
|
||||
|
||||
if ( startSplitRequired ) {
|
||||
createSplits( startSplitNodes, true );
|
||||
}
|
||||
|
||||
if ( endSplitRequired ) {
|
||||
createSplits( endSplitNodes, false );
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
QUnit.module( 've.dm.SurfaceFragment' );
|
||||
|
||||
// Tests
|
||||
/* Tests */
|
||||
|
||||
QUnit.test( 'constructor', 8, function ( assert ) {
|
||||
var doc = new ve.dm.Document( ve.copyArray( ve.dm.example.data ) ),
|
||||
|
@ -225,3 +225,48 @@ QUnit.test( 'wrapAllNodes', 2, function ( assert ) {
|
|||
'wrapping nodes can add multiple levels of wrapping to a single element'
|
||||
);
|
||||
} );
|
||||
|
||||
function runIsolateTest( assert, range, expected, label ) {
|
||||
var doc = new ve.dm.Document( ve.copyArray( ve.dm.example.isolationData ) ),
|
||||
surface = new ve.dm.Surface( doc ),
|
||||
fragment = new ve.dm.SurfaceFragment( surface, range ),
|
||||
data;
|
||||
|
||||
data = ve.copyArray( doc.getFullData() );
|
||||
fragment.isolate();
|
||||
expected( data );
|
||||
|
||||
assert.deepEqual( doc.getFullData(), data, label );
|
||||
}
|
||||
|
||||
QUnit.test( 'isolate', 2, function ( assert ) {
|
||||
var rebuilt = { 'changed': { 'rebuilt': 1 } },
|
||||
created = { 'changed': { 'created': 1 } },
|
||||
createdAndRebuilt = { 'changed': { 'created': 1, 'rebuilt': 1 } };
|
||||
|
||||
runIsolateTest( assert, new ve.Range( 11, 21 ), function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 0, { 'type': '/list' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt });
|
||||
data.splice( 23, 0, { 'type': '/list' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt });
|
||||
}, 'isolating list item "Item 2"');
|
||||
|
||||
runIsolateTest( assert, new ve.Range( 88, 108 ), function( data ) {
|
||||
data[75].internal = rebuilt;
|
||||
data[76].internal = rebuilt;
|
||||
data[77].internal = rebuilt;
|
||||
data.splice( 88, 0,
|
||||
{ 'type': '/tableRow' },
|
||||
{ 'type': '/tableSection' },
|
||||
{ 'type': '/table' },
|
||||
{ 'type': 'table', 'internal': createdAndRebuilt },
|
||||
{ 'type': 'tableSection', 'attributes': { 'style': 'body' }, 'internal': createdAndRebuilt },
|
||||
{ 'type': 'tableRow', 'internal': created }
|
||||
);
|
||||
data.splice( 115, 0,
|
||||
{ 'type': '/tableSection' },
|
||||
{ 'type': '/table' },
|
||||
{ 'type': 'table', 'internal': createdAndRebuilt },
|
||||
{ 'type': 'tableSection', 'attributes': { 'style': 'body' }, 'internal': createdAndRebuilt }
|
||||
);
|
||||
}, 'isolating table cells "Cell 2" & "Cell 3"');
|
||||
} );
|
||||
|
|
Loading…
Reference in a new issue