diff options
Diffstat (limited to 'www/wiki/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js')
-rw-r--r-- | www/wiki/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/www/wiki/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js b/www/wiki/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js new file mode 100644 index 00000000..77d5961a --- /dev/null +++ b/www/wiki/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js @@ -0,0 +1,406 @@ +( function ( mw, $ ) { + /** + * View model for saved queries + * + * @class + * @mixins OO.EventEmitter + * @mixins OO.EmitterList + * + * @constructor + * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters model + * @param {Object} [config] Configuration options + * @cfg {string} [default] Default query ID + */ + mw.rcfilters.dm.SavedQueriesModel = function MwRcfiltersDmSavedQueriesModel( filtersModel, config ) { + config = config || {}; + + // Mixin constructor + OO.EventEmitter.call( this ); + OO.EmitterList.call( this ); + + this.default = config.default; + this.filtersModel = filtersModel; + this.converted = false; + + // Events + this.aggregate( { update: 'itemUpdate' } ); + }; + + /* Initialization */ + + OO.initClass( mw.rcfilters.dm.SavedQueriesModel ); + OO.mixinClass( mw.rcfilters.dm.SavedQueriesModel, OO.EventEmitter ); + OO.mixinClass( mw.rcfilters.dm.SavedQueriesModel, OO.EmitterList ); + + /* Events */ + + /** + * @event initialize + * + * Model is initialized + */ + + /** + * @event itemUpdate + * @param {mw.rcfilters.dm.SavedQueryItemModel} Changed item + * + * An item has changed + */ + + /** + * @event default + * @param {string} New default ID + * + * The default has changed + */ + + /* Methods */ + + /** + * Initialize the saved queries model by reading it from the user's settings. + * The structure of the saved queries is: + * { + * version: (string) Version number; if version 2, the query represents + * parameters. Otherwise, the older version represented filters + * and needs to be readjusted, + * default: (string) Query ID + * queries:{ + * query_id_1: { + * data:{ + * filters: (Object) Minimal definition of the filters + * highlights: (Object) Definition of the highlights + * }, + * label: (optional) Name of this query + * } + * } + * } + * + * @param {Object} [savedQueries] An object with the saved queries with + * the above structure. + * @fires initialize + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.initialize = function ( savedQueries ) { + var model = this; + + savedQueries = savedQueries || {}; + + this.clearItems(); + this.default = null; + this.converted = false; + + if ( savedQueries.version !== '2' ) { + // Old version dealt with filter names. We need to migrate to the new structure + // The new structure: + // { + // version: (string) '2', + // default: (string) Query ID, + // queries: { + // query_id: { + // label: (string) Name of the query + // data: { + // params: (object) Representing all the parameter states + // highlights: (object) Representing all the filter highlight states + // } + // } + // } + $.each( savedQueries.queries || {}, function ( id, obj ) { + if ( obj.data && obj.data.filters ) { + obj.data = model.convertToParameters( obj.data ); + } + } ); + + this.converted = true; + savedQueries.version = '2'; + } + + // Initialize the query items + $.each( savedQueries.queries || {}, function ( id, obj ) { + var normalizedData = obj.data, + isDefault = String( savedQueries.default ) === String( id ); + + if ( normalizedData && normalizedData.params ) { + // Backwards-compat fix: Remove sticky parameters from + // the given data, if they exist + normalizedData.params = model.filtersModel.removeStickyParams( normalizedData.params ); + + // Correct the invert state for effective selection + if ( normalizedData.params.invert && !normalizedData.params.namespace ) { + delete normalizedData.params.invert; + } + + model.cleanupHighlights( normalizedData ); + + id = String( id ); + + // Skip the addNewQuery method because we don't want to unnecessarily manipulate + // the given saved queries unless we literally intend to (like in backwards compat fixes) + // And the addNewQuery method also uses a minimization routine that checks for the + // validity of items and minimizes the query. This isn't necessary for queries loaded + // from the backend, and has the risk of removing values if they're temporarily + // invalid (example: if we temporarily removed a cssClass from a filter in the backend) + model.addItems( [ + new mw.rcfilters.dm.SavedQueryItemModel( + id, + obj.label, + normalizedData, + { 'default': isDefault } + ) + ] ); + + if ( isDefault ) { + model.default = id; + } + } + } ); + + this.emit( 'initialize' ); + }; + + /** + * Clean up highlight parameters. + * 'highlight' used to be stored, it's not inferred based on the presence of absence of + * filter colors. + * + * @param {Object} data Saved query data + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.cleanupHighlights = function ( data ) { + if ( + data.params.highlight === '0' && + data.highlights && Object.keys( data.highlights ).length + ) { + data.highlights = {}; + } + delete data.params.highlight; + }; + + /** + * Convert from representation of filters to representation of parameters + * + * @param {Object} data Query data + * @return {Object} New converted query data + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.convertToParameters = function ( data ) { + var newData = {}, + defaultFilters = this.filtersModel.getFiltersFromParameters( this.filtersModel.getDefaultParams() ), + fullFilterRepresentation = $.extend( true, {}, defaultFilters, data.filters ), + highlightEnabled = data.highlights.highlight; + + delete data.highlights.highlight; + + // Filters + newData.params = this.filtersModel.getMinimizedParamRepresentation( + this.filtersModel.getParametersFromFilters( fullFilterRepresentation ) + ); + + // Highlights: appending _color to keys + newData.highlights = {}; + $.each( data.highlights, function ( highlightedFilterName, value ) { + if ( value ) { + newData.highlights[ highlightedFilterName + '_color' ] = data.highlights[ highlightedFilterName ]; + } + } ); + + // Add highlight + newData.params.highlight = String( Number( highlightEnabled || 0 ) ); + + return newData; + }; + + /** + * Add a query item + * + * @param {string} label Label for the new query + * @param {Object} fulldata Full data representation for the new query, combining highlights and filters + * @param {boolean} isDefault Item is default + * @param {string} [id] Query ID, if exists. If this isn't given, a random + * new ID will be created. + * @return {string} ID of the newly added query + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.addNewQuery = function ( label, fulldata, isDefault, id ) { + var normalizedData = { params: {}, highlights: {} }, + highlightParamNames = Object.keys( this.filtersModel.getEmptyHighlightParameters() ), + randomID = String( id || ( new Date() ).getTime() ), + data = this.filtersModel.getMinimizedParamRepresentation( fulldata ); + + // Split highlight/params + $.each( data, function ( param, value ) { + if ( param !== 'highlight' && highlightParamNames.indexOf( param ) > -1 ) { + normalizedData.highlights[ param ] = value; + } else { + normalizedData.params[ param ] = value; + } + } ); + + // Correct the invert state for effective selection + if ( normalizedData.params.invert && !this.filtersModel.areNamespacesEffectivelyInverted() ) { + delete normalizedData.params.invert; + } + + // Add item + this.addItems( [ + new mw.rcfilters.dm.SavedQueryItemModel( + randomID, + label, + normalizedData, + { 'default': isDefault } + ) + ] ); + + if ( isDefault ) { + this.setDefault( randomID ); + } + + return randomID; + }; + + /** + * Remove query from model + * + * @param {string} queryID Query ID + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.removeQuery = function ( queryID ) { + var query = this.getItemByID( queryID ); + + if ( query ) { + // Check if this item was the default + if ( String( this.getDefault() ) === String( queryID ) ) { + // Nulify the default + this.setDefault( null ); + } + + this.removeItems( [ query ] ); + } + }; + + /** + * Get an item that matches the requested query + * + * @param {Object} fullQueryComparison Object representing all filters and highlights to compare + * @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.findMatchingQuery = function ( fullQueryComparison ) { + // Minimize before comparison + fullQueryComparison = this.filtersModel.getMinimizedParamRepresentation( fullQueryComparison ); + + // Correct the invert state for effective selection + if ( fullQueryComparison.invert && !this.filtersModel.areNamespacesEffectivelyInverted() ) { + delete fullQueryComparison.invert; + } + + return this.getItems().filter( function ( item ) { + return OO.compare( + item.getCombinedData(), + fullQueryComparison + ); + } )[ 0 ]; + }; + + /** + * Get query by its identifier + * + * @param {string} queryID Query identifier + * @return {mw.rcfilters.dm.SavedQueryItemModel|undefined} Item matching + * the search. Undefined if not found. + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.getItemByID = function ( queryID ) { + return this.getItems().filter( function ( item ) { + return item.getID() === queryID; + } )[ 0 ]; + }; + + /** + * Get the full data representation of the default query, if it exists + * + * @return {Object|null} Representation of the default params if exists. + * Null if default doesn't exist or if the user is not logged in. + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.getDefaultParams = function () { + return ( !mw.user.isAnon() && this.getItemParams( this.getDefault() ) ) || {}; + }; + + /** + * Get a full parameter representation of an item data + * + * @param {Object} queryID Query ID + * @return {Object} Parameter representation + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.getItemParams = function ( queryID ) { + var item = this.getItemByID( queryID ), + data = item ? item.getData() : {}; + + return !$.isEmptyObject( data ) ? this.buildParamsFromData( data ) : {}; + }; + + /** + * Build a full parameter representation given item data and model sticky values state + * + * @param {Object} data Item data + * @return {Object} Full param representation + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.buildParamsFromData = function ( data ) { + data = data || {}; + // Return parameter representation + return this.filtersModel.getMinimizedParamRepresentation( $.extend( true, {}, + data.params, + data.highlights + ) ); + }; + + /** + * Get the object representing the state of the entire model and items + * + * @return {Object} Object representing the state of the model and items + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.getState = function () { + var obj = { queries: {}, version: '2' }; + + // Translate the items to the saved object + this.getItems().forEach( function ( item ) { + obj.queries[ item.getID() ] = item.getState(); + } ); + + if ( this.getDefault() ) { + obj.default = this.getDefault(); + } + + return obj; + }; + + /** + * Set a default query. Null to unset default. + * + * @param {string} itemID Query identifier + * @fires default + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.setDefault = function ( itemID ) { + if ( this.default !== itemID ) { + this.default = itemID; + + // Set for individual itens + this.getItems().forEach( function ( item ) { + item.toggleDefault( item.getID() === itemID ); + } ); + + this.emit( 'default', itemID ); + } + }; + + /** + * Get the default query ID + * + * @return {string} Default query identifier + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.getDefault = function () { + return this.default; + }; + + /** + * Check if the saved queries were converted + * + * @return {boolean} Saved queries were converted from the previous + * version to the new version + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.isConverted = function () { + return this.converted; + }; +}( mediaWiki, jQuery ) ); |