From: Prateek Saxena Date: Thu, 8 Mar 2018 06:09:07 +0000 (+0530) Subject: mw.widgets.CategoryMultiselectWidget use TagMultiselectWidget X-Git-Tag: 1.34.0-rc.0~4944^2 X-Git-Url: http://git.cyclocoop.org/%28%28?a=commitdiff_plain;h=3b6adf398722b9fdd7984debc2ffe29d0bd8be87;p=lhc%2Fweb%2Fwiklou.git mw.widgets.CategoryMultiselectWidget use TagMultiselectWidget …also rename mw.widgets.CategoryCapsuleItemWidget to mw.widgets.CategoryTagItemWidget. Bug: T183299 Depends-on: Ia0732faff2d98f1ca07da5fbe1f2abc27f71a617 Change-Id: I2146d8ce6505dae55750b4b12bd806b83796c1c5 --- diff --git a/resources/Resources.php b/resources/Resources.php index 0595bb031c..15669e2daf 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -2607,7 +2607,7 @@ return [ ], 'mediawiki.widgets.CategoryMultiselectWidget' => [ 'scripts' => [ - 'resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js', + 'resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js', 'resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js', ], 'dependencies' => [ diff --git a/resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js deleted file mode 100644 index 17da7d85f0..0000000000 --- a/resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js +++ /dev/null @@ -1,207 +0,0 @@ -/*! - * MediaWiki Widgets - CategoryCapsuleItemWidget class. - * - * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt - * @license The MIT License (MIT); see LICENSE.txt - */ -( function ( $, mw ) { - - var hasOwn = Object.prototype.hasOwnProperty; - - /** - * @class mw.widgets.PageExistenceCache - * @private - * @param {mw.Api} [api] - */ - function PageExistenceCache( api ) { - this.api = api || new mw.Api(); - this.processExistenceCheckQueueDebounced = OO.ui.debounce( this.processExistenceCheckQueue ); - this.currentRequest = null; - this.existenceCache = {}; - this.existenceCheckQueue = {}; - } - - /** - * Check for existence of pages in the queue. - * - * @private - */ - PageExistenceCache.prototype.processExistenceCheckQueue = function () { - var queue, titles, - cache = this; - if ( this.currentRequest ) { - // Don't fire off a million requests at the same time - this.currentRequest.always( function () { - cache.currentRequest = null; - cache.processExistenceCheckQueueDebounced(); - } ); - return; - } - queue = this.existenceCheckQueue; - this.existenceCheckQueue = {}; - titles = Object.keys( queue ).filter( function ( title ) { - if ( hasOwn.call( cache.existenceCache, title ) ) { - queue[ title ].resolve( cache.existenceCache[ title ] ); - } - return !hasOwn.call( cache.existenceCache, title ); - } ); - if ( !titles.length ) { - return; - } - this.currentRequest = this.api.get( { - formatversion: 2, - action: 'query', - prop: [ 'info' ], - titles: titles - } ).done( function ( response ) { - var - normalized = {}, - pages = {}; - $.each( response.query.normalized || [], function ( index, data ) { - normalized[ data.fromencoded ? decodeURIComponent( data.from ) : data.from ] = data.to; - } ); - $.each( response.query.pages, function ( index, page ) { - pages[ page.title ] = !page.missing; - } ); - titles.forEach( function ( title ) { - var normalizedTitle = title; - while ( hasOwn.call( normalized, normalizedTitle ) ) { - normalizedTitle = normalized[ normalizedTitle ]; - } - cache.existenceCache[ title ] = pages[ normalizedTitle ]; - queue[ title ].resolve( cache.existenceCache[ title ] ); - } ); - } ); - }; - - /** - * Register a request to check whether a page exists. - * - * @private - * @param {mw.Title} title - * @return {jQuery.Promise} Promise resolved with true if the page exists or false otherwise - */ - PageExistenceCache.prototype.checkPageExistence = function ( title ) { - var key = title.getPrefixedText(); - if ( !hasOwn.call( this.existenceCheckQueue, key ) ) { - this.existenceCheckQueue[ key ] = $.Deferred(); - } - this.processExistenceCheckQueueDebounced(); - return this.existenceCheckQueue[ key ].promise(); - }; - - /** - * @class mw.widgets.ForeignTitle - * @private - * @extends mw.Title - * - * @constructor - * @param {string} title - * @param {number} [namespace] - */ - function ForeignTitle( title, namespace ) { - // We only need to handle categories here... but we don't know the target language. - // So assume that any namespace-like prefix is the 'Category' namespace... - title = title.replace( /^(.+?)_*:_*(.*)$/, 'Category:$2' ); // HACK - ForeignTitle.parent.call( this, title, namespace ); - } - OO.inheritClass( ForeignTitle, mw.Title ); - ForeignTitle.prototype.getNamespacePrefix = function () { - // We only need to handle categories here... - return 'Category:'; // HACK - }; - - /** - * Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link - * to the given page, and to show its existence status (i.e., whether it is a redlink). - * - * @class mw.widgets.CategoryCapsuleItemWidget - * @uses mw.Api - * @extends OO.ui.CapsuleItemWidget - * - * @constructor - * @param {Object} config Configuration options - * @cfg {mw.Title} title Page title to use (required) - * @cfg {string} [apiUrl] API URL, if not the current wiki's API - */ - mw.widgets.CategoryCapsuleItemWidget = function MWWCategoryCapsuleItemWidget( config ) { - var widget = this; - // Parent constructor - mw.widgets.CategoryCapsuleItemWidget.parent.call( this, $.extend( { - data: config.title.getMainText(), - label: config.title.getMainText() - }, config ) ); - - // Properties - this.title = config.title; - this.apiUrl = config.apiUrl || ''; - this.$link = $( '' ) - .text( this.label ) - .attr( 'target', '_blank' ) - .on( 'click', function ( e ) { - // CapsuleMultiselectWidget really wants to prevent you from clicking the link, don't let it - e.stopPropagation(); - } ); - - // Initialize - this.setMissing( false ); - this.$label.replaceWith( this.$link ); - this.setLabelElement( this.$link ); - - if ( !this.constructor.static.pageExistenceCaches[ this.apiUrl ] ) { - this.constructor.static.pageExistenceCaches[ this.apiUrl ] = - new PageExistenceCache( new mw.ForeignApi( this.apiUrl ) ); - } - this.constructor.static.pageExistenceCaches[ this.apiUrl ] - .checkPageExistence( new ForeignTitle( this.title.getPrefixedText() ) ) - .done( function ( exists ) { - widget.setMissing( !exists ); - } ); - }; - - /* Setup */ - - OO.inheritClass( mw.widgets.CategoryCapsuleItemWidget, OO.ui.CapsuleItemWidget ); - - /* Static Properties */ - - /** - * Map of API URLs to PageExistenceCache objects. - * - * @static - * @inheritable - * @property {Object} - */ - mw.widgets.CategoryCapsuleItemWidget.static.pageExistenceCaches = { - '': new PageExistenceCache() - }; - - /* Methods */ - - /** - * Update label link href and CSS classes to reflect page existence status. - * - * @private - * @param {boolean} missing Whether the page is missing (does not exist) - */ - mw.widgets.CategoryCapsuleItemWidget.prototype.setMissing = function ( missing ) { - var - title = new ForeignTitle( this.title.getPrefixedText() ), // HACK - prefix = this.apiUrl.replace( '/w/api.php', '' ); // HACK - - this.missing = missing; - - if ( !missing ) { - this.$link - .attr( 'href', prefix + title.getUrl() ) - .attr( 'title', title.getPrefixedText() ) - .removeClass( 'new' ); - } else { - this.$link - .attr( 'href', prefix + title.getUrl( { action: 'edit', redlink: 1 } ) ) - .attr( 'title', mw.msg( 'red-link-title', title.getPrefixedText() ) ) - .addClass( 'new' ); - } - }; - -}( jQuery, mediaWiki ) ); diff --git a/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js index 354fcd94bd..c5063797ed 100644 --- a/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js +++ b/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js @@ -9,7 +9,7 @@ NS_CATEGORY = mw.config.get( 'wgNamespaceIds' ).category; /** - * Category selector widget. Displays an OO.ui.CapsuleMultiselectWidget + * Category selector widget. Displays an OO.ui.MenuTagMultiselectWidget * and autocompletes with available categories. * * mw.loader.using( 'mediawiki.widgets.CategoryMultiselectWidget', function () { @@ -27,7 +27,7 @@ * * @class mw.widgets.CategoryMultiselectWidget * @uses mw.Api - * @extends OO.ui.CapsuleMultiselectWidget + * @extends OO.ui.MenuTagMultiselectWidget * @mixins OO.ui.mixin.PendingElement * * @constructor @@ -62,7 +62,7 @@ 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 ) ); + this.input.$input.on( 'change input cut paste', OO.ui.debounce( this.updateMenuItems.bind( this ), 100 ) ); // Initialize this.api = config.api || new mw.Api(); @@ -71,7 +71,7 @@ /* Setup */ - OO.inheritClass( mw.widgets.CategoryMultiselectWidget, OO.ui.CapsuleMultiselectWidget ); + OO.inheritClass( mw.widgets.CategoryMultiselectWidget, OO.ui.MenuTagMultiselectWidget ); OO.mixinClass( mw.widgets.CategoryMultiselectWidget, OO.ui.mixin.PendingElement ); /* Methods */ @@ -86,12 +86,12 @@ */ mw.widgets.CategoryMultiselectWidget.prototype.updateMenuItems = function () { this.getMenu().clearItems(); - this.getNewMenuItems( this.$input.val() ).then( function ( items ) { + this.getNewMenuItems( this.input.$input.val() ).then( function ( items ) { var existingItems, filteredItems, menu = this.getMenu(); // Never show the menu if the input lost focus in the meantime - if ( !this.$input.is( ':focus' ) ) { + if ( !this.input.$input.is( ':focus' ) ) { return; } @@ -188,12 +188,10 @@ /** * @inheritdoc */ - mw.widgets.CategoryMultiselectWidget.prototype.createItemWidget = function ( data ) { + mw.widgets.CategoryMultiselectWidget.prototype.createTagItemWidget = function ( data ) { var title = mw.Title.makeTitle( NS_CATEGORY, data ); - if ( !title ) { - return null; - } - return new mw.widgets.CategoryCapsuleItemWidget( { + + return new mw.widgets.CategoryTagItemWidget( { apiUrl: this.api.apiUrl || undefined, title: title } ); @@ -204,7 +202,7 @@ */ mw.widgets.CategoryMultiselectWidget.prototype.findItemFromData = function ( data ) { // This is a bit of a hack... We have to canonicalize the data in the same way that - // #createItemWidget and CategoryCapsuleItemWidget will do, otherwise we won't find duplicates. + // #createItemWidget and CategoryTagItemWidget will do, otherwise we won't find duplicates. var title = mw.Title.makeTitle( NS_CATEGORY, data ); if ( !title ) { return null; diff --git a/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js new file mode 100644 index 0000000000..f0b2d44e35 --- /dev/null +++ b/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js @@ -0,0 +1,209 @@ +/*! + * MediaWiki Widgets - CategoryTagItemWidget class. + * + * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ +( function ( $, mw ) { + + var hasOwn = Object.prototype.hasOwnProperty; + + /** + * @class mw.widgets.PageExistenceCache + * @private + * @param {mw.Api} [api] + */ + function PageExistenceCache( api ) { + this.api = api || new mw.Api(); + this.processExistenceCheckQueueDebounced = OO.ui.debounce( this.processExistenceCheckQueue ); + this.currentRequest = null; + this.existenceCache = {}; + this.existenceCheckQueue = {}; + } + + /** + * Check for existence of pages in the queue. + * + * @private + */ + PageExistenceCache.prototype.processExistenceCheckQueue = function () { + var queue, titles, + cache = this; + if ( this.currentRequest ) { + // Don't fire off a million requests at the same time + this.currentRequest.always( function () { + cache.currentRequest = null; + cache.processExistenceCheckQueueDebounced(); + } ); + return; + } + queue = this.existenceCheckQueue; + this.existenceCheckQueue = {}; + titles = Object.keys( queue ).filter( function ( title ) { + if ( hasOwn.call( cache.existenceCache, title ) ) { + queue[ title ].resolve( cache.existenceCache[ title ] ); + } + return !hasOwn.call( cache.existenceCache, title ); + } ); + if ( !titles.length ) { + return; + } + this.currentRequest = this.api.get( { + formatversion: 2, + action: 'query', + prop: [ 'info' ], + titles: titles + } ).done( function ( response ) { + var + normalized = {}, + pages = {}; + $.each( response.query.normalized || [], function ( index, data ) { + normalized[ data.fromencoded ? decodeURIComponent( data.from ) : data.from ] = data.to; + } ); + $.each( response.query.pages, function ( index, page ) { + pages[ page.title ] = !page.missing; + } ); + titles.forEach( function ( title ) { + var normalizedTitle = title; + while ( hasOwn.call( normalized, normalizedTitle ) ) { + normalizedTitle = normalized[ normalizedTitle ]; + } + cache.existenceCache[ title ] = pages[ normalizedTitle ]; + queue[ title ].resolve( cache.existenceCache[ title ] ); + } ); + } ); + }; + + /** + * Register a request to check whether a page exists. + * + * @private + * @param {mw.Title} title + * @return {jQuery.Promise} Promise resolved with true if the page exists or false otherwise + */ + PageExistenceCache.prototype.checkPageExistence = function ( title ) { + var key = title.getPrefixedText(); + if ( !hasOwn.call( this.existenceCheckQueue, key ) ) { + this.existenceCheckQueue[ key ] = $.Deferred(); + } + this.processExistenceCheckQueueDebounced(); + return this.existenceCheckQueue[ key ].promise(); + }; + + /** + * @class mw.widgets.ForeignTitle + * @private + * @extends mw.Title + * + * @constructor + * @param {string} title + * @param {number} [namespace] + */ + function ForeignTitle( title, namespace ) { + // We only need to handle categories here... but we don't know the target language. + // So assume that any namespace-like prefix is the 'Category' namespace... + title = title.replace( /^(.+?)_*:_*(.*)$/, 'Category:$2' ); // HACK + ForeignTitle.parent.call( this, title, namespace ); + } + OO.inheritClass( ForeignTitle, mw.Title ); + ForeignTitle.prototype.getNamespacePrefix = function () { + // We only need to handle categories here... + return 'Category:'; // HACK + }; + + /** + * Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link + * to the given page, and to show its existence status (i.e., whether it is a redlink). + * + * @class mw.widgets.CategoryTagItemWidget + * @uses mw.Api + * @extends OO.ui.TagItemWidget + * + * @constructor + * @param {Object} config Configuration options + * @cfg {mw.Title} title Page title to use (required) + * @cfg {string} [apiUrl] API URL, if not the current wiki's API + */ + mw.widgets.CategoryTagItemWidget = function MWWCategoryTagItemWidget( config ) { + var widget = this; + // Parent constructor + mw.widgets.CategoryTagItemWidget.parent.call( this, $.extend( { + data: config.title.getMainText(), + label: config.title.getMainText() + }, config ) ); + + // Properties + this.title = config.title; + this.apiUrl = config.apiUrl || ''; + this.$link = $( '' ) + .text( this.label ) + .attr( 'target', '_blank' ) + .on( 'click', function ( e ) { + // TagMultiselectWidget really wants to prevent you from clicking the link, don't let it + e.stopPropagation(); + } ); + + // Initialize + this.setMissing( false ); + this.$label.replaceWith( this.$link ); + this.setLabelElement( this.$link ); + + if ( !this.constructor.static.pageExistenceCaches[ this.apiUrl ] ) { + this.constructor.static.pageExistenceCaches[ this.apiUrl ] = + new PageExistenceCache( new mw.ForeignApi( this.apiUrl ) ); + } + this.constructor.static.pageExistenceCaches[ this.apiUrl ] + .checkPageExistence( new ForeignTitle( this.title.getPrefixedText() ) ) + .done( function ( exists ) { + widget.setMissing( !exists ); + } ); + }; + + /* Setup */ + + OO.inheritClass( mw.widgets.CategoryTagItemWidget, OO.ui.TagItemWidget ); + + /* Static Properties */ + + /** + * Map of API URLs to PageExistenceCache objects. + * + * @static + * @inheritable + * @property {Object} + */ + mw.widgets.CategoryTagItemWidget.static.pageExistenceCaches = { + '': new PageExistenceCache() + }; + + /* Methods */ + + /** + * Update label link href and CSS classes to reflect page existence status. + * + * @private + * @param {boolean} missing Whether the page is missing (does not exist) + */ + mw.widgets.CategoryTagItemWidget.prototype.setMissing = function ( missing ) { + var + title = new ForeignTitle( this.title.getPrefixedText() ), // HACK + prefix = this.apiUrl.replace( '/w/api.php', '' ); // HACK + + this.missing = missing; + + if ( !missing ) { + this.$link + .attr( 'href', prefix + title.getUrl() ) + .attr( 'title', title.getPrefixedText() ) + .removeClass( 'new' ); + } else { + this.$link + .attr( 'href', prefix + title.getUrl( { action: 'edit', redlink: 1 } ) ) + .attr( 'title', mw.msg( 'red-link-title', title.getPrefixedText() ) ) + .addClass( 'new' ); + } + }; + + // For backwards compatibility. See T183299. + mw.widgets.CategoryCapsuleItemWidget = mw.widgets.CategoryTagItemWidget; +}( jQuery, mediaWiki ) );