%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/dm/
Upload File :
Create Path :
Current File : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/dm/ve.dm.MetaList.js

/*!
 * VisualEditor DataModel MetaList class.
 *
 * @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org
 */

/**
 * DataModel meta item.
 *
 * @class
 * @mixins OO.EventEmitter
 *
 * @constructor
 * @param {ve.dm.Surface} surface Surface model
 */
ve.dm.MetaList = function VeDmMetaList( surface ) {
	var i, j, jlen, metadata, item, group;

	// Mixin constructors
	OO.EventEmitter.call( this );

	// Properties
	this.surface = surface;
	this.document = surface.getDocument();
	this.groups = {};
	this.items = [];

	// Event handlers
	this.document.connect( this, { transact: 'onTransact' } );

	// Populate from document
	metadata = this.document.getMetadata();
	for ( i in metadata ) {
		if ( Object.prototype.hasOwnProperty.call( metadata, i ) && Array.isArray( metadata[ i ] ) ) {
			for ( j = 0, jlen = metadata[ i ].length; j < jlen; j++ ) {
				item = ve.dm.metaItemFactory.createFromElement( metadata[ i ][ j ] );
				group = this.groups[ item.getGroup() ];
				if ( !group ) {
					group = this.groups[ item.getGroup() ] = [];
				}
				item.attach( this, Number( i ), j );
				group.push( item );
				this.items.push( item );
			}
		}
	}
};

/* Inheritance */

OO.mixinClass( ve.dm.MetaList, OO.EventEmitter );

/* Events */

/**
 * @event insert
 * @param {ve.dm.MetaItem} item Item that was inserted
 */

/**
 * @event remove
 * @param {ve.dm.MetaItem} item Item that was removed
 * @param {number} offset Linear model offset that the item was at
 * @param {number} index Index within that offset the item was at
 */

/* Methods */

/**
 * Event handler for transactions on the document.
 *
 * When a transaction occurs, update this list to account for it:
 * - insert items for new metadata that was inserted
 * - remove items for metadata that was removed
 * - translate offsets and recompute indices for metadata that has shifted
 *
 * @param {ve.dm.Transaction} tx Transaction that was applied to the document
 * @fires insert
 * @fires remove
 */
ve.dm.MetaList.prototype.onTransact = function ( tx ) {
	var i, ilen, j, jlen, k, klen, item, ins, rm, insMeta, rmMeta,
		numItems = this.items.length,
		itemIndex = 0, // Current index into this.items
		offset = 0, // Current pre-transaction offset
		newOffset = 0, // Current post-transaction offset
		index = 0, // Current pre-transaction index
		newIndex = 0, // Current post-transaction index
		// Array of items that should appear in this.items after we're done. This includes newly
		// inserted items as well as existing items that aren't being removed.
		// [ { item: ve.dm.MetaItem, offset: offset to move to, index: index to move to } ]
		newItems = [],
		removedItems = [], // Array of items that should be removed from this.items
		events = [], // Array of events that we should emit when we're done
		ops = tx.getOperations();

	// Go through the transaction operations and plan out where to add, remove and move items. We
	// don't actually touch this.items yet, otherwise we 1) get it out of order which breaks
	// findItem() and 2) lose information about what the pre-transaction state of this.items was.
	for ( i = 0, ilen = ops.length; i < ilen; i++ ) {
		switch ( ops[ i ].type ) {
			case 'retain':
				// Advance itemIndex through the retain and update items we encounter along the way
				for ( ;
					itemIndex < numItems && this.items[ itemIndex ].offset < offset + ops[ i ].length;
					itemIndex++
				) {
					// Plan to move this item to the post-transaction offset and index
					newItems.push( {
						item: this.items[ itemIndex ],
						offset: this.items[ itemIndex ].offset + newOffset - offset,
						index: this.items[ itemIndex ].offset === offset ?
							// Adjust index for insertions or removals that happened at this offset
							newIndex - index + this.items[ itemIndex ].index :
							// Offset is retained over completely, don't adjust index
							this.items[ itemIndex ].index
					} );
				}

				offset += ops[ i ].length;
				newOffset += ops[ i ].length;
				index = 0;
				newIndex = 0;
				break;

			case 'retainMetadata':
				// Advance itemIndex through the retain and update items we encounter along the way
				for ( ;
					itemIndex < numItems && this.items[ itemIndex ].offset === offset &&
						this.items[ itemIndex ].index < index + ops[ i ].length;
					itemIndex++
				) {
					newItems.push( {
						item: this.items[ itemIndex ],
						offset: newOffset,
						index: this.items[ itemIndex ].index + newIndex - index
					} );
				}

				index += ops[ i ].length;
				newIndex += ops[ i ].length;
				break;

			case 'replace':
				ins = ops[ i ].insert;
				rm = ops[ i ].remove;
				if ( ops[ i ].removeMetadata !== undefined ) {
					insMeta = ops[ i ].insertMetadata;
					rmMeta = ops[ i ].removeMetadata;

					// Process removed metadata
					for ( ;
						itemIndex < numItems &&
							this.items[ itemIndex ].offset < offset + rmMeta.length;
						itemIndex++
					) {
						removedItems.push( this.items[ itemIndex ] );
					}

					// Process inserted metadata
					for ( j = 0, jlen = insMeta.length; j < jlen; j++ ) {
						if ( insMeta[ j ] ) {
							for ( k = 0, klen = insMeta[ j ].length; k < klen; k++ ) {
								item = ve.dm.metaItemFactory.createFromElement( insMeta[ j ][ k ] );
								newItems.push( {
									item: item,
									offset: newOffset + j,
									index: k
								} );
							}
						}
					}
				} else {
					// No metadata handling specified, which means we just have to deal with offset
					// adjustments, same as a retain
					for ( ;
							itemIndex < numItems &&
								this.items[ itemIndex ].offset < offset + rm.length;
							itemIndex++
					) {
						newItems.push( {
							item: this.items[ itemIndex ],
							offset: this.items[ itemIndex ].offset + newOffset - offset,
							index: this.items[ itemIndex ].index
						} );
					}
				}

				offset += rm.length;
				newOffset += ins.length;
				break;

			case 'replaceMetadata':
				insMeta = ops[ i ].insert;
				rmMeta = ops[ i ].remove;

				// Process removed items
				for ( ;
					itemIndex < numItems && this.items[ itemIndex ].offset === offset &&
						this.items[ itemIndex ].index < index + rmMeta.length;
					itemIndex++
				) {
					removedItems.push( this.items[ itemIndex ] );
				}

				// Process inserted items
				for ( j = 0, jlen = insMeta.length; j < jlen; j++ ) {
					item = ve.dm.metaItemFactory.createFromElement( insMeta[ j ] );
					newItems.push( { item: item, offset: newOffset, index: newIndex + j } );
				}

				index += rmMeta.length;
				newIndex += insMeta.length;
				break;
		}
	}
	// Update the remaining items that the transaction didn't touch or retain over
	for ( ; itemIndex < numItems; itemIndex++ ) {
		newItems.push( {
			item: this.items[ itemIndex ],
			offset: this.items[ itemIndex ].offset + newOffset - offset,
			index: this.items[ itemIndex ].offset === offset ?
				newIndex - index + this.items[ itemIndex ].index :
				this.items[ itemIndex ].index
		} );
	}

	// Process the changes, and queue up events. We emit the events at the end when the MetaList
	// is back in a consistent state

	// Remove removed items
	for ( i = 0, ilen = removedItems.length; i < ilen; i++ ) {
		this.deleteRemovedItem( removedItems[ i ].offset, removedItems[ i ].index );
		events.push( [
			'remove', removedItems[ i ], removedItems[ i ].offset, removedItems[ i ].index
		] );
	}

	// Move moved items (these appear as inserted items that are already attached)
	for ( i = 0, ilen = newItems.length; i < ilen; i++ ) {
		if ( newItems[ i ].item.isAttached() ) {
			if ( newItems[ i ].offset !== newItems[ i ].item.offset || newItems[ i ].index !== newItems[ i ].item.index ) {
				this.deleteRemovedItem( newItems[ i ].item.offset, newItems[ i ].item.index );
				newItems[ i ].preExisting = true;
			}
		}
	}

	// Insert new items
	for ( i = 0, ilen = newItems.length; i < ilen; i++ ) {
		if ( !newItems[ i ].item.isAttached() ) {
			this.addInsertedItem( newItems[ i ].offset, newItems[ i ].index, newItems[ i ].item );
			if ( !newItems[ i ].preExisting ) {
				events.push( [ 'insert', newItems[ i ].item ] );
			}
		}
	}

	// Emit events
	for ( i = 0, ilen = events.length; i < ilen; i++ ) {
		this.emit.apply( this, events[ i ] );
	}
};

/**
 * Find an item by its offset, index and group.
 *
 * This function is mostly for internal usage.
 *
 * @param {number} offset Offset in the linear model
 * @param {number} index Index in the metadata array associated with that offset
 * @param {string} [group] Group to search in. If not set, search in all groups
 * @param {boolean} [forInsertion] If the item is not found, return the index where it should have
 *  been rather than null
 * @return {number|null} Index into this.items or this.groups[group] where the item was found, or
 *  null if not found
 */
ve.dm.MetaList.prototype.findItem = function ( offset, index, group, forInsertion ) {
	var items = typeof group === 'string' ? ( this.groups[ group ] || [] ) : this.items;
	return ve.binarySearch( items, function ( item ) {
		return ve.compareTuples(
			[ offset, index ],
			[ item.getOffset(), item.getIndex() ]
		);
	}, forInsertion );
};

/**
 * Get the item at a given offset and index, if there is one.
 *
 * @param {number} offset Offset in the linear model
 * @param {number} index Index in the metadata array
 * @return {ve.dm.MetaItem|null} The item at (offset,index), or null if not found
 */
ve.dm.MetaList.prototype.getItemAt = function ( offset, index ) {
	var at = this.findItem( offset, index );
	return at === null ? null : this.items[ at ];
};

/**
 * Get all items in a group.
 *
 * This function returns a shallow copy, so the array isn't returned by reference but the items
 * themselves are.
 *
 * @param {string} group Group
 * @return {ve.dm.MetaItem[]} Array of items in the group (shallow copy)
 */
ve.dm.MetaList.prototype.getItemsInGroup = function ( group ) {
	return ( this.groups[ group ] || [] ).slice( 0 );
};

/**
 * Get all items in the list.
 *
 * This function returns a shallow copy, so the array isn't returned by reference but the items
 * themselves are.
 *
 * @return {ve.dm.MetaItem[]} Array of items in the list
 */
ve.dm.MetaList.prototype.getAllItems = function () {
	return this.items.slice( 0 );
};

/**
 * Insert new metadata into the document. This builds and processes a transaction that inserts
 * metadata into the document.
 *
 * Pass a plain object rather than a MetaItem into this function unless you know what you're doing.
 *
 * @param {Object|ve.dm.MetaItem} meta Metadata element (or MetaItem) to insert
 * @param {number} [offset] Offset to insert the new metadata, or undefined to add to the end
 * @param {number} [index] Index to insert the new metadata, or undefined to add to the end
 */
ve.dm.MetaList.prototype.insertMeta = function ( meta, offset, index ) {
	var tx;
	if ( meta instanceof ve.dm.MetaItem ) {
		meta = meta.getElement();
	}
	if ( offset === undefined ) {
		offset = this.document.data.getLength();
	}
	if ( index === undefined ) {
		index = ( this.document.metadata.getData( offset ) || [] ).length;
	}
	tx = ve.dm.Transaction.newFromMetadataInsertion( this.document, offset, index, [ meta ] );
	this.surface.change( tx );
};

/**
 * Remove a meta item from the document. This builds and processes a transaction that removes the
 * associated metadata from the document.
 *
 * @param {ve.dm.MetaItem} item Item to remove
 */
ve.dm.MetaList.prototype.removeMeta = function ( item ) {
	var tx;
	tx = ve.dm.Transaction.newFromMetadataRemoval(
		this.document,
		item.getOffset(),
		new ve.Range( item.getIndex(), item.getIndex() + 1 )
	);
	this.surface.change( tx );
};

/**
 * Insert an item at a given offset and index in response to a transaction.
 *
 * This function is for internal usage by onTransact(). To actually insert an item, use
 * insertMeta().
 *
 * @param {number} offset Offset in the linear model of the new item
 * @param {number} index Index of the new item in the metadata array at offset
 * @param {ve.dm.MetaItem} item Item object
 * @fires insert
 */
ve.dm.MetaList.prototype.addInsertedItem = function ( offset, index, item ) {
	var group = item.getGroup(),
		at = this.findItem( offset, index, null, true );
	this.items.splice( at, 0, item );
	if ( this.groups[ group ] ) {
		at = this.findItem( offset, index, group, true );
		this.groups[ group ].splice( at, 0, item );
	} else {
		this.groups[ group ] = [ item ];
	}
	item.attach( this, offset, index );
};

/**
 * Remove an item in response to a transaction.
 *
 * This function is for internal usage by onTransact(). To actually remove an item, use
 * removeItem().
 *
 * @param {number} offset Offset in the linear model of the item
 * @param {number} index Index of the item in the metadata array at offset
 * @fires remove
 */
ve.dm.MetaList.prototype.deleteRemovedItem = function ( offset, index ) {
	var item, group, at = this.findItem( offset, index );
	if ( at === null ) {
		return;
	}
	item = this.items[ at ];
	group = item.getGroup();
	this.items.splice( at, 1 );
	at = this.findItem( offset, index, group );
	if ( at !== null ) {
		this.groups[ group ].splice( at, 1 );
	}
	item.detach( this );
	return item;
};

Zerion Mini Shell 1.0