diff options
Diffstat (limited to 'www/wiki/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js')
-rw-r--r-- | www/wiki/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/www/wiki/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js b/www/wiki/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js new file mode 100644 index 00000000..b8ff22ee --- /dev/null +++ b/www/wiki/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js @@ -0,0 +1,294 @@ +( function ( mw, $ ) { + /* eslint no-underscore-dangle: "off" */ + /** + * 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, config ) { + config = config || {}; + this.filtersModel = filtersModel; + + this.normalizeTarget = !!config.normalizeTarget; + }; + + /* Initialization */ + OO.initClass( mw.rcfilters.UriProcessor ); + + /* Static methods */ + + /** + * Replace the url history through replaceState + * + * @param {mw.Uri} newUri New URI to replace + */ + mw.rcfilters.UriProcessor.static.replaceState = function ( newUri ) { + window.history.replaceState( + { tag: 'rcfilters' }, + document.title, + newUri.toString() + ); + }; + + /** + * Push the url to history through pushState + * + * @param {mw.Uri} newUri New URI to push + */ + mw.rcfilters.UriProcessor.static.pushState = function ( newUri ) { + window.history.pushState( + { tag: 'rcfilters' }, + document.title, + newUri.toString() + ); + }; + + /* Methods */ + + /** + * Get the version that this URL query is tagged with. + * + * @param {Object} [uriQuery] URI query + * @return {number} URL version + */ + mw.rcfilters.UriProcessor.prototype.getVersion = function ( uriQuery ) { + uriQuery = uriQuery || new mw.Uri().query; + + return Number( uriQuery.urlversion || 1 ); + }; + + /** + * Get an updated mw.Uri object based on the model state + * + * @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.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() + ) + ); + + // Reapply unrecognized params and url version + normalizedUri.query = $.extend( + true, + {}, + normalizedUri.query, + unrecognizedParams, + { urlversion: '2' } + ); + + return normalizedUri; + }; + + /** + * 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 ]; + } + + return uri; + }; + + /** + * Get an object representing given parameters that are unrecognized by the model + * + * @param {Object} params Full params object + * @return {Object} Unrecognized params + */ + 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; + }; + + /** + * 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. + * + * @param {Object} [params] Extra parameters to add to the API call + */ + 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 ); + } + }; + + /** + * 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 ) + ); + }; + + /** + * Compare two URI queries to decide whether they are different + * enough to represent a new state. + * + * @param {Object} currentUriQuery Current Uri query + * @param {Object} updatedUriQuery Updated Uri query + * @return {boolean} This is a new state + */ + mw.rcfilters.UriProcessor.prototype.isNewState = function ( currentUriQuery, updatedUriQuery ) { + var currentParamState, updatedParamState, + 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 + } ); + }; + + // Compare states instead of parameters + // 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 = $.extend( + true, + {}, + this.filtersModel.getMinimizedParamRepresentation( currentUriQuery ), + this.getUnrecognizedParams( currentUriQuery ) + ); + updatedParamState = $.extend( + true, + {}, + this.filtersModel.getMinimizedParamRepresentation( updatedUriQuery ), + this.getUnrecognizedParams( updatedUriQuery ) + ); + + return notEquivalent( currentParamState, updatedParamState ); + }; + + /** + * Check whether the given query has parameters that are + * recognized as parameters we should load the system with + * + * @param {mw.Uri} [uriQuery] Given URI query + * @return {boolean} Query contains valid recognized parameters + */ + mw.rcfilters.UriProcessor.prototype.doesQueryContainRecognizedParams = function ( uriQuery ) { + var anyValidInUrl, + validParameterNames = Object.keys( this.filtersModel.getEmptyParameterState() ); + + uriQuery = uriQuery || new mw.Uri().query; + + anyValidInUrl = Object.keys( uriQuery ).some( function ( parameter ) { + return validParameterNames.indexOf( parameter ) > -1; + } ); + + // URL version 2 is allowed to be empty or within nonrecognized params + return anyValidInUrl || this.getVersion( uriQuery ) === 2; + }; + + /** + * 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 + * @return {Object} Normalized parameters + */ + mw.rcfilters.UriProcessor.prototype._getNormalizedQueryParams = function ( uriQuery ) { + // Check whether we are dealing with urlversion=2 + // If we are, we do not merge the initial request with + // defaults. Not having urlversion=2 means we need to + // reproduce the server-side request and merge the + // requested parameters (or starting state) with the + // wiki default. + // Any subsequent change of the URL through the RCFilters + // system will receive 'urlversion=2' + var base = this.getVersion( uriQuery ) === 2 ? + {} : + this.filtersModel.getDefaultParams(); + + return $.extend( + true, + {}, + this.filtersModel.getMinimizedParamRepresentation( + $.extend( true, {}, base, uriQuery ) + ), + { urlversion: '2' } + ); + }; +}( mediaWiki, jQuery ) ); |