Merge "Add semantic tags to license info text"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.SavedQueriesModel.js
index 2b17897..77d5961 100644 (file)
@@ -80,8 +80,7 @@
         * @fires initialize
         */
        mw.rcfilters.dm.SavedQueriesModel.prototype.initialize = function ( savedQueries ) {
-               var model = this,
-                       excludedParams = this.filtersModel.getExcludedParams();
+               var model = this;
 
                savedQueries = savedQueries || {};
 
                                isDefault = String( savedQueries.default ) === String( id );
 
                        if ( normalizedData && normalizedData.params ) {
-                               // Backwards-compat fix: Remove excluded parameters from
+                               // Backwards-compat fix: Remove sticky parameters from
                                // the given data, if they exist
-                               excludedParams.forEach( function ( name ) {
-                                       delete normalizedData.params[ name ];
-                               } );
+                               normalizedData.params = model.filtersModel.removeStickyParams( normalizedData.params );
+
+                               // Correct the invert state for effective selection
+                               if ( normalizedData.params.invert && !normalizedData.params.namespace ) {
+                                       delete normalizedData.params.invert;
+                               }
+
+                               model.cleanupHighlights( normalizedData );
 
                                id = String( id );
-                               model.addNewQuery( obj.label, normalizedData, isDefault, id );
+
+                               // Skip the addNewQuery method because we don't want to unnecessarily manipulate
+                               // the given saved queries unless we literally intend to (like in backwards compat fixes)
+                               // And the addNewQuery method also uses a minimization routine that checks for the
+                               // validity of items and minimizes the query. This isn't necessary for queries loaded
+                               // from the backend, and has the risk of removing values if they're temporarily
+                               // invalid (example: if we temporarily removed a cssClass from a filter in the backend)
+                               model.addItems( [
+                                       new mw.rcfilters.dm.SavedQueryItemModel(
+                                               id,
+                                               obj.label,
+                                               normalizedData,
+                                               { 'default': isDefault }
+                                       )
+                               ] );
 
                                if ( isDefault ) {
                                        model.default = id;
                this.emit( 'initialize' );
        };
 
+       /**
+        * Clean up highlight parameters.
+        * 'highlight' used to be stored, it's not inferred based on the presence of absence of
+        * filter colors.
+        *
+        * @param {Object} data Saved query data
+        */
+       mw.rcfilters.dm.SavedQueriesModel.prototype.cleanupHighlights = function ( data ) {
+               if (
+                       data.params.highlight === '0' &&
+                       data.highlights && Object.keys( data.highlights ).length
+               ) {
+                       data.highlights = {};
+               }
+               delete data.params.highlight;
+       };
+
        /**
         * Convert from representation of filters to representation of parameters
         *
                delete data.highlights.highlight;
 
                // Filters
-               newData.params = this.filtersModel.getParametersFromFilters( fullFilterRepresentation );
+               newData.params = this.filtersModel.getMinimizedParamRepresentation(
+                       this.filtersModel.getParametersFromFilters( fullFilterRepresentation )
+               );
 
-               // Highlights (taking out 'highlight' itself, appending _color to keys)
+               // Highlights: appending _color to keys
                newData.highlights = {};
-               Object.keys( data.highlights ).forEach( function ( highlightedFilterName ) {
-                       newData.highlights[ highlightedFilterName + '_color' ] = data.highlights[ highlightedFilterName ];
+               $.each( data.highlights, function ( highlightedFilterName, value ) {
+                       if ( value ) {
+                               newData.highlights[ highlightedFilterName + '_color' ] = data.highlights[ highlightedFilterName ];
+                       }
                } );
 
-               // Add highlight and invert toggles to params
+               // Add highlight
                newData.params.highlight = String( Number( highlightEnabled || 0 ) );
-               newData.params.invert = String( Number( data.invert || 0 ) );
 
                return newData;
        };
 
-       /**
-        * Get an object representing the base state of parameters
-        * and highlights.
-        *
-        * This is meant to make sure that the saved queries that are
-        * in memory are always the same structure as what we would get
-        * by calling the current model's "getSelectedState" and by checking
-        * highlight items.
-        *
-        * In cases where a user saved a query when the system had a certain
-        * set of params, and then a filter was added to the system, we want
-        * to make sure that the stored queries can still be comparable to
-        * the current state, which means that we need the base state for
-        * two operations:
-        *
-        * - Saved queries are stored in "minimal" view (only changed params
-        *   are stored); When we initialize the system, we merge each minimal
-        *   query with the base state (using 'getMinimalParamList') so all
-        *   saved queries have the exact same structure as what we would get
-        *   by checking the getSelectedState of the filter.
-        * - When we save the queries, we minimize the object to only represent
-        *   whatever has actually changed, rather than store the entire
-        *   object. To check what actually is different so we can store it,
-        *   we need to obtain a base state to compare against, this is
-        *   what #getMinimalParamList does
-        *
-        * @return {Object} Base parameter state
-        */
-       mw.rcfilters.dm.SavedQueriesModel.prototype.getBaseParamState = function () {
-               var allParams,
-                       highlightedItems = {};
-
-               if ( !this.baseParamState ) {
-                       allParams = this.filtersModel.getParametersFromFilters( {} );
-
-                       // Prepare highlights
-                       this.filtersModel.getItemsSupportingHighlights().forEach( function ( item ) {
-                               highlightedItems[ item.getName() + '_color' ] = null;
-                       } );
-
-                       this.baseParamState = {
-                               params: $.extend( true, { invert: '0', highlight: '0' }, allParams ),
-                               highlights: highlightedItems
-                       };
-               }
-
-               return this.baseParamState;
-       };
-
-       /**
-        * Get an object that holds only the parameters and highlights that have
-        * values different than the base value.
-        *
-        * This is the reverse of the normalization we do initially on loading and
-        * initializing the saved queries model.
-        *
-        * @param {Object} valuesObject Object representing the state of both
-        *  filters and highlights in its normalized version, to be minimized.
-        * @return {Object} Minimal filters and highlights list
-        */
-       mw.rcfilters.dm.SavedQueriesModel.prototype.getMinimalParamList = function ( valuesObject ) {
-               var result = { params: {}, highlights: {} },
-                       baseState = this.getBaseParamState();
-
-               // XOR results
-               $.each( valuesObject.params, function ( name, value ) {
-                       if ( baseState.params !== undefined && baseState.params[ name ] !== value ) {
-                               result.params[ name ] = value;
-                       }
-               } );
-
-               $.each( valuesObject.highlights, function ( name, value ) {
-                       if ( baseState.highlights !== undefined && baseState.highlights[ name ] !== value ) {
-                               result.highlights[ name ] = value;
-                       }
-               } );
-
-               return result;
-       };
-
        /**
         * Add a query item
         *
         * @param {string} label Label for the new query
-        * @param {Object} data Data for the new query
+        * @param {Object} fulldata Full data representation for the new query, combining highlights and filters
         * @param {boolean} isDefault Item is default
         * @param {string} [id] Query ID, if exists. If this isn't given, a random
         *  new ID will be created.
         * @return {string} ID of the newly added query
         */
-       mw.rcfilters.dm.SavedQueriesModel.prototype.addNewQuery = function ( label, data, isDefault, id ) {
-               var randomID = String( id || ( new Date() ).getTime() ),
-                       normalizedData = this.getMinimalParamList( data );
+       mw.rcfilters.dm.SavedQueriesModel.prototype.addNewQuery = function ( label, fulldata, isDefault, id ) {
+               var normalizedData = { params: {}, highlights: {} },
+                       highlightParamNames = Object.keys( this.filtersModel.getEmptyHighlightParameters() ),
+                       randomID = String( id || ( new Date() ).getTime() ),
+                       data = this.filtersModel.getMinimizedParamRepresentation( fulldata );
+
+               // Split highlight/params
+               $.each( data, function ( param, value ) {
+                       if ( param !== 'highlight' && highlightParamNames.indexOf( param ) > -1 ) {
+                               normalizedData.highlights[ param ] = value;
+                       } else {
+                               normalizedData.params[ param ] = value;
+                       }
+               } );
+
+               // Correct the invert state for effective selection
+               if ( normalizedData.params.invert && !this.filtersModel.areNamespacesEffectivelyInverted() ) {
+                       delete normalizedData.params.invert;
+               }
 
                // Add item
                this.addItems( [
         */
        mw.rcfilters.dm.SavedQueriesModel.prototype.findMatchingQuery = function ( fullQueryComparison ) {
                // Minimize before comparison
-               fullQueryComparison = this.getMinimalParamList( fullQueryComparison );
+               fullQueryComparison = this.filtersModel.getMinimizedParamRepresentation( fullQueryComparison );
+
+               // Correct the invert state for effective selection
+               if ( fullQueryComparison.invert && !this.filtersModel.areNamespacesEffectivelyInverted() ) {
+                       delete fullQueryComparison.invert;
+               }
 
                return this.getItems().filter( function ( item ) {
                        return OO.compare(
-                               item.getData(),
+                               item.getCombinedData(),
                                fullQueryComparison
                        );
                } )[ 0 ];
        };
 
        /**
-        * Get an item's full data
+        * Get the full data representation of the default query, if it exists
         *
-        * @param {string} queryID Query identifier
-        * @return {Object} Item's full data
+        * @return {Object|null} Representation of the default params if exists.
+        *  Null if default doesn't exist or if the user is not logged in.
+        */
+       mw.rcfilters.dm.SavedQueriesModel.prototype.getDefaultParams = function () {
+               return ( !mw.user.isAnon() && this.getItemParams( this.getDefault() ) ) || {};
+       };
+
+       /**
+        * Get a full parameter representation of an item data
+        *
+        * @param  {Object} queryID Query ID
+        * @return {Object} Parameter representation
         */
-       mw.rcfilters.dm.SavedQueriesModel.prototype.getItemFullData = function ( queryID ) {
-               var item = this.getItemByID( queryID );
+       mw.rcfilters.dm.SavedQueriesModel.prototype.getItemParams = function ( queryID ) {
+               var item = this.getItemByID( queryID ),
+                       data = item ? item.getData() : {};
 
-               // Fill in the base params
-               return item ? $.extend( true, {}, this.getBaseParamState(), item.getData() ) : {};
+               return !$.isEmptyObject( data ) ? this.buildParamsFromData( data ) : {};
+       };
+
+       /**
+        * Build a full parameter representation given item data and model sticky values state
+        *
+        * @param  {Object} data Item data
+        * @return {Object} Full param representation
+        */
+       mw.rcfilters.dm.SavedQueriesModel.prototype.buildParamsFromData = function ( data ) {
+               data = data || {};
+               // Return parameter representation
+               return this.filtersModel.getMinimizedParamRepresentation( $.extend( true, {},
+                       data.params,
+                       data.highlights
+               ) );
        };
 
        /**
         * @return {Object} Object representing the state of the model and items
         */
        mw.rcfilters.dm.SavedQueriesModel.prototype.getState = function () {
-               var model = this,
-                       obj = { queries: {}, version: '2' };
+               var obj = { queries: {}, version: '2' };
 
                // Translate the items to the saved object
                this.getItems().forEach( function ( item ) {
-                       var itemState = item.getState();
-
-                       itemState.data = model.getMinimalParamList( itemState.data );
-
-                       obj.queries[ item.getID() ] = itemState;
+                       obj.queries[ item.getID() ] = item.getState();
                } );
 
                if ( this.getDefault() ) {