RCFilters: Have the model accept multiple views
authorMoriel Schottlender <moriel@gmail.com>
Wed, 21 Jun 2017 00:37:17 +0000 (17:37 -0700)
committerMoriel Schottlender <moriel@gmail.com>
Thu, 22 Jun 2017 18:31:57 +0000 (11:31 -0700)
Organize the way we create and process multiple views, to stop
hard-coding namespaces and tags.

Change-Id: Ib28c166052781b23a3b35c1b7b0af7d26f102e3f

resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js

index bec40b4..d3fb66e 100644 (file)
@@ -35,7 +35,7 @@
                this.name = name;
                this.type = config.type || 'send_unselected_if_any';
                this.view = config.view || 'default';
-               this.title = config.title;
+               this.title = config.title || name;
                this.separator = config.separator || '|';
                this.labelPrefixKey = config.labelPrefixKey;
 
index 6825fa4..ad0794e 100644 (file)
         * Set filters and preserve a group relationship based on
         * the definition given by an object
         *
-        * @param {Array} filters Filter group definition
-        * @param {Object} [namespaces] Namespace definition
-        * @param {Object[]} [tags] Tag array definition
+        * @param {Array} filters Filters definition
+        * @param {Object} [views] Extra views definition
+        *  Expected in the following format:
+        *  {
+        *     namespaces: {
+        *       label: 'namespaces', // Message key
+        *       trigger: ':',
+        *       groups: [
+        *         {
+        *            // Group info
+        *            name: 'namespaces' // Parameter name
+        *            definition: {
+        *               title: 'namespaces' // Message key
+        *               type: 'string_options',
+        *               separator: ';',
+        *               labelPrefixKey: { 'default': 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
+        *               fullCoverage: true
+        *            },
+        *            items: []
+        *         }
+        *       ]
+        *     }
+        *  }
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.initializeFilters = function ( filters, namespaces, tags ) {
+       mw.rcfilters.dm.FiltersViewModel.prototype.initializeFilters = function ( filters, views ) {
                var filterItem, filterConflictResult, groupConflictResult,
                        model = this,
                        items = [],
-                       namespaceDefinition = [],
                        groupConflictMap = {},
                        filterConflictMap = {},
                        /*!
                this.groups = {};
                this.views = {};
 
+               views = views || {};
+
                // Filters
                this.views.default = { name: 'default', label: mw.msg( 'rcfilters-filterlist-title' ) };
                filters.forEach( function ( data ) {
                        }
                } );
 
-               namespaces = namespaces || {};
-               if (
-                       mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ) &&
-                       !$.isEmptyObject( namespaces )
-               ) {
-                       // Namespaces group
-                       this.views.namespaces = { name: 'namespaces', label: mw.msg( 'namespaces' ), trigger: ':' };
-                       $.each( namespaces, function ( namespaceID, label ) {
-                               // Build and clean up the definition
-                               namespaceDefinition.push( {
-                                       name: namespaceID,
-                                       label: label || mw.msg( 'blanknamespace' ),
-                                       description: '',
-                                       identifiers: [
-                                               ( namespaceID < 0 || namespaceID % 2 === 0 ) ?
-                                                       'subject' : 'talk'
-                                       ],
-                                       cssClass: 'mw-changeslist-ns-' + namespaceID
-                               } );
-                       } );
-
-                       // Add the group
-                       model.groups.namespace = new mw.rcfilters.dm.FilterGroup(
-                               'namespace', // Parameter name is singular
-                               {
-                                       type: 'string_options',
-                                       view: 'namespaces',
-                                       title: 'namespaces', // Message key
-                                       separator: ';',
-                                       labelPrefixKey: { 'default': 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
-                                       fullCoverage: true
-                               }
-                       );
-                       // Add namespace items to group
-                       model.groups.namespace.initializeFilters( namespaceDefinition );
-                       items = items.concat( model.groups.namespace.getItems() );
-               }
+               if ( mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ) ) {
+                       $.each( views, function ( viewName, viewData ) {
+                               model.views[ viewName ] = {
+                                       name: viewData.name,
+                                       title: viewData.title,
+                                       trigger: viewData.trigger
+                               };
 
-               tags = tags || [];
-               if (
-                       mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ) &&
-                       tags.length > 0
-               ) {
-                       // Define view
-                       this.views.tags = { name: 'tags', label: mw.msg( 'rcfilters-view-tags' ), trigger: '#' };
-
-                       // Add the group
-                       model.groups.tagfilter = new mw.rcfilters.dm.FilterGroup(
-                               'tagfilter',
-                               {
-                                       type: 'string_options',
-                                       view: 'tags',
-                                       title: 'rcfilters-view-tags', // Message key
-                                       labelPrefixKey: 'rcfilters-tag-prefix-tags',
-                                       separator: '|',
-                                       fullCoverage: false
-                               }
-                       );
+                               // Group
+                               viewData.groups.forEach( function ( groupData ) {
+                                       model.groups[ groupData.name ] = new mw.rcfilters.dm.FilterGroup(
+                                               groupData.name,
+                                               $.extend( true, {}, groupData.definition, { view: viewName } )
+                                       );
 
-                       // Add tag items to group
-                       model.groups.tagfilter.initializeFilters( tags );
+                                       // Add items
+                                       model.groups[ groupData.name ].initializeFilters( groupData.items );
 
-                       // Add item references to the model, for lookup
-                       items = items.concat( model.groups.tagfilter.getItems() );
+                                       // Add to global search list
+                                       items = items.concat( model.groups[ groupData.name ].getItems() );
+                               } );
+                       } );
                }
 
                // Add item references to the model, for lookup
         * @return {string} Label for the current view
         */
        mw.rcfilters.dm.FiltersViewModel.prototype.getCurrentViewLabel = function () {
-               return this.views[ this.getCurrentView() ].label;
+               return this.views[ this.getCurrentView() ].title;
        };
 
        /**
index 95e11d5..23398e5 100644 (file)
         */
        mw.rcfilters.Controller.prototype.initialize = function ( filterStructure, namespaceStructure, tagList ) {
                var parsedSavedQueries,
+                       views = {},
+                       items = [],
                        uri = new mw.Uri(),
                        $changesList = $( '.mw-changeslist' ).first().contents();
 
+               // Prepare views
+               if ( namespaceStructure ) {
+                       items = [];
+                       $.each( namespaceStructure, function ( namespaceID, label ) {
+                               // Build and clean up the individual namespace items definition
+                               items.push( {
+                                       name: namespaceID,
+                                       label: label || mw.msg( 'blanknamespace' ),
+                                       description: '',
+                                       identifiers: [
+                                               ( namespaceID < 0 || namespaceID % 2 === 0 ) ?
+                                                       'subject' : 'talk'
+                                       ],
+                                       cssClass: 'mw-changeslist-ns-' + namespaceID
+                               } );
+                       } );
+
+                       views.namespaces = {
+                               title: mw.msg( 'namespaces' ),
+                               trigger: ':',
+                               groups: [ {
+                                       // Group definition (single group)
+                                       name: 'namespaces',
+                                       definition: {
+                                               type: 'string_options',
+                                               title: mw.msg( 'namespaces' ),
+                                               labelPrefixKey: { 'default': 'rcfilters-tag-prefix-namespace', inverted: 'rcfilters-tag-prefix-namespace-inverted' },
+                                               separator: ';',
+                                               fullCoverage: true
+                                       },
+                                       items: items
+                               } ]
+                       };
+               }
+               if ( tagList ) {
+                       views.tags = {
+                               title: mw.msg( 'rcfilters-view-tags' ),
+                               trigger: '#',
+                               groups: [ {
+                                       // Group definition (single group)
+                                       name: 'tagfilter', // Parameter name
+                                       definition: {
+                                               type: 'string_options',
+                                               title: 'rcfilters-view-tags', // Message key
+                                               labelPrefixKey: 'rcfilters-tag-prefix-tags',
+                                               separator: '|',
+                                               fullCoverage: false
+                                       },
+                                       items: tagList
+                               } ]
+                       };
+               }
+
                // Initialize the model
-               this.filtersModel.initializeFilters( filterStructure, namespaceStructure, tagList );
+               this.filtersModel.initializeFilters( filterStructure, views );
 
                this._buildBaseFilterState();
 
index 233ec76..13c8c3d 100644 (file)
                                        }
                                ]
                        } ],
-                       namespaces = {
-                               0: 'Main',
-                               1: 'Talk',
-                               2: 'User',
-                               3: 'User talk'
+                       views = {
+                               namespaces: {
+                                       label: 'Namespaces',
+                                       trigger: ':',
+                                       groups: [ {
+                                               name: 'namespace',
+                                               label: 'Namespaces',
+                                               separator: ';',
+                                               items: [
+                                                       { name: 0, label: 'Main' },
+                                                       { name: 1, label: 'Talk' },
+                                                       { name: 2, label: 'User' },
+                                                       { name: 3, label: 'User talk' }
+                                               ]
+                                       } ]
+                               }
                        },
                        model = new mw.rcfilters.dm.FiltersViewModel();
 
-               model.initializeFilters( definition, namespaces );
+               model.initializeFilters( definition, views );
 
                assert.ok(
                        model.getItemByName( 'group1__filter1' ) instanceof mw.rcfilters.dm.FilterItem &&
                                        }
                                ]
                        } ],
-                       namespaces = {
-                               0: 'Main',
-                               1: 'Talk',
-                               2: 'User',
-                               3: 'User talk'
+                       views = {
+                               namespaces: {
+                                       label: 'Namespaces',
+                                       trigger: ':',
+                                       groups: [ {
+                                               name: 'namespace',
+                                               label: 'Namespaces',
+                                               separator: ';',
+                                               items: [
+                                                       { name: 0, label: 'Main' },
+                                                       { name: 1, label: 'Talk' },
+                                                       { name: 2, label: 'User' },
+                                                       { name: 3, label: 'User talk' }
+                                               ]
+                                       } ]
+                               }
                        },
                        testCases = [
                                {
                                return result;
                        };
 
-               model.initializeFilters( definition, namespaces );
+               model.initializeFilters( definition, views );
 
                testCases.forEach( function ( testCase ) {
                        matches = model.findMatches( testCase.query );