/**
* URI Processor for RCFilters
*
+ * @class
+ *
+ * @constructor
* @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
+ * @param {Object} [config] Configuration object
+ * @cfg {boolean} [normalizeTarget] Dictates whether or not to go through the
+ * title normalization to separate title subpage/parts into the target= url
+ * parameter
*/
- mw.rcfilters.UriProcessor = function MwRcfiltersController( filtersModel ) {
- this.emptyParameterState = {};
+ mw.rcfilters.UriProcessor = function MwRcfiltersController( filtersModel, config ) {
+ config = config || {};
this.filtersModel = filtersModel;
- // Initialize
- this._buildEmptyParameterState();
+ this.normalizeTarget = !!config.normalizeTarget;
};
/* Initialization */
};
/**
- * Update the filters model based on the URI query
- * This happens on initialization, and from this moment on,
- * we consider the system synchronized, and the model serves
- * as the source of truth for the URL.
+ * Get an updated mw.Uri object based on the model state
*
- * This methods should only be called once on initialiation.
- * After initialization, the model updates the URL, not the
- * other way around.
- *
- * @param {Object} [uriQuery] URI query
+ * @param {mw.Uri} [uri] An external URI to build the new uri
+ * with. This is mainly for tests, to be able to supply external query
+ * parameters and make sure they are retained.
+ * @return {mw.Uri} Updated Uri
*/
- mw.rcfilters.UriProcessor.prototype.updateModelBasedOnQuery = function ( uriQuery ) {
- var parameters = this._getNormalizedQueryParams( uriQuery || new mw.Uri().query );
-
- // Update filter states
- this.filtersModel.toggleFiltersSelected(
- this.filtersModel.getFiltersFromParameters(
- parameters
+ mw.rcfilters.UriProcessor.prototype.getUpdatedUri = function ( uri ) {
+ var normalizedUri = this._normalizeTargetInUri( uri || new mw.Uri() ),
+ unrecognizedParams = this.getUnrecognizedParams( normalizedUri.query );
+
+ normalizedUri.query = this.filtersModel.getMinimizedParamRepresentation(
+ $.extend(
+ true,
+ {},
+ normalizedUri.query,
+ // The representation must be expanded so it can
+ // override the uri query params but we then output
+ // a minimized version for the entire URI representation
+ // for the method
+ this.filtersModel.getExpandedParamRepresentation()
)
);
- this.filtersModel.toggleInvertedNamespaces( !!Number( parameters.invert ) );
+ // Reapply unrecognized params and url version
+ normalizedUri.query = $.extend(
+ true,
+ {},
+ normalizedUri.query,
+ unrecognizedParams,
+ { urlversion: '2' }
+ );
+
+ return normalizedUri;
+ };
- // Update highlight state
- this.filtersModel.toggleHighlight( !!Number( parameters.highlight ) );
- this.filtersModel.getItems().forEach( function ( filterItem ) {
- var color = parameters[ filterItem.getName() + '_color' ];
- if ( color ) {
- filterItem.setHighlightColor( color );
- } else {
- filterItem.clearHighlightColor();
+ /**
+ * Move the subpage to the target parameter
+ *
+ * @param {mw.Uri} uri
+ * @return {mw.Uri}
+ * @private
+ */
+ mw.rcfilters.UriProcessor.prototype._normalizeTargetInUri = function ( uri ) {
+ var parts,
+ // matches [/wiki/]SpecialNS:RCL/[Namespace:]Title/Subpage/Subsubpage/etc
+ re = /^((?:\/.+?\/)?.*?:.*?)\/(.*)$/;
+
+ if ( !this.normalizeTarget ) {
+ return uri;
+ }
+
+ // target in title param
+ if ( uri.query.title ) {
+ parts = uri.query.title.match( re );
+ if ( parts ) {
+ uri.query.title = parts[ 1 ];
+ uri.query.target = parts[ 2 ];
}
- } );
+ }
+
+ // target in path
+ parts = mw.Uri.decode( uri.path ).match( re );
+ if ( parts ) {
+ uri.path = parts[ 1 ];
+ uri.query.target = parts[ 2 ];
+ }
- // Check all filter interactions
- this.filtersModel.reassessFilterInteractions();
+ return uri;
};
/**
- * Get parameters representing the current state of the model
+ * Get an object representing given parameters that are unrecognized by the model
*
- * @return {Object} Uri query parameters
+ * @param {Object} params Full params object
+ * @return {Object} Unrecognized params
*/
- mw.rcfilters.UriProcessor.prototype.getUriParametersFromModel = function () {
- return $.extend(
- true,
- {},
- this.filtersModel.getParametersFromFilters(),
- this.filtersModel.getHighlightParameters(),
- {
- highlight: String( Number( this.filtersModel.isHighlightEnabled() ) ),
- invert: String( Number( this.filtersModel.areNamespacesInverted() ) )
+ mw.rcfilters.UriProcessor.prototype.getUnrecognizedParams = function ( params ) {
+ // Start with full representation
+ var givenParamNames = Object.keys( params ),
+ unrecognizedParams = $.extend( true, {}, params );
+
+ // Extract unrecognized parameters
+ Object.keys( this.filtersModel.getEmptyParameterState() ).forEach( function ( paramName ) {
+ // Remove recognized params
+ if ( givenParamNames.indexOf( paramName ) > -1 ) {
+ delete unrecognizedParams[ paramName ];
}
- );
+ } );
+
+ return unrecognizedParams;
};
/**
- * Build the full parameter representation based on given query parameters
+ * Update the URL of the page to reflect current filters
*
- * @private
- * @param {Object} uriQuery Given URI query
- * @return {Object} Full parameter state representing the URI query
+ * 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.
+ *
+ * @param {Object} [params] Extra parameters to add to the API call
*/
- mw.rcfilters.UriProcessor.prototype._expandModelParameters = function ( uriQuery ) {
- var filterRepresentation = this.filtersModel.getFiltersFromParameters( uriQuery );
+ mw.rcfilters.UriProcessor.prototype.updateURL = function ( params ) {
+ var currentUri = new mw.Uri(),
+ updatedUri = this.getUpdatedUri();
+
+ updatedUri.extend( params || {} );
+
+ if (
+ this.getVersion( currentUri.query ) !== 2 ||
+ this.isNewState( currentUri.query, updatedUri.query )
+ ) {
+ this.constructor.static.replaceState( updatedUri );
+ }
+ };
- return $.extend( true,
- {},
- uriQuery,
- this.filtersModel.getParametersFromFilters( filterRepresentation ),
- this.filtersModel.extractHighlightValues( uriQuery ),
- {
- highlight: String( Number( uriQuery.highlight ) ),
- invert: String( Number( uriQuery.invert ) )
- }
+ /**
+ * Update the filters model based on the URI query
+ * This happens on initialization, and from this moment on,
+ * we consider the system synchronized, and the model serves
+ * as the source of truth for the URL.
+ *
+ * This methods should only be called once on initialization.
+ * After initialization, the model updates the URL, not the
+ * other way around.
+ *
+ * @param {Object} [uriQuery] URI query
+ */
+ mw.rcfilters.UriProcessor.prototype.updateModelBasedOnQuery = function ( uriQuery ) {
+ uriQuery = uriQuery || this._normalizeTargetInUri( new mw.Uri() ).query;
+ this.filtersModel.updateStateFromParams(
+ this._getNormalizedQueryParams( uriQuery )
);
};
// This will allow us to always have a proper check of whether
// the requested new url is one to change or not, regardless of
// actual parameter visibility/representation in the URL
- currentParamState = this._expandModelParameters( currentUriQuery );
- updatedParamState = this._expandModelParameters( updatedUriQuery );
+ currentParamState = $.extend(
+ true,
+ {},
+ this.filtersModel.getMinimizedParamRepresentation( currentUriQuery ),
+ this.getUnrecognizedParams( currentUriQuery )
+ );
+ updatedParamState = $.extend(
+ true,
+ {},
+ this.filtersModel.getMinimizedParamRepresentation( updatedUriQuery ),
+ this.getUnrecognizedParams( updatedUriQuery )
+ );
return notEquivalent( currentParamState, updatedParamState );
};
*/
mw.rcfilters.UriProcessor.prototype.doesQueryContainRecognizedParams = function ( uriQuery ) {
var anyValidInUrl,
- validParameterNames = Object.keys( this._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;
return anyValidInUrl || this.getVersion( uriQuery ) === 2;
};
- /**
- * Remove all parameters that have the same value as the base state
- * This method expects uri queries of the urlversion=2 format
- *
- * @private
- * @param {Object} uriQuery Current uri query
- * @return {Object} Minimized query
- */
- mw.rcfilters.UriProcessor.prototype.minimizeQuery = function ( uriQuery ) {
- var baseParams = this._getEmptyParameterState(),
- uriResult = $.extend( true, {}, uriQuery );
-
- $.each( uriResult, function ( paramName, paramValue ) {
- if (
- baseParams[ paramName ] !== undefined &&
- baseParams[ paramName ] === paramValue
- ) {
- // Remove parameter from query
- delete uriResult[ paramName ];
- }
- } );
-
- return uriResult;
- };
-
/**
* Get the adjusted URI params based on the url version
* If the urlversion is not 2, the parameters are merged with
* the model's defaults.
+ * Always merge in the hidden parameter defaults.
*
* @private
* @param {Object} uriQuery Current URI query
// wiki default.
// Any subsequent change of the URL through the RCFilters
// system will receive 'urlversion=2'
- var hiddenParamDefaults = {},
- base = this.getVersion( uriQuery ) === 2 ?
- {} :
- this.filtersModel.getDefaultParams();
-
- // Go over the model and get all hidden parameters' defaults
- // These defaults should be applied regardless of the urlversion
- // but be overridden by the URL params if they exist
- $.each( this.filtersModel.getFilterGroups(), function ( groupName, groupModel ) {
- if ( groupModel.isHidden() ) {
- $.extend( true, hiddenParamDefaults, groupModel.getDefaultParams() );
- }
- } );
-
- return this.minimizeQuery(
- $.extend( true, {}, hiddenParamDefaults, base, uriQuery, { urlversion: '2' } )
- );
- };
+ var base = this.getVersion( uriQuery ) === 2 ?
+ {} :
+ this.filtersModel.getDefaultParams();
- /**
- * Get the representation of an empty parameter state
- *
- * @private
- * @return {Object} Empty parameter state
- */
- mw.rcfilters.UriProcessor.prototype._getEmptyParameterState = function () {
- // Override empty parameter state with the sticky parameter values
- return $.extend( true, {}, this.emptyParameterState, this.filtersModel.getStickyParams() );
- };
-
- /**
- * Build an empty representation of the parameters, where all parameters
- * are either set to '0' or '' depending on their type.
- * This must run during initialization, before highlights are set.
- *
- * @private
- */
- mw.rcfilters.UriProcessor.prototype._buildEmptyParameterState = function () {
- var emptyParams = this.filtersModel.getParametersFromFilters( {} ),
- emptyHighlights = this.filtersModel.getHighlightParameters();
-
- this.emptyParameterState = $.extend(
+ return $.extend(
true,
{},
- emptyParams,
- emptyHighlights,
- { highlight: '0', invert: '0' }
+ this.filtersModel.getMinimizedParamRepresentation(
+ $.extend( true, {}, base, uriQuery )
+ ),
+ { urlversion: '2' }
);
};
}( mediaWiki, jQuery ) );