Merge "Use HTML::hidden to create input fields"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.FilterGroup.js
index b6eda0f..6acc44d 100644 (file)
         * @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 {boolean} [isSticky] This group is using a 'sticky' default; meaning
+        *  that every time a value is changed, it becomes the new default
+        * @cfg {boolean} [excludedFromSavedQueries] A specific requirement to exclude
+        *  this filter from saved queries. This is always true if the filter is 'sticky'
+        *  but can be used for non-sticky filters as an additional requirement. Similarly
+        *  to 'sticky' it works for the entire group as a whole.
         * @cfg {string} [title] Group title
         * @cfg {boolean} [hidden] This group is hidden from the regular menu views
         * @cfg {boolean} [allowArbitrary] Allows for an arbitrary value to be added to the
         *  group from the URL, even if it wasn't initially set up.
+        * @cfg {number} [range] An object defining minimum and maximum values for numeric
+        *  groups. { min: x, max: y }
+        * @cfg {number} [minValue] Minimum value for numeric groups
         * @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
                this.name = name;
                this.type = config.type || 'send_unselected_if_any';
                this.view = config.view || 'default';
+               this.sticky = !!config.isSticky;
+               this.excludedFromSavedQueries = this.sticky || !!config.excludedFromSavedQueries;
                this.title = config.title || name;
                this.hidden = !!config.hidden;
                this.allowArbitrary = !!config.allowArbitrary;
+               this.numericRange = config.range;
                this.separator = config.separator || '|';
                this.labelPrefixKey = config.labelPrefixKey;
 
                        var subsetNames = [],
                                filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, {
                                        group: model.getName(),
-                                       useDefaultAsBaseValue: !!filter.useDefaultAsBaseValue,
                                        label: filter.label || filter.name,
                                        description: filter.description || '',
                                        labelPrefixKey: model.labelPrefixKey,
                                // Store the default parameter state
                                // For this group type, parameter values are direct
                                // We need to convert from a boolean to a string ('1' and '0')
-                               model.defaultParams[ filter.name ] = String( Number( !!filter.default ) );
+                               model.defaultParams[ filter.name ] = String( Number( filter.default || 0 ) );
                        }
                } );
 
                        // For this group, the parameter is the group name,
                        // and a single item can be selected: default or first item
                        this.defaultParams[ this.getName() ] = defaultParam;
-
-                       // Single option means there must be a single option
-                       // selected, so we have to either select the default
-                       // or select the first option
-                       this.selectItemByParamName( defaultParam );
                }
 
                // Store default filter state based on default params
                this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() );
 
                // Check for filters that should be initially selected by their default value
-               this.getItems().forEach( function ( item ) {
-                       if (
-                               item.isUsingDefaultAsBaseValue() &&
-                               (
-                                       // This setting can only be applied to these groups
-                                       // the other groups are way too complex for that
-                                       model.getType() === 'single_option' ||
-                                       model.getType() === 'boolean'
-                               )
-                       ) {
-                               // Apply selection
-                               item.toggleSelected( !!model.defaultFilters[ item.getName() ] );
-                       }
-               } );
+               if ( this.isSticky() ) {
+                       $.each( this.defaultFilters, function ( filterName, filterValue ) {
+                               model.getItemByName( filterName ).toggleSelected( filterValue );
+                       } );
+               }
+
+               // Verify that single_option group has at least one item selected
+               if (
+                       this.getType() === 'single_option' &&
+                       this.getSelectedItems().length === 0
+               ) {
+                       defaultParam = groupDefault !== undefined ?
+                               groupDefault : this.getItems()[ 0 ].getParamName();
+
+                       // Single option means there must be a single option
+                       // selected, so we have to either select the default
+                       // or select the first option
+                       this.selectItemByParamName( defaultParam );
+               }
        };
 
        /**
                        // Single option means there must be a single option
                        // selected, so we have to either select the default
                        // or select the first option
-                       this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] );
+                       this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ) ||
+                               this.getItems()[ 0 ];
                        this.currSelected.toggleSelected( true );
                        changed = true;
                }
                        this.active !== active ||
                        this.currSelected !== item
                ) {
+                       if ( this.isSticky() ) {
+                               // If this group is sticky, then change the default according to the
+                               // current selection.
+                               this.defaultParams = this.getParamRepresentation( this.getSelectedState() );
+                       }
+
                        this.active = active;
                        this.currSelected = item;
 
                return this.allowArbitrary;
        };
 
+       /**
+        * Get group maximum value for numeric groups
+        *
+        * @return {number|null} Group max value
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getMaxValue = function () {
+               return this.numericRange && this.numericRange.max !== undefined ?
+                       this.numericRange.max : null;
+       };
+
+       /**
+        * Get group minimum value for numeric groups
+        *
+        * @return {number|null} Group max value
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getMinValue = function () {
+               return this.numericRange && this.numericRange.min !== undefined ?
+                       this.numericRange.min : null;
+       };
+
        /**
         * Get group name
         *
                                // This means we have not been given a filter representation
                                // so we are building one based on current state
                                filterRepresentation[ item.getName() ] = item.isSelected();
-                       } else if ( !filterRepresentation[ item.getName() ] ) {
+                       } else if ( filterRepresentation[ item.getName() ] === undefined ) {
                                // We are given a filter representation, but we have to make
                                // sure that we fill in the missing filters if there are any
-                               // we will assume they are all falsey, unless they have
-                               // isUsingDefaultAsBaseValue, in which case they get their
-                               // default state
-                               if (
-                                       item.isUsingDefaultAsBaseValue() &&
-                                       (
-                                               // This setting can only be applied to these groups
-                                               // the other groups are way too complex for that
-                                               model.getType() === 'single_option' ||
-                                               model.getType() === 'boolean'
-                                       )
-                               ) {
+                               // we will assume they are all falsey
+                               if ( model.isSticky() ) {
                                        filterRepresentation[ item.getName() ] = !!defaultFilters[ item.getName() ];
                                } else {
                                        filterRepresentation[ item.getName() ] = false;
         * @return {Object} Filter representation
         */
        mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) {
-               var areAnySelected, paramValues, defaultValue, item, currentValue,
+               var areAnySelected, paramValues, item, currentValue,
                        oneWasSelected = false,
                        defaultParams = this.getDefaultParams(),
-                       defaultFilters = this.getDefaultFilters(),
                        expandedParams = $.extend( true, {}, paramRepresentation ),
                        model = this,
                        paramToFilterMap = {},
                        result = {};
 
+               if ( this.isSticky() ) {
+                       // If the group is sticky, check if all parameters are represented
+                       // and for those that aren't represented, add them with their default
+                       // values
+                       paramRepresentation = $.extend( true, {}, this.getDefaultParams(), paramRepresentation );
+               }
+
                paramRepresentation = paramRepresentation || {};
                if (
                        this.getType() === 'send_unselected_if_any' ||
                        } );
 
                        $.each( expandedParams, function ( paramName, paramValue ) {
-                               var value = paramValue,
-                                       filterItem = paramToFilterMap[ paramName ];
+                               var filterItem = paramToFilterMap[ paramName ];
 
                                if ( model.getType() === 'send_unselected_if_any' ) {
                                        // Flip the definition between the parameter
                                                false;
                                } else if ( model.getType() === 'boolean' ) {
                                        // Straight-forward definition of state
-                                       if (
-                                               filterItem.isUsingDefaultAsBaseValue() &&
-                                               paramRepresentation[ filterItem.getParamName() ] === undefined
-                                       ) {
-                                               value = defaultParams[ filterItem.getParamName() ];
-                                       }
-                                       result[ filterItem.getName() ] = !!Number( value );
+                                       result[ filterItem.getName() ] = !!Number( paramRepresentation[ filterItem.getParamName() ] );
                                }
                        } );
                } else if ( this.getType() === 'string_options' ) {
                } else if ( this.getType() === 'single_option' ) {
                        // There is parameter that fits a single filter and if not, get the default
                        this.getItems().forEach( function ( filterItem ) {
-                               var selected = false;
+                               var selected = filterItem.getParamName() === paramRepresentation[ model.getName() ];
 
-                               if (
-                                       filterItem.isUsingDefaultAsBaseValue() &&
-                                       paramRepresentation[ model.getName() ] === undefined
-                               ) {
-                                       selected = !!Number( paramRepresentation[ model.getName() ] );
-                               } else {
-                                       selected = filterItem.getParamName() === paramRepresentation[ model.getName() ];
-                               }
                                result[ filterItem.getName() ] = selected;
                                oneWasSelected = oneWasSelected || selected;
                        } );
                // Go over result and make sure all filters are represented.
                // If any filters are missing, they will get a falsey value
                this.getItems().forEach( function ( filterItem ) {
-                       if (
-                               (
-                                       // This setting can only be applied to these groups
-                                       // the other groups are way too complex for that
-                                       model.getType() === 'single_option' ||
-                                       model.getType() === 'boolean'
-                               ) &&
-                               result[ filterItem.getName() ] === undefined &&
-                               filterItem.isUsingDefaultAsBaseValue()
-                       ) {
-                               result[ filterItem.getName() ] = !!defaultFilters[ filterItem.getName() ];
+                       if ( result[ filterItem.getName() ] === undefined ) {
+                               result[ filterItem.getName() ] = false;
                        }
-                       oneWasSelected = oneWasSelected || !!result[ filterItem.getName() ];
                } );
 
                // Make sure that at least one option is selected in
                        this.getType() === 'single_option' &&
                        !oneWasSelected
                ) {
-                       defaultValue = this.getDefaultParams();
-                       item = this.getItemByParamName( defaultValue[ this.getName() ] );
+                       item = this.getItems()[ 0 ];
+                       if ( defaultParams[ this.getName() ] ) {
+                               item = this.getItemByParamName( defaultParams[ this.getName() ] );
+                       }
+
                        result[ item.getName() ] = true;
                }
 
                return result;
        };
 
+       /**
+        * Get current selected state of all filter items in this group
+        *
+        * @return {Object} Selected state
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getSelectedState = function () {
+               var state = {};
+
+               this.getItems().forEach( function ( filterItem ) {
+                       state[ filterItem.getName() ] = filterItem.isSelected();
+               } );
+
+               return state;
+       };
+
        /**
         * Get item by its filter name
         *
         */
        mw.rcfilters.dm.FilterGroup.prototype.selectItemByParamName = function ( paramName ) {
                this.getItems().forEach( function ( item ) {
-                       item.toggleSelected( item.getParamName() === paramName );
+                       item.toggleSelected( item.getParamName() === String( paramName ) );
                } );
        };
 
         */
        mw.rcfilters.dm.FilterGroup.prototype.getItemByParamName = function ( paramName ) {
                return this.getItems().filter( function ( item ) {
-                       return item.getParamName() === paramName;
+                       return item.getParamName() === String( paramName );
                } )[ 0 ];
        };
 
        mw.rcfilters.dm.FilterGroup.prototype.isFullCoverage = function () {
                return this.fullCoverage;
        };
+
+       /**
+        * Check whether the group is defined as sticky default
+        *
+        * @return {boolean} Group is sticky default
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.isSticky = function () {
+               return this.sticky;
+       };
+
+       /**
+        * Check whether the group value is excluded from saved queries
+        *
+        * @return {boolean} Group value is excluded from saved queries
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.isExcludedFromSavedQueries = function () {
+               return this.excludedFromSavedQueries;
+       };
 }( mediaWiki ) );