2 /* eslint no-underscore-dangle: "off" */
4 * URI Processor for RCFilters
6 * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
8 mw
.rcfilters
.UriProcessor
= function MwRcfiltersController( filtersModel
) {
9 this.filtersModel
= filtersModel
;
13 OO
.initClass( mw
.rcfilters
.UriProcessor
);
18 * Replace the url history through replaceState
20 * @param {mw.Uri} newUri New URI to replace
22 mw
.rcfilters
.UriProcessor
.static.replaceState = function ( newUri
) {
23 window
.history
.replaceState(
31 * Push the url to history through pushState
33 * @param {mw.Uri} newUri New URI to push
35 mw
.rcfilters
.UriProcessor
.static.pushState = function ( newUri
) {
36 window
.history
.pushState(
46 * Get the version that this URL query is tagged with.
48 * @param {Object} [uriQuery] URI query
49 * @return {number} URL version
51 mw
.rcfilters
.UriProcessor
.prototype.getVersion = function ( uriQuery
) {
52 uriQuery
= uriQuery
|| new mw
.Uri().query
;
54 return Number( uriQuery
.urlversion
|| 1 );
58 * Get an updated mw.Uri object based on the model state
60 * @param {Object} [uriQuery] An external URI query to build the new uri
61 * with. This is mainly for tests, to be able to supply external parameters
62 * and make sure they are retained.
63 * @return {mw.Uri} Updated Uri
65 mw
.rcfilters
.UriProcessor
.prototype.getUpdatedUri = function ( uriQuery
) {
68 unrecognizedParams
= this.getUnrecognizedParams( uriQuery
|| uri
.query
);
71 // This is mainly for tests, to be able to give the method
72 // an initial URI Query and test that it retains parameters
76 // Normalize subpage to use &target= so we are always
77 // consistent in Special:RecentChangesLinked between the
78 // ?title=Special:RecentChangesLinked/TargetPage and
79 // ?title=Special:RecentChangesLinked&target=TargetPage
80 if ( uri
.query
.title
&& uri
.query
.title
.indexOf( '/' ) !== -1 ) {
81 titlePieces
= uri
.query
.title
.split( '/' );
83 unrecognizedParams
.title
= titlePieces
.shift();
84 unrecognizedParams
.target
= titlePieces
.join( '/' );
87 uri
.query
= this.filtersModel
.getMinimizedParamRepresentation(
92 // The representation must be expanded so it can
93 // override the uri query params but we then output
94 // a minimized version for the entire URI representation
96 this.filtersModel
.getExpandedParamRepresentation()
100 // Reapply unrecognized params and url version
101 uri
.query
= $.extend( true, {}, uri
.query
, unrecognizedParams
, { urlversion
: '2' } );
106 * Get an object representing given parameters that are unrecognized by the model
108 * @param {Object} params Full params object
109 * @return {Object} Unrecognized params
111 mw
.rcfilters
.UriProcessor
.prototype.getUnrecognizedParams = function ( params
) {
112 // Start with full representation
113 var givenParamNames
= Object
.keys( params
),
114 unrecognizedParams
= $.extend( true, {}, params
);
116 // Extract unrecognized parameters
117 Object
.keys( this.filtersModel
.getEmptyParameterState() ).forEach( function ( paramName
) {
118 // Remove recognized params
119 if ( givenParamNames
.indexOf( paramName
) > -1 ) {
120 delete unrecognizedParams
[ paramName
];
124 return unrecognizedParams
;
128 * Update the URL of the page to reflect current filters
130 * This should not be called directly from outside the controller.
131 * If an action requires changing the URL, it should either use the
132 * highlighting actions below, or call #updateChangesList which does
133 * the uri corrections already.
135 * @param {Object} [params] Extra parameters to add to the API call
137 mw
.rcfilters
.UriProcessor
.prototype.updateURL = function ( params
) {
138 var currentUri
= new mw
.Uri(),
139 updatedUri
= this.getUpdatedUri();
141 updatedUri
.extend( params
|| {} );
144 this.getVersion( currentUri
.query
) !== 2 ||
145 this.isNewState( currentUri
.query
, updatedUri
.query
)
147 this.constructor.static.replaceState( updatedUri
);
152 * Update the filters model based on the URI query
153 * This happens on initialization, and from this moment on,
154 * we consider the system synchronized, and the model serves
155 * as the source of truth for the URL.
157 * This methods should only be called once on initialiation.
158 * After initialization, the model updates the URL, not the
161 * @param {Object} [uriQuery] URI query
163 mw
.rcfilters
.UriProcessor
.prototype.updateModelBasedOnQuery = function ( uriQuery
) {
164 this.filtersModel
.updateStateFromParams(
165 this._getNormalizedQueryParams( uriQuery
|| new mw
.Uri().query
)
170 * Compare two URI queries to decide whether they are different
171 * enough to represent a new state.
173 * @param {Object} currentUriQuery Current Uri query
174 * @param {Object} updatedUriQuery Updated Uri query
175 * @return {boolean} This is a new state
177 mw
.rcfilters
.UriProcessor
.prototype.isNewState = function ( currentUriQuery
, updatedUriQuery
) {
178 var currentParamState
, updatedParamState
,
179 notEquivalent = function ( obj1
, obj2
) {
180 var keys
= Object
.keys( obj1
).concat( Object
.keys( obj2
) );
181 return keys
.some( function ( key
) {
182 return obj1
[ key
] != obj2
[ key
]; // eslint-disable-line eqeqeq
186 // Compare states instead of parameters
187 // This will allow us to always have a proper check of whether
188 // the requested new url is one to change or not, regardless of
189 // actual parameter visibility/representation in the URL
190 currentParamState
= $.extend(
193 this.filtersModel
.getMinimizedParamRepresentation( currentUriQuery
),
194 this.getUnrecognizedParams( currentUriQuery
)
196 updatedParamState
= $.extend(
199 this.filtersModel
.getMinimizedParamRepresentation( updatedUriQuery
),
200 this.getUnrecognizedParams( updatedUriQuery
)
203 return notEquivalent( currentParamState
, updatedParamState
);
207 * Check whether the given query has parameters that are
208 * recognized as parameters we should load the system with
210 * @param {mw.Uri} [uriQuery] Given URI query
211 * @return {boolean} Query contains valid recognized parameters
213 mw
.rcfilters
.UriProcessor
.prototype.doesQueryContainRecognizedParams = function ( uriQuery
) {
215 validParameterNames
= Object
.keys( this.filtersModel
.getEmptyParameterState() );
217 uriQuery
= uriQuery
|| new mw
.Uri().query
;
219 anyValidInUrl
= Object
.keys( uriQuery
).some( function ( parameter
) {
220 return validParameterNames
.indexOf( parameter
) > -1;
223 // URL version 2 is allowed to be empty or within nonrecognized params
224 return anyValidInUrl
|| this.getVersion( uriQuery
) === 2;
228 * Get the adjusted URI params based on the url version
229 * If the urlversion is not 2, the parameters are merged with
230 * the model's defaults.
231 * Always merge in the hidden parameter defaults.
234 * @param {Object} uriQuery Current URI query
235 * @return {Object} Normalized parameters
237 mw
.rcfilters
.UriProcessor
.prototype._getNormalizedQueryParams = function ( uriQuery
) {
238 // Check whether we are dealing with urlversion=2
239 // If we are, we do not merge the initial request with
240 // defaults. Not having urlversion=2 means we need to
241 // reproduce the server-side request and merge the
242 // requested parameters (or starting state) with the
244 // Any subsequent change of the URL through the RCFilters
245 // system will receive 'urlversion=2'
246 var hiddenParamDefaults
= this.filtersModel
.getDefaultHiddenParams(),
247 base
= this.getVersion( uriQuery
) === 2 ?
249 this.filtersModel
.getDefaultParams();
254 this.filtersModel
.getMinimizedParamRepresentation(
255 $.extend( true, {}, hiddenParamDefaults
, base
, uriQuery
)
260 }( mediaWiki
, jQuery
) );