Mooeypoo has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/352984 )

Change subject: [wip^n] Refactor filter-specific logic into group models
......................................................................

[wip^n] Refactor filter-specific logic into group models

Make the view model more generic and ready to accept other types
of filtering, like namespaces and tags, by handing off the responsibility
and logic of the parameterizing and filter relationships to the groups.

Change-Id: I9c333719e89088d96d044d72ddb9c39feeeb68ca
---
M resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
M resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
M resources/src/mediawiki.rcfilters/mw.rcfilters.js
M tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
4 files changed, 310 insertions(+), 199 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/84/352984/1

diff --git 
a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js 
b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
index 63e13fd..fc0849f 100644
--- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
+++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
@@ -38,6 +38,7 @@
                this.whatsThis = config.whatsThis || {};
 
                this.conflicts = config.conflicts || {};
+               this.defaultParams = {};
 
                this.aggregate( { update: 'filterItemUpdate' } );
                this.connect( this, { filterItemUpdate: 'onFilterItemUpdate' } 
);
@@ -57,6 +58,96 @@
         */
 
        /* Methods */
+
+       /**
+        * Initialize the group and create its filter items
+        *
+        * @param {Object} filterDefinition Filter definition for this group
+        * @param {string|Object} [groupDefault] Definition of the group default
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.initializeFilters = function ( 
filterDefinition, groupDefault ) {
+               var supersetMap = {},
+                       model = this,
+                       items = [];
+
+               filterDefinition.forEach( function ( filter ) {
+                       // Instantiate an item
+                       var subsetNames = [],
+                               filterItem = new mw.rcfilters.dm.FilterItem( 
filter.name, model, {
+                                       group: model.getName(),
+                                       label: mw.msg( filter.label ),
+                                       description: mw.msg( filter.description 
),
+                                       cssClass: filter.cssClass
+                               } );
+
+                       filter.subset = filter.subset || [];
+                       filter.subset = filter.subset.map( function ( el ) {
+                               return el.filter;
+                       } );
+
+                       if ( filter.subset ) {
+                               subsetNames = [];
+                               filter.subset.forEach( function ( 
subsetFilterName ) { // eslint-disable-line no-loop-func
+                                       // Subsets (unlike conflicts) are 
always inside the same group
+                                       // We can re-map the names of the 
filters we are getting from
+                                       // the subsets with the group prefix
+                                       var subsetName = this.getNamePrefix() + 
subsetFilterName;
+                                       // For convenience, we should store 
each filter's "supersets" -- these are
+                                       // the filters that have that item in 
their subset list. This will just
+                                       // make it easier to go through whether 
the item has any other items
+                                       // that affect it (and are selected) at 
any given time
+                                       supersetMap[ subsetName ] = 
supersetMap[ subsetName ] || [];
+                                       
mw.rcfilters.utils.addArrayElementsUnique(
+                                               supersetMap[ subsetName ],
+                                               filterItem.getName()
+                                       );
+
+                                       // Translate subset param name to add 
the group name, so we
+                                       // get consistent naming. We know that 
subsets are only within
+                                       // the same group
+                                       subsetNames.push( subsetName );
+                               } );
+
+                               // Set translated subset
+                               filterItem.setSubset( subsetNames );
+                       }
+
+                       items.push( filterItem );
+
+                       // Store default parameter state; in this case, default 
is defined per filter
+                       if ( model.getType() === 'send_unselected_if_any' ) {
+                               // Store the default parameter state
+                               // For this group type, parameter values are 
direct
+                               model.defaultParams[ filter.name ] = Number( 
!!filter.default );
+                       }
+               } );
+
+               // Add items
+               this.addItems( items );
+
+               // Now that we have all items, we can apply the superset map
+               this.getItems().forEach( function ( filterItem ) {
+                       filterItem.setSuperset( supersetMap[ 
filterItem.getName() ] );
+               } );
+
+               // Store default parameter state; in this case, default is 
defined per the
+               // entire group, given by groupDefault method parameter
+               if ( this.getType() === 'string_options' ) {
+                       // Store the default parameter group state
+                       // For this group, the parameter is group name and 
value is the names
+                       // of selected items
+                       this.defaultParams = 
mw.rcfilters.utils.normalizeParamOptions(
+                               // Current values
+                               groupDefault ?
+                                       groupDefault.split( this.getSeparator() 
) :
+                                       [],
+                               // Legal values
+                               this.getItems().map( function ( item ) {
+                                       return item.getParamName();
+                               } )
+                       ).join( this.getSeparator() );
+               }
+       };
 
        /**
         * Respond to filterItem update event
@@ -89,6 +180,15 @@
         */
        mw.rcfilters.dm.FilterGroup.prototype.getName = function () {
                return this.name;
+       };
+
+       /**
+        * Get the default param state of this group
+        *
+        * @return {Object} Default param state
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getDefaultParams = function () {
+               return this.defaultParams;
        };
 
        /**
@@ -141,6 +241,21 @@
         */
        mw.rcfilters.dm.FilterGroup.prototype.setConflicts = function ( 
conflicts ) {
                this.conflicts = conflicts;
+       };
+
+       /**
+        * Set conflicts for each filter item in the group based on the
+        * given conflict map
+        *
+        * @param {Object} conflicts Object representing the conflict map,
+        *  keyed by the item name, where its value is an object for all its 
conflicts
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.setConflicts = function ( 
conflicts ) {
+               this.getItems().forEach( function ( filterItem ) {
+                       if ( conflicts[ filterItem.getName() ] ) {
+                               filterItem.setConflicts( conflicts[ 
filterItem.getName() ] );
+                       }
+               } );
        };
 
        /**
@@ -317,6 +432,76 @@
        };
 
        /**
+        * Get the filter representation this group would provide
+        * based on given parameter states.
+        *
+        * @param {Object} paramRepresentation An object defining a parameter
+        *  state to translate the filter state from. If not given, the current 
state
+        *  of the filters is returned.
+        * @return {Object} Filter representation
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = 
function ( paramRepresentation ) {
+               var areAnySelected, paramValues,
+                       model = this,
+                       paramToFilterMap = {},
+                       result = {};
+
+               if ( this.getType() === 'send_unselected_if_any' ) {
+                       // Expand param representation to include all filters 
in the group
+                       this.getItems().forEach( function ( filterItem ) {
+                               paramRepresentation[ filterItem.getParamName() 
] = !!paramRepresentation[ filterItem.getParamName() ];
+                               paramToFilterMap[ filterItem.getParamName() ] = 
filterItem;
+
+                               if ( paramRepresentation[ 
filterItem.getParamName() ] ) {
+                                       areAnySelected = true;
+                               }
+                       } );
+
+                       $.each( paramRepresentation, function ( paramName, 
paramValue ) {
+                               var filterItem = paramToFilterMap[ paramName ];
+
+                               result[ filterItem.getName() ] = areAnySelected 
?
+                                       // Flip the definition between the 
parameter
+                                       // state and the filter state
+                                       // This is what the 'toggleSelected' 
value of the filter is
+                                       !Number( paramValue ) :
+                                       // Otherwise, there are no selected 
items in the
+                                       // group, which means the state is false
+                                       false;
+                       } );
+               } else if ( this.getType() === 'string_options' ) {
+                       // Normalize the given parameter values
+                       paramValues = mw.rcfilters.utils.normalizeParamOptions(
+                               // Given
+                               paramRepresentation[ this.getName() ].split(
+                                       this.getSeparator()
+                               ),
+                               // Allowed values
+                               this.getItems().map( function ( filterItem ) {
+                                       return filterItem.getParamName();
+                               } )
+                       );
+
+                       // Translate the parameter values into a filter 
selection state
+                       this.getItems().forEach( function ( filterItem ) {
+                               result[ filterItem.getName() ] = (
+                                               // If it is the word 'all'
+                                               paramValues.length === 1 && 
paramValues[ 0 ] === 'all' ||
+                                               // All values are written
+                                               paramValues.length === 
model.getItemCount()
+                                       ) ?
+                                       // All true (either because all values 
are written or the term 'all' is written)
+                                       // is the same as all filters set to 
true
+                                       true :
+                                       // Otherwise, the filter is selected 
only if it appears in the parameter values
+                                       paramValues.indexOf( 
filterItem.getParamName() ) > -1;
+                       } );
+               }
+
+               return result;
+       };
+
+       /**
         * Get group type
         *
         * @return {string} Group type
diff --git 
a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js 
b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
index 9054fe4..0d4ad00 100644
--- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
@@ -192,23 +192,21 @@
         * @param {Array} filters Filter group definition
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.initializeFilters = function 
( filters ) {
-               var i, filterItem, filterConflictResult, groupConflictResult, 
subsetNames,
+               var filterItem, filterConflictResult, groupConflictResult,
                        model = this,
                        items = [],
-                       supersetMap = {},
                        groupConflictMap = {},
                        filterConflictMap = {},
-                       addArrayElementsUnique = function ( arr, elements ) {
-                               elements = Array.isArray( elements ) ? elements 
: [ elements ];
-
-                               elements.forEach( function ( element ) {
-                                       if ( arr.indexOf( element ) === -1 ) {
-                                               arr.push( element );
-                                       }
-                               } );
-
-                               return arr;
-                       },
+                       /*!
+                        * Expand a conflict definition from group name to
+                        * the list of all included filters in that group.
+                        * We do this so that the direct relationship in the
+                        * models are consistently item->items rather than
+                        * mixing item->group with item->item.
+                        *
+                        * @param {Object} obj Conflict definition
+                        * @return {Object} Expanded conflict definition
+                        */
                        expandConflictDefinitions = function ( obj ) {
                                var result = {};
 
@@ -262,7 +260,8 @@
                this.groups = {};
 
                filters.forEach( function ( data ) {
-                       var group = data.name;
+                       var i,
+                               group = data.name;
 
                        if ( !model.groups[ group ] ) {
                                model.groups[ group ] = new 
mw.rcfilters.dm.FilterGroup( group, {
@@ -278,77 +277,25 @@
                                        }
                                } );
                        }
+                       model.groups[ group ].initializeFilters( data.filters, 
data.default );
+                       items.concat( model.groups[ group ].getItems() );
 
+                       // Prepare conflicts
                        if ( data.conflicts ) {
+                               // Group conflicts
                                groupConflictMap[ group ] = data.conflicts;
                        }
 
                        for ( i = 0; i < data.filters.length; i++ ) {
-                               data.filters[ i ].subset = data.filters[ i 
].subset || [];
-                               data.filters[ i ].subset = data.filters[ i 
].subset.map( function ( el ) {
-                                       return el.filter;
-                               } );
-
-                               filterItem = new mw.rcfilters.dm.FilterItem( 
data.filters[ i ].name, model.groups[ group ], {
-                                       group: group,
-                                       label: mw.msg( data.filters[ i ].label 
),
-                                       description: mw.msg( data.filters[ i 
].description ),
-                                       cssClass: data.filters[ i ].cssClass
-                               } );
-
-                               if ( data.filters[ i ].subset ) {
-                                       subsetNames = [];
-                                       data.filters[ i ].subset.forEach( 
function ( subsetFilterName ) { // eslint-disable-line no-loop-func
-                                               var subsetName = model.groups[ 
group ].getNamePrefix() + subsetFilterName;
-                                               // For convenience, we should 
store each filter's "supersets" -- these are
-                                               // the filters that have that 
item in their subset list. This will just
-                                               // make it easier to go through 
whether the item has any other items
-                                               // that affect it (and are 
selected) at any given time
-                                               supersetMap[ subsetName ] = 
supersetMap[ subsetName ] || [];
-                                               addArrayElementsUnique(
-                                                       supersetMap[ subsetName 
],
-                                                       filterItem.getName()
-                                               );
-
-                                               // Translate subset param name 
to add the group name, so we
-                                               // get consistent naming. We 
know that subsets are only within
-                                               // the same group
-                                               subsetNames.push( subsetName );
-                                       } );
-
-                                       // Set translated subset
-                                       filterItem.setSubset( subsetNames );
-                               }
-
-                               // Store conflicts
+                               // Filter conflicts
                                if ( data.filters[ i ].conflicts ) {
+                                       filterItem = model.groups[ group 
].getItemByParamName( data.filters[ i ] );
                                        filterConflictMap[ filterItem.getName() 
] = data.filters[ i ].conflicts;
                                }
-
-                               if ( data.type === 'send_unselected_if_any' ) {
-                                       // Store the default parameter state
-                                       // For this group type, parameter 
values are direct
-                                       model.defaultParams[ data.filters[ i 
].name ] = Number( !!data.filters[ i ].default );
-                               }
-
-                               model.groups[ group ].addItems( filterItem );
-                               items.push( filterItem );
-                       }
-
-                       if ( data.type === 'string_options' ) {
-                               // Store the default parameter group state
-                               // For this group, the parameter is group name 
and value is the names
-                               // of selected items
-                               model.defaultParams[ group ] = 
model.sanitizeStringOptionGroup(
-                                       group,
-                                       data.default ?
-                                               data.default.split( 
model.groups[ group ].getSeparator() ) :
-                                               []
-                               ).join( model.groups[ group ].getSeparator() );
                        }
                } );
 
-               // Add items to the model
+               // Add item references to the model, for lookup
                this.addItems( items );
 
                // Expand conflicts
@@ -358,16 +305,9 @@
                // Set conflicts for groups
                $.each( groupConflictResult, function ( group, conflicts ) {
                        model.groups[ group ].setConflicts( conflicts );
-               } );
 
-               items.forEach( function ( filterItem ) {
-                       // Apply the superset map
-                       filterItem.setSuperset( supersetMap[ 
filterItem.getName() ] );
-
-                       // set conflicts for item
-                       if ( filterConflictResult[ filterItem.getName() ] ) {
-                               filterItem.setConflicts( filterConflictResult[ 
filterItem.getName() ] );
-                       }
+                       // set conflicts for items in the group
+                       model.groups[ group ].setFilterConflicts( 
filterConflictResult );
                } );
 
                // Create a map between known parameters and their models
@@ -383,6 +323,7 @@
                        }
                } );
 
+               // Finish initialization
                this.emit( 'initialize' );
        };
 
@@ -453,12 +394,18 @@
        };
 
        /**
-        * Get the default parameters object
+        * Get an object representing default parameters state
         *
         * @return {Object} Default parameter values
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultParams = function 
() {
-               return this.defaultParams;
+               var result = {};
+
+               $.each( this.getGroups(), function ( name, model ) {
+                       result[ name ] = model.getDefaultParams();
+               } );
+
+               return result;
        };
 
        /**
@@ -502,6 +449,52 @@
        };
 
        /**
+        * This is the opposite of the #getParametersFromFilters method; this 
goes over
+        * the given parameters and translates into a selected/unselected value 
in the filters.
+        *
+        * @param {Object} params Parameters query object
+        * @return {Object} Filter state object
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.getFiltersFromParameters = 
function ( params ) {
+               var groupMap = {},
+                       model = this,
+                       result = {};
+
+               // Go over the given parameters, break apart to groupings
+               // The resulting object represents the group with its parameter
+               // values. For example:
+               // {
+               //    group1: {
+               //       param1: "1",
+               //       param2: "0",
+               //       param3: "1"
+               //    },
+               //    group2: "param4|param5"
+               // }
+               $.each( params, function ( paramName, paramValue ) {
+                       var itemOrGroup = model.parameterMap[ paramName ];
+
+                       groupMap[ itemOrGroup.getGroupName() ] = groupMap[ 
itemOrGroup.getGroupName() ] || {};
+                       if ( itemOrGroup instanceof mw.rcfilters.dm.FilterItem 
) {
+                               groupMap[ itemOrGroup.getGroupName() ][ 
itemOrGroup.getParamName() ] = paramValue;
+                       } else if ( itemOrGroup instanceof 
mw.rcfilters.dm.FilterGroup ) {
+                               groupMap[ itemOrGroup.getGroupName() ] = 
groupMap[ itemOrGroup.getGroupName() ] || {};
+                               // This parameter represents a group (values 
are the filters)
+                               // this is equivalent to checking if the group 
is 'string_options'
+                               groupMap[ itemOrGroup.getName() ] = paramValue;
+                       }
+               } );
+
+               // Go over all filter groups and ask for their representing 
filters
+               // from the given parameters
+               $.each( groupMap, function ( group, data ) {
+                       result[ group ] = model.groups[ group 
].getFilterRepresentation( data );
+               } );
+
+               return result;
+       };
+
+       /**
         * Get the highlight parameters based on current filter configuration
         *
         * @return {object} Object where keys are "<filter name>_color" and 
values
@@ -527,33 +520,11 @@
         * @return {string[]} Array of valid values
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.sanitizeStringOptionGroup = 
function ( groupName, valueArray ) {
-               var result = [],
-                       validNames = this.getGroupFilters( groupName ).map( 
function ( filterItem ) {
-                               return filterItem.getParamName();
-                       } );
-
-               if ( valueArray.indexOf( 'all' ) > -1 ) {
-                       // If anywhere in the values there's 'all', we
-                       // treat it as if only 'all' was selected.
-                       // Example: param=valid1,valid2,all
-                       // Result: param=all
-                       return [ 'all' ];
-               }
-
-               // Get rid of any dupe and invalid parameter, only output
-               // valid ones
-               // Example: param=valid1,valid2,invalid1,valid1
-               // Result: param=valid1,valid2
-               valueArray.forEach( function ( value ) {
-                       if (
-                               validNames.indexOf( value ) > -1 &&
-                               result.indexOf( value ) === -1
-                       ) {
-                               result.push( value );
-                       }
+               var validNames = this.getGroupFilters( groupName ).map( 
function ( filterItem ) {
+                       return filterItem.getParamName();
                } );
 
-               return result;
+               return mw.rcfilters.utils.normalizeParamOptions( valueArray, 
validNames );
        };
 
        /**
@@ -580,100 +551,13 @@
                if ( this.defaultFiltersEmpty !== null ) {
                        // We only need to do this test once,
                        // because defaults are set once per session
-                       defaultFilters = this.getFiltersFromParameters();
+                       defaultFilters = this.getFiltersFromParameters( 
this.getDefaultParams() );
                        this.defaultFiltersEmpty = Object.keys( defaultFilters 
).every( function ( filterName ) {
                                return !defaultFilters[ filterName ];
                        } );
                }
 
                return this.defaultFiltersEmpty;
-       };
-
-       /**
-        * This is the opposite of the #getParametersFromFilters method; this 
goes over
-        * the given parameters and translates into a selected/unselected value 
in the filters.
-        *
-        * @param {Object} params Parameters query object
-        * @return {Object} Filter state object
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getFiltersFromParameters = 
function ( params ) {
-               var i,
-                       groupMap = {},
-                       model = this,
-                       base = this.getDefaultParams(),
-                       result = {};
-
-               params = $.extend( {}, base, params );
-
-               // Go over the given parameters
-               $.each( params, function ( paramName, paramValue ) {
-                       var itemOrGroup = model.parameterMap[ paramName ];
-
-                       if ( itemOrGroup instanceof mw.rcfilters.dm.FilterItem 
) {
-                               // Mark the group if it has any items that are 
selected
-                               groupMap[ itemOrGroup.getGroupName() ] = 
groupMap[ itemOrGroup.getGroupName() ] || {};
-                               groupMap[ itemOrGroup.getGroupName() 
].hasSelected = (
-                                       groupMap[ itemOrGroup.getGroupName() 
].hasSelected ||
-                                       !!Number( paramValue )
-                               );
-
-                               // Add filters
-                               groupMap[ itemOrGroup.getGroupName() ].filters 
= groupMap[ itemOrGroup.getGroupName() ].filters || [];
-                               groupMap[ itemOrGroup.getGroupName() 
].filters.push( itemOrGroup );
-                       } else if ( itemOrGroup instanceof 
mw.rcfilters.dm.FilterGroup ) {
-                               groupMap[ itemOrGroup.getName() ] = groupMap[ 
itemOrGroup.getName() ] || {};
-                               // This parameter represents a group (values 
are the filters)
-                               // this is equivalent to checking if the group 
is 'string_options'
-                               groupMap[ itemOrGroup.getName() ].filters = 
itemOrGroup.getItems();
-                       }
-               } );
-
-               // Now that we know the groups' selection states, we need to go 
over
-               // the filters in the groups and mark their selected states 
appropriately
-               $.each( groupMap, function ( group, data ) {
-                       var paramValues, filterItem,
-                               allItemsInGroup = data.filters;
-
-                       if ( model.groups[ group ].getType() === 
'send_unselected_if_any' ) {
-                               for ( i = 0; i < allItemsInGroup.length; i++ ) {
-                                       filterItem = allItemsInGroup[ i ];
-
-                                       result[ filterItem.getName() ] = 
groupMap[ filterItem.getGroupName() ].hasSelected ?
-                                               // Flip the definition between 
the parameter
-                                               // state and the filter state
-                                               // This is what the 
'toggleSelected' value of the filter is
-                                               !Number( params[ 
filterItem.getParamName() ] ) :
-                                               // Otherwise, there are no 
selected items in the
-                                               // group, which means the state 
is false
-                                               false;
-                               }
-                       } else if ( model.groups[ group ].getType() === 
'string_options' ) {
-                               paramValues = model.sanitizeStringOptionGroup(
-                                       group,
-                                       params[ group ].split(
-                                               model.groups[ group 
].getSeparator()
-                                       )
-                               );
-
-                               for ( i = 0; i < allItemsInGroup.length; i++ ) {
-                                       filterItem = allItemsInGroup[ i ];
-
-                                       result[ filterItem.getName() ] = (
-                                                       // If it is the word 
'all'
-                                                       paramValues.length === 
1 && paramValues[ 0 ] === 'all' ||
-                                                       // All values are 
written
-                                                       paramValues.length === 
model.groups[ group ].getItemCount()
-                                               ) ?
-                                               // All true (either because all 
values are written or the term 'all' is written)
-                                               // is the same as all filters 
set to true
-                                               true :
-                                               // Otherwise, the filter is 
selected only if it appears in the parameter values
-                                               paramValues.indexOf( 
filterItem.getParamName() ) > -1;
-                               }
-                       }
-               } );
-
-               return result;
        };
 
        /**
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.js 
b/resources/src/mediawiki.rcfilters/mw.rcfilters.js
index 3ddb5a0..8cea27e 100644
--- a/resources/src/mediawiki.rcfilters/mw.rcfilters.js
+++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.js
@@ -1,3 +1,45 @@
 ( function ( mw ) {
-       mw.rcfilters = { dm: {}, ui: {} };
+       mw.rcfilters = {
+               dm: {},
+               ui: {},
+               utils: {
+                       addArrayElementsUnique: function ( arr, elements ) {
+                               elements = Array.isArray( elements ) ? elements 
: [ elements ];
+
+                               elements.forEach( function ( element ) {
+                                       if ( arr.indexOf( element ) === -1 ) {
+                                               arr.push( element );
+                                       }
+                               } );
+
+                               return arr;
+                       },
+                       normalizeParamOptions: function ( givenOptions, 
legalOptions ) {
+                               var result = [];
+
+                               if ( givenOptions.indexOf( 'all' ) > -1 ) {
+                                       // If anywhere in the values there's 
'all', we
+                                       // treat it as if only 'all' was 
selected.
+                                       // Example: param=valid1,valid2,all
+                                       // Result: param=all
+                                       return [ 'all' ];
+                               }
+
+                               // Get rid of any dupe and invalid parameter, 
only output
+                               // valid ones
+                               // Example: param=valid1,valid2,invalid1,valid1
+                               // Result: param=valid1,valid2
+                               givenOptions.forEach( function ( value ) {
+                                       if (
+                                               legalOptions.indexOf( value ) > 
-1 &&
+                                               result.indexOf( value ) === -1
+                                       ) {
+                                               result.push( value );
+                                       }
+                               } );
+
+                               return result;
+                       }
+               }
+       };
 }( mediaWiki ) );
diff --git 
a/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js 
b/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
index bc266fb..159a301 100644
--- 
a/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
+++ 
b/tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
@@ -64,7 +64,7 @@
                                ]
                        } ],
                        model = new mw.rcfilters.dm.FiltersViewModel();
-
+debugger;
                model.initializeFilters( definition );
 
                assert.ok(

-- 
To view, visit https://gerrit.wikimedia.org/r/352984
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9c333719e89088d96d044d72ddb9c39feeeb68ca
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <mor...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to