| 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 |
1×
6816×
6816×
6816×
6816×
1×
1×
6816×
6816×
1×
7978×
7978×
3005×
3005×
195×
7783×
1×
1×
2697×
1420×
1277×
1277×
1277×
1×
1×
1950×
1950×
1950×
1950×
1×
1950×
1948×
2×
2×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
3830×
4×
4×
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 {any} 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 {any} 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 ) {
if ( index === undefined || index < 0 || index >= this.items.length ) {
this.$group.append( item.$element );
} else Eif ( 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;
};
|