X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js new file mode 100644 index 00000000..f8789415 --- /dev/null +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js @@ -0,0 +1,282 @@ +( function ( mw, $ ) { + /** + * View model for saved queries + * + * @class + * @mixins OO.EventEmitter + * @mixins OO.EmitterList + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {string} [default] Default query ID + */ + mw.rcfilters.dm.SavedQueriesModel = function MwRcfiltersDmSavedQueriesModel( config ) { + config = config || {}; + + // Mixin constructor + OO.EventEmitter.call( this ); + OO.EmitterList.call( this ); + + this.default = config.default; + this.baseState = {}; + + // 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: + * { + * 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. + * @param {Object} [baseState] An object representing the base state + * so we can normalize the data + * @param {string[]} [ignoreFilters] Filters to ignore and remove from + * the data + * @fires initialize + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.initialize = function ( savedQueries, baseState, ignoreFilters ) { + var items = [], + defaultItem = null; + + savedQueries = savedQueries || {}; + ignoreFilters = ignoreFilters || {}; + + this.baseState = baseState; + + this.clearItems(); + $.each( savedQueries.queries || {}, function ( id, obj ) { + var item, + normalizedData = $.extend( true, {}, baseState, obj.data ), + isDefault = String( savedQueries.default ) === String( id ); + + // Backwards-compat fix: We stored the 'highlight' state with + // "1" and "0" instead of true/false; for already-stored states, + // we need to fix that. + // NOTE: Since this feature is only available in beta, we should + // not need this line when we release this to the general wikis. + // This method will automatically fix all saved queries anyways + // for existing users, who are only betalabs users at the moment. + normalizedData.highlights.highlight = !!Number( normalizedData.highlights.highlight ); + + // Backwards-compat fix: Remove sticky parameters from the 'ignoreFilters' list + ignoreFilters.forEach( function ( name ) { + delete normalizedData.filters[ name ]; + } ); + + item = new mw.rcfilters.dm.SavedQueryItemModel( + id, + obj.label, + normalizedData, + { 'default': isDefault } + ); + + if ( isDefault ) { + defaultItem = item; + } + + items.push( item ); + } ); + + if ( defaultItem ) { + this.default = defaultItem.getID(); + } + + this.addItems( items ); + + this.emit( 'initialize' ); + }; + + /** + * Add a query item + * + * @param {string} label Label for the new query + * @param {Object} data Data for the new query + * @return {string} ID of the newly added query + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.addNewQuery = function ( label, data ) { + var randomID = ( new Date() ).getTime(), + normalizedData = $.extend( true, {}, this.baseState, data ); + + // Add item + this.addItems( [ + new mw.rcfilters.dm.SavedQueryItemModel( + randomID, + label, + normalizedData + ) + ] ); + + 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 ) { + var model = this; + + fullQueryComparison = this.getDifferenceFromBase( fullQueryComparison ); + + return this.getItems().filter( function ( item ) { + var comparedData = model.getDifferenceFromBase( item.getData() ); + return OO.compare( + comparedData, + fullQueryComparison + ); + } )[ 0 ]; + }; + + /** + * Get a minimal representation of the state for comparison + * + * @param {Object} state Given state + * @return {Object} Minimal state + */ + mw.rcfilters.dm.SavedQueriesModel.prototype.getDifferenceFromBase = function ( state ) { + var result = { filters: {}, highlights: {}, invert: state.invert }, + baseState = this.baseState; + + // XOR results + $.each( state.filters, function ( name, value ) { + if ( baseState.filters !== undefined && baseState.filters[ name ] !== value ) { + result.filters[ name ] = value; + } + } ); + + $.each( state.highlights, function ( name, value ) { + if ( baseState.highlights !== undefined && baseState.highlights[ name ] !== value && name !== 'highlight' ) { + result.highlights[ name ] = value; + } + } ); + + return result; + }; + /** + * 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 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: {} }; + + // Translate the items to the saved object + this.getItems().forEach( function ( item ) { + var itemState = item.getState(); + + obj.queries[ item.getID() ] = itemState; + } ); + + 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; + }; +}( mediaWiki, jQuery ) );