Merge "Add 3D filetype for STL files"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / dm / mw.rcfilters.dm.FilterGroup.js
index 63e13fd..bec40b4 100644 (file)
@@ -9,11 +9,16 @@
         * @param {string} name Group name
         * @param {Object} [config] Configuration options
         * @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 {string} [title] Group title
         * @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
         * @cfg {Object} [conflicts] Defines the conflicts for this filter group
+        * @cfg {string|Object} [labelPrefixKey] An i18n key defining the prefix label for this
+        *  group. If the prefix has 'invert' state, the parameter is expected to be an object
+        *  with 'default' and 'inverted' as keys.
         * @cfg {Object} [whatsThis] Defines the messages that should appear for the 'what's this' popup
         * @cfg {string} [whatsThis.header] The header of the whatsThis popup message
         * @cfg {string} [whatsThis.body] The body of the whatsThis popup message
 
                this.name = name;
                this.type = config.type || 'send_unselected_if_any';
+               this.view = config.view || 'default';
                this.title = config.title;
                this.separator = config.separator || '|';
+               this.labelPrefixKey = config.labelPrefixKey;
 
                this.active = !!config.active;
                this.fullCoverage = !!config.fullCoverage;
@@ -38,6 +45,7 @@
                this.whatsThis = config.whatsThis || {};
 
                this.conflicts = config.conflicts || {};
+               this.defaultParams = {};
 
                this.aggregate( { update: 'filterItemUpdate' } );
                this.connect( this, { filterItemUpdate: 'onFilterItemUpdate' } );
 
        /* Methods */
 
+       /**
+        * Initialize the group and create its filter items
+        *
+        * @param {Object} filterDefinition Filter definition for this group
+        * @param {string|Object} [groupDefault] Definition of the group default
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.initializeFilters = function ( filterDefinition, groupDefault ) {
+               var supersetMap = {},
+                       model = this,
+                       items = [];
+
+               filterDefinition.forEach( function ( filter ) {
+                       // Instantiate an item
+                       var subsetNames = [],
+                               filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, {
+                                       group: model.getName(),
+                                       label: filter.label || filter.name,
+                                       description: filter.description || '',
+                                       labelPrefixKey: model.labelPrefixKey,
+                                       cssClass: filter.cssClass,
+                                       identifiers: filter.identifiers
+                               } );
+
+                       filter.subset = filter.subset || [];
+                       filter.subset = filter.subset.map( function ( el ) {
+                               return el.filter;
+                       } );
+
+                       if ( filter.subset ) {
+                               subsetNames = [];
+                               filter.subset.forEach( function ( subsetFilterName ) { // eslint-disable-line no-loop-func
+                                       // Subsets (unlike conflicts) are always inside the same group
+                                       // We can re-map the names of the filters we are getting from
+                                       // the subsets with the group prefix
+                                       var subsetName = model.getPrefixedName( subsetFilterName );
+                                       // For convenience, we should store each filter's "supersets" -- these are
+                                       // the filters that have that item in their subset list. This will just
+                                       // make it easier to go through whether the item has any other items
+                                       // that affect it (and are selected) at any given time
+                                       supersetMap[ subsetName ] = supersetMap[ subsetName ] || [];
+                                       mw.rcfilters.utils.addArrayElementsUnique(
+                                               supersetMap[ subsetName ],
+                                               filterItem.getName()
+                                       );
+
+                                       // Translate subset param name to add the group name, so we
+                                       // get consistent naming. We know that subsets are only within
+                                       // the same group
+                                       subsetNames.push( subsetName );
+                               } );
+
+                               // Set translated subset
+                               filterItem.setSubset( subsetNames );
+                       }
+
+                       items.push( filterItem );
+
+                       // Store default parameter state; in this case, default is defined per filter
+                       if ( model.getType() === 'send_unselected_if_any' ) {
+                               // 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 ) );
+                       }
+               } );
+
+               // Add items
+               this.addItems( items );
+
+               // Now that we have all items, we can apply the superset map
+               this.getItems().forEach( function ( filterItem ) {
+                       filterItem.setSuperset( supersetMap[ filterItem.getName() ] );
+               } );
+
+               // Store default parameter state; in this case, default is defined per the
+               // entire group, given by groupDefault method parameter
+               if ( this.getType() === 'string_options' ) {
+                       // Store the default parameter group state
+                       // For this group, the parameter is group name and value is the names
+                       // of selected items
+                       this.defaultParams[ this.getName() ] = mw.rcfilters.utils.normalizeParamOptions(
+                               // Current values
+                               groupDefault ?
+                                       groupDefault.split( this.getSeparator() ) :
+                                       [],
+                               // Legal values
+                               this.getItems().map( function ( item ) {
+                                       return item.getParamName();
+                               } )
+                       ).join( this.getSeparator() );
+               }
+       };
+
        /**
         * Respond to filterItem update event
         *
                return this.name;
        };
 
+       /**
+        * Get the default param state of this group
+        *
+        * @return {Object} Default param state
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getDefaultParams = function () {
+               return this.defaultParams;
+       };
+
        /**
         * Get the messags defining the 'whats this' popup for this group
         *
                this.conflicts = conflicts;
        };
 
+       /**
+        * Set conflicts for each filter item in the group based on the
+        * given conflict map
+        *
+        * @param {Object} conflicts Object representing the conflict map,
+        *  keyed by the item name, where its value is an object for all its conflicts
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.setFilterConflicts = function ( conflicts ) {
+               this.getItems().forEach( function ( filterItem ) {
+                       if ( conflicts[ filterItem.getName() ] ) {
+                               filterItem.setConflicts( conflicts[ filterItem.getName() ] );
+                       }
+               } );
+       };
+
        /**
         * Check whether this item has a potential conflict with the given item
         *
                        // Go over the items and define the correct values
                        $.each( filterRepresentation, function ( name, value ) {
                                result[ filterParamNames[ name ] ] = areAnySelected ?
-                                       Number( !value ) : 0;
+                                       // We must store all parameter values as strings '0' or '1'
+                                       String( Number( !value ) ) :
+                                       '0';
                        } );
                } else if ( this.getType() === 'string_options' ) {
                        values = [];
                return result;
        };
 
+       /**
+        * Get the filter representation this group would provide
+        * based on given parameter states.
+        *
+        * @param {Object|string} [paramRepresentation] An object defining a parameter
+        *  state to translate the filter state from. If not given, an object
+        *  representing all filters as falsey is returned; same as if the parameter
+        *  given were an empty object, or had some of the filters missing.
+        * @return {Object} Filter representation
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) {
+               var areAnySelected, paramValues,
+                       model = this,
+                       paramToFilterMap = {},
+                       result = {};
+
+               if ( this.getType() === 'send_unselected_if_any' ) {
+                       paramRepresentation = paramRepresentation || {};
+                       // Expand param representation to include all filters in the group
+                       this.getItems().forEach( function ( filterItem ) {
+                               var paramName = filterItem.getParamName();
+
+                               paramRepresentation[ paramName ] = paramRepresentation[ paramName ] || '0';
+                               paramToFilterMap[ paramName ] = filterItem;
+
+                               if ( Number( paramRepresentation[ filterItem.getParamName() ] ) ) {
+                                       areAnySelected = true;
+                               }
+                       } );
+
+                       $.each( paramRepresentation, function ( paramName, paramValue ) {
+                               var filterItem = paramToFilterMap[ paramName ];
+
+                               result[ filterItem.getName() ] = areAnySelected ?
+                                       // Flip the definition between the parameter
+                                       // state and the filter state
+                                       // This is what the 'toggleSelected' value of the filter is
+                                       !Number( paramValue ) :
+                                       // Otherwise, there are no selected items in the
+                                       // group, which means the state is false
+                                       false;
+                       } );
+               } else if ( this.getType() === 'string_options' ) {
+                       paramRepresentation = paramRepresentation || '';
+
+                       // Normalize the given parameter values
+                       paramValues = mw.rcfilters.utils.normalizeParamOptions(
+                               // Given
+                               paramRepresentation.split(
+                                       this.getSeparator()
+                               ),
+                               // Allowed values
+                               this.getItems().map( function ( filterItem ) {
+                                       return filterItem.getParamName();
+                               } )
+                       );
+                       // Translate the parameter values into a filter selection state
+                       this.getItems().forEach( function ( filterItem ) {
+                               result[ filterItem.getName() ] = (
+                                               // If it is the word 'all'
+                                               paramValues.length === 1 && paramValues[ 0 ] === 'all' ||
+                                               // All values are written
+                                               paramValues.length === model.getItemCount()
+                                       ) ?
+                                       // All true (either because all values are written or the term 'all' is written)
+                                       // is the same as all filters set to true
+                                       true :
+                                       // Otherwise, the filter is selected only if it appears in the parameter values
+                                       paramValues.indexOf( filterItem.getParamName() ) > -1;
+                       } );
+               }
+
+               // 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 ) {
+                       result[ filterItem.getName() ] = !!result[ filterItem.getName() ];
+               } );
+
+               return result;
+       };
+
+       /**
+        * Get item by its parameter name
+        *
+        * @param {string} paramName Parameter name
+        * @return {mw.rcfilters.dm.FilterItem} Filter item
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getItemByParamName = function ( paramName ) {
+               return this.getItems().filter( function ( item ) {
+                       return item.getParamName() === paramName;
+               } )[ 0 ];
+       };
+
        /**
         * Get group type
         *
        };
 
        /**
-        * Get the prefix used for the filter names inside this group
+        * Get display group
+        *
+        * @return {string} Display group
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getView = function () {
+               return this.view;
+       };
+
+       /**
+        * Get the prefix used for the filter names inside this group.
         *
+        * @param {string} [name] Filter name to prefix
         * @return {string} Group prefix
         */
        mw.rcfilters.dm.FilterGroup.prototype.getNamePrefix = function () {
                return this.getName() + '__';
        };
 
+       /**
+        * Get a filter name with the prefix used for the filter names inside this group.
+        *
+        * @param {string} name Filter name to prefix
+        * @return {string} Group prefix
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getPrefixedName = function ( name ) {
+               return this.getNamePrefix() + name;
+       };
+
        /**
         * Get group's title
         *