2 var SavedQueryItemModel
= require( './SavedQueryItemModel.js' ),
6 * View model for saved queries
8 * @class mw.rcfilters.dm.SavedQueriesModel
9 * @mixins OO.EventEmitter
10 * @mixins OO.EmitterList
13 * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters model
14 * @param {Object} [config] Configuration options
15 * @cfg {string} [default] Default query ID
17 SavedQueriesModel
= function MwRcfiltersDmSavedQueriesModel( filtersModel
, config
) {
18 config
= config
|| {};
21 OO
.EventEmitter
.call( this );
22 OO
.EmitterList
.call( this );
24 this.default = config
.default;
25 this.filtersModel
= filtersModel
;
26 this.converted
= false;
29 this.aggregate( { update
: 'itemUpdate' } );
34 OO
.initClass( SavedQueriesModel
);
35 OO
.mixinClass( SavedQueriesModel
, OO
.EventEmitter
);
36 OO
.mixinClass( SavedQueriesModel
, OO
.EmitterList
);
43 * Model is initialized
48 * @param {mw.rcfilters.dm.SavedQueryItemModel} Changed item
55 * @param {string} New default ID
57 * The default has changed
63 * Initialize the saved queries model by reading it from the user's settings.
64 * The structure of the saved queries is:
66 * version: (string) Version number; if version 2, the query represents
67 * parameters. Otherwise, the older version represented filters
68 * and needs to be readjusted,
69 * default: (string) Query ID
73 * filters: (Object) Minimal definition of the filters
74 * highlights: (Object) Definition of the highlights
76 * label: (optional) Name of this query
81 * @param {Object} [savedQueries] An object with the saved queries with
82 * the above structure.
85 SavedQueriesModel
.prototype.initialize = function ( savedQueries
) {
88 savedQueries
= savedQueries
|| {};
92 this.converted
= false;
94 if ( savedQueries
.version
!== '2' ) {
95 // Old version dealt with filter names. We need to migrate to the new structure
98 // version: (string) '2',
99 // default: (string) Query ID,
102 // label: (string) Name of the query
104 // params: (object) Representing all the parameter states
105 // highlights: (object) Representing all the filter highlight states
109 // eslint-disable-next-line jquery/no-each-util
110 $.each( savedQueries
.queries
|| {}, function ( id
, obj
) {
111 if ( obj
.data
&& obj
.data
.filters
) {
112 obj
.data
= model
.convertToParameters( obj
.data
);
116 this.converted
= true;
117 savedQueries
.version
= '2';
120 // Initialize the query items
121 // eslint-disable-next-line jquery/no-each-util
122 $.each( savedQueries
.queries
|| {}, function ( id
, obj
) {
123 var normalizedData
= obj
.data
,
124 isDefault
= String( savedQueries
.default ) === String( id
);
126 if ( normalizedData
&& normalizedData
.params
) {
127 // Backwards-compat fix: Remove sticky parameters from
128 // the given data, if they exist
129 normalizedData
.params
= model
.filtersModel
.removeStickyParams( normalizedData
.params
);
131 // Correct the invert state for effective selection
132 if ( normalizedData
.params
.invert
&& !normalizedData
.params
.namespace ) {
133 delete normalizedData
.params
.invert
;
136 model
.cleanupHighlights( normalizedData
);
140 // Skip the addNewQuery method because we don't want to unnecessarily manipulate
141 // the given saved queries unless we literally intend to (like in backwards compat fixes)
142 // And the addNewQuery method also uses a minimization routine that checks for the
143 // validity of items and minimizes the query. This isn't necessary for queries loaded
144 // from the backend, and has the risk of removing values if they're temporarily
145 // invalid (example: if we temporarily removed a cssClass from a filter in the backend)
147 new SavedQueryItemModel(
151 { default: isDefault
}
161 this.emit( 'initialize' );
165 * Clean up highlight parameters.
166 * 'highlight' used to be stored, it's not inferred based on the presence of absence of
169 * @param {Object} data Saved query data
171 SavedQueriesModel
.prototype.cleanupHighlights = function ( data
) {
173 data
.params
.highlight
=== '0' &&
174 data
.highlights
&& Object
.keys( data
.highlights
).length
176 data
.highlights
= {};
178 delete data
.params
.highlight
;
182 * Convert from representation of filters to representation of parameters
184 * @param {Object} data Query data
185 * @return {Object} New converted query data
187 SavedQueriesModel
.prototype.convertToParameters = function ( data
) {
189 defaultFilters
= this.filtersModel
.getFiltersFromParameters( this.filtersModel
.getDefaultParams() ),
190 fullFilterRepresentation
= $.extend( true, {}, defaultFilters
, data
.filters
),
191 highlightEnabled
= data
.highlights
.highlight
;
193 delete data
.highlights
.highlight
;
196 newData
.params
= this.filtersModel
.getMinimizedParamRepresentation(
197 this.filtersModel
.getParametersFromFilters( fullFilterRepresentation
)
200 // Highlights: appending _color to keys
201 newData
.highlights
= {};
202 // eslint-disable-next-line jquery/no-each-util
203 $.each( data
.highlights
, function ( highlightedFilterName
, value
) {
205 newData
.highlights
[ highlightedFilterName
+ '_color' ] = data
.highlights
[ highlightedFilterName
];
210 newData
.params
.highlight
= String( Number( highlightEnabled
|| 0 ) );
218 * @param {string} label Label for the new query
219 * @param {Object} fulldata Full data representation for the new query, combining highlights and filters
220 * @param {boolean} isDefault Item is default
221 * @param {string} [id] Query ID, if exists. If this isn't given, a random
222 * new ID will be created.
223 * @return {string} ID of the newly added query
225 SavedQueriesModel
.prototype.addNewQuery = function ( label
, fulldata
, isDefault
, id
) {
226 var normalizedData
= { params
: {}, highlights
: {} },
227 highlightParamNames
= Object
.keys( this.filtersModel
.getEmptyHighlightParameters() ),
228 randomID
= String( id
|| ( new Date() ).getTime() ),
229 data
= this.filtersModel
.getMinimizedParamRepresentation( fulldata
);
231 // Split highlight/params
232 // eslint-disable-next-line jquery/no-each-util
233 $.each( data
, function ( param
, value
) {
234 if ( param
!== 'highlight' && highlightParamNames
.indexOf( param
) > -1 ) {
235 normalizedData
.highlights
[ param
] = value
;
237 normalizedData
.params
[ param
] = value
;
241 // Correct the invert state for effective selection
242 if ( normalizedData
.params
.invert
&& !this.filtersModel
.areNamespacesEffectivelyInverted() ) {
243 delete normalizedData
.params
.invert
;
248 new SavedQueryItemModel(
252 { default: isDefault
}
257 this.setDefault( randomID
);
264 * Remove query from model
266 * @param {string} queryID Query ID
268 SavedQueriesModel
.prototype.removeQuery = function ( queryID
) {
269 var query
= this.getItemByID( queryID
);
272 // Check if this item was the default
273 if ( String( this.getDefault() ) === String( queryID
) ) {
274 // Nulify the default
275 this.setDefault( null );
278 this.removeItems( [ query
] );
283 * Get an item that matches the requested query
285 * @param {Object} fullQueryComparison Object representing all filters and highlights to compare
286 * @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
288 SavedQueriesModel
.prototype.findMatchingQuery = function ( fullQueryComparison
) {
289 // Minimize before comparison
290 fullQueryComparison
= this.filtersModel
.getMinimizedParamRepresentation( fullQueryComparison
);
292 // Correct the invert state for effective selection
293 if ( fullQueryComparison
.invert
&& !this.filtersModel
.areNamespacesEffectivelyInverted() ) {
294 delete fullQueryComparison
.invert
;
297 return this.getItems().filter( function ( item
) {
299 item
.getCombinedData(),
306 * Get query by its identifier
308 * @param {string} queryID Query identifier
309 * @return {mw.rcfilters.dm.SavedQueryItemModel|undefined} Item matching
310 * the search. Undefined if not found.
312 SavedQueriesModel
.prototype.getItemByID = function ( queryID
) {
313 return this.getItems().filter( function ( item
) {
314 return item
.getID() === queryID
;
319 * Get the full data representation of the default query, if it exists
321 * @return {Object|null} Representation of the default params if exists.
322 * Null if default doesn't exist or if the user is not logged in.
324 SavedQueriesModel
.prototype.getDefaultParams = function () {
325 return ( !mw
.user
.isAnon() && this.getItemParams( this.getDefault() ) ) || {};
329 * Get a full parameter representation of an item data
331 * @param {Object} queryID Query ID
332 * @return {Object} Parameter representation
334 SavedQueriesModel
.prototype.getItemParams = function ( queryID
) {
335 var item
= this.getItemByID( queryID
),
336 data
= item
? item
.getData() : {};
338 return !$.isEmptyObject( data
) ? this.buildParamsFromData( data
) : {};
342 * Build a full parameter representation given item data and model sticky values state
344 * @param {Object} data Item data
345 * @return {Object} Full param representation
347 SavedQueriesModel
.prototype.buildParamsFromData = function ( data
) {
349 // Return parameter representation
350 return this.filtersModel
.getMinimizedParamRepresentation( $.extend( true, {},
357 * Get the object representing the state of the entire model and items
359 * @return {Object} Object representing the state of the model and items
361 SavedQueriesModel
.prototype.getState = function () {
362 var obj
= { queries
: {}, version
: '2' };
364 // Translate the items to the saved object
365 this.getItems().forEach( function ( item
) {
366 obj
.queries
[ item
.getID() ] = item
.getState();
369 if ( this.getDefault() ) {
370 obj
.default = this.getDefault();
377 * Set a default query. Null to unset default.
379 * @param {string} itemID Query identifier
382 SavedQueriesModel
.prototype.setDefault = function ( itemID
) {
383 if ( this.default !== itemID
) {
384 this.default = itemID
;
386 // Set for individual itens
387 this.getItems().forEach( function ( item
) {
388 item
.toggleDefault( item
.getID() === itemID
);
391 this.emit( 'default', itemID
);
396 * Get the default query ID
398 * @return {string} Default query identifier
400 SavedQueriesModel
.prototype.getDefault = function () {
405 * Check if the saved queries were converted
407 * @return {boolean} Saved queries were converted from the previous
408 * version to the new version
410 SavedQueriesModel
.prototype.isConverted = function () {
411 return this.converted
;
414 module
.exports
= SavedQueriesModel
;