From 49ecc302fccb2ec372e4955369ad3d3d549bcc1a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bartosz=20Dziewo=C5=84ski?= Date: Fri, 9 Oct 2015 15:05:53 +0200 Subject: [PATCH] mw.widgets.CategorySelector: Indicate pending requests and abort useless ones * When the input loses focus, or when we fire new queries, abort any current ones. * Never display the menu after input loses focus. * Use PendingElement to indicate that we're waiting for responses. (Depends on I604fff9a9e5bfbb584b3926802dab445e6131aaa in OOjs UI for correct styling.) * Do not send needless queries for empty input. Bug: T114945 Change-Id: I80f4d0143279f1768ed2a3bdcb3f731526597577 --- .../mw.widgets.CategorySelector.js | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js b/resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js index e40caaa856..89fcc0b8f0 100644 --- a/resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js +++ b/resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js @@ -23,10 +23,10 @@ * * selector.setSearchType( [ mw.widgets.CategorySelector.SearchType.SubCategories ] ); * - * * @class mw.widgets.CategorySelector * @uses mw.Api * @extends OO.ui.CapsuleMultiSelectWidget + * @mixins OO.ui.mixin.PendingElement * * @constructor * @param {Object} [config] Configuration options @@ -54,17 +54,20 @@ allowArbitrary: true } ) ); + // Mixin constructors + OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$handle } ) ); + // Event handler to call the autocomplete methods this.$input.on( 'change input cut paste', OO.ui.debounce( this.updateMenuItems.bind( this ), 100 ) ); // Initialize this.api = new mw.Api(); - } /* Setup */ OO.inheritClass( CategorySelector, OO.ui.CapsuleMultiSelectWidget ); + OO.mixinClass( CategorySelector, OO.ui.mixin.PendingElement ); CSP = CategorySelector.prototype; /* Methods */ @@ -83,6 +86,11 @@ var existingItems, filteredItems, menu = this.getMenu(); + // Never show the menu if the input lost focus in the meantime + if ( !this.$input.is( ':focus' ) ) { + return; + } + // Array of strings of the data of OO.ui.MenuOptionsWidgets existingItems = menu.getItems().map( function ( item ) { return item.data; @@ -105,6 +113,15 @@ }.bind( this ) ); }; + /** + * @inheritdoc + */ + CSP.clearInput = function () { + CategorySelector.parent.prototype.clearInput.call( this ); + // Abort all pending requests, we won't need their results + this.api.abort(); + }; + /** * Searches for categories based on the input. * @@ -118,10 +135,19 @@ promises = [], deferred = new $.Deferred(); + if ( $.trim( input ) === '' ) { + deferred.resolve( [] ); + return deferred.promise(); + } + + // Abort all pending requests, we won't need their results + this.api.abort(); for ( i = 0; i < this.searchTypes.length; i++ ) { promises.push( this.searchCategories( input, this.searchTypes[ i ] ) ); } + this.pushPending(); + $.when.apply( $, promises ).done( function () { var categories, categoryNames, allData = [], @@ -142,7 +168,7 @@ deferred.resolve( categoryNames ); - } ); + } ).always( this.popPending.bind( this ) ); return deferred.promise(); }; @@ -226,7 +252,7 @@ } ).done( function ( res ) { var categories = res[ 1 ]; deferred.resolve( categories ); - } ); + } ).fail( deferred.reject.bind( deferred ) ); break; case CategorySelector.SearchType.InternalSearch: @@ -242,7 +268,7 @@ return page.title; } ); deferred.resolve( categories ); - } ); + } ).fail( deferred.reject.bind( deferred ) ); break; case CategorySelector.SearchType.Exists: @@ -266,7 +292,7 @@ } deferred.resolve( categories ); - } ); + } ).fail( deferred.reject.bind( deferred ) ); break; case CategorySelector.SearchType.SubCategories: @@ -286,7 +312,7 @@ return category.title; } ); deferred.resolve( categories ); - } ); + } ).fail( deferred.reject.bind( deferred ) ); break; case CategorySelector.SearchType.ParentCategories: @@ -315,7 +341,7 @@ } deferred.resolve( categories ); - } ); + } ).fail( deferred.reject.bind( deferred ) ); break; default: -- 2.20.1