RCFilters UI: Separate name from paramName in filters
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.FilterItem.js
1 ( function ( mw ) {
2 /**
3 * Filter item model
4 *
5 * @mixins OO.EventEmitter
6 *
7 * @constructor
8 * @param {string} param Filter param name
9 * @param {mw.rcfilters.dm.FilterGroup} groupModel Filter group model
10 * @param {Object} config Configuration object
11 * @cfg {string} [group] The group this item belongs to
12 * @cfg {string} [label] The label for the filter
13 * @cfg {string} [description] The description of the filter
14 * @cfg {boolean} [active=true] The filter is active and affecting the result
15 * @cfg {string[]} [excludes=[]] A list of filter names this filter, if
16 * selected, makes inactive.
17 * @cfg {boolean} [selected] The item is selected
18 * @cfg {string[]} [subset] Defining the names of filters that are a subset of this filter
19 * @cfg {Object} [conflicts] Defines the conflicts for this filter
20 * @cfg {string} [cssClass] The class identifying the results that match this filter
21 */
22 mw.rcfilters.dm.FilterItem = function MwRcfiltersDmFilterItem( param, groupModel, config ) {
23 config = config || {};
24
25 // Mixin constructor
26 OO.EventEmitter.call( this );
27
28 this.param = param;
29 this.groupModel = groupModel;
30 this.name = this.groupModel.getNamePrefix() + param;
31
32 this.label = config.label || this.name;
33 this.description = config.description;
34 this.selected = !!config.selected;
35
36 // Interaction definitions
37 this.subset = config.subset || [];
38 this.conflicts = config.conflicts || {};
39 this.superset = [];
40
41 // Interaction states
42 this.included = false;
43 this.conflicted = false;
44 this.fullyCovered = false;
45
46 // Highlight
47 this.cssClass = config.cssClass;
48 this.highlightColor = null;
49 this.highlightEnabled = false;
50 };
51
52 /* Initialization */
53
54 OO.initClass( mw.rcfilters.dm.FilterItem );
55 OO.mixinClass( mw.rcfilters.dm.FilterItem, OO.EventEmitter );
56
57 /* Events */
58
59 /**
60 * @event update
61 *
62 * The state of this filter has changed
63 */
64
65 /* Methods */
66
67 /**
68 * Return the representation of the state of this item.
69 *
70 * @return {Object} State of the object
71 */
72 mw.rcfilters.dm.FilterItem.prototype.getState = function () {
73 return {
74 selected: this.isSelected(),
75 included: this.isIncluded(),
76 conflicted: this.isConflicted(),
77 fullyCovered: this.isFullyCovered()
78 };
79 };
80
81 /**
82 * Get the name of this filter
83 *
84 * @return {string} Filter name
85 */
86 mw.rcfilters.dm.FilterItem.prototype.getName = function () {
87 return this.name;
88 };
89
90 /**
91 * Get the param name or value of this filter
92 *
93 * @return {string} Filter param name
94 */
95 mw.rcfilters.dm.FilterItem.prototype.getParamName = function () {
96 return this.param;
97 };
98
99 /**
100 * Get the model of the group this filter belongs to
101 *
102 * @return {mw.rcfilters.dm.FilterGroup} Filter group model
103 */
104 mw.rcfilters.dm.FilterItem.prototype.getGroupModel = function () {
105 return this.groupModel;
106 };
107
108 /**
109 * Get the group name this filter belongs to
110 *
111 * @return {string} Filter group name
112 */
113 mw.rcfilters.dm.FilterItem.prototype.getGroupName = function () {
114 return this.groupModel.getName();
115 };
116
117 /**
118 * Get the label of this filter
119 *
120 * @return {string} Filter label
121 */
122 mw.rcfilters.dm.FilterItem.prototype.getLabel = function () {
123 return this.label;
124 };
125
126 /**
127 * Get the description of this filter
128 *
129 * @return {string} Filter description
130 */
131 mw.rcfilters.dm.FilterItem.prototype.getDescription = function () {
132 return this.description;
133 };
134
135 /**
136 * Get the default value of this filter
137 *
138 * @return {boolean} Filter default
139 */
140 mw.rcfilters.dm.FilterItem.prototype.getDefault = function () {
141 return this.default;
142 };
143
144 /**
145 * Get filter subset
146 * This is a list of filter names that are defined to be included
147 * when this filter is selected.
148 *
149 * @return {string[]} Filter subset
150 */
151 mw.rcfilters.dm.FilterItem.prototype.getSubset = function () {
152 return this.subset;
153 };
154
155 /**
156 * Get filter superset
157 * This is a generated list of filters that define this filter
158 * to be included when either of them is selected.
159 *
160 * @return {string[]} Filter superset
161 */
162 mw.rcfilters.dm.FilterItem.prototype.getSuperset = function () {
163 return this.superset;
164 };
165
166 /**
167 * Get the selected state of this filter
168 *
169 * @return {boolean} Filter is selected
170 */
171 mw.rcfilters.dm.FilterItem.prototype.isSelected = function () {
172 return this.selected;
173 };
174
175 /**
176 * Check whether the filter is currently in a conflict state
177 *
178 * @return {boolean} Filter is in conflict state
179 */
180 mw.rcfilters.dm.FilterItem.prototype.isConflicted = function () {
181 return this.conflicted;
182 };
183
184 /**
185 * Check whether the filter is currently in an already included subset
186 *
187 * @return {boolean} Filter is in an already-included subset
188 */
189 mw.rcfilters.dm.FilterItem.prototype.isIncluded = function () {
190 return this.included;
191 };
192
193 /**
194 * Check whether the filter is currently fully covered
195 *
196 * @return {boolean} Filter is in fully-covered state
197 */
198 mw.rcfilters.dm.FilterItem.prototype.isFullyCovered = function () {
199 return this.fullyCovered;
200 };
201
202 /**
203 * Get filter conflicts
204 *
205 * Conflict object is set up by filter name keys and conflict
206 * definition. For example:
207 * {
208 * filterName: {
209 * filter: filterName,
210 * group: group1
211 * }
212 * filterName2: {
213 * filter: filterName2,
214 * group: group2
215 * }
216 * }
217 *
218 * @return {Object} Filter conflicts
219 */
220 mw.rcfilters.dm.FilterItem.prototype.getConflicts = function () {
221 return $.extend( {}, this.conflicts, this.getGroupModel().getConflicts() );
222 };
223
224 /**
225 * Set conflicts for this filter. See #getConflicts for the expected
226 * structure of the definition.
227 *
228 * @param {Object} conflicts Conflicts for this filter
229 */
230 mw.rcfilters.dm.FilterItem.prototype.setConflicts = function ( conflicts ) {
231 this.conflicts = conflicts || {};
232 };
233
234 /**
235 * Set filter superset
236 *
237 * @param {string[]} superset Filter superset
238 */
239 mw.rcfilters.dm.FilterItem.prototype.setSuperset = function ( superset ) {
240 this.superset = superset || [];
241 };
242
243 /**
244 * Set filter subset
245 *
246 * @param {string[]} subset Filter subset
247 */
248 mw.rcfilters.dm.FilterItem.prototype.setSubset = function ( subset ) {
249 this.subset = subset || [];
250 };
251
252 /**
253 * Check whether a filter exists in the subset list for this filter
254 *
255 * @param {string} filterName Filter name
256 * @return {boolean} Filter name is in the subset list
257 */
258 mw.rcfilters.dm.FilterItem.prototype.existsInSubset = function ( filterName ) {
259 return this.subset.indexOf( filterName ) > -1;
260 };
261
262 /**
263 * Check whether this item has a potential conflict with the given item
264 *
265 * This checks whether the given item is in the list of conflicts of
266 * the current item, but makes no judgment about whether the conflict
267 * is currently at play (either one of the items may not be selected)
268 *
269 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item
270 * @return {boolean} This item has a conflict with the given item
271 */
272 mw.rcfilters.dm.FilterItem.prototype.existsInConflicts = function ( filterItem ) {
273 return Object.prototype.hasOwnProperty.call( this.getConflicts(), filterItem.getName() );
274 };
275
276 /**
277 * Set the state of this filter as being conflicted
278 * (This means any filters in its conflicts are selected)
279 *
280 * @param {boolean} [conflicted] Filter is in conflict state
281 * @fires update
282 */
283 mw.rcfilters.dm.FilterItem.prototype.toggleConflicted = function ( conflicted ) {
284 conflicted = conflicted === undefined ? !this.conflicted : conflicted;
285
286 if ( this.conflicted !== conflicted ) {
287 this.conflicted = conflicted;
288 this.emit( 'update' );
289 }
290 };
291
292 /**
293 * Set the state of this filter as being already included
294 * (This means any filters in its superset are selected)
295 *
296 * @param {boolean} [included] Filter is included as part of a subset
297 * @fires update
298 */
299 mw.rcfilters.dm.FilterItem.prototype.toggleIncluded = function ( included ) {
300 included = included === undefined ? !this.included : included;
301
302 if ( this.included !== included ) {
303 this.included = included;
304 this.emit( 'update' );
305 }
306 };
307
308 /**
309 * Toggle the selected state of the item
310 *
311 * @param {boolean} [isSelected] Filter is selected
312 * @fires update
313 */
314 mw.rcfilters.dm.FilterItem.prototype.toggleSelected = function ( isSelected ) {
315 isSelected = isSelected === undefined ? !this.selected : isSelected;
316
317 if ( this.selected !== isSelected ) {
318 this.selected = isSelected;
319 this.emit( 'update' );
320 }
321 };
322
323 /**
324 * Toggle the fully covered state of the item
325 *
326 * @param {boolean} [isFullyCovered] Filter is fully covered
327 * @fires update
328 */
329 mw.rcfilters.dm.FilterItem.prototype.toggleFullyCovered = function ( isFullyCovered ) {
330 isFullyCovered = isFullyCovered === undefined ? !this.fullycovered : isFullyCovered;
331
332 if ( this.fullyCovered !== isFullyCovered ) {
333 this.fullyCovered = isFullyCovered;
334 this.emit( 'update' );
335 }
336 };
337
338 /**
339 * Set the highlight color
340 *
341 * @param {string|null} highlightColor
342 */
343 mw.rcfilters.dm.FilterItem.prototype.setHighlightColor = function ( highlightColor ) {
344 if ( this.highlightColor !== highlightColor ) {
345 this.highlightColor = highlightColor;
346 this.emit( 'update' );
347 }
348 };
349
350 /**
351 * Clear the highlight color
352 */
353 mw.rcfilters.dm.FilterItem.prototype.clearHighlightColor = function () {
354 this.setHighlightColor( null );
355 };
356
357 /**
358 * Get the highlight color, or null if none is configured
359 *
360 * @return {string|null}
361 */
362 mw.rcfilters.dm.FilterItem.prototype.getHighlightColor = function () {
363 return this.highlightColor;
364 };
365
366 /**
367 * Get the CSS class that matches changes that fit this filter
368 * or null if none is configured
369 *
370 * @return {string|null}
371 */
372 mw.rcfilters.dm.FilterItem.prototype.getCssClass = function () {
373 return this.cssClass;
374 };
375
376 /**
377 * Toggle the highlight feature on and off for this filter.
378 * It only works if highlight is supported for this filter.
379 *
380 * @param {boolean} enable Highlight should be enabled
381 */
382 mw.rcfilters.dm.FilterItem.prototype.toggleHighlight = function ( enable ) {
383 enable = enable === undefined ? !this.highlightEnabled : enable;
384
385 if ( !this.isHighlightSupported() ) {
386 return;
387 }
388
389 if ( enable === this.highlightEnabled ) {
390 return;
391 }
392
393 this.highlightEnabled = enable;
394 this.emit( 'update' );
395 };
396
397 /**
398 * Check if the highlight feature is currently enabled for this filter
399 *
400 * @return {boolean}
401 */
402 mw.rcfilters.dm.FilterItem.prototype.isHighlightEnabled = function () {
403 return !!this.highlightEnabled;
404 };
405
406 /**
407 * Check if the highlight feature is supported for this filter
408 *
409 * @return {boolean}
410 */
411 mw.rcfilters.dm.FilterItem.prototype.isHighlightSupported = function () {
412 return !!this.getCssClass();
413 };
414
415 /**
416 * Check if the filter is currently highlighted
417 *
418 * @return {boolean}
419 */
420 mw.rcfilters.dm.FilterItem.prototype.isHighlighted = function () {
421 return this.isHighlightEnabled() && !!this.getHighlightColor();
422 };
423 }( mediaWiki ) );