mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
synced 2024-11-25 00:05:29 +00:00
33126b69aa
In preparation of Code Sniffer based updates. Change-Id: Id5d43332b44a37665d57dc24ef8c432bc65b2f6a
271 lines
7 KiB
JavaScript
271 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 ) );
|