5 * @extends mw.rcfilters.dm.ItemModel
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[]} [excludes=[]] A list of filter names this filter, if
12 * selected, makes inactive.
13 * @cfg {string[]} [subset] Defining the names of filters that are a subset of this filter
14 * @cfg {Object} [conflicts] Defines the conflicts for this filter
16 mw
.rcfilters
.dm
.FilterItem
= function MwRcfiltersDmFilterItem( param
, groupModel
, config
) {
17 config
= config
|| {};
19 this.groupModel
= groupModel
;
22 mw
.rcfilters
.dm
.FilterItem
.parent
.call( this, param
, $.extend( {
23 namePrefix
: this.groupModel
.getNamePrefix()
26 OO
.EventEmitter
.call( this );
28 // Interaction definitions
29 this.subset
= config
.subset
|| [];
30 this.conflicts
= config
.conflicts
|| {};
34 this.included
= false;
35 this.conflicted
= false;
36 this.fullyCovered
= false;
41 OO
.inheritClass( mw
.rcfilters
.dm
.FilterItem
, mw
.rcfilters
.dm
.ItemModel
);
46 * Return the representation of the state of this item.
48 * @return {Object} State of the object
50 mw
.rcfilters
.dm
.FilterItem
.prototype.getState = function () {
52 selected
: this.isSelected(),
53 included
: this.isIncluded(),
54 conflicted
: this.isConflicted(),
55 fullyCovered
: this.isFullyCovered()
60 * Get the message for the display area for the currently active conflict
63 * @return {string} Conflict result message key
65 mw
.rcfilters
.dm
.FilterItem
.prototype.getCurrentConflictResultMessage = function () {
68 // First look in filter's own conflicts
69 details
= this.getConflictDetails( this.getOwnConflicts(), 'globalDescription' );
70 if ( !details
.message
) {
71 // Fall back onto conflicts in the group
72 details
= this.getConflictDetails( this.getGroupModel().getConflicts(), 'globalDescription' );
75 return details
.message
;
79 * Get the details of the active conflict on this filter
82 * @param {Object} conflicts Conflicts to examine
83 * @param {string} [key='contextDescription'] Message key
84 * @return {Object} Object with conflict message and conflict items
85 * @return {string} return.message Conflict message
86 * @return {string[]} return.names Conflicting item labels
88 mw
.rcfilters
.dm
.FilterItem
.prototype.getConflictDetails = function ( conflicts
, key
) {
93 key
= key
|| 'contextDescription';
95 $.each( conflicts
, function ( filterName
, conflict
) {
96 if ( !conflict
.item
.isSelected() ) {
100 if ( !conflictMessage
) {
101 conflictMessage
= conflict
[ key
];
102 group
= conflict
.group
;
105 if ( group
=== conflict
.group
) {
106 itemLabels
.push( mw
.msg( 'quotation-marks', conflict
.item
.getLabel() ) );
111 message
: conflictMessage
,
120 mw
.rcfilters
.dm
.FilterItem
.prototype.getStateMessage = function () {
121 var messageKey
, details
, superset
,
124 if ( this.isSelected() ) {
125 if ( this.isConflicted() ) {
126 // First look in filter's own conflicts
127 details
= this.getConflictDetails( this.getOwnConflicts() );
128 if ( !details
.message
) {
129 // Fall back onto conflicts in the group
130 details
= this.getConflictDetails( this.getGroupModel().getConflicts() );
133 messageKey
= details
.message
;
134 affectingItems
= details
.names
;
135 } else if ( this.isIncluded() && !this.isHighlighted() ) {
136 // We only show the 'no effect' full-coverage message
137 // if the item is also not highlighted. See T161273
138 superset
= this.getSuperset();
139 // For this message we need to collect the affecting superset
140 affectingItems
= this.getGroupModel().getSelectedItems( this )
141 .filter( function ( item
) {
142 return superset
.indexOf( item
.getName() ) !== -1;
144 .map( function ( item
) {
145 return mw
.msg( 'quotation-marks', item
.getLabel() );
148 messageKey
= 'rcfilters-state-message-subset';
149 } else if ( this.isFullyCovered() && !this.isHighlighted() ) {
150 affectingItems
= this.getGroupModel().getSelectedItems( this )
151 .map( function ( item
) {
152 return mw
.msg( 'quotation-marks', item
.getLabel() );
155 messageKey
= 'rcfilters-state-message-fullcoverage';
163 mw
.language
.listToText( affectingItems
),
164 affectingItems
.length
168 // Display description
169 return this.getDescription();
173 * Get the model of the group this filter belongs to
175 * @return {mw.rcfilters.dm.FilterGroup} Filter group model
177 mw
.rcfilters
.dm
.FilterItem
.prototype.getGroupModel = function () {
178 return this.groupModel
;
182 * Get the group name this filter belongs to
184 * @return {string} Filter group name
186 mw
.rcfilters
.dm
.FilterItem
.prototype.getGroupName = function () {
187 return this.groupModel
.getName();
192 * This is a list of filter names that are defined to be included
193 * when this filter is selected.
195 * @return {string[]} Filter subset
197 mw
.rcfilters
.dm
.FilterItem
.prototype.getSubset = function () {
202 * Get filter superset
203 * This is a generated list of filters that define this filter
204 * to be included when either of them is selected.
206 * @return {string[]} Filter superset
208 mw
.rcfilters
.dm
.FilterItem
.prototype.getSuperset = function () {
209 return this.superset
;
213 * Check whether the filter is currently in a conflict state
215 * @return {boolean} Filter is in conflict state
217 mw
.rcfilters
.dm
.FilterItem
.prototype.isConflicted = function () {
218 return this.conflicted
;
222 * Check whether the filter is currently in an already included subset
224 * @return {boolean} Filter is in an already-included subset
226 mw
.rcfilters
.dm
.FilterItem
.prototype.isIncluded = function () {
227 return this.included
;
231 * Check whether the filter is currently fully covered
233 * @return {boolean} Filter is in fully-covered state
235 mw
.rcfilters
.dm
.FilterItem
.prototype.isFullyCovered = function () {
236 return this.fullyCovered
;
240 * Get all conflicts associated with this filter or its group
242 * Conflict object is set up by filter name keys and conflict
243 * definition. For example:
246 * filter: filterName,
252 * filter: filterName2,
259 * @return {Object} Filter conflicts
261 mw
.rcfilters
.dm
.FilterItem
.prototype.getConflicts = function () {
262 return $.extend( {}, this.conflicts
, this.getGroupModel().getConflicts() );
266 * Get the conflicts associated with this filter
268 * @return {Object} Filter conflicts
270 mw
.rcfilters
.dm
.FilterItem
.prototype.getOwnConflicts = function () {
271 return this.conflicts
;
275 * Set conflicts for this filter. See #getConflicts for the expected
276 * structure of the definition.
278 * @param {Object} conflicts Conflicts for this filter
280 mw
.rcfilters
.dm
.FilterItem
.prototype.setConflicts = function ( conflicts
) {
281 this.conflicts
= conflicts
|| {};
285 * Set filter superset
287 * @param {string[]} superset Filter superset
289 mw
.rcfilters
.dm
.FilterItem
.prototype.setSuperset = function ( superset
) {
290 this.superset
= superset
|| [];
296 * @param {string[]} subset Filter subset
298 mw
.rcfilters
.dm
.FilterItem
.prototype.setSubset = function ( subset
) {
299 this.subset
= subset
|| [];
303 * Check whether a filter exists in the subset list for this filter
305 * @param {string} filterName Filter name
306 * @return {boolean} Filter name is in the subset list
308 mw
.rcfilters
.dm
.FilterItem
.prototype.existsInSubset = function ( filterName
) {
309 return this.subset
.indexOf( filterName
) > -1;
313 * Check whether this item has a potential conflict with the given item
315 * This checks whether the given item is in the list of conflicts of
316 * the current item, but makes no judgment about whether the conflict
317 * is currently at play (either one of the items may not be selected)
319 * @param {mw.rcfilters.dm.FilterItem} filterItem Filter item
320 * @return {boolean} This item has a conflict with the given item
322 mw
.rcfilters
.dm
.FilterItem
.prototype.existsInConflicts = function ( filterItem
) {
323 return Object
.prototype.hasOwnProperty
.call( this.getConflicts(), filterItem
.getName() );
327 * Set the state of this filter as being conflicted
328 * (This means any filters in its conflicts are selected)
330 * @param {boolean} [conflicted] Filter is in conflict state
333 mw
.rcfilters
.dm
.FilterItem
.prototype.toggleConflicted = function ( conflicted
) {
334 conflicted
= conflicted
=== undefined ? !this.conflicted
: conflicted
;
336 if ( this.conflicted
!== conflicted
) {
337 this.conflicted
= conflicted
;
338 this.emit( 'update' );
343 * Set the state of this filter as being already included
344 * (This means any filters in its superset are selected)
346 * @param {boolean} [included] Filter is included as part of a subset
349 mw
.rcfilters
.dm
.FilterItem
.prototype.toggleIncluded = function ( included
) {
350 included
= included
=== undefined ? !this.included
: included
;
352 if ( this.included
!== included
) {
353 this.included
= included
;
354 this.emit( 'update' );
359 * Toggle the fully covered state of the item
361 * @param {boolean} [isFullyCovered] Filter is fully covered
364 mw
.rcfilters
.dm
.FilterItem
.prototype.toggleFullyCovered = function ( isFullyCovered
) {
365 isFullyCovered
= isFullyCovered
=== undefined ? !this.fullycovered
: isFullyCovered
;
367 if ( this.fullyCovered
!== isFullyCovered
) {
368 this.fullyCovered
= isFullyCovered
;
369 this.emit( 'update' );