mediawiki-extensions-Visual.../modules/ve/dm/ve.dm.InternalList.js
Catrope 238d68eb74 Merging and remapping of IVStores and InternalLists
Add merge() methods to IndexValueStore and InternalList, which merge
another store/list in to the current one and return a mapping
translating old indexes to new ones.

Also add functions that, given such a mapping, traverse a linear
model data array and remap store/list indexes using simple logic for
annotations and node type-specific functions for node attributes.

Change-Id: I1e90755ced1a87c190947c037cf151c4d17cf8b7
2013-05-25 11:33:35 +00:00

166 lines
4.3 KiB
JavaScript

/*!
* VisualEditor DataModel InternalList class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* DataModel meta item.
*
* @class
* @mixins ve.EventEmitter
*
* @constructor
* @param {ve.dm.Document} doc Document model
*/
ve.dm.InternalList = function VeDmInternalList( doc ) {
// Mixin constructors
ve.EventEmitter.call( this );
// Properties
this.document = doc;
this.store = new ve.dm.IndexValueStore();
this.itemsHtml = [];
this.listNode = null;
// Event handlers
//this.document.connect( this, { 'transact', 'onTransact' } );
};
/* Inheritance */
ve.mixinClass( ve.dm.InternalList, ve.EventEmitter );
/* Methods */
/**
* Add an item to the list.
*
* If an item with this key already exists it will be ignored.
*
* @method
* @param {string} key Item key
* @param {string} body Item contents
* @returns {number} Index of the item in the index-value store, and also the list
*/
ve.dm.InternalList.prototype.addItem = function ( key, body ) {
var index = this.store.indexOfHash( key );
if ( index === null ) {
index = this.store.index( body, key );
this.itemsHtml.push( index );
}
return index;
};
/**
* Gets all the item's HTML strings
* @method
* @returns {Object} Name-indexed object containing HTMLElements
*/
ve.dm.InternalList.prototype.getItemsHtml = function () {
return this.store.values( this.itemsHtml );
};
/**
* Gets the internal list's document model
* @method
* @returns {ve.dm.Document} Document model
*/
ve.dm.InternalList.prototype.getDocument = function () {
return this.document;
};
/**
* Get the list node
* @method
* @returns {ve.dm.InternalListNode} List node
*/
ve.dm.InternalList.prototype.getListNode = function () {
var i, nodes;
// find listNode if not set, or unattached
if ( !this.listNode || !this.listNode.doc ) {
nodes = this.getDocument().documentNode.children;
for ( i = nodes.length; i >= 0; i-- ) {
if ( nodes[i] instanceof ve.dm.InternalListNode ) {
this.listNode = nodes[i];
break;
}
}
}
return this.listNode;
};
/**
* Get the item node from a specific index
* @method
* @param {number} index Item's index
* @returns {ve.dm.InternalItemNode} Item node
*/
ve.dm.InternalList.prototype.getItemNode = function ( index ) {
return this.getListNode().children[index];
};
/**
* Gets linear model data for all the stored item's HTML.
*
* Each item is an InternalItem, and they are wrapped in an InternalList.
* If there are no items an empty array is returned.
*
* @method
* @param {ve.dm.Converter} converter Converter object
* @returns {Array} Linear model data
*/
ve.dm.InternalList.prototype.getDataFromDom = function ( converter ) {
var i, length, itemData,
itemsHtml = this.getItemsHtml(), list = [];
if ( itemsHtml.length ) {
list.push( { 'type': 'internalList' } );
for ( i = 0, length = itemsHtml.length; i < length; i++ ) {
itemData = converter.getDataFromDomRecursion( $( itemsHtml[i] )[0] );
list = list.concat(
[{ 'type': 'internalItem' }],
itemData,
[{ 'type': '/internalItem' }]
);
}
list.push( { 'type': '/internalList' } );
}
return list;
};
/**
* 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;
};
/**
* Merge another document's internal list into this one.
*
* Objects that are in other but not in this are added to this, possibly with a different index.
*
* @param {ve.dm.InternalList} other List to merge into this one
* @returns {Object} Object in which the keys are indexes in other and the values are the corresponding keys in this
*/
ve.dm.InternalList.prototype.merge = function ( other ) {
var i, len, index, storeMapping = this.store.merge( other.store ), mapping = {};
for ( i = 0, len = other.itemsHtml.length; i < len; i++ ) {
other.itemsHtml[i] = storeMapping[other.itemsHtml[i]];
index = ve.indexOf( other.itemsHtml[i], this.itemsHtml );
if ( index === -1 ) {
index = this.itemsHtml.push( other.itemsHtml[i] ) - 1;
}
mapping[i] = index;
}
return mapping;
};