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