Merge "RCFilters: Only selectively scroll to popup when it's focused"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / ui / mw.rcfilters.ui.FilterTagMultiselectWidget.js
index 5c5a2d2..df3263a 100644 (file)
@@ -2,6 +2,7 @@
        /**
         * List displaying all filter groups
         *
+        * @class
         * @extends OO.ui.MenuTagMultiselectWidget
         * @mixins OO.ui.mixin.PendingElement
         *
@@ -14,7 +15,6 @@
         */
        mw.rcfilters.ui.FilterTagMultiselectWidget = function MwRcfiltersUiFilterTagMultiselectWidget( controller, model, savedQueriesModel, config ) {
                var rcFiltersRow,
-                       areSavedQueriesEnabled = mw.config.get( 'wgStructuredChangeFiltersEnableSaving' ),
                        title = new OO.ui.LabelWidget( {
                                label: mw.msg( 'rcfilters-activefilters' ),
                                classes: [ 'mw-rcfilters-ui-filterTagMultiselectWidget-wrapper-content-title' ]
@@ -29,7 +29,7 @@
                this.queriesModel = savedQueriesModel;
                this.$overlay = config.$overlay || this.$element;
                this.matchingQuery = null;
-               this.areSavedQueriesEnabled = areSavedQueriesEnabled;
+               this.currentView = this.model.getCurrentView();
 
                // Parent
                mw.rcfilters.ui.FilterTagMultiselectWidget.parent.call( this, $.extend( true, {
@@ -85,7 +85,7 @@
                        classes: [ 'mw-rcfilters-ui-filterTagMultiselectWidget-resetButton' ]
                } );
 
-               if ( areSavedQueriesEnabled ) {
+               if ( !mw.user.isAnon() ) {
                        this.saveQueryButton = new mw.rcfilters.ui.SaveFiltersPopupButtonWidget(
                                this.controller,
                                this.queriesModel
                                click: 'onSaveQueryButtonClick',
                                saveCurrent: 'setSavedQueryVisibility'
                        } );
+                       this.queriesModel.connect( this, {
+                               itemUpdate: 'onSavedQueriesItemUpdate',
+                               initialize: 'onSavedQueriesInitialize'
+                       } );
                }
 
                this.emptyFilterMessage = new OO.ui.LabelWidget( {
                        highlightChange: 'onModelHighlightChange'
                } );
                this.input.connect( this, { change: 'onInputChange' } );
-               this.queriesModel.connect( this, { itemUpdate: 'onSavedQueriesItemUpdate' } );
 
                // The filter list and button should appear side by side regardless of how
                // wide the button is; the button also changes its width depending
                                        .addClass( 'mw-rcfilters-ui-filterTagMultiselectWidget-cell-filters' )
                        );
 
-               if ( areSavedQueriesEnabled ) {
+               if ( !mw.user.isAnon() ) {
                        rcFiltersRow.append(
                                $( '<div>' )
                                        .addClass( 'mw-rcfilters-ui-cell' )
                this.viewsSelectWidget = new OO.ui.ButtonSelectWidget( {
                        classes: [ 'mw-rcfilters-ui-filterTagMultiselectWidget-views-select-widget' ],
                        items: [
+                               new OO.ui.ButtonOptionWidget( {
+                                       framed: false,
+                                       data: '',
+                                       disabled: true,
+                                       classes: [ 'mw-rcfilters-ui-filterTagMultiselectWidget-views-select-widget-label' ],
+                                       label: mw.msg( 'rcfilters-view-advanced-filters-label' )
+                               } ),
                                new OO.ui.ButtonOptionWidget( {
                                        framed: false,
                                        data: 'namespaces',
         * @param {string} value Value of the input
         */
        mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onInputChange = function ( value ) {
-               var view = this.model.getViewByTrigger( value.substr( 0, 1 ) );
+               var view;
+
+               value = value.trim();
+
+               view = this.model.getViewByTrigger( value.substr( 0, 1 ) );
 
                this.controller.switchView( view );
        };
                this.getMenu().toggle( false );
        };
 
+       /**
+        * Respond to save query model initialization
+        */
+       mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onSavedQueriesInitialize = function () {
+               this.setSavedQueryVisibility();
+       };
+
        /**
         * Respond to save query item change. Mainly this is done to update the label in case
         * a query item has been edited
 
                        // Clear input if the only thing in the input is the prefix
                        if (
-                               this.input.getValue() === this.model.getViewTrigger( this.model.getCurrentView() )
+                               this.input.getValue().trim() === this.model.getViewTrigger( this.model.getCurrentView() )
                        ) {
                                // Clear the input
                                this.input.setValue( '' );
                // Parent
                mw.rcfilters.ui.FilterTagMultiselectWidget.parent.prototype.onInputFocus.call( this );
 
-               // Scroll to top
-               this.scrollToTop( this.$element );
+               // Only scroll to top of the viewport if:
+               // - The widget is more than 20px from the top
+               // - The widget is not above the top of the viewport (do not scroll downwards)
+               //   (This isn't represented because >20 is, anyways and always, bigger than 0)
+               this.scrollToTop( this.$element, 0, { min: 20, max: Infinity } );
        };
 
        /**
        };
 
        /**
-        * @inheridoc
+        * @inheritdoc
         */
        mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onChangeTags = function () {
                // Parent method
         */
        mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.updateElementsForView = function () {
                var view = this.model.getCurrentView(),
-                       inputValue = this.input.getValue(),
+                       inputValue = this.input.getValue().trim(),
                        inputView = this.model.getViewByTrigger( inputValue.substr( 0, 1 ) );
 
                if ( inputView !== 'default' ) {
 
                // Update input
                this.input.setValue( inputValue );
+
+               if ( this.currentView !== view ) {
+                       this.scrollToTop( this.$element );
+                       this.currentView = view;
+               }
        };
 
        /**
         * Set the visibility of the saved query button
         */
        mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.setSavedQueryVisibility = function () {
-               if ( this.areSavedQueriesEnabled ) {
-                       this.matchingQuery = this.controller.findQueryMatchingCurrentState();
+               if ( mw.user.isAnon() ) {
+                       return;
+               }
 
-                       this.savedQueryTitle.setLabel(
-                               this.matchingQuery ? this.matchingQuery.getLabel() : ''
-                       );
-                       this.savedQueryTitle.toggle( !!this.matchingQuery );
-                       this.saveQueryButton.toggle(
-                               !this.isEmpty() &&
-                               !this.matchingQuery
-                       );
+               this.matchingQuery = this.controller.findQueryMatchingCurrentState();
 
-                       if ( this.matchingQuery ) {
-                               this.emphasize();
-                       }
+               this.savedQueryTitle.setLabel(
+                       this.matchingQuery ? this.matchingQuery.getLabel() : ''
+               );
+               this.savedQueryTitle.toggle( !!this.matchingQuery );
+               this.saveQueryButton.toggle( !this.matchingQuery );
+
+               if ( this.matchingQuery ) {
+                       this.emphasize();
                }
        };
 
        mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.onTagSelect = function ( tagItem ) {
                var widget = this,
                        menuOption = this.menu.getItemFromModel( tagItem.getModel() ),
-                       oldInputValue = this.input.getValue();
+                       oldInputValue = this.input.getValue().trim();
 
                this.menu.setUserSelecting( true );
 
         *
         * @private
         * @param {jQuery} $element Element to position
-        * @param {number} [marginFromTop] When scrolling the entire widget to the top, leave this
+        * @param {number} [marginFromTop=0] When scrolling the entire widget to the top, leave this
         *  much space (in pixels) above the widget.
+        * @param {Object} [threshold] Minimum distance from the top of the element to scroll at all
+        * @param {number} [threshold.min] Minimum distance above the element
+        * @param {number} [threshold.max] Minimum distance below the element
         */
-       mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.scrollToTop = function ( $element, marginFromTop ) {
+       mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.scrollToTop = function ( $element, marginFromTop, threshold ) {
                var container = OO.ui.Element.static.getClosestScrollableContainer( $element[ 0 ], 'y' ),
                        pos = OO.ui.Element.static.getRelativePosition( $element, $( container ) ),
-                       containerScrollTop = $( container ).is( 'body, html' ) ? 0 : $( container ).scrollTop();
+                       containerScrollTop = $( container ).scrollTop(),
+                       effectiveScrollTop = $( container ).is( 'body, html' ) ? 0 : containerScrollTop,
+                       newScrollTop = effectiveScrollTop + pos.top - ( marginFromTop || 0 );
 
                // Scroll to item
-               $( container ).animate( {
-                       scrollTop: containerScrollTop + pos.top - ( marginFromTop || 0 )
-               } );
+               if (
+                       threshold === undefined ||
+                       (
+                               (
+                                       threshold.min === undefined ||
+                                       newScrollTop - containerScrollTop >= threshold.min
+                               ) &&
+                               (
+                                       threshold.max === undefined ||
+                                       newScrollTop - containerScrollTop <= threshold.max
+                               )
+                       )
+               ) {
+                       $( container ).animate( {
+                               scrollTop: newScrollTop
+                       } );
+               }
        };
 }( mediaWiki ) );