Merge "RCFilters UI: Select filter when searching and add it on 'enter'"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / mw.rcfilters.Controller.js
index 88f32b4..98eaa59 100644 (file)
 
        /**
         * Initialize the filter and parameter states
+        *
+        * @param {Object} filterStructure Filter definition and structure for the model
         */
-       mw.rcfilters.Controller.prototype.initialize = function () {
-               this.updateFromURL();
-       };
-
-       /**
-        * Update the model state based on the URL parameters.
-        */
-       mw.rcfilters.Controller.prototype.updateFromURL = function () {
+       mw.rcfilters.Controller.prototype.initialize = function ( filterStructure ) {
                var uri = new mw.Uri();
 
+               // Initialize the model
+               this.filtersModel.initializeFilters( filterStructure );
+
+               // Set filter states based on defaults and URL params
                this.filtersModel.updateFilters(
-                       // Translate the url params to filter select states
-                       this.filtersModel.getFiltersFromParameters( uri.query )
+                       this.filtersModel.getFiltersFromParameters(
+                               // Merge defaults with URL params for initialization
+                               $.extend(
+                                       true,
+                                       {},
+                                       this.filtersModel.getDefaultParams(),
+                                       // URI query overrides defaults
+                                       uri.query
+                               )
+                       )
                );
+
+               // Initialize highlights
+               this.filtersModel.toggleHighlight( !!uri.query.highlight );
+               this.filtersModel.getItems().forEach( function ( filterItem ) {
+                       var color = uri.query[ filterItem.getName() + '_color' ];
+                       if ( !color ) {
+                               return;
+                       }
+
+                       filterItem.setHighlightColor( color );
+               } );
+
+               // Check all filter interactions
+               this.filtersModel.reassessFilterInteractions();
        };
 
        /**
@@ -38,7 +59,9 @@
         */
        mw.rcfilters.Controller.prototype.resetToDefaults = function () {
                this.filtersModel.setFiltersToDefaults();
-               this.updateURL();
+               // Check all filter interactions
+               this.filtersModel.reassessFilterInteractions();
+
                this.updateChangesList();
        };
 
         */
        mw.rcfilters.Controller.prototype.emptyFilters = function () {
                this.filtersModel.emptyAllFilters();
-               this.updateURL();
+               this.filtersModel.clearAllHighlightColors();
+               // Check all filter interactions
+               this.filtersModel.reassessFilterInteractions();
+
                this.updateChangesList();
        };
 
        /**
-        * Update the state of a filter
+        * Update the selected state of a filter
         *
         * @param {string} filterName Filter name
-        * @param {boolean} isSelected Filter selected state
+        * @param {boolean} [isSelected] Filter selected state
         */
-       mw.rcfilters.Controller.prototype.updateFilter = function ( filterName, isSelected ) {
-               var obj = {};
+       mw.rcfilters.Controller.prototype.toggleFilterSelect = function ( filterName, isSelected ) {
+               var obj = {},
+                       filterItem = this.filtersModel.getItemByName( filterName );
 
-               obj[ filterName ] = isSelected;
-               this.filtersModel.updateFilters( obj );
-               this.updateURL();
-               this.updateChangesList();
+               isSelected = isSelected === undefined ? !filterItem.isSelected() : isSelected;
+
+               if ( filterItem.isSelected() !== isSelected ) {
+                       obj[ filterName ] = isSelected;
+                       this.filtersModel.updateFilters( obj );
+
+                       this.updateChangesList();
+
+                       // Check filter interactions
+                       this.filtersModel.reassessFilterInteractions( this.filtersModel.getItemByName( filterName ) );
+               }
        };
 
        /**
         * Update the URL of the page to reflect current filters
+        *
+        * This should not be called directly from outside the controller.
+        * If an action requires changing the URL, it should either use the
+        * highlighting actions below, or call #updateChangesList which does
+        * the uri corrections already.
+        *
+        * @private
+        * @param {Object} [params] Extra parameters to add to the API call
         */
-       mw.rcfilters.Controller.prototype.updateURL = function () {
-               var uri = new mw.Uri();
+       mw.rcfilters.Controller.prototype.updateURL = function ( params ) {
+               var uri;
+
+               params = params || {};
+
+               uri = this.getUpdatedUri();
+               uri.extend( params );
+
+               window.history.pushState( { tag: 'rcfilters' }, document.title, uri.toString() );
+       };
+
+       /**
+        * Get an updated mw.Uri object based on the model state
+        *
+        * @return {mw.Uri} Updated Uri
+        */
+       mw.rcfilters.Controller.prototype.getUpdatedUri = function () {
+               var uri = new mw.Uri(),
+                       highlightParams = this.filtersModel.getHighlightParameters();
 
                // Add to existing queries in URL
                // TODO: Clean up the list of filters; perhaps 'falsy' filters
                // and see if current state of a specific filter is needed?
                uri.extend( this.filtersModel.getParametersFromFilters() );
 
-               // Update the URL itself
-               window.history.pushState( { tag: 'rcfilters' }, document.title, uri.toString() );
+               // highlight params
+               Object.keys( highlightParams ).forEach( function ( paramName ) {
+                       if ( highlightParams[ paramName ] ) {
+                               uri.query[ paramName ] = highlightParams[ paramName ];
+                       } else {
+                               delete uri.query[ paramName ];
+                       }
+               } );
+
+               return uri;
        };
 
        /**
         * Fetch the list of changes from the server for the current filters
         *
-        * @returns {jQuery.Promise} Promise object that will resolve with the changes list
+        * @return {jQuery.Promise} Promise object that will resolve with the changes list
+        *  or with a string denoting no results.
         */
        mw.rcfilters.Controller.prototype.fetchChangesList = function () {
-               var uri = new mw.Uri(),
+               var uri = this.getUpdatedUri(),
                        requestId = ++this.requestCounter,
                        latestRequest = function () {
                                return requestId === this.requestCounter;
                        }.bind( this );
-               uri.extend( this.filtersModel.getParametersFromFilters() );
+
                return $.ajax( uri.toString(), { contentType: 'html' } )
-                       .then( function ( html ) {
-                               return latestRequest() ?
-                                       $( $.parseHTML( html ) ).find( '.mw-changeslist' ).first().contents() :
-                                       null;
-                       } ).then( null, function () {
-                               return latestRequest() ? 'NO_RESULTS' : null;
-                       } );
+                       .then(
+                               // Success
+                               function ( html ) {
+                                       var $parsed;
+                                       if ( !latestRequest() ) {
+                                               return $.Deferred().reject();
+                                       }
+
+                                       $parsed = $( $.parseHTML( html ) );
+
+                                       return {
+                                               // Changes list
+                                               changes: $parsed.find( '.mw-changeslist' ).first().contents(),
+                                               // Fieldset
+                                               fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
+                                       };
+                               },
+                               // Failure
+                               function ( responseObj ) {
+                                       var $parsed;
+
+                                       if ( !latestRequest() ) {
+                                               return $.Deferred().reject();
+                                       }
+
+                                       $parsed = $( $.parseHTML( responseObj.responseText ) );
+
+                                       // Force a resolve state to this promise
+                                       return $.Deferred().resolve( {
+                                               changes: 'NO_RESULTS',
+                                               fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
+                                       } ).promise();
+                               }
+                       );
        };
 
        /**
         * Update the list of changes and notify the model
+        *
+        * @param {Object} [params] Extra parameters to add to the API call
         */
-       mw.rcfilters.Controller.prototype.updateChangesList = function () {
+       mw.rcfilters.Controller.prototype.updateChangesList = function ( params ) {
+               this.updateURL( params );
                this.changesListModel.invalidate();
                this.fetchChangesList()
-                       .always( function ( changesListContent ) {
-                               if ( changesListContent ) {
-                                       this.changesListModel.update( changesListContent );
-                               }
-                       }.bind( this ) );
+                       .then(
+                               // Success
+                               function ( pieces ) {
+                                       var $changesListContent = pieces.changes,
+                                               $fieldset = pieces.fieldset;
+
+                                       this.changesListModel.update( $changesListContent, $fieldset );
+                               }.bind( this )
+                               // Do nothing for failure
+                       );
+       };
+
+       /**
+        * Toggle the highlight feature on and off
+        */
+       mw.rcfilters.Controller.prototype.toggleHighlight = function () {
+               this.filtersModel.toggleHighlight();
+               this.updateURL();
+       };
+
+       /**
+        * Set the highlight color for a filter item
+        *
+        * @param {string} filterName Name of the filter item
+        * @param {string} color Selected color
+        */
+       mw.rcfilters.Controller.prototype.setHighlightColor = function ( filterName, color ) {
+               this.filtersModel.setHighlightColor( filterName, color );
+               this.updateURL();
+       };
+
+       /**
+        * Clear highlight for a filter item
+        *
+        * @param {string} filterName Name of the filter item
+        */
+       mw.rcfilters.Controller.prototype.clearHighlightColor = function ( filterName ) {
+               this.filtersModel.clearHighlightColor( filterName );
+               this.updateURL();
        };
 }( mediaWiki, jQuery ) );