Show redirect fragments on Special:ListRedirects
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.FilterGroup.js
1 ( function ( mw ) {
2 /**
3 * View model for a filter group
4 *
5 * @mixins OO.EventEmitter
6 * @mixins OO.EmitterList
7 *
8 * @constructor
9 * @param {string} name Group name
10 * @param {Object} [config] Configuration options
11 * @cfg {string} [type='send_unselected_if_any'] Group type
12 * @cfg {string} [view='default'] Name of the display group this group
13 * is a part of.
14 * @cfg {boolean} [isSticky] This group is using a 'sticky' default; meaning
15 * that every time a value is changed, it becomes the new default
16 * @cfg {string} [title] Group title
17 * @cfg {boolean} [hidden] This group is hidden from the regular menu views
18 * @cfg {boolean} [allowArbitrary] Allows for an arbitrary value to be added to the
19 * group from the URL, even if it wasn't initially set up.
20 * @cfg {string} [separator='|'] Value separator for 'string_options' groups
21 * @cfg {boolean} [active] Group is active
22 * @cfg {boolean} [fullCoverage] This filters in this group collectively cover all results
23 * @cfg {Object} [conflicts] Defines the conflicts for this filter group
24 * @cfg {string|Object} [labelPrefixKey] An i18n key defining the prefix label for this
25 * group. If the prefix has 'invert' state, the parameter is expected to be an object
26 * with 'default' and 'inverted' as keys.
27 * @cfg {Object} [whatsThis] Defines the messages that should appear for the 'what's this' popup
28 * @cfg {string} [whatsThis.header] The header of the whatsThis popup message
29 * @cfg {string} [whatsThis.body] The body of the whatsThis popup message
30 * @cfg {string} [whatsThis.url] The url for the link in the whatsThis popup message
31 * @cfg {string} [whatsThis.linkMessage] The text for the link in the whatsThis popup message
32 */
33 mw.rcfilters.dm.FilterGroup = function MwRcfiltersDmFilterGroup( name, config ) {
34 config = config || {};
35
36 // Mixin constructor
37 OO.EventEmitter.call( this );
38 OO.EmitterList.call( this );
39
40 this.name = name;
41 this.type = config.type || 'send_unselected_if_any';
42 this.view = config.view || 'default';
43 this.sticky = !!config.isSticky;
44 this.title = config.title || name;
45 this.hidden = !!config.hidden;
46 this.allowArbitrary = !!config.allowArbitrary;
47 this.separator = config.separator || '|';
48 this.labelPrefixKey = config.labelPrefixKey;
49
50 this.currSelected = null;
51 this.active = !!config.active;
52 this.fullCoverage = !!config.fullCoverage;
53
54 this.whatsThis = config.whatsThis || {};
55
56 this.conflicts = config.conflicts || {};
57 this.defaultParams = {};
58 this.defaultFilters = {};
59
60 this.aggregate( { update: 'filterItemUpdate' } );
61 this.connect( this, { filterItemUpdate: 'onFilterItemUpdate' } );
62 };
63
64 /* Initialization */
65 OO.initClass( mw.rcfilters.dm.FilterGroup );
66 OO.mixinClass( mw.rcfilters.dm.FilterGroup, OO.EventEmitter );
67 OO.mixinClass( mw.rcfilters.dm.FilterGroup, OO.EmitterList );
68
69 /* Events */
70
71 /**
72 * @event update
73 *
74 * Group state has been updated
75 */
76
77 /* Methods */
78
79 /**
80 * Initialize the group and create its filter items
81 *
82 * @param {Object} filterDefinition Filter definition for this group
83 * @param {string|Object} [groupDefault] Definition of the group default
84 */
85 mw.rcfilters.dm.FilterGroup.prototype.initializeFilters = function ( filterDefinition, groupDefault ) {
86 var defaultParam,
87 supersetMap = {},
88 model = this,
89 items = [];
90
91 filterDefinition.forEach( function ( filter ) {
92 // Instantiate an item
93 var subsetNames = [],
94 filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, {
95 group: model.getName(),
96 label: filter.label || filter.name,
97 description: filter.description || '',
98 labelPrefixKey: model.labelPrefixKey,
99 cssClass: filter.cssClass,
100 identifiers: filter.identifiers
101 } );
102
103 if ( filter.subset ) {
104 filter.subset = filter.subset.map( function ( el ) {
105 return el.filter;
106 } );
107
108 subsetNames = [];
109
110 filter.subset.forEach( function ( subsetFilterName ) { // eslint-disable-line no-loop-func
111 // Subsets (unlike conflicts) are always inside the same group
112 // We can re-map the names of the filters we are getting from
113 // the subsets with the group prefix
114 var subsetName = model.getPrefixedName( subsetFilterName );
115 // For convenience, we should store each filter's "supersets" -- these are
116 // the filters that have that item in their subset list. This will just
117 // make it easier to go through whether the item has any other items
118 // that affect it (and are selected) at any given time
119 supersetMap[ subsetName ] = supersetMap[ subsetName ] || [];
120 mw.rcfilters.utils.addArrayElementsUnique(
121 supersetMap[ subsetName ],
122 filterItem.getName()
123 );
124
125 // Translate subset param name to add the group name, so we
126 // get consistent naming. We know that subsets are only within
127 // the same group
128 subsetNames.push( subsetName );
129 } );
130
131 // Set translated subset
132 filterItem.setSubset( subsetNames );
133 }
134
135 items.push( filterItem );
136
137 // Store default parameter state; in this case, default is defined per filter
138 if (
139 model.getType() === 'send_unselected_if_any' ||
140 model.getType() === 'boolean'
141 ) {
142 // Store the default parameter state
143 // For this group type, parameter values are direct
144 // We need to convert from a boolean to a string ('1' and '0')
145 model.defaultParams[ filter.name ] = String( Number( !!filter.default ) );
146 }
147 } );
148
149 // Add items
150 this.addItems( items );
151
152 // Now that we have all items, we can apply the superset map
153 this.getItems().forEach( function ( filterItem ) {
154 filterItem.setSuperset( supersetMap[ filterItem.getName() ] );
155 } );
156
157 // Store default parameter state; in this case, default is defined per the
158 // entire group, given by groupDefault method parameter
159 if ( this.getType() === 'string_options' ) {
160 // Store the default parameter group state
161 // For this group, the parameter is group name and value is the names
162 // of selected items
163 this.defaultParams[ this.getName() ] = mw.rcfilters.utils.normalizeParamOptions(
164 // Current values
165 groupDefault ?
166 groupDefault.split( this.getSeparator() ) :
167 [],
168 // Legal values
169 this.getItems().map( function ( item ) {
170 return item.getParamName();
171 } )
172 ).join( this.getSeparator() );
173 } else if ( this.getType() === 'single_option' ) {
174 defaultParam = groupDefault !== undefined ?
175 groupDefault : this.getItems()[ 0 ].getParamName();
176
177 // For this group, the parameter is the group name,
178 // and a single item can be selected: default or first item
179 this.defaultParams[ this.getName() ] = defaultParam;
180 }
181
182 // Store default filter state based on default params
183 this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() );
184
185 // Check for filters that should be initially selected by their default value
186 if ( this.isSticky() ) {
187 $.each( this.defaultFilters, function ( filterName, filterValue ) {
188 model.getItemByName( filterName ).toggleSelected( filterValue );
189 } );
190 }
191
192 // Verify that single_option group has at least one item selected
193 if (
194 this.getType() === 'single_option' &&
195 this.getSelectedItems().length === 0
196 ) {
197 defaultParam = groupDefault !== undefined ?
198 groupDefault : this.getItems()[ 0 ].getParamName();
199
200 // Single option means there must be a single option
201 // selected, so we have to either select the default
202 // or select the first option
203 this.selectItemByParamName( defaultParam );
204 }
205 };
206
207 /**
208 * Respond to filterItem update event
209 *
210 * @param {mw.rcfilters.dm.FilterItem} item Updated filter item
211 * @fires update
212 */
213 mw.rcfilters.dm.FilterGroup.prototype.onFilterItemUpdate = function ( item ) {
214 // Update state
215 var changed = false,
216 active = this.areAnySelected();
217
218 if (
219 item.isSelected() &&
220 this.getType() === 'single_option' &&
221 this.currSelected &&
222 this.currSelected !== item
223 ) {
224 this.currSelected.toggleSelected( false );
225 }
226
227 // For 'single_option' groups, check if we just unselected all
228 // items. This should never be the result. If we did unselect
229 // all (like resetting all filters to false) then this group
230 // must choose its default item or the first item in the group
231 if (
232 this.getType() === 'single_option' &&
233 !this.getItems().some( function ( filterItem ) {
234 return filterItem.isSelected();
235 } )
236 ) {
237 // Single option means there must be a single option
238 // selected, so we have to either select the default
239 // or select the first option
240 this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ) ||
241 this.getItems()[ 0 ];
242 this.currSelected.toggleSelected( true );
243 changed = true;
244 }
245
246 if (
247 changed ||
248 this.active !== active ||
249 this.currSelected !== item
250 ) {
251 if ( this.isSticky() ) {
252 // If this group is sticky, then change the default according to the
253 // current selection.
254 this.defaultParams = this.getParamRepresentation( this.getSelectedState() );
255 }
256
257 this.active = active;
258 this.currSelected = item;
259
260 this.emit( 'update' );
261 }
262 };
263
264 /**
265 * Get group active state
266 *
267 * @return {boolean} Active state
268 */
269 mw.rcfilters.dm.FilterGroup.prototype.isActive = function () {
270 return this.active;
271 };
272
273 /**
274 * Get group hidden state
275 *
276 * @return {boolean} Hidden state
277 */
278 mw.rcfilters.dm.FilterGroup.prototype.isHidden = function () {
279 return this.hidden;
280 };
281
282 /**
283 * Get group allow arbitrary state
284 *
285 * @return {boolean} Group allows an arbitrary value from the URL
286 */
287 mw.rcfilters.dm.FilterGroup.prototype.isAllowArbitrary = function () {
288 return this.allowArbitrary;
289 };
290
291 /**
292 * Get group name
293 *
294 * @return {string} Group name
295 */
296 mw.rcfilters.dm.FilterGroup.prototype.getName = function () {
297 return this.name;
298 };
299
300 /**
301 * Get the default param state of this group
302 *
303 * @return {Object} Default param state
304 */
305 mw.rcfilters.dm.FilterGroup.prototype.getDefaultParams = function () {
306 return this.defaultParams;
307 };
308
309 /**
310 * Get the default filter state of this group
311 *
312 * @return {Object} Default filter state
313 */
314 mw.rcfilters.dm.FilterGroup.prototype.getDefaultFilters = function () {
315 return this.defaultFilters;
316 };
317
318 /**
319 * This is for a single_option and string_options group types
320 * it returns the value of the default
321 *
322 * @return {string} Value of the default
323 */
324 mw.rcfilters.dm.FilterGroup.prototype.getDefaulParamValue = function () {
325 return this.defaultParams[ this.getName() ];
326 };
327 /**
328 * Get the messags defining the 'whats this' popup for this group
329 *
330 * @return {Object} What's this messages
331 */
332 mw.rcfilters.dm.FilterGroup.prototype.getWhatsThis = function () {
333 return this.whatsThis;
334 };
335
336 /**
337 * Check whether this group has a 'what's this' message
338 *
339 * @return {boolean} This group has a what's this message
340 */
341 mw.rcfilters.dm.FilterGroup.prototype.hasWhatsThis = function () {
342 return !!this.whatsThis.body;
343 };
344
345 /**
346 * Get the conflicts associated with the entire group.
347 * Conflict object is set up by filter name keys and conflict
348 * definition. For example:
349 * [
350 * {
351 * filterName: {
352 * filter: filterName,
353 * group: group1
354 * }
355 * },
356 * {
357 * filterName2: {
358 * filter: filterName2,
359 * group: group2
360 * }
361 * }
362 * ]
363 * @return {Object} Conflict definition
364 */
365 mw.rcfilters.dm.FilterGroup.prototype.getConflicts = function () {
366 return this.conflicts;
367 };
368
369 /**
370 * Set conflicts for this group. See #getConflicts for the expected
371 * structure of the definition.
372 *
373 * @param {Object} conflicts Conflicts for this group
374 */
375 mw.rcfilters.dm.FilterGroup.prototype.setConflicts = function ( conflicts ) {
376 this.conflicts = conflicts;
377 };
378
379 /**
380 * Set conflicts for each filter item in the group based on the
381 * given conflict map
382 *
383 * @param {Object} conflicts Object representing the conflict map,
384 * keyed by the item name, where its value is an object for all its conflicts
385 */
386 mw.rcfilters.dm.FilterGroup.prototype.setFilterConflicts = function ( conflicts ) {
387 this.getItems().forEach( function ( filterItem ) {
388 if ( conflicts[ filterItem.getName() ] ) {
389 filterItem.setConflicts( conflicts[ filterItem.getName() ] );
390 }
391 } );
392 };
393
394 /**
395 * Check whether this item has a potential conflict with the given item
396 *
397 * This checks whether the given item is in the list of conflicts of
398 * the current item, but makes no judgment about whether the conflict
399 * is currently at play (either one of the items may not be selected)
400 *
401 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item
402 * @return {boolean} This item has a conflict with the given item
403 */
404 mw.rcfilters.dm.FilterGroup.prototype.existsInConflicts = function ( filterItem ) {
405 return Object.prototype.hasOwnProperty.call( this.getConflicts(), filterItem.getName() );
406 };
407
408 /**
409 * Check whether there are any items selected
410 *
411 * @return {boolean} Any items in the group are selected
412 */
413 mw.rcfilters.dm.FilterGroup.prototype.areAnySelected = function () {
414 return this.getItems().some( function ( filterItem ) {
415 return filterItem.isSelected();
416 } );
417 };
418
419 /**
420 * Check whether all items selected
421 *
422 * @return {boolean} All items are selected
423 */
424 mw.rcfilters.dm.FilterGroup.prototype.areAllSelected = function () {
425 var selected = [],
426 unselected = [];
427
428 this.getItems().forEach( function ( filterItem ) {
429 if ( filterItem.isSelected() ) {
430 selected.push( filterItem );
431 } else {
432 unselected.push( filterItem );
433 }
434 } );
435
436 if ( unselected.length === 0 ) {
437 return true;
438 }
439
440 // check if every unselected is a subset of a selected
441 return unselected.every( function ( unselectedFilterItem ) {
442 return selected.some( function ( selectedFilterItem ) {
443 return selectedFilterItem.existsInSubset( unselectedFilterItem.getName() );
444 } );
445 } );
446 };
447
448 /**
449 * Get all selected items in this group
450 *
451 * @param {mw.rcfilters.dm.FilterItem} [excludeItem] Item to exclude from the list
452 * @return {mw.rcfilters.dm.FilterItem[]} Selected items
453 */
454 mw.rcfilters.dm.FilterGroup.prototype.getSelectedItems = function ( excludeItem ) {
455 var excludeName = ( excludeItem && excludeItem.getName() ) || '';
456
457 return this.getItems().filter( function ( item ) {
458 return item.getName() !== excludeName && item.isSelected();
459 } );
460 };
461
462 /**
463 * Check whether all selected items are in conflict with the given item
464 *
465 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item to test
466 * @return {boolean} All selected items are in conflict with this item
467 */
468 mw.rcfilters.dm.FilterGroup.prototype.areAllSelectedInConflictWith = function ( filterItem ) {
469 var selectedItems = this.getSelectedItems( filterItem );
470
471 return selectedItems.length > 0 &&
472 (
473 // The group as a whole is in conflict with this item
474 this.existsInConflicts( filterItem ) ||
475 // All selected items are in conflict individually
476 selectedItems.every( function ( selectedFilter ) {
477 return selectedFilter.existsInConflicts( filterItem );
478 } )
479 );
480 };
481
482 /**
483 * Check whether any of the selected items are in conflict with the given item
484 *
485 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item to test
486 * @return {boolean} Any of the selected items are in conflict with this item
487 */
488 mw.rcfilters.dm.FilterGroup.prototype.areAnySelectedInConflictWith = function ( filterItem ) {
489 var selectedItems = this.getSelectedItems( filterItem );
490
491 return selectedItems.length > 0 && (
492 // The group as a whole is in conflict with this item
493 this.existsInConflicts( filterItem ) ||
494 // Any selected items are in conflict individually
495 selectedItems.some( function ( selectedFilter ) {
496 return selectedFilter.existsInConflicts( filterItem );
497 } )
498 );
499 };
500
501 /**
502 * Get the parameter representation from this group
503 *
504 * @param {Object} [filterRepresentation] An object defining the state
505 * of the filters in this group, keyed by their name and current selected
506 * state value.
507 * @return {Object} Parameter representation
508 */
509 mw.rcfilters.dm.FilterGroup.prototype.getParamRepresentation = function ( filterRepresentation ) {
510 var values,
511 areAnySelected = false,
512 buildFromCurrentState = !filterRepresentation,
513 defaultFilters = this.getDefaultFilters(),
514 result = {},
515 model = this,
516 filterParamNames = {},
517 getSelectedParameter = function ( filters ) {
518 var item,
519 selected = [];
520
521 // Find if any are selected
522 $.each( filters, function ( name, value ) {
523 if ( value ) {
524 selected.push( name );
525 }
526 } );
527
528 item = model.getItemByName( selected[ 0 ] );
529 return ( item && item.getParamName() ) || '';
530 };
531
532 filterRepresentation = filterRepresentation || {};
533
534 // Create or complete the filterRepresentation definition
535 this.getItems().forEach( function ( item ) {
536 // Map filter names to their parameter names
537 filterParamNames[ item.getName() ] = item.getParamName();
538
539 if ( buildFromCurrentState ) {
540 // This means we have not been given a filter representation
541 // so we are building one based on current state
542 filterRepresentation[ item.getName() ] = item.isSelected();
543 } else if ( filterRepresentation[ item.getName() ] === undefined ) {
544 // We are given a filter representation, but we have to make
545 // sure that we fill in the missing filters if there are any
546 // we will assume they are all falsey
547 if ( model.isSticky() ) {
548 filterRepresentation[ item.getName() ] = !!defaultFilters[ item.getName() ];
549 } else {
550 filterRepresentation[ item.getName() ] = false;
551 }
552 }
553
554 if ( filterRepresentation[ item.getName() ] ) {
555 areAnySelected = true;
556 }
557 } );
558
559 // Build result
560 if (
561 this.getType() === 'send_unselected_if_any' ||
562 this.getType() === 'boolean'
563 ) {
564 // First, check if any of the items are selected at all.
565 // If none is selected, we're treating it as if they are
566 // all false
567
568 // Go over the items and define the correct values
569 $.each( filterRepresentation, function ( name, value ) {
570 // We must store all parameter values as strings '0' or '1'
571 if ( model.getType() === 'send_unselected_if_any' ) {
572 result[ filterParamNames[ name ] ] = areAnySelected ?
573 String( Number( !value ) ) :
574 '0';
575 } else if ( model.getType() === 'boolean' ) {
576 // Representation is straight-forward and direct from
577 // the parameter value to the filter state
578 result[ filterParamNames[ name ] ] = String( Number( !!value ) );
579 }
580 } );
581 } else if ( this.getType() === 'string_options' ) {
582 values = [];
583
584 $.each( filterRepresentation, function ( name, value ) {
585 // Collect values
586 if ( value ) {
587 values.push( filterParamNames[ name ] );
588 }
589 } );
590
591 result[ this.getName() ] = ( values.length === Object.keys( filterRepresentation ).length ) ?
592 'all' : values.join( this.getSeparator() );
593 } else if ( this.getType() === 'single_option' ) {
594 result[ this.getName() ] = getSelectedParameter( filterRepresentation );
595 }
596
597 return result;
598 };
599
600 /**
601 * Get the filter representation this group would provide
602 * based on given parameter states.
603 *
604 * @param {Object} [paramRepresentation] An object defining a parameter
605 * state to translate the filter state from. If not given, an object
606 * representing all filters as falsey is returned; same as if the parameter
607 * given were an empty object, or had some of the filters missing.
608 * @return {Object} Filter representation
609 */
610 mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) {
611 var areAnySelected, paramValues, item, currentValue,
612 oneWasSelected = false,
613 defaultParams = this.getDefaultParams(),
614 expandedParams = $.extend( true, {}, paramRepresentation ),
615 model = this,
616 paramToFilterMap = {},
617 result = {};
618
619 if ( this.isSticky() ) {
620 // If the group is sticky, check if all parameters are represented
621 // and for those that aren't represented, add them with their default
622 // values
623 paramRepresentation = $.extend( true, {}, this.getDefaultParams(), paramRepresentation );
624 }
625
626 paramRepresentation = paramRepresentation || {};
627 if (
628 this.getType() === 'send_unselected_if_any' ||
629 this.getType() === 'boolean'
630 ) {
631 // Go over param representation; map and check for selections
632 this.getItems().forEach( function ( filterItem ) {
633 var paramName = filterItem.getParamName();
634
635 expandedParams[ paramName ] = paramRepresentation[ paramName ] || '0';
636 paramToFilterMap[ paramName ] = filterItem;
637
638 if ( Number( paramRepresentation[ filterItem.getParamName() ] ) ) {
639 areAnySelected = true;
640 }
641 } );
642
643 $.each( expandedParams, function ( paramName, paramValue ) {
644 var filterItem = paramToFilterMap[ paramName ];
645
646 if ( model.getType() === 'send_unselected_if_any' ) {
647 // Flip the definition between the parameter
648 // state and the filter state
649 // This is what the 'toggleSelected' value of the filter is
650 result[ filterItem.getName() ] = areAnySelected ?
651 !Number( paramValue ) :
652 // Otherwise, there are no selected items in the
653 // group, which means the state is false
654 false;
655 } else if ( model.getType() === 'boolean' ) {
656 // Straight-forward definition of state
657 result[ filterItem.getName() ] = !!Number( paramRepresentation[ filterItem.getParamName() ] );
658 }
659 } );
660 } else if ( this.getType() === 'string_options' ) {
661 currentValue = paramRepresentation[ this.getName() ] || '';
662
663 // Normalize the given parameter values
664 paramValues = mw.rcfilters.utils.normalizeParamOptions(
665 // Given
666 currentValue.split(
667 this.getSeparator()
668 ),
669 // Allowed values
670 this.getItems().map( function ( filterItem ) {
671 return filterItem.getParamName();
672 } )
673 );
674 // Translate the parameter values into a filter selection state
675 this.getItems().forEach( function ( filterItem ) {
676 // All true (either because all values are written or the term 'all' is written)
677 // is the same as all filters set to true
678 result[ filterItem.getName() ] = (
679 // If it is the word 'all'
680 paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
681 // All values are written
682 paramValues.length === model.getItemCount()
683 ) ?
684 true :
685 // Otherwise, the filter is selected only if it appears in the parameter values
686 paramValues.indexOf( filterItem.getParamName() ) > -1;
687 } );
688 } else if ( this.getType() === 'single_option' ) {
689 // There is parameter that fits a single filter and if not, get the default
690 this.getItems().forEach( function ( filterItem ) {
691 var selected = filterItem.getParamName() === paramRepresentation[ model.getName() ];
692
693 result[ filterItem.getName() ] = selected;
694 oneWasSelected = oneWasSelected || selected;
695 } );
696 }
697
698 // Go over result and make sure all filters are represented.
699 // If any filters are missing, they will get a falsey value
700 this.getItems().forEach( function ( filterItem ) {
701 if ( result[ filterItem.getName() ] === undefined ) {
702 result[ filterItem.getName() ] = false;
703 }
704 } );
705
706 // Make sure that at least one option is selected in
707 // single_option groups, no matter what path was taken
708 // If none was selected by the given definition, then
709 // we need to select the one in the base state -- either
710 // the default given, or the first item
711 if (
712 this.getType() === 'single_option' &&
713 !oneWasSelected
714 ) {
715 if ( defaultParams[ this.getName() ] ) {
716 item = this.getItemByParamName( defaultParams[ this.getName() ] );
717 } else {
718 item = this.getItems()[ 0 ];
719 }
720 result[ item.getName() ] = true;
721 }
722
723 return result;
724 };
725
726 /**
727 * Get current selected state of all filter items in this group
728 *
729 * @return {Object} Selected state
730 */
731 mw.rcfilters.dm.FilterGroup.prototype.getSelectedState = function () {
732 var state = {};
733
734 this.getItems().forEach( function ( filterItem ) {
735 state[ filterItem.getName() ] = filterItem.isSelected();
736 } );
737
738 return state;
739 };
740
741 /**
742 * Get item by its filter name
743 *
744 * @param {string} filterName Filter name
745 * @return {mw.rcfilters.dm.FilterItem} Filter item
746 */
747 mw.rcfilters.dm.FilterGroup.prototype.getItemByName = function ( filterName ) {
748 return this.getItems().filter( function ( item ) {
749 return item.getName() === filterName;
750 } )[ 0 ];
751 };
752
753 /**
754 * Select an item by its parameter name
755 *
756 * @param {string} paramName Filter parameter name
757 */
758 mw.rcfilters.dm.FilterGroup.prototype.selectItemByParamName = function ( paramName ) {
759 this.getItems().forEach( function ( item ) {
760 item.toggleSelected( item.getParamName() === String( paramName ) );
761 } );
762 };
763
764 /**
765 * Get item by its parameter name
766 *
767 * @param {string} paramName Parameter name
768 * @return {mw.rcfilters.dm.FilterItem} Filter item
769 */
770 mw.rcfilters.dm.FilterGroup.prototype.getItemByParamName = function ( paramName ) {
771 return this.getItems().filter( function ( item ) {
772 return item.getParamName() === String( paramName );
773 } )[ 0 ];
774 };
775
776 /**
777 * Get group type
778 *
779 * @return {string} Group type
780 */
781 mw.rcfilters.dm.FilterGroup.prototype.getType = function () {
782 return this.type;
783 };
784
785 /**
786 * Get display group
787 *
788 * @return {string} Display group
789 */
790 mw.rcfilters.dm.FilterGroup.prototype.getView = function () {
791 return this.view;
792 };
793
794 /**
795 * Get the prefix used for the filter names inside this group.
796 *
797 * @param {string} [name] Filter name to prefix
798 * @return {string} Group prefix
799 */
800 mw.rcfilters.dm.FilterGroup.prototype.getNamePrefix = function () {
801 return this.getName() + '__';
802 };
803
804 /**
805 * Get a filter name with the prefix used for the filter names inside this group.
806 *
807 * @param {string} name Filter name to prefix
808 * @return {string} Group prefix
809 */
810 mw.rcfilters.dm.FilterGroup.prototype.getPrefixedName = function ( name ) {
811 return this.getNamePrefix() + name;
812 };
813
814 /**
815 * Get group's title
816 *
817 * @return {string} Title
818 */
819 mw.rcfilters.dm.FilterGroup.prototype.getTitle = function () {
820 return this.title;
821 };
822
823 /**
824 * Get group's values separator
825 *
826 * @return {string} Values separator
827 */
828 mw.rcfilters.dm.FilterGroup.prototype.getSeparator = function () {
829 return this.separator;
830 };
831
832 /**
833 * Check whether the group is defined as full coverage
834 *
835 * @return {boolean} Group is full coverage
836 */
837 mw.rcfilters.dm.FilterGroup.prototype.isFullCoverage = function () {
838 return this.fullCoverage;
839 };
840
841 /**
842 * Check whether the group is defined as sticky default
843 *
844 * @return {boolean} Group is sticky default
845 */
846 mw.rcfilters.dm.FilterGroup.prototype.isSticky = function () {
847 return this.sticky;
848 };
849 }( mediaWiki ) );