Current File : /home/jvzmxxx/wiki/extensions/Flow/modules/flow/dm/mixins/mw.flow.dm.List.js
( function ( $ ) {
	/**
	 * Flow List mixin
	 * Must be mixed into an mw.flow.dm.Item element
	 *
	 * @mixin
	 * @abstract
	 * @constructor
	 * @param {Object} config Configuration options
	 */
	mw.flow.dm.List = function mwFlowDmList( config ) {
		// Configuration initialization
		config = config || {};

		this.items = [];

		// Store references to items by their ids
		this.itemsById = {};

		this.aggregateItemEvents = {};
	};

	/* Events */

	/**
	 * @event add Items have been added
	 * @param {mw.flow.dm.Item[]} items Added items
	 * @param {number} index Index items were added at
	 */

	/**
	 * @event remove Items have been removed
	 * @param {mw.flow.dm.Item[]} items Removed items
	 */

	/* Methods */

	/**
	 * Get all items
	 *
	 * @return {mw.flow.dm.Item[]} Items in the list
	 */
	mw.flow.dm.List.prototype.getItems = function () {
		return this.items.slice( 0 );
	};

	/**
	 * Get an item by its id
	 * @param {string} id Item id
	 * @return {mw.flow.dm.Item} Item
	 */
	mw.flow.dm.List.prototype.getItemById = function ( id ) {
		return this.itemsById[ id ];
	};

	/**
	 * Get the index of a specific item
	 *
	 * @param {mw.flow.dm.Item} item Requested item
	 * @return {number} Index of the item
	 */
	mw.flow.dm.List.prototype.getItemIndex = function ( item ) {
		return this.items.indexOf( item );
	};

	/**
	 * Get number of items
	 *
	 * @return {number} Number of items in the list
	 */
	mw.flow.dm.List.prototype.getItemCount = function () {
		return this.items.length;
	};

	/**
	 * Check if a list contains no items.
	 *
	 * @return {boolean} Group is empty
	 */
	mw.flow.dm.List.prototype.isEmpty = function () {
		return !this.items.length;
	};

	/**
	 * Aggregate the events emitted by the group.
	 * Taken from oojs-ui's OO.ui.GroupElement#aggregate
	 *
	 * When events are aggregated, the group will listen to all contained items for the event,
	 * and then emit the event under a new name. The new event will contain an additional leading
	 * parameter containing the item that emitted the original event. Other arguments emitted from
	 * the original event are passed through.
	 *
	 * @param {Object.<string,string|null>} events An object keyed by the name of the event that should be
	 *  aggregated  (e.g., ‘click’) and the value of the new name to use (e.g., ‘groupClick’).
	 *  A `null` value will remove aggregated events.

	 * @throws {Error} An error is thrown if aggregation already exists.
	 */
	mw.flow.dm.List.prototype.aggregate = function ( events ) {
		var i, len, item, add, remove, itemEvent, groupEvent;

		for ( itemEvent in events ) {
			groupEvent = events[ itemEvent ];

			// Remove existing aggregated event
			if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
				// Don't allow duplicate aggregations
				if ( groupEvent ) {
					throw new Error( 'Duplicate item event aggregation for ' + itemEvent );
				}
				// Remove event aggregation from existing items
				for ( i = 0, len = this.items.length; i < len; i++ ) {
					item = this.items[ i ];
					if ( item.connect && item.disconnect ) {
						remove = {};
						remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
						item.disconnect( this, remove );
					}
				}
				// Prevent future items from aggregating event
				delete this.aggregateItemEvents[ itemEvent ];
			}

			// Add new aggregate event
			if ( groupEvent ) {
				// Make future items aggregate event
				this.aggregateItemEvents[ itemEvent ] = groupEvent;
				// Add event aggregation to existing items
				for ( i = 0, len = this.items.length; i < len; i++ ) {
					item = this.items[ i ];
					if ( item.connect && item.disconnect ) {
						add = {};
						add[ itemEvent ] = [ 'emit', groupEvent, item ];
						item.connect( this, add );
					}
				}
			}
		}
	};

	/**
	 * Add items
	 *
	 * @param {mw.flow.dm.Item[]} items Items to add
	 * @param {number} index Index to add items at
	 * @chainable
	 * @fires add
	 */
	mw.flow.dm.List.prototype.addItems = function ( items, index ) {
		var i, len, item, event, events, currentIndex, existingItem, at;

		if ( items.length === 0 ) {
			return this;
		}

		// Support adding existing items at new locations
		for ( i = 0, len = items.length; i < len; i++ ) {
			item = items[ i ];
			existingItem = this.getItemById( item.getId() );

			// Check if item exists then remove it first, effectively "moving" it
			currentIndex = this.items.indexOf( existingItem );
			if ( currentIndex >= 0 ) {
				this.removeItems( [ existingItem ] );
				// Adjust index to compensate for removal
				if ( currentIndex < index ) {
					index--;
				}
			}

			// Add the item
			if ( item.connect && item.disconnect && !$.isEmptyObject( this.aggregateItemEvents ) ) {
				events = {};
				for ( event in this.aggregateItemEvents ) {
					events[ event ] = [ 'emit', this.aggregateItemEvents[ event ], item ];
				}
				item.connect( this, events );
			}

			// Add by reference
			this.itemsById[ item.getId() ] = items[ i ];
		}

		if ( index === undefined || index < 0 || index >= this.items.length ) {
			at = this.items.length;
			this.items.push.apply( this.items, items );
		} else if ( index === 0 ) {
			at = 0;
			this.items.unshift.apply( this.items, items );
		} else {
			at = index;
			this.items.splice.apply( this.items, [ index, 0 ].concat( items ) );
		}
		this.emit( 'add', items, at );

		return this;
	};

	/**
	 * Remove items
	 *
	 * @param {mw.flow.dm.Item[]} items Items to remove
	 * @chainable
	 * @fires remove
	 */
	mw.flow.dm.List.prototype.removeItems = function ( items ) {
		var i, len, item, index, remove, itemEvent,
			removed = [];

		if ( items.length === 0 ) {
			return this;
		}

		// Remove specific items
		for ( i = 0, len = items.length; i < len; i++ ) {
			item = items[ i ];
			index = this.items.indexOf( item );
			if ( index !== -1 ) {
				if (
					item.connect && item.disconnect &&
					!$.isEmptyObject( this.aggregateItemEvents )
				) {
					remove = {};
					if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
						remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
					}
					item.disconnect( this, remove );
				}
				this.items.splice( index, 1 );
				// Remove reference by Id
				delete this.itemsById[ item.getId() ];
			}
		}
		this.emit( 'remove', removed );

		return this;
	};

	/**
	 * Clear all items
	 *
	 * @fires clear
	 */
	mw.flow.dm.List.prototype.clearItems = function () {
		var i, len, item, remove, itemEvent;

		// Remove all items
		for ( i = 0, len = this.items.length; i < len; i++ ) {
			item = this.items[ i ];
			if (
				item.connect && item.disconnect &&
				!$.isEmptyObject( this.aggregateItemEvents )
			) {
				remove = {};
				if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
					remove[ itemEvent ] = [ 'emit', this.aggregateItemEvents[ itemEvent ], item ];
				}
				item.disconnect( this, remove );
			}
		}

		this.items = [];
		this.itemsById = {};

		this.emit( 'clear' );

		return this;
	};
}( jQuery ) );