+ };
+
+ /**
+ * Add items to the list.
+ *
+ * @param {OO.EventEmitter|OO.EventEmitter[]} items Item to add or
+ * an array of items to add
+ * @param {number} [index] Index to add items at. If no index is
+ * given, or if the index that is given is invalid, the item
+ * will be added at the end of the list.
+ * @chainable
+ * @fires add
+ * @fires move
+ */
+ oo.EmitterList.prototype.addItems = function ( items, index ) {
+ var i, oldIndex;
+
+ if ( !Array.isArray( items ) ) {
+ items = [ items ];
+ }
+
+ if ( items.length === 0 ) {
+ return this;
+ }
+
+ index = normalizeArrayIndex( this.items, index );
+ for ( i = 0; i < items.length; i++ ) {
+ oldIndex = this.items.indexOf( items[ i ] );
+ if ( oldIndex !== -1 ) {
+ // Move item to new index
+ index = this.moveItem( items[ i ], index );
+ this.emit( 'move', items[ i ], index, oldIndex );
+ } else {
+ // insert item at index
+ index = this.insertItem( items[ i ], index );
+ this.emit( 'add', items[ i ], index );
+ }
+ index++;
+ }
+
+ return this;
+ };
+
+ /**
+ * Move an item from its current position to a new index.
+ *
+ * The item is expected to exist in the list. If it doesn't,
+ * the method will throw an exception.
+ *
+ * @private
+ * @param {OO.EventEmitter} item Items to add
+ * @param {number} newIndex Index to move the item to
+ * @return {number} The index the item was moved to
+ * @throws {Error} If item is not in the list
+ */
+ oo.EmitterList.prototype.moveItem = function ( item, newIndex ) {
+ var existingIndex = this.items.indexOf( item );
+
+ if ( existingIndex === -1 ) {
+ throw new Error( 'Item cannot be moved, because it is not in the list.' );
+ }
+
+ newIndex = normalizeArrayIndex( this.items, newIndex );
+
+ // Remove the item from the current index
+ this.items.splice( existingIndex, 1 );
+
+ // If necessary, adjust new index after removal
+ if ( existingIndex < newIndex ) {
+ newIndex--;
+ }
+
+ // Move the item to the new index
+ this.items.splice( newIndex, 0, item );
+
+ return newIndex;
+ };
+
+ /**
+ * Utility method to insert an item into the list, and
+ * connect it to aggregate events.
+ *
+ * Don't call this directly unless you know what you're doing.
+ * Use #addItems instead.
+ *
+ * This method can be extended in child classes to produce
+ * different behavior when an item is inserted. For example,
+ * inserted items may also be attached to the DOM or may
+ * interact with some other nodes in certain ways. Extending
+ * this method is allowed, but if overriden, the aggregation
+ * of events must be preserved, or behavior of emitted events
+ * will be broken.
+ *
+ * If you are extending this method, please make sure the
+ * parent method is called.
+ *
+ * @protected
+ * @param {OO.EventEmitter} item Items to add
+ * @param {number} index Index to add items at
+ * @return {number} The index the item was added at
+ */
+ oo.EmitterList.prototype.insertItem = function ( item, index ) {
+ var events, event;
+
+ // Add the item to event aggregation
+ if ( item.connect && item.disconnect ) {
+ events = {};
+ for ( event in this.aggregateItemEvents ) {
+ events[ event ] = [ 'emit', this.aggregateItemEvents[ event ], item ];
+ }
+ item.connect( this, events );
+ }
+
+ index = normalizeArrayIndex( this.items, index );
+
+ // Insert into items array
+ this.items.splice( index, 0, item );
+ return index;
+ };
+
+ /**
+ * Remove items.
+ *
+ * @param {OO.EventEmitter[]} items Items to remove
+ * @chainable
+ * @fires remove
+ */
+ oo.EmitterList.prototype.removeItems = function ( items ) {
+ var i, item, index;
+
+ if ( !Array.isArray( items ) ) {
+ items = [ items ];
+ }
+
+ if ( items.length === 0 ) {
+ return this;
+ }
+
+ // Remove specific items
+ for ( i = 0; i < items.length; i++ ) {
+ item = items[ i ];
+ index = this.items.indexOf( item );
+ if ( index !== -1 ) {
+ if ( item.connect && item.disconnect ) {
+ // Disconnect all listeners from the item
+ item.disconnect( this );
+ }
+ this.items.splice( index, 1 );
+ this.emit( 'remove', item, index );
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Clear all items
+ *
+ * @chainable
+ * @fires clear
+ */
+ oo.EmitterList.prototype.clearItems = function () {
+ var i, item,
+ cleared = this.items.splice( 0, this.items.length );
+
+ // Disconnect all items
+ for ( i = 0; i < cleared.length; i++ ) {
+ item = cleared[ i ];
+ if ( item.connect && item.disconnect ) {
+ item.disconnect( this );
+ }
+ }
+
+ this.emit( 'clear' );