mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 06:24:08 +00:00
Change ve.dm.DocumentSlice to a mixin to ve.dm.LinearData
Document slice only ever contained linear data, with extra functionality to preserve the range. It pre-dated LinearData, but now we should refactor it to reflect its purpose. Change-Id: Ifc908f7526c83a43a51372c8d2494d7260e7facd
This commit is contained in:
parent
1957eb3e28
commit
5c31d3215b
|
@ -37,7 +37,6 @@
|
|||
"ve.dm.ModelRegistry",
|
||||
"ve.dm.Converter",
|
||||
"ve.dm.DataString",
|
||||
"ve.dm.DocumentSlice",
|
||||
"ve.dm.DocumentSynchronizer",
|
||||
"ve.dm.IndexValueStore",
|
||||
"ve.dm.NodeFactory",
|
||||
|
@ -66,7 +65,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Linear data",
|
||||
"classes": ["ve.dm.*LinearData"]
|
||||
"classes": ["ve.dm.*LinearData*"]
|
||||
},
|
||||
{
|
||||
"name": "Models",
|
||||
|
|
|
@ -294,11 +294,11 @@ $wgResourceModules += array(
|
|||
've/dm/ve.dm.DataString.js',
|
||||
've/dm/ve.dm.Document.js',
|
||||
've/dm/ve.dm.LinearData.js',
|
||||
've/dm/ve.dm.DocumentSlice.js',
|
||||
've/dm/ve.dm.DocumentSynchronizer.js',
|
||||
've/dm/ve.dm.IndexValueStore.js',
|
||||
've/dm/ve.dm.Converter.js',
|
||||
|
||||
've/dm/lineardata/ve.dm.SlicedLinearData.js',
|
||||
've/dm/lineardata/ve.dm.ElementLinearData.js',
|
||||
've/dm/lineardata/ve.dm.MetaLinearData.js',
|
||||
|
||||
|
|
|
@ -147,10 +147,10 @@ $html = file_get_contents( $page );
|
|||
<script src="../../modules/ve/dm/ve.dm.DataString.js"></script>
|
||||
<script src="../../modules/ve/dm/ve.dm.Document.js"></script>
|
||||
<script src="../../modules/ve/dm/ve.dm.LinearData.js"></script>
|
||||
<script src="../../modules/ve/dm/ve.dm.DocumentSlice.js"></script>
|
||||
<script src="../../modules/ve/dm/ve.dm.DocumentSynchronizer.js"></script>
|
||||
<script src="../../modules/ve/dm/ve.dm.IndexValueStore.js"></script>
|
||||
<script src="../../modules/ve/dm/ve.dm.Converter.js"></script>
|
||||
<script src="../../modules/ve/dm/lineardata/ve.dm.SlicedLinearData.js"></script>
|
||||
<script src="../../modules/ve/dm/lineardata/ve.dm.ElementLinearData.js"></script>
|
||||
<script src="../../modules/ve/dm/lineardata/ve.dm.MetaLinearData.js"></script>
|
||||
<script src="../../modules/ve/dm/nodes/ve.dm.GeneratedContentNode.js"></script>
|
||||
|
|
|
@ -655,7 +655,7 @@ ve.ce.Surface.prototype.onCopy = function ( e ) {
|
|||
clipboardIndex, clipboardItem,
|
||||
scrollTop,
|
||||
view = this,
|
||||
slice = this.documentView.model.getSlice( this.model.getSelection() ),
|
||||
slice = this.documentView.model.getSlicedLinearData( this.model.getSelection() ),
|
||||
clipboardData = e.originalEvent.clipboardData,
|
||||
$window = $( ve.Element.getWindow( this.$$.context ) );
|
||||
|
||||
|
@ -666,7 +666,7 @@ ve.ce.Surface.prototype.onCopy = function ( e ) {
|
|||
|
||||
ve.dm.converter.store = this.documentView.model.getStore();
|
||||
ve.dm.converter.internalList = this.documentView.model.getInternalList();
|
||||
ve.dm.converter.getDomSubtreeFromData( slice.getBalancedData(), this.$pasteTarget[0] );
|
||||
ve.dm.converter.getDomSubtreeFromData( slice.getData(), this.$pasteTarget[0] );
|
||||
|
||||
clipboardItem = { 'data': slice, 'hash': null };
|
||||
clipboardIndex = this.clipboard.push( clipboardItem ) - 1;
|
||||
|
@ -813,7 +813,8 @@ ve.ce.Surface.prototype.afterPaste = function () {
|
|||
if ( !beforePasteData.plain ) {
|
||||
beforePasteData.plain = this.$pasteTarget.text();
|
||||
}
|
||||
slice = new ve.dm.DocumentSlice(
|
||||
slice = new ve.dm.ElementLinearDataSlice(
|
||||
new ve.dm.IndexValueStore(),
|
||||
ve.splitClusters(
|
||||
// TODO: handle plain text line breaks better
|
||||
beforePasteData.plain.replace( /\n+/gm, ' ' )
|
||||
|
@ -825,7 +826,7 @@ ve.ce.Surface.prototype.afterPaste = function () {
|
|||
// Try to paste in the orignal data
|
||||
// Take a copy to prevent the data being annotated a second time in the catch block
|
||||
// and to prevent actions in the data model affecting view.clipboard
|
||||
pasteData = ve.copy( slice.getData() );
|
||||
pasteData = ve.copy( slice.getOriginalData() );
|
||||
|
||||
// Annotate
|
||||
ve.dm.Document.addAnnotationsToData( pasteData, this.model.getInsertionAnnotations() );
|
||||
|
@ -839,7 +840,7 @@ ve.ce.Surface.prototype.afterPaste = function () {
|
|||
} catch ( err ) {
|
||||
// If that fails, balance the data before pasting
|
||||
// Take a copy to prevent actions in the data model affecting view.clipboard
|
||||
pasteData = ve.copy( slice.getBalancedData() );
|
||||
pasteData = ve.copy( slice.getData() );
|
||||
|
||||
// Annotate
|
||||
ve.dm.Document.addAnnotationsToData( pasteData, this.model.getInsertionAnnotations() );
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*!
|
||||
* VisualEditor ElementLinearData class.
|
||||
* VisualEditor ElementLinearData classes.
|
||||
*
|
||||
* Class containing element linear data and an index-value store.
|
||||
*
|
||||
|
@ -778,3 +778,27 @@ ve.dm.ElementLinearData.prototype.remapInteralListIndexes = function ( mapping )
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sliced element linear data storage
|
||||
*
|
||||
* @class
|
||||
* @extends ve.dm.ElementLinearData
|
||||
* @mixins ve.dm.SlicedLinearData
|
||||
* @constructor
|
||||
* @param {ve.dm.IndexValueStore} store Index-value store
|
||||
* @param {Array} [data] Linear data
|
||||
* @param {ve.Range} [range] Original context within data
|
||||
*/
|
||||
ve.dm.ElementLinearDataSlice = function VeDmElementLinearDataSlice( store, data, range ) {
|
||||
ve.dm.ElementLinearData.call( this, store, data );
|
||||
|
||||
// Mixins
|
||||
ve.dm.SlicedLinearData.call( this, range );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
ve.inheritClass( ve.dm.ElementLinearDataSlice, ve.dm.ElementLinearData );
|
||||
|
||||
ve.mixinClass( ve.dm.ElementLinearDataSlice, ve.dm.SlicedLinearData );
|
||||
|
|
58
modules/ve/dm/lineardata/ve.dm.SlicedLinearData.js
Normal file
58
modules/ve/dm/lineardata/ve.dm.SlicedLinearData.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*!
|
||||
* VisualEditor DataModel SlicedLinearData class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sliced linear data storage.
|
||||
*
|
||||
* @abstract
|
||||
* @constructor
|
||||
* @param {ve.Range} [range] Original context within data
|
||||
*/
|
||||
ve.dm.SlicedLinearData = function VeDmSlicedLinearData( range ) {
|
||||
// Clone data
|
||||
this.data = ve.copy( this.data );
|
||||
|
||||
// Properties
|
||||
this.range = range || new ve.Range( 0, this.data.length );
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Get the range representing the original context within the data
|
||||
*
|
||||
* @returns {ve.Range} Original context within data
|
||||
*/
|
||||
ve.dm.SlicedLinearData.prototype.getRange = function () {
|
||||
return this.range;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the original data excluding any balancing data that was added.
|
||||
*
|
||||
* @method
|
||||
* @param {number} [offset] Offset to get data from
|
||||
* @returns {Object|Array} Data from index, or all data (by reference)
|
||||
*/
|
||||
ve.dm.SlicedLinearData.prototype.getOriginalData = function ( offset ) {
|
||||
return offset === undefined ? this.data.slice( this.range.start, this.range.end ) : this.data[this.range.start + offset];
|
||||
};
|
||||
|
||||
/**
|
||||
* Run all elements in this document slice through getClonedElement(). This should be done if
|
||||
* you intend to insert the sliced data back into the document as a copy of the original data
|
||||
* (e.g. for copy and paste).
|
||||
*/
|
||||
ve.dm.SlicedLinearData.prototype.cloneElements = function () {
|
||||
var i, len, node;
|
||||
for ( i = 0, len = this.getLength(); i < len; i++ ) {
|
||||
if ( this.isOpenElementData( i ) ) {
|
||||
node = ve.dm.nodeFactory.create( this.getType( i ), [], this.getData( i ) );
|
||||
this.data[i] = node.getClonedElement();
|
||||
}
|
||||
}
|
||||
};
|
|
@ -824,24 +824,24 @@ ve.dm.Document.prototype.fixupInsertion = function ( data, offset ) {
|
|||
/**
|
||||
* Get the document data for a range.
|
||||
*
|
||||
* Data will be fixed up so that unopened closings and unclosed openings in the document data slice
|
||||
* are balanced.
|
||||
* Data will be fixed up so that unopened closings and unclosed openings in the
|
||||
* linear data slice are balanced.
|
||||
*
|
||||
* @param {ve.Range} range Range to get contents of
|
||||
* @returns {ve.dm.DocumentSlice} Balanced slice of linear model data
|
||||
* @returns {ve.dm.ElementLinearDataSlice} Balanced slice of linear model data
|
||||
*/
|
||||
ve.dm.Document.prototype.getSlice = function ( range ) {
|
||||
ve.dm.Document.prototype.getSlicedLinearData = function ( range ) {
|
||||
var first, last, firstNode, lastNode,
|
||||
node = this.getNodeFromOffset( range.start ),
|
||||
selection = this.selectNodes( range, 'siblings' ),
|
||||
addOpenings = [],
|
||||
addClosings = [];
|
||||
if ( selection.length === 0 ) {
|
||||
return new ve.dm.DocumentSlice( [] );
|
||||
return new ve.dm.ElementLinearDataSlice( this.getStore(), [] );
|
||||
}
|
||||
if ( selection.length === 1 && selection[0].range && selection[0].range.equalsSelection( range ) ) {
|
||||
// Nothing to fix up
|
||||
return new ve.dm.DocumentSlice( this.data.slice( range.start, range.end ) );
|
||||
return new ve.dm.ElementLinearDataSlice( this.getStore(), this.data.slice( range.start, range.end ) );
|
||||
}
|
||||
|
||||
first = selection[0];
|
||||
|
@ -882,7 +882,8 @@ ve.dm.Document.prototype.getSlice = function ( range ) {
|
|||
}
|
||||
}
|
||||
|
||||
return new ve.dm.DocumentSlice(
|
||||
return new ve.dm.ElementLinearDataSlice(
|
||||
this.getStore(),
|
||||
addOpenings.reverse()
|
||||
.concat( this.data.slice( range.start, range.end ) )
|
||||
.concat( addClosings ),
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*!
|
||||
* VisualEditor DataModel DocumentSlice class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* DataModel document slice.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Array} data Balanced sliced data (will be deep copied internally)
|
||||
* @param {ve.Range} [range] Original context within data
|
||||
*/
|
||||
ve.dm.DocumentSlice = function VeDmDocumentSlice( data, range ) {
|
||||
// Properties
|
||||
this.data = ve.copy( data );
|
||||
this.range = range || new ve.Range( 0, data.length );
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Get a deep copy of the sliced data.
|
||||
*
|
||||
* @method
|
||||
* @returns {Array} Document data
|
||||
*/
|
||||
ve.dm.DocumentSlice.prototype.getData = function () {
|
||||
return this.data.slice( this.range.start, this.range.end );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a balanced version of the sliced data.
|
||||
*
|
||||
* @method
|
||||
* @returns {Array} Document data
|
||||
*/
|
||||
ve.dm.DocumentSlice.prototype.getBalancedData = function () {
|
||||
return this.data.slice( 0 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Run all elements in this document slice through getClonedElement(). This should be done if
|
||||
* you intend to insert the sliced data back into the document as a copy of the original data
|
||||
* (e.g. for copy and paste).
|
||||
*/
|
||||
ve.dm.DocumentSlice.prototype.cloneElements = function () {
|
||||
var i, len, node;
|
||||
for ( i = 0, len = this.data.length; i < len; i++ ) {
|
||||
if ( this.data[i].type && this.data[i].type.charAt( 0 ) !== '/' ) {
|
||||
node = ve.dm.nodeFactory.create( this.data[i].type, [], this.data[i] );
|
||||
this.data[i] = node.getClonedElement();
|
||||
}
|
||||
}
|
||||
};
|
|
@ -271,19 +271,21 @@ QUnit.test( 'selectNodes', function ( assert ) {
|
|||
} );
|
||||
|
||||
QUnit.test( 'getSlice', function ( assert ) {
|
||||
var i, data, doc = ve.dm.example.createExampleDocument(),
|
||||
var i, expectedData, doc = ve.dm.example.createExampleDocument(),
|
||||
cases = [
|
||||
{
|
||||
'msg': 'empty range',
|
||||
'range': new ve.Range( 2, 2 ),
|
||||
'expected': []
|
||||
'expected': [],
|
||||
'expectedRange': new ve.Range( 0 )
|
||||
},
|
||||
{
|
||||
'msg': 'range with one character',
|
||||
'range': new ve.Range( 2, 3 ),
|
||||
'expected': [
|
||||
['b', [ ve.dm.example.bold ]]
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 0, 1 )
|
||||
},
|
||||
{
|
||||
'msg': 'range with two characters',
|
||||
|
@ -291,7 +293,8 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
'expected': [
|
||||
['b', [ ve.dm.example.bold ]],
|
||||
['c', [ ve.dm.example.italic ]]
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 0, 2 )
|
||||
},
|
||||
{
|
||||
'msg': 'range with two characters and a header closing',
|
||||
|
@ -301,7 +304,8 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
['b', [ ve.dm.example.bold ]],
|
||||
['c', [ ve.dm.example.italic ]],
|
||||
{ 'type': '/heading' }
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 1, 4 )
|
||||
},
|
||||
{
|
||||
'msg': 'range with one character, a header closing and a table opening',
|
||||
|
@ -312,7 +316,8 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
{ 'type': '/heading' },
|
||||
{ 'type': 'table' },
|
||||
{ 'type': '/table' }
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 1, 4 )
|
||||
},
|
||||
{
|
||||
'msg': 'range from a paragraph into a list',
|
||||
|
@ -328,7 +333,8 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
{ 'type': '/paragraph' },
|
||||
{ 'type': '/listItem' },
|
||||
{ 'type': '/list' }
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 1, 7 )
|
||||
},
|
||||
{
|
||||
'msg': 'range from a paragraph inside a nested list into the next list',
|
||||
|
@ -347,7 +353,8 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
{ 'type': '/list' },
|
||||
{ 'type': 'list', 'attributes': { 'style': 'number' } },
|
||||
{ 'type': '/list' }
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 5, 12 )
|
||||
},
|
||||
{
|
||||
'msg': 'range from a paragraph inside a nested list out of both lists',
|
||||
|
@ -364,7 +371,8 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
{ 'type': '/list' },
|
||||
{ 'type': '/listItem' },
|
||||
{ 'type': '/list' }
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 5, 11 )
|
||||
},
|
||||
{
|
||||
'msg': 'range from a paragraph inside a nested list out of the outer listItem',
|
||||
|
@ -379,16 +387,22 @@ QUnit.test( 'getSlice', function ( assert ) {
|
|||
{ 'type': '/listItem' },
|
||||
{ 'type': '/list' },
|
||||
{ 'type': '/listItem' }
|
||||
]
|
||||
],
|
||||
'expectedRange': new ve.Range( 4, 9 )
|
||||
}
|
||||
];
|
||||
QUnit.expect( cases.length );
|
||||
QUnit.expect( 2 * cases.length );
|
||||
for ( i = 0; i < cases.length; i++ ) {
|
||||
data = ve.dm.example.preprocessAnnotations( cases[i].expected.slice(), doc.getStore() );
|
||||
expectedData = ve.dm.example.preprocessAnnotations( cases[i].expected.slice(), doc.getStore() ).getData();
|
||||
assert.deepEqual(
|
||||
doc.getSlice( cases[i].range ).getBalancedData(),
|
||||
data.getData(),
|
||||
cases[i].msg
|
||||
doc.getSlicedLinearData( cases[i].range ).getData(),
|
||||
expectedData,
|
||||
cases[i].msg + ': balanced data'
|
||||
);
|
||||
assert.deepEqual(
|
||||
doc.getSlicedLinearData( cases[i].range ).getRange(),
|
||||
cases[i].expectedRange,
|
||||
cases[i].msg + ': range'
|
||||
);
|
||||
}
|
||||
} );
|
||||
|
|
|
@ -92,10 +92,10 @@
|
|||
<script src="../../ve/dm/ve.dm.DataString.js"></script>
|
||||
<script src="../../ve/dm/ve.dm.Document.js"></script>
|
||||
<script src="../../ve/dm/ve.dm.LinearData.js"></script>
|
||||
<script src="../../ve/dm/ve.dm.DocumentSlice.js"></script>
|
||||
<script src="../../ve/dm/ve.dm.DocumentSynchronizer.js"></script>
|
||||
<script src="../../ve/dm/ve.dm.IndexValueStore.js"></script>
|
||||
<script src="../../ve/dm/ve.dm.Converter.js"></script>
|
||||
<script src="../../ve/dm/lineardata/ve.dm.SlicedLinearData.js"></script>
|
||||
<script src="../../ve/dm/lineardata/ve.dm.ElementLinearData.js"></script>
|
||||
<script src="../../ve/dm/lineardata/ve.dm.MetaLinearData.js"></script>
|
||||
<script src="../../ve/dm/nodes/ve.dm.GeneratedContentNode.js"></script>
|
||||
|
|
Loading…
Reference in a new issue