X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;ds=sidebyside;f=resources%2Fsrc%2Fmediawiki.rcfilters%2Fdm%2Fmw.rcfilters.dm.FilterGroup.js;h=4dc86f6f657dd872891b1f18d83da920db46377c;hb=3df3b575c6617df64ec98533cc7141bd2314e274;hp=b6eda0fa0cd75d4e6e1131c072d54ae13446e5a1;hpb=83b139da98f3851c107f87769346a1ffaaaab125;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js index b6eda0fa0c..4dc86f6f65 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js @@ -11,10 +11,19 @@ * @cfg {string} [type='send_unselected_if_any'] Group type * @cfg {string} [view='default'] Name of the display group this group * is a part of. + * @cfg {boolean} [isSticky] This group is using a 'sticky' default; meaning + * that every time a value is changed, it becomes the new default + * @cfg {boolean} [excludedFromSavedQueries] A specific requirement to exclude + * this filter from saved queries. This is always true if the filter is 'sticky' + * but can be used for non-sticky filters as an additional requirement. Similarly + * to 'sticky' it works for the entire group as a whole. * @cfg {string} [title] Group title * @cfg {boolean} [hidden] This group is hidden from the regular menu views * @cfg {boolean} [allowArbitrary] Allows for an arbitrary value to be added to the * group from the URL, even if it wasn't initially set up. + * @cfg {number} [range] An object defining minimum and maximum values for numeric + * groups. { min: x, max: y } + * @cfg {number} [minValue] Minimum value for numeric groups * @cfg {string} [separator='|'] Value separator for 'string_options' groups * @cfg {boolean} [active] Group is active * @cfg {boolean} [fullCoverage] This filters in this group collectively cover all results @@ -38,9 +47,12 @@ this.name = name; this.type = config.type || 'send_unselected_if_any'; this.view = config.view || 'default'; + this.sticky = !!config.isSticky; + this.excludedFromSavedQueries = this.sticky || !!config.excludedFromSavedQueries; this.title = config.title || name; this.hidden = !!config.hidden; this.allowArbitrary = !!config.allowArbitrary; + this.numericRange = config.range; this.separator = config.separator || '|'; this.labelPrefixKey = config.labelPrefixKey; @@ -90,7 +102,6 @@ var subsetNames = [], filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, { group: model.getName(), - useDefaultAsBaseValue: !!filter.useDefaultAsBaseValue, label: filter.label || filter.name, description: filter.description || '', labelPrefixKey: model.labelPrefixKey, @@ -140,7 +151,7 @@ // Store the default parameter state // For this group type, parameter values are direct // We need to convert from a boolean to a string ('1' and '0') - model.defaultParams[ filter.name ] = String( Number( !!filter.default ) ); + model.defaultParams[ filter.name ] = String( Number( filter.default || 0 ) ); } } ); @@ -175,31 +186,31 @@ // For this group, the parameter is the group name, // and a single item can be selected: default or first item this.defaultParams[ this.getName() ] = defaultParam; - - // Single option means there must be a single option - // selected, so we have to either select the default - // or select the first option - this.selectItemByParamName( defaultParam ); } // Store default filter state based on default params this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() ); // Check for filters that should be initially selected by their default value - this.getItems().forEach( function ( item ) { - if ( - item.isUsingDefaultAsBaseValue() && - ( - // This setting can only be applied to these groups - // the other groups are way too complex for that - model.getType() === 'single_option' || - model.getType() === 'boolean' - ) - ) { - // Apply selection - item.toggleSelected( !!model.defaultFilters[ item.getName() ] ); - } - } ); + if ( this.isSticky() ) { + $.each( this.defaultFilters, function ( filterName, filterValue ) { + model.getItemByName( filterName ).toggleSelected( filterValue ); + } ); + } + + // Verify that single_option group has at least one item selected + if ( + this.getType() === 'single_option' && + this.getSelectedItems().length === 0 + ) { + defaultParam = groupDefault !== undefined ? + groupDefault : this.getItems()[ 0 ].getParamName(); + + // Single option means there must be a single option + // selected, so we have to either select the default + // or select the first option + this.selectItemByParamName( defaultParam ); + } }; /** @@ -211,33 +222,39 @@ mw.rcfilters.dm.FilterGroup.prototype.onFilterItemUpdate = function ( item ) { // Update state var changed = false, - active = this.areAnySelected(); - - if ( - item.isSelected() && - this.getType() === 'single_option' && - this.currSelected && - this.currSelected !== item - ) { - this.currSelected.toggleSelected( false ); - } - - // For 'single_option' groups, check if we just unselected all - // items. This should never be the result. If we did unselect - // all (like resetting all filters to false) then this group - // must choose its default item or the first item in the group - if ( - this.getType() === 'single_option' && - !this.getItems().some( function ( filterItem ) { - return filterItem.isSelected(); - } ) - ) { - // Single option means there must be a single option - // selected, so we have to either select the default - // or select the first option - this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ); - this.currSelected.toggleSelected( true ); - changed = true; + active = this.areAnySelected(), + model = this; + + if ( this.getType() === 'single_option' ) { + // This group must have one item selected always + // and must never have more than one item selected at a time + if ( this.getSelectedItems().length === 0 ) { + // Nothing is selected anymore + // Select the default or the first item + this.currSelected = this.getItemByParamName( this.defaultParams[ this.getName() ] ) || + this.getItems()[ 0 ]; + this.currSelected.toggleSelected( true ); + changed = true; + } else if ( this.getSelectedItems().length > 1 ) { + // There is more than one item selected + // This should only happen if the item given + // is the one that is selected, so unselect + // all items that is not it + this.getSelectedItems().forEach( function ( itemModel ) { + // Note that in case the given item is actually + // not selected, this loop will end up unselecting + // all items, which would trigger the case above + // when the last item is unselected anyways + var selected = itemModel.getName() === item.getName() && + item.isSelected(); + + itemModel.toggleSelected( selected ); + if ( selected ) { + model.currSelected = itemModel; + } + } ); + changed = true; + } } if ( @@ -245,6 +262,12 @@ this.active !== active || this.currSelected !== item ) { + if ( this.isSticky() ) { + // If this group is sticky, then change the default according to the + // current selection. + this.defaultParams = this.getParamRepresentation( this.getSelectedState() ); + } + this.active = active; this.currSelected = item; @@ -279,6 +302,26 @@ return this.allowArbitrary; }; + /** + * Get group maximum value for numeric groups + * + * @return {number|null} Group max value + */ + mw.rcfilters.dm.FilterGroup.prototype.getMaxValue = function () { + return this.numericRange && this.numericRange.max !== undefined ? + this.numericRange.max : null; + }; + + /** + * Get group minimum value for numeric groups + * + * @return {number|null} Group max value + */ + mw.rcfilters.dm.FilterGroup.prototype.getMinValue = function () { + return this.numericRange && this.numericRange.min !== undefined ? + this.numericRange.min : null; + }; + /** * Get group name * @@ -531,21 +574,11 @@ // This means we have not been given a filter representation // so we are building one based on current state filterRepresentation[ item.getName() ] = item.isSelected(); - } else if ( !filterRepresentation[ item.getName() ] ) { + } else if ( filterRepresentation[ item.getName() ] === undefined ) { // We are given a filter representation, but we have to make // sure that we fill in the missing filters if there are any - // we will assume they are all falsey, unless they have - // isUsingDefaultAsBaseValue, in which case they get their - // default state - if ( - item.isUsingDefaultAsBaseValue() && - ( - // This setting can only be applied to these groups - // the other groups are way too complex for that - model.getType() === 'single_option' || - model.getType() === 'boolean' - ) - ) { + // we will assume they are all falsey + if ( model.isSticky() ) { filterRepresentation[ item.getName() ] = !!defaultFilters[ item.getName() ]; } else { filterRepresentation[ item.getName() ] = false; @@ -609,15 +642,21 @@ * @return {Object} Filter representation */ mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) { - var areAnySelected, paramValues, defaultValue, item, currentValue, + var areAnySelected, paramValues, item, currentValue, oneWasSelected = false, defaultParams = this.getDefaultParams(), - defaultFilters = this.getDefaultFilters(), expandedParams = $.extend( true, {}, paramRepresentation ), model = this, paramToFilterMap = {}, result = {}; + if ( this.isSticky() ) { + // If the group is sticky, check if all parameters are represented + // and for those that aren't represented, add them with their default + // values + paramRepresentation = $.extend( true, {}, this.getDefaultParams(), paramRepresentation ); + } + paramRepresentation = paramRepresentation || {}; if ( this.getType() === 'send_unselected_if_any' || @@ -636,8 +675,7 @@ } ); $.each( expandedParams, function ( paramName, paramValue ) { - var value = paramValue, - filterItem = paramToFilterMap[ paramName ]; + var filterItem = paramToFilterMap[ paramName ]; if ( model.getType() === 'send_unselected_if_any' ) { // Flip the definition between the parameter @@ -650,13 +688,7 @@ false; } else if ( model.getType() === 'boolean' ) { // Straight-forward definition of state - if ( - filterItem.isUsingDefaultAsBaseValue() && - paramRepresentation[ filterItem.getParamName() ] === undefined - ) { - value = defaultParams[ filterItem.getParamName() ]; - } - result[ filterItem.getName() ] = !!Number( value ); + result[ filterItem.getName() ] = !!Number( paramRepresentation[ filterItem.getParamName() ] ); } } ); } else if ( this.getType() === 'string_options' ) { @@ -690,16 +722,8 @@ } else if ( this.getType() === 'single_option' ) { // There is parameter that fits a single filter and if not, get the default this.getItems().forEach( function ( filterItem ) { - var selected = false; + var selected = filterItem.getParamName() === paramRepresentation[ model.getName() ]; - if ( - filterItem.isUsingDefaultAsBaseValue() && - paramRepresentation[ model.getName() ] === undefined - ) { - selected = !!Number( paramRepresentation[ model.getName() ] ); - } else { - selected = filterItem.getParamName() === paramRepresentation[ model.getName() ]; - } result[ filterItem.getName() ] = selected; oneWasSelected = oneWasSelected || selected; } ); @@ -708,19 +732,9 @@ // Go over result and make sure all filters are represented. // If any filters are missing, they will get a falsey value this.getItems().forEach( function ( filterItem ) { - if ( - ( - // This setting can only be applied to these groups - // the other groups are way too complex for that - model.getType() === 'single_option' || - model.getType() === 'boolean' - ) && - result[ filterItem.getName() ] === undefined && - filterItem.isUsingDefaultAsBaseValue() - ) { - result[ filterItem.getName() ] = !!defaultFilters[ filterItem.getName() ]; + if ( result[ filterItem.getName() ] === undefined ) { + result[ filterItem.getName() ] = false; } - oneWasSelected = oneWasSelected || !!result[ filterItem.getName() ]; } ); // Make sure that at least one option is selected in @@ -732,14 +746,32 @@ this.getType() === 'single_option' && !oneWasSelected ) { - defaultValue = this.getDefaultParams(); - item = this.getItemByParamName( defaultValue[ this.getName() ] ); + item = this.getItems()[ 0 ]; + if ( defaultParams[ this.getName() ] ) { + item = this.getItemByParamName( defaultParams[ this.getName() ] ); + } + result[ item.getName() ] = true; } return result; }; + /** + * Get current selected state of all filter items in this group + * + * @return {Object} Selected state + */ + mw.rcfilters.dm.FilterGroup.prototype.getSelectedState = function () { + var state = {}; + + this.getItems().forEach( function ( filterItem ) { + state[ filterItem.getName() ] = filterItem.isSelected(); + } ); + + return state; + }; + /** * Get item by its filter name * @@ -759,7 +791,7 @@ */ mw.rcfilters.dm.FilterGroup.prototype.selectItemByParamName = function ( paramName ) { this.getItems().forEach( function ( item ) { - item.toggleSelected( item.getParamName() === paramName ); + item.toggleSelected( item.getParamName() === String( paramName ) ); } ); }; @@ -771,7 +803,7 @@ */ mw.rcfilters.dm.FilterGroup.prototype.getItemByParamName = function ( paramName ) { return this.getItems().filter( function ( item ) { - return item.getParamName() === paramName; + return item.getParamName() === String( paramName ); } )[ 0 ]; }; @@ -839,4 +871,22 @@ mw.rcfilters.dm.FilterGroup.prototype.isFullCoverage = function () { return this.fullCoverage; }; + + /** + * Check whether the group is defined as sticky default + * + * @return {boolean} Group is sticky default + */ + mw.rcfilters.dm.FilterGroup.prototype.isSticky = function () { + return this.sticky; + }; + + /** + * Check whether the group value is excluded from saved queries + * + * @return {boolean} Group value is excluded from saved queries + */ + mw.rcfilters.dm.FilterGroup.prototype.isExcludedFromSavedQueries = function () { + return this.excludedFromSavedQueries; + }; }( mediaWiki ) );