Merge "RCFilters: Change `What's this?` i18n based on user testing"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.FilterGroup.js
index 22b2619..4915803 100644 (file)
@@ -9,11 +9,17 @@
         * @param {string} name Group name
         * @param {Object} [config] Configuration options
         * @cfg {string} [type='send_unselected_if_any'] Group type
+        * @cfg {string} [view='default'] Name of the display group this group
+        *  is a part of.
         * @cfg {string} [title] Group title
+        * @cfg {boolean} [hidden] This group is hidden from the regular menu views
         * @cfg {string} [separator='|'] Value separator for 'string_options' groups
         * @cfg {boolean} [active] Group is active
         * @cfg {boolean} [fullCoverage] This filters in this group collectively cover all results
         * @cfg {Object} [conflicts] Defines the conflicts for this filter group
+        * @cfg {string|Object} [labelPrefixKey] An i18n key defining the prefix label for this
+        *  group. If the prefix has 'invert' state, the parameter is expected to be an object
+        *  with 'default' and 'inverted' as keys.
         * @cfg {Object} [whatsThis] Defines the messages that should appear for the 'what's this' popup
         * @cfg {string} [whatsThis.header] The header of the whatsThis popup message
         * @cfg {string} [whatsThis.body] The body of the whatsThis popup message
 
                this.name = name;
                this.type = config.type || 'send_unselected_if_any';
-               this.title = config.title;
+               this.view = config.view || 'default';
+               this.title = config.title || name;
+               this.hidden = !!config.hidden;
                this.separator = config.separator || '|';
+               this.labelPrefixKey = config.labelPrefixKey;
 
                this.active = !!config.active;
                this.fullCoverage = !!config.fullCoverage;
                        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
+                                       label: filter.label || filter.name,
+                                       description: filter.description || '',
+                                       labelPrefixKey: model.labelPrefixKey,
+                                       cssClass: filter.cssClass,
+                                       identifiers: filter.identifiers
                                } );
 
-                       filter.subset = filter.subset || [];
-                       filter.subset = filter.subset.map( function ( el ) {
-                               return el.filter;
-                       } );
-
                        if ( filter.subset ) {
+                               filter.subset = filter.subset.map( function ( el ) {
+                                       return el.filter;
+                               } );
+
                                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
                        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 );
+                               // We need to convert from a boolean to a string ('1' and '0')
+                               model.defaultParams[ filter.name ] = String( Number( !!filter.default ) );
                        }
                } );
 
                                        return item.getParamName();
                                } )
                        ).join( this.getSeparator() );
+               } else if ( this.getType() === 'single_option' ) {
+                       // For this group, the parameter is the group name,
+                       // and a single item can be selected, or none at all
+                       // The item also must be recognized or none is set as
+                       // default
+                       model.defaultParams[ this.getName() ] = this.getItemByParamName( groupDefault ) ? groupDefault : '';
                }
        };
 
        /**
         * Respond to filterItem update event
         *
+        * @param {mw.rcfilters.dm.FilterItem} item Updated filter item
         * @fires update
         */
-       mw.rcfilters.dm.FilterGroup.prototype.onFilterItemUpdate = function () {
+       mw.rcfilters.dm.FilterGroup.prototype.onFilterItemUpdate = function ( item ) {
                // Update state
-               var active = this.areAnySelected();
+               var active = this.areAnySelected(),
+                       itemName = item && item.getName();
+
+               if ( item.isSelected() && this.getType() === 'single_option' ) {
+                       // Change the selection to only be the newly selected item
+                       this.getItems().forEach( function ( filterItem ) {
+                               if ( filterItem.getName() !== itemName ) {
+                                       filterItem.toggleSelected( false );
+                               }
+                       } );
+               }
 
                if ( this.active !== active ) {
                        this.active = active;
                return this.active;
        };
 
+       /**
+        * Get group hidden state
+        *
+        * @return {boolean} Hidden state
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.isHidden = function () {
+               return this.hidden;
+       };
+
        /**
         * Get group name
         *
                        areAnySelected = false,
                        buildFromCurrentState = !filterRepresentation,
                        result = {},
-                       filterParamNames = {};
+                       model = this,
+                       filterParamNames = {},
+                       getSelectedParameter = function ( filters ) {
+                               var item,
+                                       selected = [];
+
+                               // Find if any are selected
+                               $.each( filters, function ( name, value ) {
+                                       if ( value ) {
+                                               selected.push( name );
+                                       }
+                               } );
+
+                               item = model.getItemByName( selected[ 0 ] );
+                               return ( item && item.getParamName() ) || '';
+                       };
 
                filterRepresentation = filterRepresentation || {};
 
 
                        // Go over the items and define the correct values
                        $.each( filterRepresentation, function ( name, value ) {
+                               // We must store all parameter values as strings '0' or '1'
                                result[ filterParamNames[ name ] ] = areAnySelected ?
-                                       Number( !value ) : 0;
+                                       String( Number( !value ) ) :
+                                       '0';
                        } );
                } else if ( this.getType() === 'string_options' ) {
                        values = [];
 
                        result[ this.getName() ] = ( values.length === Object.keys( filterRepresentation ).length ) ?
                                'all' : values.join( this.getSeparator() );
+               } else if ( this.getType() === 'single_option' ) {
+                       result[ this.getName() ] = getSelectedParameter( filterRepresentation );
                }
 
                return result;
                        paramRepresentation = paramRepresentation || {};
                        // 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;
+                               var paramName = filterItem.getParamName();
+
+                               paramRepresentation[ paramName ] = paramRepresentation[ paramName ] || '0';
+                               paramToFilterMap[ paramName ] = filterItem;
 
-                               if ( paramRepresentation[ filterItem.getParamName() ] ) {
+                               if ( Number( paramRepresentation[ filterItem.getParamName() ] ) ) {
                                        areAnySelected = true;
                                }
                        } );
                        $.each( paramRepresentation, function ( paramName, paramValue ) {
                                var filterItem = paramToFilterMap[ paramName ];
 
+                               // Flip the definition between the parameter
+                               // state and the filter state
+                               // This is what the 'toggleSelected' value of the filter is
                                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
                        );
                        // Translate the parameter values into a filter selection state
                        this.getItems().forEach( function ( filterItem ) {
+                               // All true (either because all values are written or the term 'all' is written)
+                               // is the same as all filters set to true
                                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
+                                       // If it is the word 'all'
+                                       paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
+                                       // All values are written
+                                       paramValues.length === model.getItemCount()
+                               ) ?
                                        true :
                                        // Otherwise, the filter is selected only if it appears in the parameter values
                                        paramValues.indexOf( filterItem.getParamName() ) > -1;
                        } );
+               } else if ( this.getType() === 'single_option' ) {
+                       // There is parameter that fits a single filter, or none at all
+                       this.getItems().forEach( function ( filterItem ) {
+                               result[ filterItem.getName() ] = filterItem.getParamName() === paramRepresentation;
+                       } );
                }
 
                // Go over result and make sure all filters are represented.
                return result;
        };
 
+       /**
+        * Get item by its filter name
+        *
+        * @param {string} filterName Filter name
+        * @return {mw.rcfilters.dm.FilterItem} Filter item
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getItemByName = function ( filterName ) {
+               return this.getItems().filter( function ( item ) {
+                       return item.getName() === filterName;
+               } )[ 0 ];
+       };
+
        /**
         * Get item by its parameter name
         *
                return this.type;
        };
 
+       /**
+        * Get display group
+        *
+        * @return {string} Display group
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getView = function () {
+               return this.view;
+       };
+
        /**
         * Get the prefix used for the filter names inside this group.
         *