jenkins-bot has submitted this change and it was merged.

Change subject: Add getDocumentSlice
......................................................................


Add getDocumentSlice

A document slice is a document built from a data slice of an existing
document. It's completely separate from the original document and has
its own store and internalList. The new document's data also contains
the entirety of the original document's internal list. It's possible
to create a document slice of data located inside the internal list,
in which case the resulting document will contain that data twice (one
mutable copy at the top level, and one immutable copy in the internal
list).

ve.dm.Document.js:
* Optionally take an internalList in the constructor. This allows us to
  create a document with a clone of an existing internalList rather than
  an empty one.
* Add edgeMetadata flag to getFullData()

ve.dm.IndexValueStore.js, ve.dm.InternalList.js:
* Make these classes cloneable

Change-Id: I93e06f764ace16aee9df941b07f8c2bff1a28e2b
---
M modules/ve/dm/ve.dm.Document.js
M modules/ve/dm/ve.dm.IndexValueStore.js
M modules/ve/dm/ve.dm.InternalList.js
M modules/ve/test/dm/ve.dm.Document.test.js
4 files changed, 134 insertions(+), 8 deletions(-)

Approvals:
  Catrope: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/modules/ve/dm/ve.dm.Document.js b/modules/ve/dm/ve.dm.Document.js
index 859e9f3..a30e486 100644
--- a/modules/ve/dm/ve.dm.Document.js
+++ b/modules/ve/dm/ve.dm.Document.js
@@ -18,8 +18,9 @@
  * @constructor
  * @param {HTMLDocument|Array|ve.dm.LinearData} documentOrData HTML document, 
raw linear model data or LinearData to start with
  * @param {ve.dm.Document} [parentDocument] Document to use as root for 
created nodes
+ * @param {ve.dm.InternalList} [internalList] Internal list to clone; passed 
when creating a document slice
  */
-ve.dm.Document = function VeDmDocument( documentOrData, parentDocument ) {
+ve.dm.Document = function VeDmDocument( documentOrData, parentDocument, 
internalList ) {
        // Parent constructor
        ve.Document.call( this, new ve.dm.DocumentNode() );
 
@@ -41,7 +42,7 @@
                currentNode = this.documentNode;
        this.documentNode.setDocument( doc );
        this.documentNode.setRoot( root );
-       this.internalList = new ve.dm.InternalList( this );
+       this.internalList = internalList ? internalList.clone( this ) : new 
ve.dm.InternalList( this );
 
        // Properties
        this.parentDocument = parentDocument;
@@ -293,6 +294,36 @@
 };
 
 /**
+ * Get a document from a slice of this document. The new document's store and 
internal list will be
+ * clones of the ones in this document.
+ *
+ * @param {ve.Range|ve.dm.Node} rangeOrNode Range of data to clone, or node 
whose contents should be cloned
+ * @returns {ve.dm.Document} New document
+ * @throws {Error} rangeOrNode must be a ve.Range or a ve.dm.Node
+ */
+ve.dm.Document.prototype.getDocumentSlice = function ( rangeOrNode ) {
+       var data, range,
+               store = this.store.clone(),
+               listRange = this.internalList.getListNode().getOuterRange();
+       if ( rangeOrNode instanceof ve.dm.Node ) {
+               range = rangeOrNode.getRange();
+       } else if ( rangeOrNode instanceof ve.Range ) {
+               range = rangeOrNode;
+       } else {
+               throw new Error( 'rangeOrNode must be a ve.Range or a 
ve.dm.Node' );
+       }
+       data = ve.copyArray( this.getFullData( range, true ) );
+       if ( range.start > listRange.start || range.end < listRange.end ) {
+               // The range does not include the entire internal list, so add 
it
+               data = data.concat( this.getFullData( listRange ) );
+       }
+       return new this.constructor(
+               new ve.dm.ElementLinearData( store, data ),
+               undefined, this.internalList
+       );
+};
+
+/**
  * Get the metadata replace operation required to keep data & metadata in sync 
after a splice
  *
  * @method
@@ -349,14 +380,25 @@
 /**
  * Get the full document data including metadata.
  *
- * Metadata will be into the document data to produce the "full data" result.
+ * Metadata will be into the document data to produce the "full data" result. 
If a range is passed,
+ * metadata at the edges of the range won't be included unless edgeMetadata is 
set to true. If
+ * no range is passed, the entire document's data is returned and metadata at 
the edges is
+ * included.
  *
+ * @param {ve.Range} [range] Range to get full data for. If omitted, all data 
will be returned
+ * @param {boolean} [edgeMetadata=false] Include metadata at the edges of the 
range
  * @returns {Array} Data with metadata interleaved
  */
-ve.dm.Document.prototype.getFullData = function () {
-       var result = [], i, j, jLen, iLen = this.data.getLength();
-       for ( i = 0; i <= iLen; i++ ) {
-               if ( this.metadata.getData( i ) ) {
+ve.dm.Document.prototype.getFullData = function ( range, edgeMetadata ) {
+       var j, jLen,
+               i = range ? range.start : 0,
+               iLen = range ? range.end : this.data.getLength(),
+               result = [];
+       if ( edgeMetadata === undefined ) {
+               edgeMetadata = !range;
+       }
+       while ( i <= iLen ) {
+               if ( this.metadata.getData( i ) && ( edgeMetadata || ( i !== 
range.start && i !== range.end ) ) ) {
                        for ( j = 0, jLen = this.metadata.getData( i ).length; 
j < jLen; j++ ) {
                                result.push( this.metadata.getData( i )[j] );
                                result.push( { 'type': '/' + 
this.metadata.getData( i )[j].type } );
@@ -365,6 +407,7 @@
                if ( i < iLen ) {
                        result.push( this.data.getData( i ) );
                }
+               i++;
        }
        return result;
 };
diff --git a/modules/ve/dm/ve.dm.IndexValueStore.js 
b/modules/ve/dm/ve.dm.IndexValueStore.js
index 2646a09..513766c 100644
--- a/modules/ve/dm/ve.dm.IndexValueStore.js
+++ b/modules/ve/dm/ve.dm.IndexValueStore.js
@@ -106,3 +106,21 @@
        }
        return values;
 };
+
+/**
+ * Clone a store.
+ *
+ * The returned clone is shallow: the valueStore array and the hashStore array 
are cloned, but
+ * the values inside them are copied by reference. These values are supposed 
to be immutable,
+ * though.
+ *
+ * @returns {ve.dm.IndexValueStore} New store with the same contents as this 
one
+ */
+ve.dm.IndexValueStore.prototype.clone = function () {
+       var key, clone = new this.constructor();
+       clone.valueStore = this.valueStore.slice();
+       for ( key in this.hashStore ) {
+               clone.hashStore[key] = this.hashStore[key];
+       }
+       return clone;
+};
\ No newline at end of file
diff --git a/modules/ve/dm/ve.dm.InternalList.js 
b/modules/ve/dm/ve.dm.InternalList.js
index c5f1a8c..7e80005 100644
--- a/modules/ve/dm/ve.dm.InternalList.js
+++ b/modules/ve/dm/ve.dm.InternalList.js
@@ -128,4 +128,17 @@
                list.push( { 'type': '/internalList' } );
        }
        return list;
-};
\ No newline at end of file
+};
+
+/**
+ * Clone this internal list.
+ *
+ * @param {ve.dm.Document} [doc] The new list's document. Defaults to this 
list's document.
+ * @returns {ve.dm.InternalList} Clone of this internal
+ */
+ve.dm.InternalList.prototype.clone = function ( doc ) {
+       var clone = new this.constructor( doc || this.doc );
+       clone.store = this.store.clone();
+       clone.itemsHtml = this.itemsHtml.slice();
+       return clone;
+};
diff --git a/modules/ve/test/dm/ve.dm.Document.test.js 
b/modules/ve/test/dm/ve.dm.Document.test.js
index 5f67de3..0f2caa4 100644
--- a/modules/ve/test/dm/ve.dm.Document.test.js
+++ b/modules/ve/test/dm/ve.dm.Document.test.js
@@ -67,6 +67,58 @@
        assert.deepEqual( doc.getFullData(), ve.dm.example.withMeta );
 } );
 
+QUnit.test( 'getDocumentSlice', function ( assert ) {
+       var i, doc2, doc = ve.dm.example.createExampleDocument( 'internalData' 
),
+               cases = [
+                       {
+                               'msg': 'with range',
+                               'doc': 'internalData',
+                               'arg': new ve.Range( 7, 12 ),
+                               'expectedData': doc.data.slice( 7, 12 ).concat( 
doc.data.slice( 5, 21 ) )
+                       },
+                       {
+                               'msg': 'with node',
+                               'doc': 'internalData',
+                               'arg': doc.getInternalList().getItemNode( 1 ),
+                               'expectedData': doc.data.slice( 14, 19 
).concat( doc.data.slice( 5, 21 ) )
+                       },
+                       {
+                               'msg': 'paragraph at the start',
+                               'doc': 'internalData',
+                               'arg': new ve.Range( 0, 5 ),
+                               'expectedData': doc.data.slice( 0, 21 )
+                       },
+                       {
+                               'msg': 'paragraph at the end',
+                               'doc': 'internalData',
+                               'arg': new ve.Range( 21, 27 ),
+                               'expectedData': doc.data.slice( 21, 27 
).concat( doc.data.slice( 5, 21 ) )
+                       }
+               ];
+       QUnit.expect( 8*cases.length );
+       for ( i = 0; i < cases.length; i++ ) {
+               doc = ve.dm.example.createExampleDocument( cases[i].doc );
+               doc2 = doc.getDocumentSlice( cases[i].arg );
+               assert.deepEqual( doc2.data.data, cases[i].expectedData,
+                       cases[i].msg + ': sliced data' );
+               assert.notStrictEqual( doc2.data[0], cases[i].expectedData[0],
+                       cases[i].msg + ': data is cloned, not the same' );
+               assert.deepEqual( doc2.store, doc.store,
+                       cases[i].msg + ': store is copied' );
+               assert.notStrictEqual( doc2.store, doc.store,
+                       cases[i].msg + ': store is a clone, not the same' );
+               assert.deepEqual( doc2.internalList.itemsHtml, 
doc.internalList.itemsHtml,
+                       cases[i].msg + ': internal list items are copied' );
+               assert.notStrictEqual( doc2.internalList.itemsHtml, 
doc.internalList.itemsHtml,
+                       cases[i].msg + ': internal list items array is cloned, 
not the same' );
+               assert.deepEqual( doc2.internalList.store, 
doc.internalList.store,
+                       cases[i].msg + ': internal list store is copied' );
+               assert.notStrictEqual( doc2.internalList.store, 
doc.internalList.store,
+                       cases[i].msg + ': internal list store is a clone, not 
the same' );
+       }
+
+} );
+
 QUnit.test( 'getMetadataReplace', 3, function ( assert ) {
        var replace, expectedReplace,
                doc = ve.dm.example.createExampleDocument( 'withMeta' );

-- 
To view, visit https://gerrit.wikimedia.org/r/64958
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I93e06f764ace16aee9df941b07f8c2bff1a28e2b
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Catrope <roan.katt...@gmail.com>
Gerrit-Reviewer: Catrope <roan.katt...@gmail.com>
Gerrit-Reviewer: Esanders <esand...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to