3 * View model for saved queries
6 * @mixins OO.EventEmitter
7 * @mixins OO.EmitterList
10 * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters model
11 * @param {Object} [config] Configuration options
12 * @cfg {string} [default] Default query ID
14 mw
.rcfilters
.dm
.SavedQueriesModel
= function MwRcfiltersDmSavedQueriesModel( filtersModel
, config
) {
15 config
= config
|| {};
18 OO
.EventEmitter
.call( this );
19 OO
.EmitterList
.call( this );
21 this.default = config
.default;
22 this.filtersModel
= filtersModel
;
23 this.converted
= false;
26 this.aggregate( { update
: 'itemUpdate' } );
31 OO
.initClass( mw
.rcfilters
.dm
.SavedQueriesModel
);
32 OO
.mixinClass( mw
.rcfilters
.dm
.SavedQueriesModel
, OO
.EventEmitter
);
33 OO
.mixinClass( mw
.rcfilters
.dm
.SavedQueriesModel
, OO
.EmitterList
);
40 * Model is initialized
45 * @param {mw.rcfilters.dm.SavedQueryItemModel} Changed item
52 * @param {string} New default ID
54 * The default has changed
60 * Initialize the saved queries model by reading it from the user's settings.
61 * The structure of the saved queries is:
63 * version: (string) Version number; if version 2, the query represents
64 * parameters. Otherwise, the older version represented filters
65 * and needs to be readjusted,
66 * default: (string) Query ID
70 * filters: (Object) Minimal definition of the filters
71 * highlights: (Object) Definition of the highlights
73 * label: (optional) Name of this query
78 * @param {Object} [savedQueries] An object with the saved queries with
79 * the above structure.
82 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.initialize = function ( savedQueries
) {
84 excludedParams
= this.filtersModel
.getExcludedParams();
86 savedQueries
= savedQueries
|| {};
90 this.converted
= false;
92 if ( savedQueries
.version
!== '2' ) {
93 // Old version dealt with filter names. We need to migrate to the new structure
96 // version: (string) '2',
97 // default: (string) Query ID,
100 // label: (string) Name of the query
102 // params: (object) Representing all the parameter states
103 // highlights: (object) Representing all the filter highlight states
107 $.each( savedQueries
.queries
|| {}, function ( id
, obj
) {
108 if ( obj
.data
&& obj
.data
.filters
) {
109 obj
.data
= model
.convertToParameters( obj
.data
);
113 this.converted
= true;
114 savedQueries
.version
= '2';
117 // Initialize the query items
118 $.each( savedQueries
.queries
|| {}, function ( id
, obj
) {
119 var normalizedData
= obj
.data
,
120 isDefault
= String( savedQueries
.default ) === String( id
);
122 if ( normalizedData
&& normalizedData
.params
) {
123 // Backwards-compat fix: Remove excluded parameters from
124 // the given data, if they exist
125 excludedParams
.forEach( function ( name
) {
126 delete normalizedData
.params
[ name
];
130 model
.addNewQuery( obj
.label
, normalizedData
, isDefault
, id
);
138 this.emit( 'initialize' );
142 * Convert from representation of filters to representation of parameters
144 * @param {Object} data Query data
145 * @return {Object} New converted query data
147 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.convertToParameters = function ( data
) {
149 defaultFilters
= this.filtersModel
.getFiltersFromParameters( this.filtersModel
.getDefaultParams() ),
150 fullFilterRepresentation
= $.extend( true, {}, defaultFilters
, data
.filters
),
151 highlightEnabled
= data
.highlights
.highlight
;
153 delete data
.highlights
.highlight
;
156 newData
.params
= this.filtersModel
.getParametersFromFilters( fullFilterRepresentation
);
158 // Highlights (taking out 'highlight' itself, appending _color to keys)
159 newData
.highlights
= {};
160 Object
.keys( data
.highlights
).forEach( function ( highlightedFilterName
) {
161 newData
.highlights
[ highlightedFilterName
+ '_color' ] = data
.highlights
[ highlightedFilterName
];
165 newData
.params
.highlight
= String( Number( highlightEnabled
|| 0 ) );
171 * Get an object representing the base state of parameters
174 * This is meant to make sure that the saved queries that are
175 * in memory are always the same structure as what we would get
176 * by calling the current model's "getSelectedState" and by checking
179 * In cases where a user saved a query when the system had a certain
180 * set of params, and then a filter was added to the system, we want
181 * to make sure that the stored queries can still be comparable to
182 * the current state, which means that we need the base state for
185 * - Saved queries are stored in "minimal" view (only changed params
186 * are stored); When we initialize the system, we merge each minimal
187 * query with the base state (using 'getMinimalParamList') so all
188 * saved queries have the exact same structure as what we would get
189 * by checking the getSelectedState of the filter.
190 * - When we save the queries, we minimize the object to only represent
191 * whatever has actually changed, rather than store the entire
192 * object. To check what actually is different so we can store it,
193 * we need to obtain a base state to compare against, this is
194 * what #getMinimalParamList does
196 * @return {Object} Base parameter state
198 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.getBaseParamState = function () {
200 highlightedItems
= {};
202 if ( !this.baseParamState
) {
203 allParams
= this.filtersModel
.getParametersFromFilters( {} );
205 // Prepare highlights
206 this.filtersModel
.getItemsSupportingHighlights().forEach( function ( item
) {
207 highlightedItems
[ item
.getName() + '_color' ] = null;
210 this.baseParamState
= {
211 params
: $.extend( true, { highlight
: '0' }, allParams
),
212 highlights
: highlightedItems
216 return this.baseParamState
;
220 * Get an object that holds only the parameters and highlights that have
221 * values different than the base value.
223 * This is the reverse of the normalization we do initially on loading and
224 * initializing the saved queries model.
226 * @param {Object} valuesObject Object representing the state of both
227 * filters and highlights in its normalized version, to be minimized.
228 * @return {Object} Minimal filters and highlights list
230 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.getMinimalParamList = function ( valuesObject
) {
231 var result
= { params
: {}, highlights
: {} },
232 baseState
= this.getBaseParamState();
235 $.each( valuesObject
.params
, function ( name
, value
) {
236 if ( baseState
.params
!== undefined && baseState
.params
[ name
] !== value
) {
237 result
.params
[ name
] = value
;
241 $.each( valuesObject
.highlights
, function ( name
, value
) {
242 if ( baseState
.highlights
!== undefined && baseState
.highlights
[ name
] !== value
) {
243 result
.highlights
[ name
] = value
;
253 * @param {string} label Label for the new query
254 * @param {Object} data Data for the new query
255 * @param {boolean} isDefault Item is default
256 * @param {string} [id] Query ID, if exists. If this isn't given, a random
257 * new ID will be created.
258 * @return {string} ID of the newly added query
260 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.addNewQuery = function ( label
, data
, isDefault
, id
) {
261 var randomID
= String( id
|| ( new Date() ).getTime() ),
262 normalizedData
= this.getMinimalParamList( data
);
266 new mw
.rcfilters
.dm
.SavedQueryItemModel(
270 { 'default': isDefault
}
275 this.setDefault( randomID
);
282 * Remove query from model
284 * @param {string} queryID Query ID
286 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.removeQuery = function ( queryID
) {
287 var query
= this.getItemByID( queryID
);
290 // Check if this item was the default
291 if ( String( this.getDefault() ) === String( queryID
) ) {
292 // Nulify the default
293 this.setDefault( null );
296 this.removeItems( [ query
] );
301 * Get an item that matches the requested query
303 * @param {Object} fullQueryComparison Object representing all filters and highlights to compare
304 * @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
306 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.findMatchingQuery = function ( fullQueryComparison
) {
307 // Minimize before comparison
308 fullQueryComparison
= this.getMinimalParamList( fullQueryComparison
);
310 return this.getItems().filter( function ( item
) {
319 * Get query by its identifier
321 * @param {string} queryID Query identifier
322 * @return {mw.rcfilters.dm.SavedQueryItemModel|undefined} Item matching
323 * the search. Undefined if not found.
325 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.getItemByID = function ( queryID
) {
326 return this.getItems().filter( function ( item
) {
327 return item
.getID() === queryID
;
332 * Get an item's full data
334 * @param {string} queryID Query identifier
335 * @return {Object} Item's full data
337 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.getItemFullData = function ( queryID
) {
338 var item
= this.getItemByID( queryID
);
340 // Fill in the base params
341 return item
? $.extend( true, {}, this.getBaseParamState(), item
.getData() ) : {};
345 * Get the object representing the state of the entire model and items
347 * @return {Object} Object representing the state of the model and items
349 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.getState = function () {
351 obj
= { queries
: {}, version
: '2' };
353 // Translate the items to the saved object
354 this.getItems().forEach( function ( item
) {
355 var itemState
= item
.getState();
357 itemState
.data
= model
.getMinimalParamList( itemState
.data
);
359 obj
.queries
[ item
.getID() ] = itemState
;
362 if ( this.getDefault() ) {
363 obj
.default = this.getDefault();
370 * Set a default query. Null to unset default.
372 * @param {string} itemID Query identifier
375 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.setDefault = function ( itemID
) {
376 if ( this.default !== itemID
) {
377 this.default = itemID
;
379 // Set for individual itens
380 this.getItems().forEach( function ( item
) {
381 item
.toggleDefault( item
.getID() === itemID
);
384 this.emit( 'default', itemID
);
389 * Get the default query ID
391 * @return {string} Default query identifier
393 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.getDefault = function () {
398 * Check if the saved queries were converted
400 * @return {boolean} Saved queries were converted from the previous
401 * version to the new version
403 mw
.rcfilters
.dm
.SavedQueriesModel
.prototype.isConverted = function () {
404 return this.converted
;
406 }( mediaWiki
, jQuery
) );