63e13fdd8a8c8dcd3a800cf15c8b34512d6f53d0
3 * View model for a filter group
5 * @mixins OO.EventEmitter
6 * @mixins OO.EmitterList
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} [title] Group title
13 * @cfg {string} [separator='|'] Value separator for 'string_options' groups
14 * @cfg {boolean} [active] Group is active
15 * @cfg {boolean} [fullCoverage] This filters in this group collectively cover all results
16 * @cfg {Object} [conflicts] Defines the conflicts for this filter group
17 * @cfg {Object} [whatsThis] Defines the messages that should appear for the 'what's this' popup
18 * @cfg {string} [whatsThis.header] The header of the whatsThis popup message
19 * @cfg {string} [whatsThis.body] The body of the whatsThis popup message
20 * @cfg {string} [whatsThis.url] The url for the link in the whatsThis popup message
21 * @cfg {string} [whatsThis.linkMessage] The text for the link in the whatsThis popup message
23 mw
.rcfilters
.dm
.FilterGroup
= function MwRcfiltersDmFilterGroup( name
, config
) {
24 config
= config
|| {};
27 OO
.EventEmitter
.call( this );
28 OO
.EmitterList
.call( this );
31 this.type
= config
.type
|| 'send_unselected_if_any';
32 this.title
= config
.title
;
33 this.separator
= config
.separator
|| '|';
35 this.active
= !!config
.active
;
36 this.fullCoverage
= !!config
.fullCoverage
;
38 this.whatsThis
= config
.whatsThis
|| {};
40 this.conflicts
= config
.conflicts
|| {};
42 this.aggregate( { update
: 'filterItemUpdate' } );
43 this.connect( this, { filterItemUpdate
: 'onFilterItemUpdate' } );
47 OO
.initClass( mw
.rcfilters
.dm
.FilterGroup
);
48 OO
.mixinClass( mw
.rcfilters
.dm
.FilterGroup
, OO
.EventEmitter
);
49 OO
.mixinClass( mw
.rcfilters
.dm
.FilterGroup
, OO
.EmitterList
);
56 * Group state has been updated
62 * Respond to filterItem update event
66 mw
.rcfilters
.dm
.FilterGroup
.prototype.onFilterItemUpdate = function () {
68 var active
= this.areAnySelected();
70 if ( this.active
!== active
) {
72 this.emit( 'update' );
77 * Get group active state
79 * @return {boolean} Active state
81 mw
.rcfilters
.dm
.FilterGroup
.prototype.isActive = function () {
88 * @return {string} Group name
90 mw
.rcfilters
.dm
.FilterGroup
.prototype.getName = function () {
95 * Get the messags defining the 'whats this' popup for this group
97 * @return {Object} What's this messages
99 mw
.rcfilters
.dm
.FilterGroup
.prototype.getWhatsThis = function () {
100 return this.whatsThis
;
104 * Check whether this group has a 'what's this' message
106 * @return {boolean} This group has a what's this message
108 mw
.rcfilters
.dm
.FilterGroup
.prototype.hasWhatsThis = function () {
109 return !!this.whatsThis
.body
;
113 * Get the conflicts associated with the entire group.
114 * Conflict object is set up by filter name keys and conflict
115 * definition. For example:
119 * filter: filterName,
125 * filter: filterName2,
130 * @return {Object} Conflict definition
132 mw
.rcfilters
.dm
.FilterGroup
.prototype.getConflicts = function () {
133 return this.conflicts
;
137 * Set conflicts for this group. See #getConflicts for the expected
138 * structure of the definition.
140 * @param {Object} conflicts Conflicts for this group
142 mw
.rcfilters
.dm
.FilterGroup
.prototype.setConflicts = function ( conflicts
) {
143 this.conflicts
= conflicts
;
147 * Check whether this item has a potential conflict with the given item
149 * This checks whether the given item is in the list of conflicts of
150 * the current item, but makes no judgment about whether the conflict
151 * is currently at play (either one of the items may not be selected)
153 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item
154 * @return {boolean} This item has a conflict with the given item
156 mw
.rcfilters
.dm
.FilterGroup
.prototype.existsInConflicts = function ( filterItem
) {
157 return Object
.prototype.hasOwnProperty
.call( this.getConflicts(), filterItem
.getName() );
161 * Check whether there are any items selected
163 * @return {boolean} Any items in the group are selected
165 mw
.rcfilters
.dm
.FilterGroup
.prototype.areAnySelected = function () {
166 return this.getItems().some( function ( filterItem
) {
167 return filterItem
.isSelected();
172 * Check whether all items selected
174 * @return {boolean} All items are selected
176 mw
.rcfilters
.dm
.FilterGroup
.prototype.areAllSelected = function () {
180 this.getItems().forEach( function ( filterItem
) {
181 if ( filterItem
.isSelected() ) {
182 selected
.push( filterItem
);
184 unselected
.push( filterItem
);
188 if ( unselected
.length
=== 0 ) {
192 // check if every unselected is a subset of a selected
193 return unselected
.every( function ( unselectedFilterItem
) {
194 return selected
.some( function ( selectedFilterItem
) {
195 return selectedFilterItem
.existsInSubset( unselectedFilterItem
.getName() );
201 * Get all selected items in this group
203 * @param {mw.rcfilters.dm.FilterItem} [excludeItem] Item to exclude from the list
204 * @return {mw.rcfilters.dm.FilterItem[]} Selected items
206 mw
.rcfilters
.dm
.FilterGroup
.prototype.getSelectedItems = function ( excludeItem
) {
207 var excludeName
= ( excludeItem
&& excludeItem
.getName() ) || '';
209 return this.getItems().filter( function ( item
) {
210 return item
.getName() !== excludeName
&& item
.isSelected();
215 * Check whether all selected items are in conflict with the given item
217 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item to test
218 * @return {boolean} All selected items are in conflict with this item
220 mw
.rcfilters
.dm
.FilterGroup
.prototype.areAllSelectedInConflictWith = function ( filterItem
) {
221 var selectedItems
= this.getSelectedItems( filterItem
);
223 return selectedItems
.length
> 0 &&
225 // The group as a whole is in conflict with this item
226 this.existsInConflicts( filterItem
) ||
227 // All selected items are in conflict individually
228 selectedItems
.every( function ( selectedFilter
) {
229 return selectedFilter
.existsInConflicts( filterItem
);
235 * Check whether any of the selected items are in conflict with the given item
237 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item to test
238 * @return {boolean} Any of the selected items are in conflict with this item
240 mw
.rcfilters
.dm
.FilterGroup
.prototype.areAnySelectedInConflictWith = function ( filterItem
) {
241 var selectedItems
= this.getSelectedItems( filterItem
);
243 return selectedItems
.length
> 0 && (
244 // The group as a whole is in conflict with this item
245 this.existsInConflicts( filterItem
) ||
246 // Any selected items are in conflict individually
247 selectedItems
.some( function ( selectedFilter
) {
248 return selectedFilter
.existsInConflicts( filterItem
);
254 * Get the parameter representation from this group
256 * @param {Object} [filterRepresentation] An object defining the state
257 * of the filters in this group, keyed by their name and current selected
259 * @return {Object} Parameter representation
261 mw
.rcfilters
.dm
.FilterGroup
.prototype.getParamRepresentation = function ( filterRepresentation
) {
263 areAnySelected
= false,
264 buildFromCurrentState
= !filterRepresentation
,
266 filterParamNames
= {};
268 filterRepresentation
= filterRepresentation
|| {};
270 // Create or complete the filterRepresentation definition
271 this.getItems().forEach( function ( item
) {
272 // Map filter names to their parameter names
273 filterParamNames
[ item
.getName() ] = item
.getParamName();
275 if ( buildFromCurrentState
) {
276 // This means we have not been given a filter representation
277 // so we are building one based on current state
278 filterRepresentation
[ item
.getName() ] = item
.isSelected();
279 } else if ( !filterRepresentation
[ item
.getName() ] ) {
280 // We are given a filter representation, but we have to make
281 // sure that we fill in the missing filters if there are any
282 // we will assume they are all falsey
283 filterRepresentation
[ item
.getName() ] = false;
286 if ( filterRepresentation
[ item
.getName() ] ) {
287 areAnySelected
= true;
292 if ( this.getType() === 'send_unselected_if_any' ) {
293 // First, check if any of the items are selected at all.
294 // If none is selected, we're treating it as if they are
297 // Go over the items and define the correct values
298 $.each( filterRepresentation
, function ( name
, value
) {
299 result
[ filterParamNames
[ name
] ] = areAnySelected
?
300 Number( !value
) : 0;
302 } else if ( this.getType() === 'string_options' ) {
305 $.each( filterRepresentation
, function ( name
, value
) {
308 values
.push( filterParamNames
[ name
] );
312 result
[ this.getName() ] = ( values
.length
=== Object
.keys( filterRepresentation
).length
) ?
313 'all' : values
.join( this.getSeparator() );
322 * @return {string} Group type
324 mw
.rcfilters
.dm
.FilterGroup
.prototype.getType = function () {
329 * Get the prefix used for the filter names inside this group
331 * @return {string} Group prefix
333 mw
.rcfilters
.dm
.FilterGroup
.prototype.getNamePrefix = function () {
334 return this.getName() + '__';
340 * @return {string} Title
342 mw
.rcfilters
.dm
.FilterGroup
.prototype.getTitle = function () {
347 * Get group's values separator
349 * @return {string} Values separator
351 mw
.rcfilters
.dm
.FilterGroup
.prototype.getSeparator = function () {
352 return this.separator
;
356 * Check whether the group is defined as full coverage
358 * @return {boolean} Group is full coverage
360 mw
.rcfilters
.dm
.FilterGroup
.prototype.isFullCoverage = function () {
361 return this.fullCoverage
;