mw.ForeignStructuredUpload: Provide category suggestions from the right wiki
[mediawiki.git] / resources / src / mediawiki.widgets / mw.widgets.CategoryCapsuleItemWidget.js
blob24b0e72b9a79324201de05d069dc5e72e9f67228
1 /*!
2  * MediaWiki Widgets - CategoryCapsuleItemWidget class.
3  *
4  * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
5  * @license The MIT License (MIT); see LICENSE.txt
6  */
7 ( function ( $, mw ) {
9         /**
10          * @class mw.widgets.PageExistenceCache
11          * @private
12          * @param {mw.Api} [api]
13          */
14         function PageExistenceCache( api ) {
15                 this.api = api || new mw.Api();
16                 this.processExistenceCheckQueueDebounced = OO.ui.debounce( this.processExistenceCheckQueue );
17                 this.currentRequest = null;
18                 this.existenceCache = {};
19                 this.existenceCheckQueue = {};
20         }
22         /**
23          * Check for existence of pages in the queue.
24          *
25          * @private
26          */
27         PageExistenceCache.prototype.processExistenceCheckQueue = function () {
28                 var queue, titles;
29                 if ( this.currentRequest ) {
30                         // Don't fire off a million requests at the same time
31                         this.currentRequest.always( function () {
32                                 this.currentRequest = null;
33                                 this.processExistenceCheckQueueDebounced();
34                         }.bind( this ) );
35                         return;
36                 }
37                 queue = this.existenceCheckQueue;
38                 this.existenceCheckQueue = {};
39                 titles = Object.keys( queue ).filter( function ( title ) {
40                         if ( this.existenceCache.hasOwnProperty( title ) ) {
41                                 queue[ title ].resolve( this.existenceCache[ title ] );
42                         }
43                         return !this.existenceCache.hasOwnProperty( title );
44                 }.bind( this ) );
45                 if ( !titles.length ) {
46                         return;
47                 }
48                 this.currentRequest = this.api.get( {
49                         action: 'query',
50                         prop: [ 'info' ],
51                         titles: titles
52                 } ).done( function ( response ) {
53                         var index, curr, title;
54                         for ( index in response.query.pages ) {
55                                 curr = response.query.pages[ index ];
56                                 title = new ForeignTitle( curr.title ).getPrefixedText();
57                                 this.existenceCache[ title ] = curr.missing === undefined;
58                                 queue[ title ].resolve( this.existenceCache[ title ] );
59                         }
60                 }.bind( this ) );
61         };
63         /**
64          * Register a request to check whether a page exists.
65          *
66          * @private
67          * @param {mw.Title} title
68          * @return {jQuery.Promise} Promise resolved with true if the page exists or false otherwise
69          */
70         PageExistenceCache.prototype.checkPageExistence = function ( title ) {
71                 var key = title.getPrefixedText();
72                 if ( !this.existenceCheckQueue[ key ] ) {
73                         this.existenceCheckQueue[ key ] = $.Deferred();
74                 }
75                 this.processExistenceCheckQueueDebounced();
76                 return this.existenceCheckQueue[ key ].promise();
77         };
79         /**
80          * @class mw.widgets.ForeignTitle
81          * @private
82          * @extends mw.Title
83          *
84          * @constructor
85          * @inheritdoc
86          */
87         function ForeignTitle() {
88                 ForeignTitle.parent.apply( this, arguments );
89         }
90         OO.inheritClass( ForeignTitle, mw.Title );
91         ForeignTitle.prototype.getNamespacePrefix = function () {
92                 // We only need to handle categories here...
93                 return 'Category:'; // HACK
94         };
96         /**
97          * @class mw.widgets.CategoryCapsuleItemWidget
98          *
99          * Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link
100          * to the given page, and to show its existence status (i.e., whether it is a redlink).
101          *
102          * @uses mw.Api
103          * @extends OO.ui.CapsuleItemWidget
104          *
105          * @constructor
106          * @param {Object} config Configuration options
107          * @cfg {mw.Title} title Page title to use (required)
108          * @cfg {string} [apiUrl] API URL, if not the current wiki's API
109          */
110         mw.widgets.CategoryCapsuleItemWidget = function MWWCategoryCapsuleItemWidget( config ) {
111                 // Parent constructor
112                 mw.widgets.CategoryCapsuleItemWidget.parent.call( this, $.extend( {
113                         data: config.title.getMainText(),
114                         label: config.title.getMainText()
115                 }, config ) );
117                 // Properties
118                 this.title = config.title;
119                 this.apiUrl = config.apiUrl || '';
120                 this.$link = $( '<a>' )
121                         .text( this.label )
122                         .attr( 'target', '_blank' )
123                         .on( 'click', function ( e ) {
124                                 // CapsuleMultiSelectWidget really wants to prevent you from clicking the link, don't let it
125                                 e.stopPropagation();
126                         } );
128                 // Initialize
129                 this.setMissing( false );
130                 this.$label.replaceWith( this.$link );
131                 this.setLabelElement( this.$link );
133                 /*jshint -W024*/
134                 if ( !this.constructor.static.pageExistenceCaches[ this.apiUrl ] ) {
135                         this.constructor.static.pageExistenceCaches[ this.apiUrl ] =
136                                 new PageExistenceCache( new mw.ForeignApi( this.apiUrl ) );
137                 }
138                 this.constructor.static.pageExistenceCaches[ this.apiUrl ]
139                         .checkPageExistence( new ForeignTitle( this.title.getPrefixedText() ) )
140                         .done( function ( exists ) {
141                                 this.setMissing( !exists );
142                         }.bind( this ) );
143                 /*jshint +W024*/
144         };
146         /* Setup */
148         OO.inheritClass( mw.widgets.CategoryCapsuleItemWidget, OO.ui.CapsuleItemWidget );
150         /* Static Properties */
152         /*jshint -W024*/
153         /**
154          * Map of API URLs to PageExistenceCache objects.
155          *
156          * @static
157          * @inheritable
158          * @property {Object}
159          */
160         mw.widgets.CategoryCapsuleItemWidget.static.pageExistenceCaches = {
161                 '': new PageExistenceCache()
162         };
163         /*jshint +W024*/
165         /* Methods */
167         /**
168          * Update label link href and CSS classes to reflect page existence status.
169          *
170          * @private
171          * @param {boolean} missing Whether the page is missing (does not exist)
172          */
173         mw.widgets.CategoryCapsuleItemWidget.prototype.setMissing = function ( missing ) {
174                 var
175                         title = new ForeignTitle( this.title.getPrefixedText() ), // HACK
176                         prefix = this.apiUrl.replace( '/w/api.php', '' ); // HACK
178                 if ( !missing ) {
179                         this.$link
180                                 .attr( 'href', prefix + title.getUrl() )
181                                 .removeClass( 'new' );
182                 } else {
183                         this.$link
184                                 .attr( 'href', prefix + title.getUrl( { action: 'edit', redlink: 1 } ) )
185                                 .addClass( 'new' );
186                 }
187         };
189 }( jQuery, mediaWiki ) );