RCFilters: refactor highlight state
authorStephane Bisson <sbisson@wikimedia.org>
Thu, 5 Oct 2017 20:10:42 +0000 (16:10 -0400)
committerMooeypoo <moriel@gmail.com>
Thu, 19 Oct 2017 19:28:41 +0000 (19:28 +0000)
* Consider highlight to be enabled when applying
  parameters that contain highlight colors.

* Don't store 'highlight=0|1' in the URL or
  saved query.

Bug: T177009
Change-Id: I8f3a1c609cef89bc08077776d453ced6f2d0f5e2

18 files changed:
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueryItemModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuOptionWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagItemWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.TagItemWidget.js
tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.SavedQueriesModel.test.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.SavedQueryItemModel.test.js

index b17355f..d20e2e7 100644 (file)
@@ -93,7 +93,6 @@
         */
        mw.rcfilters.dm.FilterGroup.prototype.initializeFilters = function ( filterDefinition, groupDefault ) {
                var defaultParam,
-                       anyHighlighted,
                        supersetMap = {},
                        model = this,
                        items = [];
                }
 
                // add highlights to defaultParams
-               anyHighlighted = false;
                this.getItems().forEach( function ( filterItem ) {
                        if ( filterItem.isHighlighted() ) {
-                               anyHighlighted = true;
                                this.defaultParams[ filterItem.getName() + '_color' ] = filterItem.getHighlightColor();
                        }
                }.bind( this ) );
-               if ( anyHighlighted ) {
-                       this.defaultParams.highlight = '1';
-               }
 
                // Store default filter state based on default params
                this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() );
index b8e1129..3a6efe2 100644 (file)
 
                this.currentView = 'default';
 
-               if ( this.getHighlightedItems().length > 0 ) {
-                       this.toggleHighlight( true );
-               }
+               this.updateHighlightedState();
 
                // Finish initialization
                this.emit( 'initialize' );
                                filterItem.clearHighlightColor();
                        }
                } );
-               this.toggleHighlight( !!Number( params.highlight ) );
+               this.updateHighlightedState();
 
                // Check all filter interactions
                this.reassessFilterInteractions();
                                true,
                                {},
                                this.getParametersFromFilters( {} ),
-                               this.getEmptyHighlightParameters(),
-                               { highlight: '0' }
+                               this.getEmptyHighlightParameters()
                        );
                }
                return this.emptyParameterState;
 
                // Highlights
                Object.keys( this.getEmptyHighlightParameters() ).forEach( function ( param ) {
-                       if ( param !== 'highlight' && parameters[ param ] ) {
+                       if ( parameters[ param ] ) {
                                // If a highlight parameter is not undefined and not null
                                // add it to the result
-                               // Ignore "highlight" parameter because that, we checked already with
-                               // the empty parameter state (and this soon changes to an implicit value)
                                result[ param ] = parameters[ param ];
                        }
                } );
                                true,
                                {},
                                this.getParametersFromFilters( this.getSelectedState() ),
-                               this.getHighlightParameters(),
-                               {
-                                       // HACK: Add highlight. This is only needed while it's
-                                       // stored as an outside state
-                                       highlight: String( Number( this.isHighlightEnabled() ) )
-                               }
+                               this.getHighlightParameters()
                        ) );
 
                if ( removeExcludedParams ) {
                return this.getItems().map( function ( item ) { return item.getName(); } );
        };
 
+       /**
+        * Turn the highlight feature on or off
+        */
+       mw.rcfilters.dm.FiltersViewModel.prototype.updateHighlightedState = function () {
+               this.toggleHighlight( this.getHighlightedItems().length > 0 );
+       };
+
        /**
         * Get the object that defines groups by their name.
         *
         *                  are the selected highlight colors.
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.getHighlightParameters = function () {
-               var result = {};
+               var highlightEnabled = this.isHighlightEnabled(),
+                       result = {};
 
                this.getItems().forEach( function ( filterItem ) {
                        if ( filterItem.isHighlightSupported() ) {
-                               result[ filterItem.getName() + '_color' ] = filterItem.getHighlightColor() || null;
+                               result[ filterItem.getName() + '_color' ] = highlightEnabled && filterItem.isHighlighted() ?
+                                       filterItem.getHighlightColor() :
+                                       null;
                        }
                } );
-               result.highlight = String( Number( this.isHighlightEnabled() ) );
 
                return result;
        };
                                result[ filterItem.getName() + '_color' ] = null;
                        }
                } );
-               result.highlight = '0';
-
-               return result;
-       };
-
-       /**
-        * Extract the highlight values from given object. Since highlights are
-        * the same for filter and parameters, it doesn't matter which one is
-        * given; values will be returned with a full list of the highlights
-        * with colors or null values.
-        *
-        * @param {Object} representation Object containing representation of
-        *  some or all highlight values
-        * @return {Object} Object where keys are `<filter name>_color` and values
-        *                  are the selected highlight colors. The returned object
-        *                  contains all available filters either with a color value
-        *                  or with null.
-        */
-       mw.rcfilters.dm.FiltersViewModel.prototype.extractHighlightValues = function ( representation ) {
-               var result = {};
-
-               this.getItems().forEach( function ( filterItem ) {
-                       var highlightName = filterItem.getName() + '_color';
-                       result[ highlightName ] = representation[ highlightName ] || null;
-               } );
 
                return result;
        };
        mw.rcfilters.dm.FiltersViewModel.prototype.getCurrentlyUsedHighlightColors = function () {
                var result = [];
 
-               this.getHighlightedItems().forEach( function ( filterItem ) {
-                       var color = filterItem.getHighlightColor();
+               if ( this.isHighlightEnabled() ) {
+                       this.getHighlightedItems().forEach( function ( filterItem ) {
+                               var color = filterItem.getHighlightColor();
 
-                       if ( result.indexOf( color ) === -1 ) {
-                               result.push( color );
-                       }
-               } );
+                               if ( result.indexOf( color ) === -1 ) {
+                                       result.push( color );
+                               }
+                       } );
+               }
 
                return result;
        };
                enable = enable === undefined ? !this.highlightEnabled : enable;
 
                if ( this.highlightEnabled !== enable ) {
-                       // HACK make sure highlights are disabled globally while we toggle on the items,
-                       // otherwise we'll call clearHighlight() and applyHighlight() many many times
-                       this.highlightEnabled = false;
-                       this.getItems().forEach( function ( filterItem ) {
-                               filterItem.toggleHighlight( enable );
-                       } );
-
                        this.highlightEnabled = enable;
                        this.emit( 'highlightChange', this.highlightEnabled );
                }
index 4a8869a..d940321 100644 (file)
         * @param {string|null} highlightColor
         */
        mw.rcfilters.dm.ItemModel.prototype.setHighlightColor = function ( highlightColor ) {
+               if ( !this.isHighlightSupported() ) {
+                       return;
+               }
+
                if ( this.highlightColor !== highlightColor ) {
                        this.highlightColor = highlightColor;
                        this.emit( 'update' );
                return this.identifiers;
        };
 
-       /**
-        * Toggle the highlight feature on and off for this filter.
-        * It only works if highlight is supported for this filter.
-        *
-        * @param {boolean} enable Highlight should be enabled
-        */
-       mw.rcfilters.dm.ItemModel.prototype.toggleHighlight = function ( enable ) {
-               enable = enable === undefined ? !this.highlightEnabled : enable;
-
-               if ( !this.isHighlightSupported() ) {
-                       return;
-               }
-
-               if ( enable === this.highlightEnabled ) {
-                       return;
-               }
-
-               this.highlightEnabled = enable;
-               this.emit( 'update' );
-       };
-
-       /**
-        * Check if the highlight feature is currently enabled for this filter
-        *
-        * @return {boolean}
-        */
-       mw.rcfilters.dm.ItemModel.prototype.isHighlightEnabled = function () {
-               return !!this.highlightEnabled;
-       };
-
        /**
         * Check if the highlight feature is supported for this filter
         *
         * @return {boolean}
         */
        mw.rcfilters.dm.ItemModel.prototype.isHighlighted = function () {
-               return this.isHighlightEnabled() && !!this.getHighlightColor();
+               return !!this.getHighlightColor();
        };
 }( mediaWiki ) );
index 29585e9..23f6007 100644 (file)
                                // the given data, if they exist
                                normalizedData.params = model.filtersModel.removeExcludedParams( normalizedData.params );
 
+                               model.cleanupHighlights( normalizedData );
+
                                id = String( id );
 
                                // Skip the addNewQuery method because we don't want to unnecessarily manipulate
                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
         *
                        this.filtersModel.getParametersFromFilters( fullFilterRepresentation )
                );
 
-               // Highlights (taking out 'highlight' itself, appending _color to keys)
+               // Highlights: appending _color to keys
                newData.highlights = {};
                $.each( data.highlights, function ( highlightedFilterName, value ) {
                        if ( value ) {
index a6ff9a1..c74648e 100644 (file)
@@ -10,7 +10,7 @@
         * @param {string} label Saved query label
         * @param {Object} data Saved query data
         * @param {Object} [config] Configuration options
-        * @param {boolean} [default] This item is the default
+        * @cfg {boolean} [default] This item is the default
         */
        mw.rcfilters.dm.SavedQueryItemModel = function MwRcfiltersDmSavedQueriesModel( id, label, data, config ) {
                config = config || {};
index 0b2dd8d..ac998d7 100644 (file)
         * Check whether the current filter and highlight state exists
         * in the saved queries model.
         *
-        * @return {boolean} Query exists
+        * @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
         */
        mw.rcfilters.Controller.prototype.findQueryMatchingCurrentState = function () {
                return this.savedQueriesModel.findMatchingQuery(
index 044712c..53557f6 100644 (file)
         */
        mw.rcfilters.UriProcessor.prototype.doesQueryContainRecognizedParams = function ( uriQuery ) {
                var anyValidInUrl,
-                       validParameterNames = Object.keys( this.filtersModel.getEmptyParameterState() )
-                               .filter( function ( param ) {
-                                       // Remove 'highlight' parameter from this check;
-                                       // if it's the only parameter in the URL we still
-                                       // want to consider the URL 'empty' for defaults to load
-                                       return param !== 'highlight';
-                               } );
+                       validParameterNames = Object.keys( this.filtersModel.getEmptyParameterState() );
 
                uriQuery = uriQuery || new mw.Uri().query;
 
index e0c7e3d..8f3bacf 100644 (file)
@@ -62,7 +62,7 @@
        table.mw-enhanced-rc td {
                vertical-align: middle;
 
-               &:last-child{
+               &:last-child {
                        width: 100%;
                }
        }
index 1292901..926502d 100644 (file)
@@ -6,19 +6,22 @@
         *
         * @constructor
         * @param {mw.rcfilters.Controller} controller RCFilters controller
+        * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
         * @param {mw.rcfilters.dm.FilterItem} invertModel
-        * @param {mw.rcfilters.dm.FilterItem} model Filter item model
+        * @param {mw.rcfilters.dm.FilterItem} itemModel Filter item model
         * @param {Object} config Configuration object
         */
-       mw.rcfilters.ui.FilterMenuOptionWidget = function MwRcfiltersUiFilterMenuOptionWidget( controller, invertModel, model, config ) {
+       mw.rcfilters.ui.FilterMenuOptionWidget = function MwRcfiltersUiFilterMenuOptionWidget(
+               controller, filtersViewModel, invertModel, itemModel, config
+       ) {
                config = config || {};
 
                this.controller = controller;
                this.invertModel = invertModel;
-               this.model = model;
+               this.model = itemModel;
 
                // Parent
-               mw.rcfilters.ui.FilterMenuOptionWidget.parent.call( this, controller, this.invertModel, model, config );
+               mw.rcfilters.ui.FilterMenuOptionWidget.parent.call( this, controller, filtersViewModel, this.invertModel, itemModel, config );
 
                // Event
                this.model.getGroupModel().connect( this, { update: 'onGroupModelUpdate' } );
@@ -40,9 +43,9 @@
        /**
         * @inheritdoc
         */
-       mw.rcfilters.ui.FilterMenuOptionWidget.prototype.onModelUpdate = function () {
+       mw.rcfilters.ui.FilterMenuOptionWidget.prototype.updateUiBasedOnState = function () {
                // Parent
-               mw.rcfilters.ui.FilterMenuOptionWidget.parent.prototype.onModelUpdate.call( this );
+               mw.rcfilters.ui.FilterMenuOptionWidget.parent.prototype.updateUiBasedOnState.call( this );
 
                this.setCurrentMuteState();
        };
index 43a301f..41c7bae 100644 (file)
@@ -7,14 +7,17 @@
         *
         * @constructor
         * @param {mw.rcfilters.Controller} controller
+        * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
         * @param {mw.rcfilters.dm.FilterItem} invertModel
-        * @param {mw.rcfilters.dm.FilterItem} model Item model
+        * @param {mw.rcfilters.dm.FilterItem} itemModel Item model
         * @param {Object} config Configuration object
         */
-       mw.rcfilters.ui.FilterTagItemWidget = function MwRcfiltersUiFilterTagItemWidget( controller, invertModel, model, config ) {
+       mw.rcfilters.ui.FilterTagItemWidget = function MwRcfiltersUiFilterTagItemWidget(
+               controller, filtersViewModel, invertModel, itemModel, config
+       ) {
                config = config || {};
 
-               mw.rcfilters.ui.FilterTagItemWidget.parent.call( this, controller, invertModel, model, config );
+               mw.rcfilters.ui.FilterTagItemWidget.parent.call( this, controller, filtersViewModel, invertModel, itemModel, config );
 
                this.$element
                        .addClass( 'mw-rcfilters-ui-filterTagItemWidget' );
        mw.rcfilters.ui.FilterTagItemWidget.prototype.setCurrentMuteState = function () {
                this.setFlags( {
                        muted: (
-                               !this.model.isSelected() ||
-                               this.model.isIncluded() ||
-                               this.model.isFullyCovered()
+                               !this.itemModel.isSelected() ||
+                               this.itemModel.isIncluded() ||
+                               this.itemModel.isFullyCovered()
                        ),
-                       invalid: this.model.isSelected() && this.model.isConflicted()
+                       invalid: this.itemModel.isSelected() && this.itemModel.isConflicted()
                } );
        };
 }( mediaWiki, jQuery ) );
index ef95f2f..404cb98 100644 (file)
 
        /**
         * Respond to model itemUpdate event
+        * fixme: when a new state is applied to the model this function is called 60+ times in a row
         *
         * @param {mw.rcfilters.dm.FilterItem} item Filter item model
         */
                                item.isSelected() ||
                                (
                                        this.model.isHighlightEnabled() &&
-                                       item.isHighlightSupported() &&
                                        item.getHighlightColor()
                                )
                        ) {
                                }
                        }.bind( this ) );
                }
+
+               this.setSavedQueryVisibility();
        };
 
        /**
                if ( filterItem ) {
                        return new mw.rcfilters.ui.FilterTagItemWidget(
                                this.controller,
+                               this.model,
                                this.model.getInvertModel(),
                                filterItem,
                                {
index 36bc6cb..db43a53 100644 (file)
@@ -6,11 +6,14 @@
         *
         * @constructor
         * @param {mw.rcfilters.Controller} controller RCFilters controller
+        * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
         * @param {mw.rcfilters.dm.ItemModel} invertModel
-        * @param {mw.rcfilters.dm.ItemModel} model Item model
+        * @param {mw.rcfilters.dm.ItemModel} itemModel Item model
         * @param {Object} config Configuration object
         */
-       mw.rcfilters.ui.ItemMenuOptionWidget = function MwRcfiltersUiItemMenuOptionWidget( controller, invertModel, model, config ) {
+       mw.rcfilters.ui.ItemMenuOptionWidget = function MwRcfiltersUiItemMenuOptionWidget(
+               controller, filtersViewModel, invertModel, itemModel, config
+       ) {
                var layout,
                        classes = [],
                        $label = $( '<div>' )
                config = config || {};
 
                this.controller = controller;
+               this.filtersViewModel = filtersViewModel;
                this.invertModel = invertModel;
-               this.model = model;
+               this.itemModel = itemModel;
 
                // Parent
                mw.rcfilters.ui.ItemMenuOptionWidget.parent.call( this, $.extend( {
                        // Override the 'check' icon that OOUI defines
                        icon: '',
-                       data: this.model.getName(),
-                       label: this.model.getLabel()
+                       data: this.itemModel.getName(),
+                       label: this.itemModel.getLabel()
                }, config ) );
 
                this.checkboxWidget = new mw.rcfilters.ui.CheckboxInputWidget( {
-                       value: this.model.getName(),
-                       selected: this.model.isSelected()
+                       value: this.itemModel.getName(),
+                       selected: this.itemModel.isSelected()
                } );
 
                $label.append(
                                .addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-label-title' )
                                .append( this.$label )
                );
-               if ( this.model.getDescription() ) {
+               if ( this.itemModel.getDescription() ) {
                        $label.append(
                                $( '<div>' )
                                        .addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-label-desc' )
-                                       .text( this.model.getDescription() )
+                                       .text( this.itemModel.getDescription() )
                        );
                }
 
                this.highlightButton = new mw.rcfilters.ui.FilterItemHighlightButton(
                        this.controller,
-                       this.model,
+                       this.itemModel,
                        {
                                $overlay: config.$overlay || this.$element,
                                title: mw.msg( 'rcfilters-highlightmenu-help' )
                        }
                );
-               this.highlightButton.toggle( this.model.isHighlightEnabled() );
+               this.highlightButton.toggle( this.filtersViewModel.isHighlightEnabled() );
 
                this.excludeLabel = new OO.ui.LabelWidget( {
                        label: mw.msg( 'rcfilters-filter-excluded' )
                } );
-               this.excludeLabel.toggle( this.model.isSelected() && this.invertModel.isSelected() );
+               this.excludeLabel.toggle( this.itemModel.isSelected() && this.invertModel.isSelected() );
 
                layout = new OO.ui.FieldLayout( this.checkboxWidget, {
                        label: $label,
@@ -69,8 +73,9 @@
                } );
 
                // Events
-               this.invertModel.connect( this, { update: 'onModelUpdate' } );
-               this.model.connect( this, { update: 'onModelUpdate' } );
+               this.filtersViewModel.connect( this, { highlightChange: 'updateUiBasedOnState' } );
+               this.invertModel.connect( this, { update: 'updateUiBasedOnState' } );
+               this.itemModel.connect( this, { update: 'updateUiBasedOnState' } );
                // HACK: Prevent defaults on 'click' for the label so it
                // doesn't steal the focus away from the input. This means
                // we can continue arrow-movement after we click the label
@@ -80,7 +85,7 @@
 
                this.$element
                        .addClass( 'mw-rcfilters-ui-itemMenuOptionWidget' )
-                       .addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-view-' + this.model.getGroupModel().getView() )
+                       .addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-view-' + this.itemModel.getGroupModel().getView() )
                        .append(
                                $( '<div>' )
                                        .addClass( 'mw-rcfilters-ui-table' )
                                        )
                        );
 
-               if ( this.model.getIdentifiers() ) {
-                       this.model.getIdentifiers().forEach( function ( ident ) {
+               if ( this.itemModel.getIdentifiers() ) {
+                       this.itemModel.getIdentifiers().forEach( function ( ident ) {
                                classes.push( 'mw-rcfilters-ui-itemMenuOptionWidget-identifier-' + ident );
                        } );
 
        /**
         * Respond to item model update event
         */
-       mw.rcfilters.ui.ItemMenuOptionWidget.prototype.onModelUpdate = function () {
-               this.checkboxWidget.setSelected( this.model.isSelected() );
+       mw.rcfilters.ui.ItemMenuOptionWidget.prototype.updateUiBasedOnState = function () {
+               this.checkboxWidget.setSelected( this.itemModel.isSelected() );
 
-               this.highlightButton.toggle( this.model.isHighlightEnabled() );
-               this.excludeLabel.toggle( this.model.isSelected() && this.invertModel.isSelected() );
+               this.highlightButton.toggle( this.filtersViewModel.isHighlightEnabled() );
+               this.excludeLabel.toggle( this.itemModel.isSelected() && this.invertModel.isSelected() );
        };
 
        /**
         * @return {string} Filter name
         */
        mw.rcfilters.ui.ItemMenuOptionWidget.prototype.getName = function () {
-               return this.model.getName();
+               return this.itemModel.getName();
        };
 
        mw.rcfilters.ui.ItemMenuOptionWidget.prototype.getModel = function () {
-               return this.model;
+               return this.itemModel;
        };
 
 }( mediaWiki ) );
index 63a563c..22c176f 100644 (file)
                                        currentItems.push(
                                                new mw.rcfilters.ui.FilterMenuOptionWidget(
                                                        widget.controller,
+                                                       widget.model,
                                                        widget.model.getInvertModel(),
                                                        filterItem,
                                                        {
index cc314ac..7e324b6 100644 (file)
@@ -8,22 +8,26 @@
         *
         * @constructor
         * @param {mw.rcfilters.Controller} controller
+        * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
         * @param {mw.rcfilters.dm.FilterItem} invertModel
-        * @param {mw.rcfilters.dm.FilterItem} model Item model
+        * @param {mw.rcfilters.dm.FilterItem} itemModel Item model
         * @param {Object} config Configuration object
         * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
         */
-       mw.rcfilters.ui.TagItemWidget = function MwRcfiltersUiTagItemWidget( controller, invertModel, model, config ) {
+       mw.rcfilters.ui.TagItemWidget = function MwRcfiltersUiTagItemWidget(
+               controller, filtersViewModel, invertModel, itemModel, config
+       ) {
                // Configuration initialization
                config = config || {};
 
                this.controller = controller;
                this.invertModel = invertModel;
-               this.model = model;
+               this.filtersViewModel = filtersViewModel;
+               this.itemModel = itemModel;
                this.selected = false;
 
                mw.rcfilters.ui.TagItemWidget.parent.call( this, $.extend( {
-                       data: this.model.getName()
+                       data: this.itemModel.getName()
                }, config ) );
 
                this.$overlay = config.$overlay || this.$element;
                        .addClass( 'mw-rcfilters-ui-tagItemWidget-highlight' );
 
                // Add title attribute with the item label to 'x' button
-               this.closeButton.setTitle( mw.msg( 'rcfilters-tag-remove', this.model.getLabel() ) );
+               this.closeButton.setTitle( mw.msg( 'rcfilters-tag-remove', this.itemModel.getLabel() ) );
 
                // Events
+               this.filtersViewModel.connect( this, { highlightChange: 'updateUiBasedOnState' } );
                this.invertModel.connect( this, { update: 'updateUiBasedOnState' } );
-               this.model.connect( this, { update: 'updateUiBasedOnState' } );
+               this.itemModel.connect( this, { update: 'updateUiBasedOnState' } );
 
                // Initialization
                this.$overlay.append( this.popup.$element );
                this.setCurrentMuteState();
 
                // Update label if needed
-               this.setLabel( $( '<div>' ).html( this.model.getPrefixedLabel( this.invertModel.isSelected() ) ).contents() );
+               this.setLabel( $( '<div>' ).html( this.itemModel.getPrefixedLabel( this.invertModel.isSelected() ) ).contents() );
 
                this.setHighlightColor();
        };
 
+       /**
+        * Set the current highlight color for this item
+        */
        mw.rcfilters.ui.TagItemWidget.prototype.setHighlightColor = function () {
-               var selectedColor = this.model.isHighlightEnabled() ? this.model.getHighlightColor() : null;
+               var selectedColor = this.filtersViewModel.isHighlightEnabled() && this.itemModel.isHighlighted ?
+                       this.itemModel.getHighlightColor() :
+                       null;
 
                this.$highlight
                        .attr( 'data-color', selectedColor )
         * Respond to mouse enter event
         */
        mw.rcfilters.ui.TagItemWidget.prototype.onMouseEnter = function () {
-               var labelText = this.model.getStateMessage();
+               var labelText = this.itemModel.getStateMessage();
 
                if ( labelText ) {
                        this.popupLabel.setLabel( labelText );
         * @return {string} Filter name
         */
        mw.rcfilters.ui.TagItemWidget.prototype.getName = function () {
-               return this.model.getName();
+               return this.itemModel.getName();
        };
 
        /**
         * @return {string} Filter model
         */
        mw.rcfilters.ui.TagItemWidget.prototype.getModel = function () {
-               return this.model;
+               return this.itemModel;
        };
 
        /**
         * @return {string} Filter view
         */
        mw.rcfilters.ui.TagItemWidget.prototype.getView = function () {
-               return this.model.getGroupModel().getView();
+               return this.itemModel.getGroupModel().getView();
        };
 
        /**
                this.popup.$element.detach();
 
                // Disconnect events
-               this.model.disconnect( this );
+               this.itemModel.disconnect( this );
                this.closeButton.disconnect( this );
        };
 }( mediaWiki, jQuery ) );
index 291d5c7..534af86 100644 (file)
                        $.extend( true, {}, { filter1: '1' } ),
                        'Parameters in Uri query set parameter value in the model'
                );
-
-               uriProcessor.updateModelBasedOnQuery( { highlight: '1', group1__filter1_color: 'c1', urlversion: '2' } );
-               assert.deepEqual(
-                       filtersModel.getCurrentParameterState(),
-                       {
-                               highlight: '1',
-                               group1__filter1_color: 'c1'
-                       },
-                       'Highlight parameters in Uri query set highlight state in the model'
-               );
        } );
 
        QUnit.test( 'isNewState', function ( assert ) {
index dde49ba..a700e30 100644 (file)
                        group6option3: '0',
                        group7: '',
                        namespace: '',
-                       highlight: '0',
                        // Null highlights
                        group1__filter1_color: null,
                        group1__filter2_color: null,
index 539bab4..58524ec 100644 (file)
                                                        // in param representation
                                                        filter2: '1', filter3: '1',
                                                        // Group type string_options
-                                                       group2: 'filter4',
+                                                       group2: 'filter4'
                                                        // Note - Group3 is sticky, so it won't show in output
-                                                       // highlight toggle
-                                                       highlight: '1'
                                                },
                                                highlights: {
                                                        group1__filter1_color: 'c5',
                                        }
                                }
                        }
+               },
+               removeHighlights = function ( data ) {
+                       var copy = $.extend( true, {}, data );
+                       copy.queries[ 1234 ].data.highlights = {};
+                       return copy;
                };
 
        QUnit.module( 'mediawiki.rcfilters - SavedQueriesModel' );
                                        finalState: $.extend( true, { default: '1234' }, queriesParamRepresentation ),
                                        msg: 'Conversion from filter representation to parameters, with default set up, retains data.'
                                },
+                               {
+                                       // Converting from old structure and cleaning up highlights
+                                       input: $.extend( true, queriesFilterRepresentation, { queries: { 1234: { data: { highlights: { highlight: false } } } } } ),
+                                       finalState: removeHighlights( queriesParamRepresentation ),
+                                       msg: 'Conversion from filter representation to parameters and highlight cleanup'
+                               },
                                {
                                        // New structure
                                        input: $.extend( true, {}, queriesParamRepresentation ),
                                        input: $.extend( true, { queries: { 1234: { data: { highlights: { group2__filter5_color: 'c2' } } } } }, exampleQueryStructure ),
                                        finalState: $.extend( true, { queries: { 1234: { data: { highlights: { group2__filter5_color: 'c2' } } } } }, exampleQueryStructure ),
                                        msg: 'Structure that contains invalid highlights remains the same in initialization'
+                               },
+                               {
+                                       // Trim colors when highlight=false is stored
+                                       input: $.extend( true, { queries: { 1234: { data: { params: { highlight: '0' } } } } }, queriesParamRepresentation ),
+                                       finalState: removeHighlights( queriesParamRepresentation ),
+                                       msg: 'Colors are removed when highlight=false'
+                               },
+                               {
+                                       // Remove highlight when it is true but no colors are specified
+                                       input: $.extend( true, { queries: { 1234: { data: { params: { highlight: '1' } } } } }, removeHighlights( queriesParamRepresentation ) ),
+                                       finalState: removeHighlights( queriesParamRepresentation ),
+                                       msg: 'remove highlight when it is true but there is no colors'
                                }
                        ];
 
                        'New query 1',
                        {
                                group2: 'filter5',
-                               highlight: '1',
                                group1__filter1_color: 'c5',
                                group3__group3option1_color: 'c1'
                        }
                        label: 'New query 1',
                        data: {
                                params: {
-                                       group2: 'filter5',
-                                       highlight: '1'
+                                       group2: 'filter5'
                                },
                                highlights: {
                                        group1__filter1_color: 'c5',
                // Find matching query
                matchingItem = queriesModel.findMatchingQuery(
                        {
-                               highlight: '1',
                                group2: 'filter5',
                                group1__filter1_color: 'c5',
                                group3__group3option1_color: 'c1'
                                group2: 'filter5',
                                filter1: '0',
                                filter2: '0',
-                               highlight: '1',
                                invert: '0',
                                group1__filter1_color: 'c5',
                                group3__group3option1_color: 'c1'
index a91dff9..181e992 100644 (file)
@@ -4,7 +4,6 @@
                params: {
                        param1: '1',
                        param2: 'foo|bar',
-                       highlight: '1',
                        invert: '0'
                },
                highlights: {