-( function ( mw ) {
+( function ( mw, $ ) {
/**
* Controller for the filters in Recent Changes
*
- * @param {mw.rcfilters.dm.FiltersViewModel} model View model
+ * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
+ * @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel Changes list view model
*/
- mw.rcfilters.Controller = function MwRcfiltersController( model ) {
- this.model = model;
- // TODO: When we are ready, update the URL when a filter is updated
- // this.model.connect( this, { itemUpdate: 'updateURL' } );
+ mw.rcfilters.Controller = function MwRcfiltersController( filtersModel, changesListModel ) {
+ this.filtersModel = filtersModel;
+ this.changesListModel = changesListModel;
+ this.requestCounter = 0;
};
/* Initialization */
/**
* Initialize the filter and parameter states
+ *
+ * @param {Object} filterStructure Filter definition and structure for the model
*/
- mw.rcfilters.Controller.prototype.initialize = function () {
+ mw.rcfilters.Controller.prototype.initialize = function ( filterStructure ) {
var uri = new mw.Uri();
- // Give the model a full parameter state from which to
- // update the filters
- this.model.updateFilters(
- // Translate the url params to filter select states
- this.model.getFiltersFromParameters( uri.query )
+ // Initialize the model
+ this.filtersModel.initializeFilters( filterStructure );
+
+ // Set filter states based on defaults and URL params
+ this.filtersModel.updateFilters(
+ 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();
};
/**
* Reset to default filters
*/
mw.rcfilters.Controller.prototype.resetToDefaults = function () {
- this.model.setFiltersToDefaults();
+ this.filtersModel.setFiltersToDefaults();
+ // Check all filter interactions
+ this.filtersModel.reassessFilterInteractions();
+
+ this.updateChangesList();
};
/**
* Empty all selected filters
*/
mw.rcfilters.Controller.prototype.emptyFilters = function () {
- this.model.emptyAllFilters();
+ this.filtersModel.emptyAllFilters();
+ 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.model.updateFilters( obj );
+ 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
// shouldn't appear at all? Or compare to existing query string
// and see if current state of a specific filter is needed?
- uri.extend( this.model.getParametersFromFilters() );
+ 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
+ *
+ * @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 = this.getUpdatedUri(),
+ requestId = ++this.requestCounter,
+ latestRequest = function () {
+ return requestId === this.requestCounter;
+ }.bind( this );
+
+ return $.ajax( uri.toString(), { contentType: 'html' } )
+ .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 ( params ) {
+ this.updateURL( params );
+ this.changesListModel.invalidate();
+ this.fetchChangesList()
+ .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 ) );
+}( mediaWiki, jQuery ) );