summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js
diff options
context:
space:
mode:
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.js294
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 ) );