--- /dev/null
+( function ( mw ) {
+ /**
+ * View model for a filter group
+ *
+ * @mixins OO.EventEmitter
+ * @mixins OO.EmitterList
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [type='send_unselected_if_any'] Group type
+ * @cfg {string} [title] Group title
+ * @cfg {string} [separator='|'] Value separator for 'string_options' groups
+ * @cfg {string} [exclusionType='default'] Group exclusion type
+ * @cfg {boolean} [active] Group is active
+ */
+ mw.rcfilters.dm.FilterGroup = function MwRcfiltersDmFilterGroup( config ) {
+ config = config || {};
+
+ // Mixin constructor
+ OO.EventEmitter.call( this );
+ OO.EmitterList.call( this );
+
+ this.type = config.type || 'send_unselected_if_any';
+ this.title = config.title;
+ this.separator = config.separator || '|';
+ this.exclusionType = config.exclusionType || 'default';
+ this.active = !!config.active;
+ };
+
+ /* Initialization */
+ OO.initClass( mw.rcfilters.dm.FilterGroup );
+ OO.mixinClass( mw.rcfilters.dm.FilterGroup, OO.EventEmitter );
+ OO.mixinClass( mw.rcfilters.dm.FilterGroup, OO.EmitterList );
+
+ /* Events */
+
+ /**
+ * @event update
+ *
+ * Group state has been updated
+ */
+
+ /* Methods */
+
+ /**
+ * Check the active status of the group and set it accordingly.
+ *
+ * @fires update
+ */
+ mw.rcfilters.dm.FilterGroup.prototype.checkActive = function () {
+ var active,
+ count = 0;
+
+ // Recheck group activity
+ this.getItems().forEach( function ( filterItem ) {
+ count += Number( filterItem.isSelected() );
+ } );
+
+ active = (
+ count > 0 &&
+ count < this.getItemCount()
+ );
+
+ if ( this.active !== active ) {
+ this.active = active;
+ this.emit( 'update' );
+ }
+ };
+
+ /**
+ * Get group active state
+ *
+ * @return {boolean} Active state
+ */
+ mw.rcfilters.dm.FilterGroup.prototype.isActive = function () {
+ return this.active;
+ };
+
+ /**
+ * Get group type
+ *
+ * @return {string} Group type
+ */
+ mw.rcfilters.dm.FilterGroup.prototype.getType = function () {
+ return this.type;
+ };
+
+ /**
+ * Get group's title
+ *
+ * @return {string} Title
+ */
+ mw.rcfilters.dm.FilterGroup.prototype.getTitle = function () {
+ return this.title;
+ };
+
+ /**
+ * Get group's values separator
+ *
+ * @return {string} Values separator
+ */
+ mw.rcfilters.dm.FilterGroup.prototype.getSeparator = function () {
+ return this.separator;
+ };
+
+ /**
+ * Get group exclusion type
+ *
+ * @return {string} Exclusion type
+ */
+ mw.rcfilters.dm.FilterGroup.prototype.getExclusionType = function () {
+ return this.exclusionType;
+ };
+}( mediaWiki ) );
// Reapply the active state of filters
this.reapplyActiveFilters( item );
+ // Recheck group activity state
+ this.getGroup( item.getGroup() ).checkActive();
+
this.emit( 'itemUpdate', item );
};
group = item.getGroup(),
model = this;
if (
- !this.groups[ group ].exclusionType ||
- this.groups[ group ].exclusionType === 'default'
+ !this.getGroup( group ).getExclusionType() ||
+ this.getGroup( group ).getExclusionType() === 'default'
) {
// Default behavior
// If any parameter is selected, but:
// - If the entire group is selected, all are inactive
// Check what's selected in the group
- selectedItemsCount = this.groups[ group ].filters.filter( function ( filterItem ) {
+ selectedItemsCount = this.getGroupFilters( group ).filter( function ( filterItem ) {
return filterItem.isSelected();
} ).length;
- this.groups[ group ].filters.forEach( function ( filterItem ) {
+ this.getGroupFilters( group ).forEach( function ( filterItem ) {
filterItem.toggleActive(
selectedItemsCount > 0 ?
// If some items are selected
(
- selectedItemsCount === model.groups[ group ].filters.length ?
+ selectedItemsCount === model.groups[ group ].getItemCount() ?
// If **all** items are selected, they're all inactive
false :
// If not all are selected, then the selected are active
true
);
} );
- } else if ( this.groups[ group ].exclusionType === 'explicit' ) {
+ } else if ( this.getGroup( group ).getExclusionType() === 'explicit' ) {
// Explicit behavior
// - Go over the list of excluded filters to change their
// active states accordingly
this.excludedByMap = {};
$.each( filters, function ( group, data ) {
- model.groups[ group ] = model.groups[ group ] || {};
- model.groups[ group ].filters = model.groups[ group ].filters || [];
-
- model.groups[ group ].title = data.title;
- model.groups[ group ].type = data.type;
- model.groups[ group ].separator = data.separator || '|';
- model.groups[ group ].exclusionType = data.exclusionType || 'default';
+ if ( !model.groups[ group ] ) {
+ model.groups[ group ] = new mw.rcfilters.dm.FilterGroup( {
+ type: data.type,
+ title: data.title,
+ separator: data.separator,
+ exclusionType: data.exclusionType
+ } );
+ }
selectedFilterNames = [];
for ( i = 0; i < data.filters.length; i++ ) {
selectedFilterNames.push( data.filters[ i ].name );
}
- model.groups[ group ].filters.push( filterItem );
+ model.groups[ group ].addItems( filterItem );
items.push( filterItem );
}
// Store the default parameter group state
// For this group, the parameter is group name and value is the names
// of selected items
- model.defaultParams[ group ] = model.sanitizeStringOptionGroup( group, selectedFilterNames ).join( model.groups[ group ].separator );
+ model.defaultParams[ group ] = model.sanitizeStringOptionGroup( group, selectedFilterNames ).join( model.groups[ group ].getSeparator() );
}
} );
};
/**
- * Get the object that defines groups and their filter items.
- * The structure of this response:
- * {
- * groupName: {
- * title: {string} Group title
- * type: {string} Group type
- * filters: {string[]} Filters in the group
- * }
- * }
+ * Get the object that defines groups by their name.
*
* @return {Object} Filter groups
*/
return this.groups;
};
- /**
- * Get the current state of the filters.
- *
- * Checks whether the filter group is active. This means at least one
- * filter is selected, but not all filters are selected.
- *
- * @param {string} groupName Group name
- * @return {boolean} Filter group is active
- */
- mw.rcfilters.dm.FiltersViewModel.prototype.isFilterGroupActive = function ( groupName ) {
- var count = 0,
- filters = this.groups[ groupName ].filters;
-
- filters.forEach( function ( filterItem ) {
- count += Number( filterItem.isSelected() );
- } );
-
- return (
- count > 0 &&
- count < filters.length
- );
- };
-
/**
* Update the representation of the parameters. These are the back-end
* parameters representing the filters, but they represent the given
result = {},
groupItems = filterGroups || this.getFilterGroups();
- $.each( groupItems, function ( group, data ) {
- filterItems = data.filters;
+ $.each( groupItems, function ( group, model ) {
+ filterItems = model.getItems();
- if ( data.type === 'send_unselected_if_any' ) {
+ if ( model.getType() === 'send_unselected_if_any' ) {
// First, check if any of the items are selected at all.
// If none is selected, we're treating it as if they are
// all false
result[ filterItems[ i ].getName() ] = anySelected ?
Number( !filterItems[ i ].isSelected() ) : 0;
}
- } else if ( data.type === 'string_options' ) {
+ } else if ( model.getType() === 'string_options' ) {
values = [];
for ( i = 0; i < filterItems.length; i++ ) {
if ( filterItems[ i ].isSelected() ) {
if ( values.length === 0 || values.length === filterItems.length ) {
result[ group ] = 'all';
} else {
- result[ group ] = values.join( data.separator );
+ result[ group ] = values.join( model.getSeparator() );
}
}
} );
*/
mw.rcfilters.dm.FiltersViewModel.prototype.sanitizeStringOptionGroup = function( groupName, valueArray ) {
var result = [],
- validNames = this.groups[ groupName ].filters.map( function ( filterItem ) {
+ validNames = this.getGroupFilters( groupName ).map( function ( filterItem ) {
return filterItem.getName();
} );
} else if ( model.groups.hasOwnProperty( paramName ) ) {
// This parameter represents a group (values are the filters)
// this is equivalent to checking if the group is 'string_options'
- groupMap[ paramName ] = { filters: model.groups[ paramName ].filters };
+ groupMap[ paramName ] = { filters: model.groups[ paramName ].getItems() };
}
} );
var paramValues, filterItem,
allItemsInGroup = data.filters;
- if ( model.groups[ group ].type === 'send_unselected_if_any' ) {
+ if ( model.groups[ group ].getType() === 'send_unselected_if_any' ) {
for ( i = 0; i < allItemsInGroup.length; i++ ) {
filterItem = allItemsInGroup[ i ];
// group, which means the state is false
false;
}
- } else if ( model.groups[ group ].type === 'string_options' ) {
- paramValues = model.sanitizeStringOptionGroup( group, params[ group ].split( model.groups[ group ].separator ) );
+ } else if ( model.groups[ group ].getType() === 'string_options' ) {
+ paramValues = model.sanitizeStringOptionGroup( group, params[ group ].split( model.groups[ group ].getSeparator() ) );
for ( i = 0; i < allItemsInGroup.length; i++ ) {
filterItem = allItemsInGroup[ i ];
// If it is the word 'all'
paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
// All values are written
- paramValues.length === model.groups[ group ].filters.length
+ paramValues.length === model.groups[ group ].getItemCount()
) ?
// All true (either because all values are written or the term 'all' is written)
// is the same as all filters set to false
}
};
+ /**
+ * Get a group model from its name
+ *
+ * @param {string} groupName Group name
+ * @return {mw.rcfilters.dm.FilterGroup} Group model
+ */
+ mw.rcfilters.dm.FiltersViewModel.prototype.getGroup = function ( groupName ) {
+ return this.groups[ groupName ];
+ };
+
+ /**
+ * Get all filters within a specified group by its name
+ *
+ * @param {string} groupName Group name
+ * @return {mw.rcfilters.dm.FilterItem[]} Filters belonging to this group
+ */
+ mw.rcfilters.dm.FiltersViewModel.prototype.getGroupFilters = function ( groupName ) {
+ return ( this.getGroup( groupName ) && this.getGroup( groupName ).getItems() ) || [];
+ };
+
/**
* Find items whose labels match the given string
*
* @mixins OO.ui.mixin.LabelElement
*
* @constructor
- * @param {string} name Group name
+ * @param {mw.rcfilters.Controller} controller Controller
+ * @param {mw.rcfilters.dm.FilterGroup} model Filter group model
* @param {Object} config Configuration object
*/
- mw.rcfilters.ui.FilterGroupWidget = function MwRcfiltersUiFilterGroupWidget( name, config ) {
+ mw.rcfilters.ui.FilterGroupWidget = function MwRcfiltersUiFilterGroupWidget( controller, model, config ) {
config = config || {};
// Parent
mw.rcfilters.ui.FilterGroupWidget.parent.call( this, config );
+
+ this.controller = controller;
+ this.model = model;
+
// Mixin constructors
OO.ui.mixin.GroupWidget.call( this, config );
OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, {
+ label: this.model.getTitle(),
$label: $( '<div>' )
.addClass( 'mw-rcfilters-ui-filterGroupWidget-title' )
} ) );
- this.name = name;
+ // Populate
+ this.populateFromModel();
+
+ this.model.connect( this, { update: 'onModelUpdate' } );
this.$element
.addClass( 'mw-rcfilters-ui-filterGroupWidget' )
OO.mixinClass( mw.rcfilters.ui.FilterGroupWidget, OO.ui.mixin.LabelElement );
/**
- * Get the group name
- *
- * @return {string} Group name
+ * Respond to model update event
*/
- mw.rcfilters.ui.FilterGroupWidget.prototype.getName = function () {
- return this.name;
+ mw.rcfilters.ui.FilterGroupWidget.prototype.onModelUpdate = function () {
+ this.$element.toggleClass(
+ 'mw-rcfilters-ui-filterGroupWidget-active',
+ this.model.isActive()
+ );
+ };
+
+ mw.rcfilters.ui.FilterGroupWidget.prototype.populateFromModel = function () {
+ var widget = this;
+
+ this.addItems(
+ this.model.getItems().map( function ( filterItem ) {
+ return new mw.rcfilters.ui.FilterItemWidget(
+ widget.controller,
+ filterItem,
+ {
+ label: filterItem.getLabel(),
+ description: filterItem.getDescription()
+ }
+ );
+ } )
+ );
};
/**
- * Toggle the active state of this group
+ * Get the group name
*
- * @param {boolean} isActive The group is active
+ * @return {string} Group name
*/
- mw.rcfilters.ui.FilterGroupWidget.prototype.toggleActiveState = function ( isActive ) {
- this.$element.toggleClass( 'mw-rcfilters-ui-filterGroupWidget-active', isActive );
+ mw.rcfilters.ui.FilterGroupWidget.prototype.getName = function () {
+ return this.model.getName();
};
-
}( mediaWiki, jQuery ) );