Merge "Change php extract() to explicit code"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.FiltersViewModel.js
index 3a6efe2..bbc1d7e 100644 (file)
 
                this.groups = {};
                this.defaultParams = {};
-               this.defaultFiltersEmpty = null;
                this.highlightEnabled = false;
                this.parameterMap = {};
                this.emptyParameterState = null;
 
                this.views = {};
                this.currentView = 'default';
+               this.searchQuery = null;
 
                // Events
                this.aggregate( { update: 'filterItemUpdate' } );
                $.each( this.groups, function ( group, groupModel ) {
                        if (
                                groupModel.getType() === 'send_unselected_if_any' ||
-                               groupModel.getType() === 'boolean'
+                               groupModel.getType() === 'boolean' ||
+                               groupModel.getType() === 'any_value'
                        ) {
                                // Individual filters
                                groupModel.getItems().forEach( function ( filterItem ) {
                        }
                } );
 
-               this.currentView = 'default';
+               this.setSearch( '' );
 
                this.updateHighlightedState();
 
         * @param {Object} params Parameters object
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.updateStateFromParams = function ( params ) {
+               var filtersValue;
                // For arbitrary numeric single_option values make sure the values
                // are normalized to fit within the limits
                $.each( this.getFilterGroups(), function ( groupName, groupModel ) {
                        params[ groupName ] = groupModel.normalizeArbitraryValue( params[ groupName ] );
                } );
 
-               // Update filter states
-               this.toggleFiltersSelected(
-                       this.getFiltersFromParameters(
-                               params
-                       )
-               );
+               // Update filter values
+               filtersValue = this.getFiltersFromParameters( params );
+               Object.keys( filtersValue ).forEach( function ( filterName ) {
+                       this.getItemByName( filterName ).setValue( filtersValue[ filterName ] );
+               }.bind( this ) );
 
                // Update highlight state
                this.getItemsSupportingHighlights().forEach( function ( filterItem ) {
        /**
         * Get a representation of the full parameter list, including all base values
         *
-        * @param {Object} [parameters] A given parameter state to minimize. If not given the current
-        *  state of the system will be used.
-        * @param {boolean} [removeExcluded] Remove excluded and sticky parameters
         * @return {Object} Full parameter representation
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getExpandedParamRepresentation = function ( parameters, removeExcluded ) {
-               var result = {};
-
-               parameters = parameters ? $.extend( true, {}, parameters ) : this.getCurrentParameterState();
-
-               result = $.extend(
+       mw.rcfilters.dm.FiltersViewModel.prototype.getExpandedParamRepresentation = function () {
+               return $.extend(
                        true,
                        {},
                        this.getEmptyParameterState(),
-                       parameters
+                       this.getCurrentParameterState()
                );
-
-               if ( removeExcluded ) {
-                       result = this.removeExcludedParams( result );
-               }
-
-               return result;
        };
 
        /**
         * Get a parameter representation of the current state of the model
         *
-        * @param {boolean} [removeExcludedParams] Remove excluded filters from final result
+        * @param {boolean} [removeStickyParams] Remove sticky filters from final result
         * @return {Object} Parameter representation of the current state of the model
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getCurrentParameterState = function ( removeExcludedParams ) {
-               var excludedParams,
-                       state = this.getMinimizedParamRepresentation( $.extend(
-                               true,
-                               {},
-                               this.getParametersFromFilters( this.getSelectedState() ),
-                               this.getHighlightParameters()
-                       ) );
-
-               if ( removeExcludedParams ) {
-                       excludedParams = this.getExcludedParams();
-                       // Delete all excluded filters
-                       $.each( state, function ( param ) {
-                               if ( excludedParams.indexOf( param ) > -1 ) {
-                                       delete state[ param ];
-                               }
-                       } );
+       mw.rcfilters.dm.FiltersViewModel.prototype.getCurrentParameterState = function ( removeStickyParams ) {
+               var state = this.getMinimizedParamRepresentation( $.extend(
+                       true,
+                       {},
+                       this.getParametersFromFilters( this.getSelectedState() ),
+                       this.getHighlightParameters()
+               ) );
+
+               if ( removeStickyParams ) {
+                       state = this.removeStickyParams( state );
                }
 
                return state;
        };
 
        /**
-        * Delete excluded and sticky filters from given object. If object isn't given, output
-        * the current filter state without the excluded values
-        *
-        * @param {Object} [filterState] Filter state
-        * @return {Object} Filter state without excluded filters
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.removeExcludedFilters = function ( filterState ) {
-               filterState = filterState !== undefined ?
-                       $.extend( true, {}, filterState ) :
-                       this.getFiltersFromParameters();
-
-               // Remove excluded filters
-               Object.keys( this.getExcludedFiltersState() ).forEach( function ( filterName ) {
-                       delete filterState[ filterName ];
-               } );
-
-               // Remove sticky filters
-               Object.keys( this.getStickyFiltersState() ).forEach( function ( filterName ) {
-                       delete filterState[ filterName ];
-               } );
-
-               return filterState;
-       };
-       /**
-        * Delete excluded and sticky parameters from given object. If object isn't given, output
-        * the current param state without the excluded values
+        * Delete sticky parameters from given object.
         *
-        * @param {Object} [paramState] Parameter state
-        * @return {Object} Parameter state without excluded filters
+        * @param {Object} paramState Parameter state
+        * @return {Object} Parameter state without sticky parameters
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.removeExcludedParams = function ( paramState ) {
-               paramState = paramState !== undefined ?
-                       $.extend( true, {}, paramState ) :
-                       this.getCurrentParameterState();
-
-               // Remove excluded filters
-               this.getExcludedParams().forEach( function ( paramName ) {
-                       delete paramState[ paramName ];
-               } );
-
-               // Remove sticky filters
+       mw.rcfilters.dm.FiltersViewModel.prototype.removeStickyParams = function ( paramState ) {
                this.getStickyParams().forEach( function ( paramName ) {
                        delete paramState[ paramName ];
                } );
                return paramState;
        };
 
-       /**
-        * Get the names of all available filters
-        *
-        * @return {string[]} An array of filter names
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getFilterNames = function () {
-               return this.getItems().map( function ( item ) { return item.getName(); } );
-       };
-
        /**
         * Turn the highlight feature on or off
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.getViewTrigger = function ( view ) {
                return ( this.views[ view ] && this.views[ view ].trigger ) || '';
        };
+
        /**
         * Get the value of a specific parameter
         *
        /**
         * Get the current selected state of the filters
         *
+        * @param {boolean} [onlySelected] return an object containing only the filters with a value
         * @return {Object} Filters selected state
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedState = function () {
+       mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedState = function ( onlySelected ) {
                var i,
                        items = this.getItems(),
                        result = {};
 
                for ( i = 0; i < items.length; i++ ) {
-                       result[ items[ i ].getName() ] = items[ i ].isSelected();
+                       if ( !onlySelected || items[ i ].getValue() ) {
+                               result[ items[ i ].getName() ] = items[ i ].getValue();
+                       }
                }
 
                return result;
        /**
         * Get an object representing default parameters state
         *
-        * @param {boolean} [excludeHiddenParams] Exclude hidden and sticky params
         * @return {Object} Default parameter values
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultParams = function ( excludeHiddenParams ) {
+       mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultParams = function () {
                var result = {};
 
                // Get default filter state
                $.each( this.groups, function ( name, model ) {
-                       $.extend( true, result, model.getDefaultParams() );
-               } );
-
-               if ( excludeHiddenParams ) {
-                       Object.keys( this.getDefaultHiddenParams() ).forEach( function ( paramName ) {
-                               delete result[ paramName ];
-                       } );
-               }
-
-               return result;
-       };
-
-       /**
-        * Get an object representing defaults for the hidden parameters state
-        *
-        * @return {Object} Default values for hidden parameters
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultHiddenParams = function () {
-               var result = {};
-
-               // Get default filter state
-               $.each( this.groups, function ( name, model ) {
-                       if ( model.isHidden() ) {
+                       if ( !model.isSticky() ) {
                                $.extend( true, result, model.getDefaultParams() );
                        }
                } );
 
                $.each( this.groups, function ( name, model ) {
                        if ( model.isSticky() ) {
-                               $.extend( true, result, model.getDefaultParams() );
-                       }
-               } );
-
-               return result;
-       };
-
-       /**
-        * Get a filter representation of all sticky parameters
-        *
-        * @return {Object} Sticky filters values
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getStickyFiltersState = function () {
-               var result = {};
-
-               $.each( this.groups, function ( name, model ) {
-                       if ( model.isSticky() ) {
-                               $.extend( true, result, model.getSelectedState() );
-                       }
-               } );
-
-               return result;
-       };
-
-       /**
-        * Get a filter representation of all parameters that are marked
-        * as being excluded from saved query.
-        *
-        * @return {Object} Excluded filters values
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getExcludedFiltersState = function () {
-               var result = {};
-
-               $.each( this.groups, function ( name, model ) {
-                       if ( model.isExcludedFromSavedQueries() ) {
-                               $.extend( true, result, model.getSelectedState() );
-                       }
-               } );
-
-               return result;
-       };
-
-       /**
-        * Get the parameter names that represent filters that are excluded
-        * from saved queries.
-        *
-        * @return {string[]} Parameter names
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getExcludedParams = function () {
-               var result = [];
-
-               $.each( this.groups, function ( name, model ) {
-                       if ( model.isExcludedFromSavedQueries() ) {
-                               if ( model.isPerGroupRequestParameter() ) {
-                                       result.push( name );
-                               } else {
-                                       // Each filter is its own param
-                                       result = result.concat( model.getItems().map( function ( filterItem ) {
-                                               return filterItem.getParamName();
-                                       } ) );
-                               }
+                               $.extend( true, result, model.getParamRepresentation() );
                        }
                } );
 
                        // all filters (set to false)
                        this.getItems().forEach( function ( filterItem ) {
                                groupItemDefinition[ filterItem.getGroupName() ] = groupItemDefinition[ filterItem.getGroupName() ] || {};
-                               groupItemDefinition[ filterItem.getGroupName() ][ filterItem.getName() ] = !!filterDefinition[ filterItem.getName() ];
+                               groupItemDefinition[ filterItem.getGroupName() ][ filterItem.getName() ] = filterItem.coerceValue( filterDefinition[ filterItem.getName() ] );
                        } );
                }
 
        };
 
        /**
-        * Check whether the current filter state is set to all false.
+        * Check whether no visible filter is selected.
+        *
+        * Filter groups that are hidden or sticky are not shown in the
+        * active filters area and therefore not included in this check.
         *
-        * @return {boolean} Current filters are all empty
+        * @return {boolean} No visible filter is selected
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.areCurrentFiltersEmpty = function () {
+       mw.rcfilters.dm.FiltersViewModel.prototype.areVisibleFiltersEmpty = function () {
                // Check if there are either any selected items or any items
                // that have highlight enabled
                return !this.getItems().some( function ( filterItem ) {
-                       return !filterItem.getGroupModel().isHidden() && ( filterItem.isSelected() || filterItem.isHighlighted() );
+                       var visible = !filterItem.getGroupModel().isSticky() && !filterItem.getGroupModel().isHidden(),
+                               active = ( filterItem.isSelected() || filterItem.isHighlighted() );
+                       return visible && active;
                } );
        };
 
+       /**
+        * Check whether the invert state is a valid one. A valid invert state is one where
+        * there are actual namespaces selected.
+        *
+        * This is done to compare states to previous ones that may have had the invert model
+        * selected but effectively had no namespaces, so are not effectively different than
+        * ones where invert is not selected.
+        *
+        * @return {boolean} Invert is effectively selected
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.areNamespacesEffectivelyInverted = function () {
+               return this.getInvertModel().isSelected() &&
+                       this.getSelectedItems().some( function ( itemModel ) {
+                               return itemModel.getGroupModel().getView() === 'namespace';
+                       } );
+       };
+
        /**
         * Get the item that matches the given name
         *
 
                return allSelected;
        };
-       /**
-        * Switch the current view
-        *
-        * @param {string} view View name
-        * @fires update
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.switchView = function ( view ) {
-               if ( this.views[ view ] && this.currentView !== view ) {
-                       this.currentView = view;
-                       this.emit( 'update' );
-               }
-       };
 
        /**
         * Get the current view
                return this.views[ viewName ] && this.views[ viewName ].title;
        };
 
-       /**
-        * Get an array of all available view names
-        *
-        * @return {string} Available view names
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.getAvailableViews = function () {
-               return Object.keys( this.views );
-       };
-
        /**
         * Get the view that fits the given trigger
         *
                return result;
        };
 
+       /**
+        * Return a version of the given string that is without any
+        * view triggers.
+        *
+        * @param {string} str Given string
+        * @return {string} Result
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.removeViewTriggers = function ( str ) {
+               if ( this.getViewFromString( str ) !== 'default' ) {
+                       str = str.substr( 1 );
+               }
+
+               return str;
+       };
+
+       /**
+        * Get the view from the given string by a trigger, if it exists
+        *
+        * @param {string} str Given string
+        * @return {string} View name
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.getViewFromString = function ( str ) {
+               return this.getViewByTrigger( str.substr( 0, 1 ) );
+       };
+
+       /**
+        * Set the current search for the system.
+        * This also dictates what items and groups are visible according
+        * to the search in #findMatches
+        *
+        * @param {string} searchQuery Search query, including triggers
+        * @fires searchChange
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.setSearch = function ( searchQuery ) {
+               var visibleGroups, visibleGroupNames;
+
+               if ( this.searchQuery !== searchQuery ) {
+                       // Check if the view changed
+                       this.switchView( this.getViewFromString( searchQuery ) );
+
+                       visibleGroups = this.findMatches( searchQuery );
+                       visibleGroupNames = Object.keys( visibleGroups );
+
+                       // Update visibility of items and groups
+                       $.each( this.getFilterGroups(), function ( groupName, groupModel ) {
+                               // Check if the group is visible at all
+                               groupModel.toggleVisible( visibleGroupNames.indexOf( groupName ) !== -1 );
+                               groupModel.setVisibleItems( visibleGroups[ groupName ] || [] );
+                       } );
+
+                       this.searchQuery = searchQuery;
+                       this.emit( 'searchChange', this.searchQuery );
+               }
+       };
+
+       /**
+        * Get the current search
+        *
+        * @return {string} Current search query
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.getSearch = function () {
+               return this.searchQuery;
+       };
+
+       /**
+        * Switch the current view
+        *
+        * @private
+        * @param {string} view View name
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.switchView = function ( view ) {
+               if ( this.views[ view ] && this.currentView !== view ) {
+                       this.currentView = view;
+               }
+       };
+
        /**
         * Toggle the highlight feature on and off.
         * Propagate the change to filter items.
                this.getItemByName( filterName ).clearHighlightColor();
        };
 
-       /**
-        * Clear highlight for all filter items
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.clearAllHighlightColors = function () {
-               this.getItems().forEach( function ( filterItem ) {
-                       filterItem.clearHighlightColor();
-               } );
-       };
-
-       /**
-        * Return a version of the given string that is without any
-        * view triggers.
-        *
-        * @param {string} str Given string
-        * @return {string} Result
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.removeViewTriggers = function ( str ) {
-               if ( this.getViewByTrigger( str.substr( 0, 1 ) ) !== 'default' ) {
-                       str = str.substr( 1 );
-               }
-
-               return str;
-       };
 }( mediaWiki, jQuery ) );