ve.dm.SurfaceFragment: Implement wrapNodes and wrapAllNodes

Change-Id: I378f0aad0286a6c90adeb4602a57d6617154e8b6
This commit is contained in:
Trevor Parscal 2012-08-24 15:25:37 -07:00 committed by Timo Tijhof
parent 8a725f8f92
commit eabe5e6f61
4 changed files with 120 additions and 2 deletions

View file

@ -75,6 +75,7 @@ class VisualEditorHooks {
'dm/ve.dm.Transaction.test.js',
'dm/ve.dm.TransactionProcessor.test.js',
'dm/ve.dm.Surface.test.js',
'dm/ve.dm.SurfaceFragment.test.js',
// VisualEditor ContentEditable Tests
'ce/ve.ce.test.js',
'ce/ve.ce.Document.test.js',

View file

@ -169,6 +169,7 @@ $wgResourceModules += array(
've/dm/ve.dm.TransactionProcessor.js',
've/dm/ve.dm.Transaction.js',
've/dm/ve.dm.Surface.js',
've/dm/ve.dm.SurfaceFragment.js',
've/dm/ve.dm.Document.js',
've/dm/ve.dm.DocumentSynchronizer.js',
've/dm/ve.dm.Converter.js',

View file

@ -460,6 +460,13 @@ ve.dm.SurfaceFragment.prototype.convertNodes = function ( type, attr ) {
* A wrapper object is a linear model element; a plain object containing a type property and an
* optional attributes property.
*
* Example:
* // fragment is a selection of: <p>a</p><p>b</p>
* fragment.wrapNodes(
* [{ 'type': 'list', 'attributes': { 'style': 'bullet' } }, { 'type': 'listItem' }]
* )
* // fragment is now a selection of: <ul><li><p>a</p></li></ul><ul><li><p>b</p></li></ul>
*
* @method
* @param {Object|Object[]} wrapper Wrapper object, or array of wrapper objects (see above)
* @param {String} wrapper.type Node type of wrapper
@ -471,7 +478,12 @@ ve.dm.SurfaceFragment.prototype.wrapNodes = function ( wrapper ) {
if ( !this.surface ) {
return this;
}
// TODO: Implement
if ( !ve.isArray( wrapper ) ) {
wrapper = [wrapper];
}
var tx = ve.dm.Transaction.newFromWrap( this.document, this.range, [], [], [], wrapper );
this.range = tx.translateRange( this.range );
this.surface.change( tx, this.autoSelect && this.range );
return this;
};
@ -519,6 +531,13 @@ ve.dm.SurfaceFragment.prototype.rewrapNodes = function ( type, wrapper ) {
* A wrapper object is a linear model element; a plain object containing a type property and an
* optional attributes property.
*
* Example:
* // fragment is a selection of: <p>a</p><p>b</p>
* fragment.wrapAllNodes(
* [{ 'type': 'list', 'attributes': { 'style': 'bullet' } }, { 'type': 'listItem' }]
* )
* // fragment is now a selection of: <ul><li><p>a</p><p>b</p></li></ul>
*
* @method
* @param {Object|Object[]} wrapper Wrapper object, or array of wrapper objects (see above)
* @param {String} wrapper.type Node type of wrapper
@ -530,7 +549,12 @@ ve.dm.SurfaceFragment.prototype.wrapAllNodes = function ( wrapper ) {
if ( !this.surface ) {
return this;
}
// TODO: Implement
if ( !ve.isArray( wrapper ) ) {
wrapper = [wrapper];
}
var tx = ve.dm.Transaction.newFromWrap( this.document, this.range, [], wrapper, [], [] );
this.range = tx.translateRange( this.range );
this.surface.change( tx, this.autoSelect && this.range );
return this;
};

View file

@ -112,3 +112,95 @@ QUnit.test( 'insertContent', 3, function ( assert ) {
'strings get converted into data when inserting content'
);
} );
QUnit.test( 'wrapNodes', 2, function ( assert ) {
var doc = new ve.dm.Document( ve.dm.example.data ),
surface = new ve.dm.Surface( doc ),
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 55, 61 ) );
// Make 2 paragraphs into 2 lists of 1 item each
fragment.wrapNodes(
[{ 'type': 'list', 'attributes': { 'style': 'bullet' } }, { 'type': 'listItem' }]
);
assert.deepEqual(
doc.getData( new ve.Range( 55, 69 ) ),
[
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
{ 'type': 'listItem' },
{ 'type': 'paragraph' },
'l',
{ 'type': '/paragraph' },
{ 'type': '/listItem' },
{ 'type': '/list' },
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
{ 'type': 'listItem' },
{ 'type': 'paragraph' },
'm',
{ 'type': '/paragraph' },
{ 'type': '/listItem' },
{ 'type': '/list' }
],
'wrapping nodes can add multiple levels of wrapping to multiple elements'
);
// Make a 1 paragraph into 1 list with 1 item
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 9, 12 ) );
fragment.wrapNodes(
[{ 'type': 'list', 'attributes': { 'style': 'bullet' } }, { 'type': 'listItem' }]
);
assert.deepEqual(
doc.getData( new ve.Range( 9, 16 ) ),
[
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
{ 'type': 'listItem' },
{ 'type': 'paragraph' },
'd',
{ 'type': '/paragraph' },
{ 'type': '/listItem' },
{ 'type': '/list' }
],
'wrapping nodes can add multiple levels of wrapping to a single element'
);
} );
QUnit.test( 'wrapAllNodes', 2, function ( assert ) {
var doc = new ve.dm.Document( ve.dm.example.data ),
surface = new ve.dm.Surface( doc ),
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 55, 61 ) );
// Make 2 paragraphs into 1 lists of 1 item with 2 paragraphs
fragment.wrapAllNodes(
[{ 'type': 'list', 'attributes': { 'style': 'bullet' } }, { 'type': 'listItem' }]
);
assert.deepEqual(
doc.getData( new ve.Range( 55, 65 ) ),
[
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
{ 'type': 'listItem' },
{ 'type': 'paragraph' },
'l',
{ 'type': '/paragraph' },
{ 'type': 'paragraph' },
'm',
{ 'type': '/paragraph' },
{ 'type': '/listItem' },
{ 'type': '/list' }
],
'wrapping nodes can add multiple levels of wrapping to multiple elements'
);
// Make a 1 paragraph into 1 list with 1 item
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 9, 12 ) );
fragment.wrapAllNodes(
[{ 'type': 'list', 'attributes': { 'style': 'bullet' } }, { 'type': 'listItem' }]
);
assert.deepEqual(
doc.getData( new ve.Range( 9, 16 ) ),
[
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
{ 'type': 'listItem' },
{ 'type': 'paragraph' },
'd',
{ 'type': '/paragraph' },
{ 'type': '/listItem' },
{ 'type': '/list' }
],
'wrapping nodes can add multiple levels of wrapping to a single element'
);
} );