this.changesListModel = changesListModel;
this.savedQueriesModel = savedQueriesModel;
this.requestCounter = 0;
- this.baseState = {};
+ this.baseFilterState = {};
+ this.uriProcessor = null;
+ this.initializing = false;
};
/* Initialization */
*/
mw.rcfilters.Controller.prototype.initialize = function ( filterStructure ) {
var parsedSavedQueries,
+ uri = new mw.Uri(),
$changesList = $( '.mw-changeslist' ).first().contents();
+
// Initialize the model
this.filtersModel.initializeFilters( filterStructure );
this._buildBaseFilterState();
+ this.uriProcessor = new mw.rcfilters.UriProcessor(
+ this.filtersModel
+ );
try {
parsedSavedQueries = JSON.parse( mw.user.options.get( 'rcfilters-saved-queries' ) || '{}' );
// can normalize them per each query item
this.savedQueriesModel.initialize(
parsedSavedQueries,
- this._getBaseState()
+ this._getBaseFilterState()
);
- this.updateStateBasedOnUrl();
- // Update the changes list with the existing data
- // so it gets processed
- this.changesListModel.update(
- $changesList.length ? $changesList : 'NO_RESULTS',
- $( 'fieldset.rcoptions' ).first()
- );
+ // Check whether we need to load defaults.
+ // We do this by checking whether the current URI query
+ // contains any parameters recognized by the system.
+ // If it does, we load the given state.
+ // If it doesn't, we have no values at all, and we assume
+ // the user loads the base-page and we load defaults.
+ // Defaults should only be applied on load (if necessary)
+ // or on request
+ this.initializing = true;
+ if (
+ this.savedQueriesModel.getDefault() &&
+ !this.uriProcessor.doesQueryContainRecognizedParams( uri.query )
+ ) {
+ // We have defaults from a saved query.
+ // We will load them straight-forward (as if
+ // they were clicked in the menu) so we trigger
+ // a full ajax request and change of URL
+ this.applySavedQuery( this.savedQueriesModel.getDefault() );
+ } else {
+ // There are either recognized parameters in the URL
+ // or there are none, but there is also no default
+ // saved query (so defaults are from the backend)
+ // We want to update the state but not fetch results
+ // again
+ this.updateStateFromUrl( false );
+
+ // Update the changes list with the existing data
+ // so it gets processed
+ this.changesListModel.update(
+ $changesList.length ? $changesList : 'NO_RESULTS',
+ $( 'fieldset.rcoptions' ).first()
+ );
+ }
+ this.initializing = false;
};
/**
* Reset to default filters
*/
mw.rcfilters.Controller.prototype.resetToDefaults = function () {
- this._updateModelState( this._getDefaultParams() );
+ this.uriProcessor.updateModelBasedOnQuery( this._getDefaultParams() );
this.updateChangesList();
};
highlightedItems[ item.getName() ] = highlightEnabled ?
item.getHighlightColor() : null;
} );
- highlightedItems.highlights = this.filtersModel.isHighlightEnabled();
+ // These are filter states; highlight is stored as boolean
+ highlightedItems.highlight = this.filtersModel.isHighlightEnabled();
// Add item
this.savedQueriesModel.addNewQuery(
data = queryItem.getData();
highlights = data.highlights;
+ // Backwards compatibility; initial version mispelled 'highlight' with 'highlights'
+ highlights.highlight = highlights.highlights || highlights.highlight;
+
// Update model state from filters
this.filtersModel.toggleFiltersSelected( data.filters );
// Update highlight state
- this.filtersModel.toggleHighlight( !!highlights.highlights );
+ this.filtersModel.toggleHighlight( !!Number( highlights.highlight ) );
this.filtersModel.getItems().forEach( function ( filterItem ) {
var color = highlights[ filterItem.getName() ];
if ( color ) {
this.filtersModel.getItemsSupportingHighlights().forEach( function ( item ) {
highlightedItems[ item.getName() ] = item.getHighlightColor();
} );
- highlightedItems.highlights = this.filtersModel.isHighlightEnabled();
+ highlightedItems.highlight = this.filtersModel.isHighlightEnabled();
return this.savedQueriesModel.findMatchingQuery(
{
this.filtersModel.getItemsSupportingHighlights().forEach( function ( item ) {
highlightedItems[ item.getName() ] = null;
} );
- highlightedItems.highlights = false;
+ highlightedItems.highlight = false;
- this.baseState = {
+ this.baseFilterState = {
filters: this.filtersModel.getFiltersFromParameters( defaultParams ),
highlights: highlightedItems
};
};
/**
- * Get an object representing the base state of parameters
- * and highlights. The structure is similar to what we use
+ * Get an object representing the base filter state of both
+ * filters and highlights. The structure is similar to what we use
* to store each query in the saved queries object:
* {
* filters: {
* @return {Object} Object representing the base state of
* parameters and highlights
*/
- mw.rcfilters.Controller.prototype._getBaseState = function () {
- return this.baseState;
+ mw.rcfilters.Controller.prototype._getBaseFilterState = function () {
+ return this.baseFilterState;
};
/**
*/
mw.rcfilters.Controller.prototype._getMinimalFilterList = function ( valuesObject ) {
var result = { filters: {}, highlights: {} },
- baseState = this._getBaseState();
+ baseState = this._getBaseFilterState();
// XOR results
$.each( valuesObject.filters, function ( name, value ) {
* without adding an history entry.
*/
mw.rcfilters.Controller.prototype.replaceUrl = function () {
- window.history.replaceState(
- { tag: 'rcfilters' },
- document.title,
- this._getUpdatedUri().toString()
- );
+ mw.rcfilters.UriProcessor.static.replaceState( this._getUpdatedUri() );
};
/**
* Update filter state (selection and highlighting) based
- * on current URL and default values.
+ * on current URL values.
+ *
+ * @param {boolean} [fetchChangesList=true] Fetch new results into the changes
+ * list based on the updated model.
*/
- mw.rcfilters.Controller.prototype.updateStateBasedOnUrl = function () {
- var uri = new mw.Uri(),
- defaultParams = this._getDefaultParams();
+ mw.rcfilters.Controller.prototype.updateStateFromUrl = function ( fetchChangesList ) {
+ fetchChangesList = fetchChangesList === undefined ? true : !!fetchChangesList;
- this._updateModelState( $.extend( {}, defaultParams, uri.query ) );
- this.updateChangesList();
+ this.uriProcessor.updateModelBasedOnQuery( new mw.Uri().query );
+
+ // Only update and fetch new results if it is requested
+ if ( fetchChangesList ) {
+ this.updateChangesList();
+ }
};
/**
};
/**
- * Update the model state from given the given parameters.
- *
- * This is an internal method, and should only be used from inside
- * the controller.
+ * Get an object representing the default parameter state, whether
+ * it is from the model defaults or from the saved queries.
*
- * @param {Object} parameters Object representing the parameters for
- * filters and highlights
+ * @return {Object} Default parameters
*/
- mw.rcfilters.Controller.prototype._updateModelState = function ( parameters ) {
- // Update filter states
- this.filtersModel.toggleFiltersSelected(
- this.filtersModel.getFiltersFromParameters(
- parameters
- )
- );
+ mw.rcfilters.Controller.prototype._getDefaultParams = function () {
+ var data, queryHighlights,
+ savedParams = {},
+ savedHighlights = {},
+ defaultSavedQueryItem = this.savedQueriesModel.getItemByID( this.savedQueriesModel.getDefault() );
- // Update highlight state
- this.filtersModel.toggleHighlight( !!parameters.highlights );
- this.filtersModel.getItems().forEach( function ( filterItem ) {
- var color = parameters[ filterItem.getName() + '_color' ];
- if ( color ) {
- filterItem.setHighlightColor( color );
- } else {
- filterItem.clearHighlightColor();
- }
- } );
+ if ( mw.config.get( 'wgStructuredChangeFiltersEnableSaving' ) &&
+ defaultSavedQueryItem ) {
- // Check all filter interactions
- this.filtersModel.reassessFilterInteractions();
+ data = defaultSavedQueryItem.getData();
+
+ queryHighlights = data.highlights || {};
+ savedParams = this.filtersModel.getParametersFromFilters( data.filters || {} );
+
+ // Translate highlights to parameters
+ savedHighlights.highlight = String( Number( queryHighlights.highlight ) );
+ $.each( queryHighlights, function ( filterName, color ) {
+ if ( filterName !== 'highlights' ) {
+ savedHighlights[ filterName + '_color' ] = color;
+ }
+ } );
+
+ return $.extend( true, {}, savedParams, savedHighlights );
+ }
+
+ return $.extend(
+ { highlight: '0' },
+ this.filtersModel.getDefaultParams()
+ );
};
/**
savedHighlights = {},
defaultSavedQueryItem = this.savedQueriesModel.getItemByID( this.savedQueriesModel.getDefault() );
- if ( defaultSavedQueryItem ) {
+ if ( mw.config.get( 'wgStructuredChangeFiltersEnableSaving' ) &&
+ defaultSavedQueryItem ) {
+
data = defaultSavedQueryItem.getData();
queryHighlights = data.highlights || {};
savedParams = this.filtersModel.getParametersFromFilters( data.filters || {} );
// Translate highlights to parameters
- savedHighlights.highlights = queryHighlights.highlights;
+ savedHighlights.highlight = String( Number( queryHighlights.highlight ) );
$.each( queryHighlights, function ( filterName, color ) {
if ( filterName !== 'highlights' ) {
savedHighlights[ filterName + '_color' ] = color;
* @param {Object} [params] Extra parameters to add to the API call
*/
mw.rcfilters.Controller.prototype._updateURL = function ( params ) {
- var updatedUri,
- notEquivalent = function ( obj1, obj2 ) {
- var keys = Object.keys( obj1 ).concat( Object.keys( obj2 ) );
- return keys.some( function ( key ) {
- return obj1[ key ] != obj2[ key ]; // eslint-disable-line eqeqeq
- } );
- };
-
- params = params || {};
-
- updatedUri = this._getUpdatedUri();
- updatedUri.extend( params );
-
- if ( notEquivalent( updatedUri.query, new mw.Uri().query ) ) {
- window.history.pushState( { tag: 'rcfilters' }, document.title, updatedUri.toString() );
+ var currentUri = new mw.Uri(),
+ updatedUri = this._getUpdatedUri();
+
+ updatedUri.extend( params || {} );
+
+ if (
+ this.uriProcessor.getVersion( currentUri.query ) !== 2 ||
+ this.uriProcessor.isNewState( currentUri.query, updatedUri.query )
+ ) {
+ if ( this.initializing ) {
+ // Initially, when we just build the first page load
+ // out of defaults, we want to replace the history
+ mw.rcfilters.UriProcessor.static.replaceState( updatedUri );
+ } else {
+ mw.rcfilters.UriProcessor.static.pushState( updatedUri );
+ }
}
};
* @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.filtersModel.getParametersFromFilters() );
-
- // highlight params
- Object.keys( highlightParams ).forEach( function ( paramName ) {
- if ( highlightParams[ paramName ] ) {
- uri.query[ paramName ] = highlightParams[ paramName ];
- } else {
- delete uri.query[ paramName ];
- }
- } );
+ var uri = new mw.Uri();
+
+ // Minimize url
+ uri.query = this.uriProcessor.minimizeQuery(
+ $.extend(
+ true,
+ {},
+ // We want to retain unrecognized params
+ // The uri params from model will override
+ // any recognized value in the current uri
+ // query, retain unrecognized params, and
+ // the result will then be minimized
+ uri.query,
+ this.uriProcessor.getUriParametersFromModel(),
+ { urlversion: '2' }
+ )
+ );
return uri;
};