* @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 {boolean} [sticky] This group is 'sticky'. It is synchronized
+ * with a preference, does not participate in Saved Queries, and is
+ * not shown in the active filters area.
* @cfg {string} [title] Group title
* @cfg {boolean} [hidden] This group is hidden from the regular menu views
+ * and the active filters area.
* @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
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.sticky = !!config.sticky;
this.title = config.title || name;
this.hidden = !!config.hidden;
this.allowArbitrary = !!config.allowArbitrary;
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;
- };
-
/**
* Normalize a value given to this group. This is mostly for correcting
* arbitrary values for 'single option' groups, given by the user settings
this.groups = {};
this.defaultParams = {};
- this.defaultFiltersEmpty = null;
this.highlightEnabled = false;
this.parameterMap = {};
this.emptyParameterState = null;
/**
* Get a representation of the full parameter list, including all base values
*
- * @param {Object} [parameters] A given parameter state to minimize. If not given the current
- * state of the system will be used.
- * @param {boolean} [removeExcluded] Remove excluded and sticky parameters
* @return {Object} Full parameter representation
*/
- mw.rcfilters.dm.FiltersViewModel.prototype.getExpandedParamRepresentation = function ( parameters, removeExcluded ) {
- var result = {};
-
- parameters = parameters ? $.extend( true, {}, parameters ) : this.getCurrentParameterState();
-
- result = $.extend(
+ mw.rcfilters.dm.FiltersViewModel.prototype.getExpandedParamRepresentation = function () {
+ return $.extend(
true,
{},
this.getEmptyParameterState(),
- parameters
+ this.getCurrentParameterState()
);
-
- if ( removeExcluded ) {
- result = this.removeExcludedParams( result );
- }
-
- return result;
};
/**
* Get a parameter representation of the current state of the model
*
- * @param {boolean} [removeExcludedParams] Remove excluded filters from final result
+ * @param {boolean} [removeStickyParams] Remove sticky filters from final result
* @return {Object} Parameter representation of the current state of the model
*/
- mw.rcfilters.dm.FiltersViewModel.prototype.getCurrentParameterState = function ( removeExcludedParams ) {
- var excludedParams,
- state = this.getMinimizedParamRepresentation( $.extend(
- true,
- {},
- this.getParametersFromFilters( this.getSelectedState() ),
- this.getHighlightParameters()
- ) );
-
- if ( removeExcludedParams ) {
- excludedParams = this.getExcludedParams();
- // Delete all excluded filters
- $.each( state, function ( param ) {
- if ( excludedParams.indexOf( param ) > -1 ) {
- delete state[ param ];
- }
- } );
+ mw.rcfilters.dm.FiltersViewModel.prototype.getCurrentParameterState = function ( removeStickyParams ) {
+ var state = this.getMinimizedParamRepresentation( $.extend(
+ true,
+ {},
+ this.getParametersFromFilters( this.getSelectedState() ),
+ this.getHighlightParameters()
+ ) );
+
+ if ( removeStickyParams ) {
+ state = this.removeStickyParams( state );
}
return state;
};
/**
- * Delete excluded and sticky filters from given object. If object isn't given, output
- * the current filter state without the excluded values
+ * Delete sticky parameters from given object.
*
- * @param {Object} [filterState] Filter state
- * @return {Object} Filter state without excluded filters
+ * @param {Object} paramState Parameter state
+ * @return {Object} Parameter state without sticky parameters
*/
- mw.rcfilters.dm.FiltersViewModel.prototype.removeExcludedFilters = function ( filterState ) {
- filterState = filterState !== undefined ?
- $.extend( true, {}, filterState ) :
- this.getFiltersFromParameters();
-
- // Remove excluded filters
- Object.keys( this.getExcludedFiltersState() ).forEach( function ( filterName ) {
- delete filterState[ filterName ];
- } );
-
- // Remove sticky filters
- Object.keys( this.getStickyFiltersState() ).forEach( function ( filterName ) {
- delete filterState[ filterName ];
- } );
-
- return filterState;
- };
- /**
- * Delete excluded and sticky parameters from given object. If object isn't given, output
- * the current param state without the excluded values
- *
- * @param {Object} [paramState] Parameter state
- * @return {Object} Parameter state without excluded filters
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.removeExcludedParams = function ( paramState ) {
- paramState = paramState !== undefined ?
- $.extend( true, {}, paramState ) :
- this.getCurrentParameterState();
-
- // Remove excluded filters
- this.getExcludedParams().forEach( function ( paramName ) {
- delete paramState[ paramName ];
- } );
-
- // Remove sticky filters
+ mw.rcfilters.dm.FiltersViewModel.prototype.removeStickyParams = function ( paramState ) {
this.getStickyParams().forEach( function ( paramName ) {
delete paramState[ paramName ];
} );
return paramState;
};
- /**
- * Get the names of all available filters
- *
- * @return {string[]} An array of filter names
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.getFilterNames = function () {
- return this.getItems().map( function ( item ) { return item.getName(); } );
- };
-
/**
* Turn the highlight feature on or off
*/
mw.rcfilters.dm.FiltersViewModel.prototype.getViewTrigger = function ( view ) {
return ( this.views[ view ] && this.views[ view ].trigger ) || '';
};
+
/**
* Get the value of a specific parameter
*
/**
* Get the current selected state of the filters
*
+ * @param {boolean} onlySelected return an object containing only the selected filters
* @return {Object} Filters selected state
*/
- mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedState = function () {
+ mw.rcfilters.dm.FiltersViewModel.prototype.getSelectedState = function ( onlySelected ) {
var i,
items = this.getItems(),
result = {};
for ( i = 0; i < items.length; i++ ) {
- result[ items[ i ].getName() ] = items[ i ].isSelected();
+ if ( !onlySelected || items[ i ].isSelected() ) {
+ result[ items[ i ].getName() ] = items[ i ].isSelected();
+ }
}
return result;
/**
* Get an object representing default parameters state
*
- * @param {boolean} [excludeHiddenParams] Exclude hidden and sticky params
* @return {Object} Default parameter values
*/
- mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultParams = function ( excludeHiddenParams ) {
- var result = {};
-
- // Get default filter state
- $.each( this.groups, function ( name, model ) {
- $.extend( true, result, model.getDefaultParams() );
- } );
-
- if ( excludeHiddenParams ) {
- Object.keys( this.getDefaultHiddenParams() ).forEach( function ( paramName ) {
- delete result[ paramName ];
- } );
- }
-
- return result;
- };
-
- /**
- * Get an object representing defaults for the hidden parameters state
- *
- * @return {Object} Default values for hidden parameters
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultHiddenParams = function () {
+ mw.rcfilters.dm.FiltersViewModel.prototype.getDefaultParams = function () {
var result = {};
// Get default filter state
$.each( this.groups, function ( name, model ) {
- if ( model.isHidden() ) {
+ if ( !model.isSticky() ) {
$.extend( true, result, model.getDefaultParams() );
}
} );
return result;
};
- /**
- * Get a filter representation of all sticky parameters
- *
- * @return {Object} Sticky filters values
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.getStickyFiltersState = function () {
- var result = {};
-
- $.each( this.groups, function ( name, model ) {
- if ( model.isSticky() ) {
- $.extend( true, result, model.getSelectedState() );
- }
- } );
-
- return result;
- };
-
- /**
- * Get a filter representation of all parameters that are marked
- * as being excluded from saved query.
- *
- * @return {Object} Excluded filters values
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.getExcludedFiltersState = function () {
- var result = {};
-
- $.each( this.groups, function ( name, model ) {
- if ( model.isExcludedFromSavedQueries() ) {
- $.extend( true, result, model.getSelectedState() );
- }
- } );
-
- return result;
- };
-
- /**
- * Get the parameter names that represent filters that are excluded
- * from saved queries.
- *
- * @return {string[]} Parameter names
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.getExcludedParams = function () {
- var result = [];
-
- $.each( this.groups, function ( name, model ) {
- if ( model.isExcludedFromSavedQueries() ) {
- if ( model.isPerGroupRequestParameter() ) {
- result.push( name );
- } else {
- // Each filter is its own param
- result = result.concat( model.getItems().map( function ( filterItem ) {
- return filterItem.getParamName();
- } ) );
- }
- }
- } );
-
- return result;
- };
-
/**
* Analyze the groups and their filters and output an object representing
* the state of the parameters they represent.
};
/**
- * Check whether the current filter state is set to all false.
+ * Check whether no visible filter is selected.
+ *
+ * Filter groups that are hidden or sticky are not shown in the
+ * active filters area and therefore not included in this check.
*
- * @return {boolean} Current filters are all empty
+ * @return {boolean} No visible filter is selected
*/
- mw.rcfilters.dm.FiltersViewModel.prototype.areCurrentFiltersEmpty = function () {
+ mw.rcfilters.dm.FiltersViewModel.prototype.areVisibleFiltersEmpty = function () {
// Check if there are either any selected items or any items
// that have highlight enabled
return !this.getItems().some( function ( filterItem ) {
- return !filterItem.getGroupModel().isHidden() && ( filterItem.isSelected() || filterItem.isHighlighted() );
+ var visible = !filterItem.getGroupModel().isSticky() && !filterItem.getGroupModel().isHidden(),
+ active = ( filterItem.isSelected() || filterItem.isHighlighted() );
+ return visible && active;
} );
};
return allSelected;
};
+
/**
* Switch the current view
*
return this.views[ viewName ] && this.views[ viewName ].title;
};
- /**
- * Get an array of all available view names
- *
- * @return {string} Available view names
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.getAvailableViews = function () {
- return Object.keys( this.views );
- };
-
/**
* Get the view that fits the given trigger
*
isDefault = String( savedQueries.default ) === String( id );
if ( normalizedData && normalizedData.params ) {
- // Backwards-compat fix: Remove excluded parameters from
+ // Backwards-compat fix: Remove sticky parameters from
// the given data, if they exist
- normalizedData.params = model.filtersModel.removeExcludedParams( normalizedData.params );
+ normalizedData.params = model.filtersModel.removeStickyParams( normalizedData.params );
// Correct the invert state for effective selection
if ( normalizedData.params.invert && !normalizedData.params.namespaces ) {
/**
* Get the full data representation of the default query, if it exists
*
- * @param {boolean} [excludeHiddenParams] Exclude hidden parameters in the result
* @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 ( excludeHiddenParams ) {
- var data = ( !mw.user.isAnon() && this.getItemParams( this.getDefault() ) ) || {};
-
- if ( excludeHiddenParams ) {
- Object.keys( this.filtersModel.getDefaultHiddenParams() ).forEach( function ( paramName ) {
- delete data[ paramName ];
- } );
- }
-
- return data;
+ mw.rcfilters.dm.SavedQueriesModel.prototype.getDefaultParams = function () {
+ return ( !mw.user.isAnon() && this.getItemParams( this.getDefault() ) ) || {};
};
/**
* @return {Object} Full param representation
*/
mw.rcfilters.dm.SavedQueriesModel.prototype.buildParamsFromData = function ( data ) {
- // Merge saved filter state with sticky filter values
- var savedFilters;
-
data = data || {};
-
- // In order to merge sticky filters with the data, we have to
- // transform this to filters first, merge, and then back to
- // parameters
- savedFilters = $.extend(
- true, {},
- this.filtersModel.getFiltersFromParameters( data.params ),
- this.filtersModel.getStickyFiltersState()
- );
-
// Return parameter representation
return this.filtersModel.getMinimizedParamRepresentation( $.extend( true, {},
- this.filtersModel.getParametersFromFilters( savedFilters ),
+ data.params,
data.highlights
) );
};
},
sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
'default': mw.user.options.get( this.limitPreferenceName, displayConfig.limitDefault ),
- isSticky: true,
- excludedFromSavedQueries: true,
+ sticky: true,
filters: displayConfig.limitArray.map( function ( num ) {
return controller._createFilterDataFromNumber( num, num );
} )
Number( i );
},
'default': mw.user.options.get( this.daysPreferenceName, displayConfig.daysDefault ),
- isSticky: true,
- excludedFromSavedQueries: true,
+ sticky: true,
filters: [
// Hours (1, 2, 6, 12)
0.04166, 0.0833, 0.25, 0.5
type: 'boolean',
title: '', // Because it's a hidden group, this title actually appears nowhere
hidden: true,
- isSticky: true,
+ sticky: true,
filters: [
{
name: 'enhanced',
* @return {boolean} Defaults are all false
*/
mw.rcfilters.Controller.prototype.areDefaultsEmpty = function () {
- return $.isEmptyObject( this._getDefaultParams( true ) );
+ return $.isEmptyObject( this._getDefaultParams() );
};
/**
* Get an object representing the default parameter state, whether
* it is from the model defaults or from the saved queries.
*
- * @param {boolean} [excludeHiddenParams] Exclude hidden and sticky params
* @return {Object} Default parameters
*/
- mw.rcfilters.Controller.prototype._getDefaultParams = function ( excludeHiddenParams ) {
+ mw.rcfilters.Controller.prototype._getDefaultParams = function () {
if ( this.savedQueriesModel.getDefault() ) {
- return this.savedQueriesModel.getDefaultParams( excludeHiddenParams );
+ return this.savedQueriesModel.getDefaultParams();
} else {
- return this.filtersModel.getDefaultParams( excludeHiddenParams );
+ return this.filtersModel.getDefaultParams();
}
};
// wiki default.
// Any subsequent change of the URL through the RCFilters
// system will receive 'urlversion=2'
- var hiddenParamDefaults = this.filtersModel.getDefaultHiddenParams(),
- base = this.getVersion( uriQuery ) === 2 ?
- {} :
- this.filtersModel.getDefaultParams();
+ var base = this.getVersion( uriQuery ) === 2 ?
+ {} :
+ this.filtersModel.getDefaultParams();
return $.extend(
true,
{},
this.filtersModel.getMinimizedParamRepresentation(
- $.extend( true, {}, hiddenParamDefaults, base, uriQuery )
+ $.extend( true, {}, base, uriQuery )
),
{ urlversion: '2' }
);
* Respond to click event on the reset button
*/
mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onResetButtonClick = function () {
- if ( this.model.areCurrentFiltersEmpty() ) {
+ if ( this.model.areVisibleFiltersEmpty() ) {
// Reset to default filters
this.controller.resetToDefaults();
} else {
*/
mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.reevaluateResetRestoreState = function () {
var defaultsAreEmpty = this.controller.areDefaultsEmpty(),
- currFiltersAreEmpty = this.model.areCurrentFiltersEmpty(),
+ currFiltersAreEmpty = this.model.areVisibleFiltersEmpty(),
hideResetButton = currFiltersAreEmpty && defaultsAreEmpty;
this.resetButton.setIcon(
{ name: 'filter5', cssClass: 'filter5class' },
{ name: 'filter6' } // Not supporting highlights
]
+ }, {
+ name: 'group4',
+ title: 'Group 4',
+ type: 'boolean',
+ sticky: true,
+ filters: [
+ { name: 'stickyFilter7', cssClass: 'filter7class' },
+ { name: 'stickyFilter8', cssClass: 'filter8class' }
+ ]
} ],
minimalDefaultParams = {
filter1: '1',
name: 'group2',
type: 'send_unselected_if_any',
fullCoverage: true,
- excludedFromSavedQueries: true,
conflicts: [ { group: 'group1', filter: 'filter1' } ],
filters: [
{ name: 'filter4', label: 'group2filter4-label', description: 'group2filter4-desc', cssClass: 'filter4class' },
}, {
name: 'group4',
type: 'single_option',
+ hidden: true,
default: 'option2',
filters: [
// NOTE: The entire group has no highlight supported
}, {
name: 'group6',
type: 'boolean',
- isSticky: true,
+ sticky: true,
filters: [
{ name: 'group6option1', label: 'group6option1-label', description: 'group6option1-desc', cssClass: 'group6opt1class' },
{ name: 'group6option2', label: 'group6option2-label', description: 'group6option2-desc', default: true, cssClass: 'group6opt2class' },
}, {
name: 'group7',
type: 'single_option',
- isSticky: true,
+ sticky: true,
default: 'group7option2',
filters: [
{ name: 'group7option1', label: 'group7option1-label', description: 'group7option1-desc', cssClass: 'group7opt1class' },
{ name: 'group7option3', label: 'group7option3-label', description: 'group7option3-desc', cssClass: 'group7opt3class' }
]
} ],
+ shortFilterDefinition = [ {
+ name: 'group1',
+ type: 'send_unselected_if_any',
+ filters: [ { name: 'filter1' }, { name: 'filter2' } ]
+ }, {
+ name: 'group2',
+ type: 'boolean',
+ hidden: true,
+ filters: [ { name: 'filter3' }, { name: 'filter4' } ]
+ }, {
+ name: 'group3',
+ type: 'string_options',
+ sticky: true,
+ default: 'filter6',
+ filters: [ { name: 'filter5' }, { name: 'filter6' }, { name: 'filter7' } ]
+ } ],
viewsDefinition = {
namespaces: {
label: 'Namespaces',
group3: 'filter8',
group4: 'option2',
group5: 'option1',
- group6option1: '0',
- group6option2: '1',
- group6option3: '1',
- group7: 'group7option2',
namespace: ''
},
baseParamRepresentation = {
assert.deepEqual(
model.getDefaultParams(),
defaultParameters,
- 'Default parameters are stored properly per filter and group'
- );
-
- // Change sticky filter
- model.toggleFiltersSelected( {
- group7__group7option1: true
- } );
-
- // Make sure defaults have changed
- assert.deepEqual(
- model.getDefaultParams(),
- $.extend( true, {}, defaultParameters, {
- group7: 'group7option1'
- } ),
- 'Default parameters are stored properly per filter and group'
+ 'Default parameters are stored properly per filter and group (sticky groups are ignored)'
);
} );
{
input: {
filter1: '1', // Regular (do not strip)
- group6option1: '1', // Sticky
- filter4: '1', // Excluded
- filter5: '0' // Excluded
+ group6option1: '1' // Sticky
},
result: { filter1: '1' },
- msg: 'Valid input strips all sticky and excluded params regardless of value'
+ msg: 'Valid input strips all sticky params regardless of value'
}
];
cases.forEach( function ( test ) {
assert.deepEqual(
- model.removeExcludedParams( test.input ),
+ model.removeStickyParams( test.input ),
test.result,
test.msg
);
'Items without a specified class identifier are not highlighted.'
);
} );
+
+ QUnit.test( 'emptyAllFilters', function ( assert ) {
+ var model = new mw.rcfilters.dm.FiltersViewModel();
+
+ model.initializeFilters( shortFilterDefinition, null );
+
+ model.toggleFiltersSelected( {
+ group1__filter1: true,
+ group2__filter4: true, // hidden
+ group3__filter5: true // sticky
+ } );
+
+ model.emptyAllFilters();
+
+ assert.deepEqual(
+ model.getSelectedState( true ),
+ {
+ group3__filter5: true,
+ group3__filter6: true
+ },
+ 'Emptying filters does not affect sticky filters'
+ );
+ } );
+
+ QUnit.test( 'areVisibleFiltersEmpty', function ( assert ) {
+ var model = new mw.rcfilters.dm.FiltersViewModel();
+ model.initializeFilters( shortFilterDefinition, null );
+
+ model.emptyAllFilters();
+ assert.ok( model.areVisibleFiltersEmpty() );
+
+ model.toggleFiltersSelected( {
+ group3__filter5: true // sticky
+ } );
+ assert.ok( model.areVisibleFiltersEmpty() );
+
+ model.toggleFiltersSelected( {
+ group1__filter1: true
+ } );
+ assert.notOk( model.areVisibleFiltersEmpty() );
+ } );
}( mediaWiki, jQuery ) );
}, {
name: 'group3',
type: 'boolean',
- isSticky: true,
+ sticky: true,
filters: [
{ name: 'group3option1', cssClass: 'filter1class' },
{ name: 'group3option2', cssClass: 'filter1class' },