1 var SavedQueryItemModel
= require( './SavedQueryItemModel.js' ),
5 * View model for saved queries
7 * @class mw.rcfilters.dm.SavedQueriesModel
8 * @mixins OO.EventEmitter
9 * @mixins OO.EmitterList
12 * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters model
13 * @param {Object} [config] Configuration options
14 * @cfg {string} [default] Default query ID
16 SavedQueriesModel
= function MwRcfiltersDmSavedQueriesModel( filtersModel
, config
) {
17 config
= config
|| {};
20 OO
.EventEmitter
.call( this );
21 OO
.EmitterList
.call( this );
23 this.default = config
.default;
24 this.filtersModel
= filtersModel
;
25 this.converted
= false;
28 this.aggregate( { update
: 'itemUpdate' } );
33 OO
.initClass( SavedQueriesModel
);
34 OO
.mixinClass( SavedQueriesModel
, OO
.EventEmitter
);
35 OO
.mixinClass( SavedQueriesModel
, OO
.EmitterList
);
42 * Model is initialized
47 * @param {mw.rcfilters.dm.SavedQueryItemModel} Changed item
54 * @param {string} New default ID
56 * The default has changed
62 * Initialize the saved queries model by reading it from the user's settings.
63 * The structure of the saved queries is:
65 * version: (string) Version number; if version 2, the query represents
66 * parameters. Otherwise, the older version represented filters
67 * and needs to be readjusted,
68 * default: (string) Query ID
72 * filters: (Object) Minimal definition of the filters
73 * highlights: (Object) Definition of the highlights
75 * label: (optional) Name of this query
80 * @param {Object} [savedQueries] An object with the saved queries with
81 * the above structure.
84 SavedQueriesModel
.prototype.initialize = function ( savedQueries
) {
87 savedQueries
= savedQueries
|| {};
91 this.converted
= false;
93 if ( savedQueries
.version
!== '2' ) {
94 // Old version dealt with filter names. We need to migrate to the new structure
97 // version: (string) '2',
98 // default: (string) Query ID,
101 // label: (string) Name of the query
103 // params: (object) Representing all the parameter states
104 // highlights: (object) Representing all the filter highlight states
108 // eslint-disable-next-line no-jquery/no-each-util
109 $.each( savedQueries
.queries
|| {}, function ( id
, obj
) {
110 if ( obj
.data
&& obj
.data
.filters
) {
111 obj
.data
= model
.convertToParameters( obj
.data
);
115 this.converted
= true;
116 savedQueries
.version
= '2';
119 // Initialize the query items
120 // eslint-disable-next-line no-jquery/no-each-util
121 $.each( savedQueries
.queries
|| {}, function ( id
, obj
) {
122 var normalizedData
= obj
.data
,
123 isDefault
= String( savedQueries
.default ) === String( id
);
125 if ( normalizedData
&& normalizedData
.params
) {
126 // Backwards-compat fix: Remove sticky parameters from
127 // the given data, if they exist
128 normalizedData
.params
= model
.filtersModel
.removeStickyParams( normalizedData
.params
);
130 // Correct the invert state for effective selection
131 if ( normalizedData
.params
.invert
&& !normalizedData
.params
.namespace ) {
132 delete normalizedData
.params
.invert
;
135 model
.cleanupHighlights( normalizedData
);
139 // Skip the addNewQuery method because we don't want to unnecessarily manipulate
140 // the given saved queries unless we literally intend to (like in backwards compat fixes)
141 // And the addNewQuery method also uses a minimization routine that checks for the
142 // validity of items and minimizes the query. This isn't necessary for queries loaded
143 // from the backend, and has the risk of removing values if they're temporarily
144 // invalid (example: if we temporarily removed a cssClass from a filter in the backend)
146 new SavedQueryItemModel(
150 { default: isDefault
}
160 this.emit( 'initialize' );
164 * Clean up highlight parameters.
165 * 'highlight' used to be stored, it's not inferred based on the presence of absence of
168 * @param {Object} data Saved query data
170 SavedQueriesModel
.prototype.cleanupHighlights = function ( data
) {
172 data
.params
.highlight
=== '0' &&
173 data
.highlights
&& Object
.keys( data
.highlights
).length
175 data
.highlights
= {};
177 delete data
.params
.highlight
;
181 * Convert from representation of filters to representation of parameters
183 * @param {Object} data Query data
184 * @return {Object} New converted query data
186 SavedQueriesModel
.prototype.convertToParameters = function ( data
) {
188 defaultFilters
= this.filtersModel
.getFiltersFromParameters( this.filtersModel
.getDefaultParams() ),
189 fullFilterRepresentation
= $.extend( true, {}, defaultFilters
, data
.filters
),
190 highlightEnabled
= data
.highlights
.highlight
;
192 delete data
.highlights
.highlight
;
195 newData
.params
= this.filtersModel
.getMinimizedParamRepresentation(
196 this.filtersModel
.getParametersFromFilters( fullFilterRepresentation
)
199 // Highlights: appending _color to keys
200 newData
.highlights
= {};
201 // eslint-disable-next-line no-jquery/no-each-util
202 $.each( data
.highlights
, function ( highlightedFilterName
, value
) {
204 newData
.highlights
[ highlightedFilterName
+ '_color' ] = data
.highlights
[ highlightedFilterName
];
209 newData
.params
.highlight
= String( Number( highlightEnabled
|| 0 ) );
217 * @param {string} label Label for the new query
218 * @param {Object} fulldata Full data representation for the new query, combining highlights and filters
219 * @param {boolean} isDefault Item is default
220 * @param {string} [id] Query ID, if exists. If this isn't given, a random
221 * new ID will be created.
222 * @return {string} ID of the newly added query
224 SavedQueriesModel
.prototype.addNewQuery = function ( label
, fulldata
, isDefault
, id
) {
225 var normalizedData
= { params
: {}, highlights
: {} },
226 highlightParamNames
= Object
.keys( this.filtersModel
.getEmptyHighlightParameters() ),
227 randomID
= String( id
|| ( new Date() ).getTime() ),
228 data
= this.filtersModel
.getMinimizedParamRepresentation( fulldata
);
230 // Split highlight/params
231 // eslint-disable-next-line no-jquery/no-each-util
232 $.each( data
, function ( param
, value
) {
233 if ( param
!== 'highlight' && highlightParamNames
.indexOf( param
) > -1 ) {
234 normalizedData
.highlights
[ param
] = value
;
236 normalizedData
.params
[ param
] = value
;
240 // Correct the invert state for effective selection
241 if ( normalizedData
.params
.invert
&& !this.filtersModel
.areNamespacesEffectivelyInverted() ) {
242 delete normalizedData
.params
.invert
;
247 new SavedQueryItemModel(
251 { default: isDefault
}
256 this.setDefault( randomID
);
263 * Remove query from model
265 * @param {string} queryID Query ID
267 SavedQueriesModel
.prototype.removeQuery = function ( queryID
) {
268 var query
= this.getItemByID( queryID
);
271 // Check if this item was the default
272 if ( String( this.getDefault() ) === String( queryID
) ) {
273 // Nulify the default
274 this.setDefault( null );
277 this.removeItems( [ query
] );
282 * Get an item that matches the requested query
284 * @param {Object} fullQueryComparison Object representing all filters and highlights to compare
285 * @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
287 SavedQueriesModel
.prototype.findMatchingQuery = function ( fullQueryComparison
) {
288 // Minimize before comparison
289 fullQueryComparison
= this.filtersModel
.getMinimizedParamRepresentation( fullQueryComparison
);
291 // Correct the invert state for effective selection
292 if ( fullQueryComparison
.invert
&& !this.filtersModel
.areNamespacesEffectivelyInverted() ) {
293 delete fullQueryComparison
.invert
;
296 return this.getItems().filter( function ( item
) {
298 item
.getCombinedData(),
305 * Get query by its identifier
307 * @param {string} queryID Query identifier
308 * @return {mw.rcfilters.dm.SavedQueryItemModel|undefined} Item matching
309 * the search. Undefined if not found.
311 SavedQueriesModel
.prototype.getItemByID = function ( queryID
) {
312 return this.getItems().filter( function ( item
) {
313 return item
.getID() === queryID
;
318 * Get the full data representation of the default query, if it exists
320 * @return {Object|null} Representation of the default params if exists.
321 * Null if default doesn't exist or if the user is not logged in.
323 SavedQueriesModel
.prototype.getDefaultParams = function () {
324 return ( !mw
.user
.isAnon() && this.getItemParams( this.getDefault() ) ) || {};
328 * Get a full parameter representation of an item data
330 * @param {Object} queryID Query ID
331 * @return {Object} Parameter representation
333 SavedQueriesModel
.prototype.getItemParams = function ( queryID
) {
334 var item
= this.getItemByID( queryID
),
335 data
= item
? item
.getData() : {};
337 return !$.isEmptyObject( data
) ? this.buildParamsFromData( data
) : {};
341 * Build a full parameter representation given item data and model sticky values state
343 * @param {Object} data Item data
344 * @return {Object} Full param representation
346 SavedQueriesModel
.prototype.buildParamsFromData = function ( data
) {
348 // Return parameter representation
349 return this.filtersModel
.getMinimizedParamRepresentation( $.extend( true, {},
356 * Get the object representing the state of the entire model and items
358 * @return {Object} Object representing the state of the model and items
360 SavedQueriesModel
.prototype.getState = function () {
361 var obj
= { queries
: {}, version
: '2' };
363 // Translate the items to the saved object
364 this.getItems().forEach( function ( item
) {
365 obj
.queries
[ item
.getID() ] = item
.getState();
368 if ( this.getDefault() ) {
369 obj
.default = this.getDefault();
376 * Set a default query. Null to unset default.
378 * @param {string} itemID Query identifier
381 SavedQueriesModel
.prototype.setDefault = function ( itemID
) {
382 if ( this.default !== itemID
) {
383 this.default = itemID
;
385 // Set for individual itens
386 this.getItems().forEach( function ( item
) {
387 item
.toggleDefault( item
.getID() === itemID
);
390 this.emit( 'default', itemID
);
395 * Get the default query ID
397 * @return {string} Default query identifier
399 SavedQueriesModel
.prototype.getDefault = function () {
404 * Check if the saved queries were converted
406 * @return {boolean} Saved queries were converted from the previous
407 * version to the new version
409 SavedQueriesModel
.prototype.isConverted = function () {
410 return this.converted
;
413 module
.exports
= SavedQueriesModel
;