%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.DocumentSynchronizer.js

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

/**
 * DataModel document synchronizer.
 *
 * This object is a utility for collecting actions to be performed on the model tree in multiple
 * steps as the linear model is modified by a transaction processor and then processing those queued
 * actions when the transaction is done being processed.
 *
 * IMPORTANT NOTE: It is assumed that:
 *
 *   - The linear model has already been updated for the pushed actions
 *   - Actions are pushed in increasing offset order
 *   - Actions are non-overlapping
 *
 * @class
 * @constructor
 * @param {ve.dm.Document} doc Document to synchronize
 * @param {ve.dm.Transaction} transaction The transaction being synchronized for
 * @param {boolean} isStaging Transaction is being applied in staging mode
 */
ve.dm.DocumentSynchronizer = function VeDmDocumentSynchronizer( doc, transaction, isStaging ) {
	// Properties
	this.document = doc;
	this.actionQueue = [];
	this.eventQueue = [];
	this.adjustment = 0;
	this.transaction = transaction;
	this.isStaging = isStaging;
};

/* Static Properties */

/**
 * Synchronization methods.
 *
 * Each method is specific to a type of action. Methods are called in the context of a document
 * synchronizer, so they work similar to normal methods on the object.
 *
 * @static
 * @property
 */
ve.dm.DocumentSynchronizer.synchronizers = {};

/* Static Methods */

/**
 * Synchronize an annotation action.
 *
 * This method is called within the context of a document synchronizer instance.
 *
 * @static
 * @method
 * @param {Object} action
 */
ve.dm.DocumentSynchronizer.synchronizers.annotation = function ( action ) {
	// Queue events for all leaf nodes covered by the range
	var i,
		adjustedRange = action.range.translate( this.adjustment ),
		selection = this.document.selectNodes( adjustedRange, 'leaves' );
	for ( i = 0; i < selection.length; i++ ) {
		// No tree synchronization needed
		// Queue events
		this.queueEvent( selection[ i ].node, 'annotation' );
		this.queueEvent( selection[ i ].node, 'update', this.isStaging );
	}
};

/**
 * Synchronize an attribute change action.
 *
 * This method is called within the context of a document synchronizer instance.
 *
 * @static
 * @method
 * @param {Object} action
 */
ve.dm.DocumentSynchronizer.synchronizers.attributeChange = function ( action ) {
	// No tree synchronization needed
	// Queue events
	this.queueEvent( action.node, 'attributeChange', action.key, action.from, action.to );
	this.queueEvent( action.node, 'update', this.isStaging );
};

/**
 * Synchronize a resize action.
 *
 * This method is called within the context of a document synchronizer instance.
 *
 * @static
 * @method
 * @param {Object} action
 */
ve.dm.DocumentSynchronizer.synchronizers.resize = function ( action ) {
	var node = action.node,
		parent = node.getParent();

	if ( parent && node.getType() === 'text' && node.getLength() + action.adjustment === 0 ) {
		// Auto-prune empty text nodes
		parent.splice( parent.indexOf( node ), 1 );
	} else {
		// Apply length change to tree
		// No update event needed, adjustLength causes an update event on its own
		// FIXME however, any queued update event will still be emitted, resulting in a duplicate
		node.adjustLength( action.adjustment );
	}
	// Update adjustment
	this.adjustment += action.adjustment;
};

/**
 * Synchronize a text node insertion.
 *
 * This method is called within the context of a document synchronizer instance.
 *
 * @static
 * @method
 * @param {Object} action
 */
ve.dm.DocumentSynchronizer.synchronizers.insertTextNode = function ( action ) {
	var textNode = new ve.dm.TextNode();
	textNode.setLength( action.length );
	action.parentNode.splice( action.index, 0, textNode );
	this.adjustment += action.length;
};

/**
 * Synchronize a rebuild action.
 *
 * This method is called within the context of a document synchronizer instance.
 *
 * @static
 * @method
 * @param {Object} action
 */
ve.dm.DocumentSynchronizer.synchronizers.rebuild = function ( action ) {
	var firstNode, parent, index, numNodes,
		// Find the nodes contained by oldRange
		adjustedOldRange = action.oldRange.translate( this.adjustment ),
		selection = this.document.selectNodes( adjustedOldRange, 'siblings' );

	// If the document is empty, selection[0].node will be the document (so no parent)
	// but we won't get indexInNode either. Detect this and use index=0 in that case.
	if ( 'indexInNode' in selection[ 0 ] || !selection[ 0 ].node.getParent() ) {
		// Insertion
		parent = selection[ 0 ].node;
		index = selection[ 0 ].indexInNode || 0;
		numNodes = 0;
	} else {
		// Rebuild
		firstNode = selection[ 0 ].node;
		parent = firstNode.getParent();
		index = selection[ 0 ].index;
		numNodes = selection.length;
	}
	// Perform rebuild in tree
	this.document.rebuildNodes( parent, index, numNodes, adjustedOldRange.from,
		action.newRange.getLength()
	);
	// Update adjustment
	this.adjustment += action.newRange.getLength() - adjustedOldRange.getLength();
};

/* Methods */

/**
 * Get the document being synchronized.
 *
 * @method
 * @return {ve.dm.Document} Document being synchronized
 */
ve.dm.DocumentSynchronizer.prototype.getDocument = function () {
	return this.document;
};

/**
 * Add an annotation action to the queue.
 *
 * This finds all leaf nodes covered wholly or partially by the given range, and emits annotation
 * events for all of them.
 *
 * @method
 * @param {ve.Range} range Range that was annotated
 */
ve.dm.DocumentSynchronizer.prototype.pushAnnotation = function ( range ) {
	this.actionQueue.push( {
		type: 'annotation',
		range: range
	} );
};

/**
 * Add an attribute change to the queue.
 *
 * This emits an attributeChange event for the given node with the provided metadata.
 *
 * @method
 * @param {ve.dm.Node} node Node whose attribute changed
 * @param {string} key Key of the attribute that changed
 * @param {Mixed} from Old value of the attribute
 * @param {Mixed} to New value of the attribute
 */
ve.dm.DocumentSynchronizer.prototype.pushAttributeChange = function ( node, key, from, to ) {
	this.actionQueue.push( {
		type: 'attributeChange',
		node: node,
		key: key,
		from: from,
		to: to
	} );
};

/**
 * Add a resize action to the queue.
 *
 * This changes the length of a text node.
 *
 * @method
 * @param {ve.dm.TextNode} node Node to resize
 * @param {number} adjustment Length adjustment to apply to the node
 */
ve.dm.DocumentSynchronizer.prototype.pushResize = function ( node, adjustment ) {
	this.actionQueue.push( {
		type: 'resize',
		node: node,
		adjustment: adjustment
	} );
};

/**
 * Add a text node insertion action to the queue.
 *
 * This inserts a new text node.
 *
 * @param {ve.dm.Node} parentNode Node to insert text node into
 * @param {number} index Index in parentNode to insert text node at
 * @param {number} length Length of new text node
 */
ve.dm.DocumentSynchronizer.prototype.pushInsertTextNode = function ( parentNode, index, length ) {
	this.actionQueue.push( {
		type: 'insertTextNode',
		parentNode: parentNode,
		index: index,
		length: length
	} );
};

/**
 * Add a rebuild action to the queue.
 *
 * When a range of data has been changed arbitrarily this can be used to drop the nodes that
 * represented the original range and replace them with new nodes that represent the new range.
 *
 * @method
 * @param {ve.Range} oldRange Range of old nodes to be dropped
 * @param {ve.Range} newRange Range for new nodes to be built from
 */
ve.dm.DocumentSynchronizer.prototype.pushRebuild = function ( oldRange, newRange ) {
	this.actionQueue.push( {
		type: 'rebuild',
		oldRange: oldRange,
		newRange: newRange
	} );
};

/**
 * Queue an event to be emitted on a node.
 *
 * This method is called by methods defined in {ve.dm.DocumentSynchronizer.synchronizers}.
 *
 * Duplicate events will be ignored only if all arguments match exactly. Hashes of each event that
 * has been queued are stored in the nodes they will eventually be fired on.
 *
 * @method
 * @param {ve.dm.Node} node
 * @param {string} event Event name
 * @param {...Mixed} [args] Additional arguments to be passed to the event when fired
 */
ve.dm.DocumentSynchronizer.prototype.queueEvent = function ( node ) {
	// Check if this is already queued
	var
		args = Array.prototype.slice.call( arguments, 1 ),
		hash = OO.getHash( args );

	if ( !node.queuedEventHashes ) {
		node.queuedEventHashes = {};
	}
	if ( !node.queuedEventHashes[ hash ] ) {
		node.queuedEventHashes[ hash ] = true;
		this.eventQueue.push( {
			node: node,
			args: args.concat( this.transaction )
		} );
	}
};

/**
 * Synchronize node tree using queued actions.
 *
 * This method uses the static methods defined in {ve.dm.DocumentSynchronizer.synchronizers} and
 * calls them in the context of {this}.
 *
 * After synchronization is complete all queued events will be emitted. Hashes of queued events that
 * have been stored on nodes are removed from the nodes after the events have all been emitted.
 *
 * This method also clears both action and event queues.
 *
 * @method
 */
ve.dm.DocumentSynchronizer.prototype.synchronize = function () {
	var action,
		event,
		i;
	// Execute the actions in the queue
	for ( i = 0; i < this.actionQueue.length; i++ ) {
		action = this.actionQueue[ i ];
		if ( Object.prototype.hasOwnProperty.call( ve.dm.DocumentSynchronizer.synchronizers, action.type ) ) {
			ve.dm.DocumentSynchronizer.synchronizers[ action.type ].call( this, action );
		} else {
			throw new Error( 'Invalid action type ' + action.type );
		}
	}
	// Emit events in the event queue
	for ( i = 0; i < this.eventQueue.length; i++ ) {
		event = this.eventQueue[ i ];
		event.node.emit.apply( event.node, event.args );
		delete event.node.queuedEventHashes;
	}
	// Clear queues
	this.actionQueue = [];
	this.eventQueue = [];
};

Zerion Mini Shell 1.0