mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
synced 2024-11-29 01:54:33 +00:00
273 lines
7 KiB
JavaScript
273 lines
7 KiB
JavaScript
|
( function ( $, mw ) {
|
|||
|
/**
|
|||
|
* Echo List mixin
|
|||
|
*
|
|||
|
* @mixin
|
|||
|
* @abstract
|
|||
|
* @constructor
|
|||
|
* @param {Object} config Configuration options
|
|||
|
*/
|
|||
|
mw.echo.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.echo.dm.NotificationItem[]} items Added items
|
|||
|
* @param {number} index Index items were added at
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @event remove Items have been removed
|
|||
|
* @param {mw.echo.dm.NotificationItem[]} items Removed items
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @event clear All items have been removed
|
|||
|
*/
|
|||
|
|
|||
|
/* Methods */
|
|||
|
|
|||
|
/**
|
|||
|
* Get all items
|
|||
|
*
|
|||
|
* @return {mw.echo.dm.NotificationItem[]} Items in the list
|
|||
|
*/
|
|||
|
mw.echo.dm.List.prototype.getItems = function () {
|
|||
|
return this.items.slice( 0 );
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Get an item by its id
|
|||
|
* @param {string} id Item id
|
|||
|
* @return {mw.echo.dm.NotificationItem} Item
|
|||
|
*/
|
|||
|
mw.echo.dm.List.prototype.getItemById = function ( id ) {
|
|||
|
return this.itemsById[ id ];
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Get the index of a specific item
|
|||
|
*
|
|||
|
* @param {mw.echo.dm.NotificationItem} item Requested item
|
|||
|
* @return {number} Index of the item
|
|||
|
*/
|
|||
|
mw.echo.dm.List.prototype.getItemIndex = function ( item ) {
|
|||
|
return this.items.indexOf( item );
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Get number of items
|
|||
|
*
|
|||
|
* @return {number} Number of items in the list
|
|||
|
*/
|
|||
|
mw.echo.dm.List.prototype.getItemCount = function () {
|
|||
|
return this.items.length;
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Check if a list contains no items.
|
|||
|
*
|
|||
|
* @return {boolean} Group is empty
|
|||
|
*/
|
|||
|
mw.echo.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.echo.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.echo.dm.NotificationItem[]} items Items to add
|
|||
|
* @param {number} index Index to add items at
|
|||
|
* @chainable
|
|||
|
* @fires add
|
|||
|
*/
|
|||
|
mw.echo.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.echo.dm.NotificationItem[]} items Items to remove
|
|||
|
* @chainable
|
|||
|
* @fires remove
|
|||
|
*/
|
|||
|
mw.echo.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.echo.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, mediaWiki ) );
|