all files / src/src/mixins/ GroupElement.js

77.33% Statements 58/75
65.22% Branches 15/23
80% Functions 8/10
76.92% Lines 50/65
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260                                    6812×     6812×     6812×     6812×                                               6812× 6812×                           7974×   7974× 2999× 2999× 195×       7779×                                                                         2691× 1418×       1273×   1273× 1273×                                                                                                     1946× 1946×     1946×   1946×                   1946× 1946×                                                                             3830×       3830×   3830× 3830×    
/**
 * Any OOUI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
 * {@link OO.ui.OptionWidget options}) mixes in GroupElement. Adding, removing, and clearing
 * items from the group is done through the interface the class provides.
 * For more information, please see the [OOUI documentation on MediaWiki][1].
 *
 * [1]: https://www.mediawiki.org/wiki/OOUI/Elements/Groups
 *
 * @abstract
 * @mixes OO.EmitterList
 * @class
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @param {jQuery} [config.$group] The container element created by the class. If this configuration
 *  is omitted, the group element will use a generated `<div>`.
 */
OO.ui.mixin.GroupElement = function OoUiMixinGroupElement( config ) {
	// Configuration initialization
	config = config || {};
 
	// Mixin constructors
	OO.EmitterList.call( this, config );
 
	// Properties
	this.$group = null;
 
	// Initialization
	this.setGroupElement( config.$group || $( '<div>' ) );
};
 
/* Setup */
 
OO.mixinClass( OO.ui.mixin.GroupElement, OO.EmitterList );
 
/* Events */
 
/**
 * A change event is emitted when the set of selected items changes.
 *
 * @event OO.ui.mixin.GroupElement#change
 * @param {OO.ui.Element[]} items Items currently in the group
 */
 
/* Methods */
 
/**
 * Set the group element.
 *
 * If an element is already set, items will be moved to the new element.
 *
 * @param {jQuery} $group Element to use as group
 */
OO.ui.mixin.GroupElement.prototype.setGroupElement = function ( $group ) {
	this.$group = $group;
	for ( let i = 0, len = this.items.length; i < len; i++ ) {
		this.$group.append( this.items[ i ].$element );
	}
};
 
/**
 * Find an item by its data.
 *
 * Only the first item with matching data will be returned. To return all matching items,
 * use the #findItemsFromData method.
 *
 * @param {Mixed} data Item data to search for
 * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
 */
OO.ui.mixin.GroupElement.prototype.findItemFromData = function ( data ) {
	const hash = OO.getHash( data );
 
	for ( let i = 0, len = this.items.length; i < len; i++ ) {
		const item = this.items[ i ];
		if ( hash === OO.getHash( item.getData() ) ) {
			return item;
		}
	}
 
	return null;
};
 
/**
 * Find items by their data.
 *
 * All items with matching data will be returned. To return only the first match, use the
 * #findItemFromData method instead.
 *
 * @param {Mixed} data Item data to search for
 * @return {OO.ui.Element[]} Items with equivalent data
 */
OO.ui.mixin.GroupElement.prototype.findItemsFromData = function ( data ) {
	const hash = OO.getHash( data ),
		items = [];
 
	for ( let i = 0, len = this.items.length; i < len; i++ ) {
		const item = this.items[ i ];
		if ( hash === OO.getHash( item.getData() ) ) {
			items.push( item );
		}
	}
 
	return items;
};
 
/**
 * Add items to the group.
 *
 * Items will be added to the end of the group array unless the optional `index` parameter
 * specifies a different insertion point. Adding an existing item will move it to the end of the
 * array or the point specified by the `index`.
 *
 * @param {OO.ui.Element|OO.ui.Element[]} [items] Elements to add to the group
 * @param {number} [index] Index of the insertion point
 * @chainable
 * @return {OO.ui.Element} The element, for chaining
 */
OO.ui.mixin.GroupElement.prototype.addItems = function ( items, index ) {
	if ( !items || items.length === 0 ) {
		return this;
	}
 
	// Mixin method
	OO.EmitterList.prototype.addItems.call( this, items, index );
 
	this.emit( 'change', this.getItems() );
	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.
 *
 * See https://doc.wikimedia.org/oojs/master/OO.EmitterList.html
 *
 * @private
 * @param {OO.EventEmitter} items Item 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.ui.mixin.GroupElement.prototype.moveItem = function ( items, newIndex ) {
	// insertItemElements expects this.items to not have been modified yet, so call before the mixin
	this.insertItemElements( items, newIndex );
 
	// Mixin method
	newIndex = OO.EmitterList.prototype.moveItem.call( this, items, newIndex );
 
	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 overridden, 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.
 *
 * See https://doc.wikimedia.org/oojs/master/OO.EmitterList.html
 *
 * @protected
 * @param {OO.EventEmitter|Object} item Item to add
 * @param {number} index Index to add items at
 * @return {number} The index the item was added at
 */
OO.ui.mixin.GroupElement.prototype.insertItem = function ( item, index ) {
	item.setElementGroup( this );
	this.insertItemElements( item, index );
 
	// Mixin method
	index = OO.EmitterList.prototype.insertItem.call( this, item, index );
 
	return index;
};
 
/**
 * Insert elements into the group
 *
 * @private
 * @param {OO.ui.Element} item Item to insert
 * @param {number} index Insertion index
 */
OO.ui.mixin.GroupElement.prototype.insertItemElements = function ( item, index ) {
	Eif ( index === undefined || index < 0 || index >= this.items.length ) {
		this.$group.append( item.$element );
	} else if ( index === 0 ) {
		this.$group.prepend( item.$element );
	} else {
		this.items[ index ].$element.before( item.$element );
	}
};
 
/**
 * Remove the specified items from a group.
 *
 * Removed items are detached (not removed) from the DOM so that they may be reused.
 * To remove all items from a group, you may wish to use the #clearItems method instead.
 *
 * @param {OO.ui.Element[]} items An array of items to remove
 * @chainable
 * @return {OO.ui.Element} The element, for chaining
 */
OO.ui.mixin.GroupElement.prototype.removeItems = function ( items ) {
	Iif ( items.length === 0 ) {
		return this;
	}
 
	// Remove specific items elements
	for ( let i = 0, len = items.length; i < len; i++ ) {
		const item = items[ i ];
		const index = this.items.indexOf( item );
		Eif ( index !== -1 ) {
			item.setElementGroup( null );
			item.$element.detach();
		}
	}
 
	// Mixin method
	OO.EmitterList.prototype.removeItems.call( this, items );
 
	this.emit( 'change', this.getItems() );
	return this;
};
 
/**
 * Clear all items from the group.
 *
 * Cleared items are detached from the DOM, not removed, so that they may be reused.
 * To remove only a subset of items from a group, use the #removeItems method.
 *
 * @chainable
 * @return {OO.ui.Element} The element, for chaining
 */
OO.ui.mixin.GroupElement.prototype.clearItems = function () {
	// Remove all item elements
	for ( let i = 0, len = this.items.length; i < len; i++ ) {
		this.items[ i ].setElementGroup( null );
		this.items[ i ].$element.detach();
	}
 
	// Mixin method
	OO.EmitterList.prototype.clearItems.call( this );
 
	this.emit( 'change', this.getItems() );
	return this;
};