diff --git a/.docs/categories.json b/.docs/categories.json
index d5eba63906..4d8cebe92d 100644
--- a/.docs/categories.json
+++ b/.docs/categories.json
@@ -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",
diff --git a/VisualEditor.php b/VisualEditor.php
index 25c1d28073..1829e0dcb7 100644
--- a/VisualEditor.php
+++ b/VisualEditor.php
@@ -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',
diff --git a/demos/ve/index.php b/demos/ve/index.php
index eef13e0d35..c3e5f29689 100644
--- a/demos/ve/index.php
+++ b/demos/ve/index.php
@@ -147,10 +147,10 @@ $html = file_get_contents( $page );
-
+
diff --git a/modules/ve/ce/ve.ce.Surface.js b/modules/ve/ce/ve.ce.Surface.js
index c3d4d683f1..db9867a721 100644
--- a/modules/ve/ce/ve.ce.Surface.js
+++ b/modules/ve/ce/ve.ce.Surface.js
@@ -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() );
diff --git a/modules/ve/dm/lineardata/ve.dm.ElementLinearData.js b/modules/ve/dm/lineardata/ve.dm.ElementLinearData.js
index 1944af343a..96df182e4c 100644
--- a/modules/ve/dm/lineardata/ve.dm.ElementLinearData.js
+++ b/modules/ve/dm/lineardata/ve.dm.ElementLinearData.js
@@ -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 );
diff --git a/modules/ve/dm/lineardata/ve.dm.SlicedLinearData.js b/modules/ve/dm/lineardata/ve.dm.SlicedLinearData.js
new file mode 100644
index 0000000000..19c9a2be05
--- /dev/null
+++ b/modules/ve/dm/lineardata/ve.dm.SlicedLinearData.js
@@ -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();
+ }
+ }
+};
diff --git a/modules/ve/dm/ve.dm.Document.js b/modules/ve/dm/ve.dm.Document.js
index e5b372462a..874fd79d83 100644
--- a/modules/ve/dm/ve.dm.Document.js
+++ b/modules/ve/dm/ve.dm.Document.js
@@ -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 ),
diff --git a/modules/ve/dm/ve.dm.DocumentSlice.js b/modules/ve/dm/ve.dm.DocumentSlice.js
deleted file mode 100644
index 9d6d1a1714..0000000000
--- a/modules/ve/dm/ve.dm.DocumentSlice.js
+++ /dev/null
@@ -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();
- }
- }
-};
diff --git a/modules/ve/test/dm/ve.dm.Document.test.js b/modules/ve/test/dm/ve.dm.Document.test.js
index eef892050b..7335e09779 100644
--- a/modules/ve/test/dm/ve.dm.Document.test.js
+++ b/modules/ve/test/dm/ve.dm.Document.test.js
@@ -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'
);
}
} );
diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php
index d188d32e77..e3f1636c94 100644
--- a/modules/ve/test/index.php
+++ b/modules/ve/test/index.php
@@ -92,10 +92,10 @@
-
+