946823d119644f122ff69c21722570b201d5ecc6
[lhc/web/wiklou.git] / resources / src / mediawiki.widgets / mw.widgets.CategoryCapsuleItemWidget.js
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 ) {
8
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 }
21
22 /**
23 * Check for existence of pages in the queue.
24 *
25 * @private
26 */
27 PageExistenceCache.prototype.processExistenceCheckQueue = function () {
28 var queue, titles,
29 cache = this;
30 if ( this.currentRequest ) {
31 // Don't fire off a million requests at the same time
32 this.currentRequest.always( function () {
33 cache.currentRequest = null;
34 cache.processExistenceCheckQueueDebounced();
35 } );
36 return;
37 }
38 queue = this.existenceCheckQueue;
39 this.existenceCheckQueue = {};
40 titles = Object.keys( queue ).filter( function ( title ) {
41 if ( cache.existenceCache.hasOwnProperty( title ) ) {
42 queue[ title ].resolve( cache.existenceCache[ title ] );
43 }
44 return !cache.existenceCache.hasOwnProperty( title );
45 } );
46 if ( !titles.length ) {
47 return;
48 }
49 this.currentRequest = this.api.get( {
50 formatversion: 2,
51 action: 'query',
52 prop: [ 'info' ],
53 titles: titles
54 } ).done( function ( response ) {
55 $.each( response.query.pages, function ( index, page ) {
56 var title = new ForeignTitle( page.title ).getPrefixedText();
57 cache.existenceCache[ title ] = !page.missing;
58 if ( !queue[ title ] ) {
59 // Debugging for T139130
60 throw new Error( 'No queue for "' + title + '", requested "' + titles.join( '|' ) + '"' );
61 }
62 queue[ title ].resolve( cache.existenceCache[ title ] );
63 } );
64 } );
65 };
66
67 /**
68 * Register a request to check whether a page exists.
69 *
70 * @private
71 * @param {mw.Title} title
72 * @return {jQuery.Promise} Promise resolved with true if the page exists or false otherwise
73 */
74 PageExistenceCache.prototype.checkPageExistence = function ( title ) {
75 var key = title.getPrefixedText();
76 if ( !this.existenceCheckQueue[ key ] ) {
77 this.existenceCheckQueue[ key ] = $.Deferred();
78 }
79 this.processExistenceCheckQueueDebounced();
80 return this.existenceCheckQueue[ key ].promise();
81 };
82
83 /**
84 * @class mw.widgets.ForeignTitle
85 * @private
86 * @extends mw.Title
87 *
88 * @constructor
89 * @inheritdoc
90 */
91 function ForeignTitle( title, namespace ) {
92 // We only need to handle categories here... but we don't know the target language.
93 // So assume that any namespace-like prefix is the 'Category' namespace...
94 title = title.replace( /^(.+?)_*:_*(.*)$/, 'Category:$2' ); // HACK
95 ForeignTitle.parent.call( this, title, namespace );
96 }
97 OO.inheritClass( ForeignTitle, mw.Title );
98 ForeignTitle.prototype.getNamespacePrefix = function () {
99 // We only need to handle categories here...
100 return 'Category:'; // HACK
101 };
102
103 /**
104 * @class mw.widgets.CategoryCapsuleItemWidget
105 *
106 * Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link
107 * to the given page, and to show its existence status (i.e., whether it is a redlink).
108 *
109 * @uses mw.Api
110 * @extends OO.ui.CapsuleItemWidget
111 *
112 * @constructor
113 * @param {Object} config Configuration options
114 * @cfg {mw.Title} title Page title to use (required)
115 * @cfg {string} [apiUrl] API URL, if not the current wiki's API
116 */
117 mw.widgets.CategoryCapsuleItemWidget = function MWWCategoryCapsuleItemWidget( config ) {
118 var widget = this;
119 // Parent constructor
120 mw.widgets.CategoryCapsuleItemWidget.parent.call( this, $.extend( {
121 data: config.title.getMainText(),
122 label: config.title.getMainText()
123 }, config ) );
124
125 // Properties
126 this.title = config.title;
127 this.apiUrl = config.apiUrl || '';
128 this.$link = $( '<a>' )
129 .text( this.label )
130 .attr( 'target', '_blank' )
131 .on( 'click', function ( e ) {
132 // CapsuleMultiselectWidget really wants to prevent you from clicking the link, don't let it
133 e.stopPropagation();
134 } );
135
136 // Initialize
137 this.setMissing( false );
138 this.$label.replaceWith( this.$link );
139 this.setLabelElement( this.$link );
140
141 /*jshint -W024*/
142 if ( !this.constructor.static.pageExistenceCaches[ this.apiUrl ] ) {
143 this.constructor.static.pageExistenceCaches[ this.apiUrl ] =
144 new PageExistenceCache( new mw.ForeignApi( this.apiUrl ) );
145 }
146 this.constructor.static.pageExistenceCaches[ this.apiUrl ]
147 .checkPageExistence( new ForeignTitle( this.title.getPrefixedText() ) )
148 .done( function ( exists ) {
149 widget.setMissing( !exists );
150 } );
151 /*jshint +W024*/
152 };
153
154 /* Setup */
155
156 OO.inheritClass( mw.widgets.CategoryCapsuleItemWidget, OO.ui.CapsuleItemWidget );
157
158 /* Static Properties */
159
160 /*jshint -W024*/
161 /**
162 * Map of API URLs to PageExistenceCache objects.
163 *
164 * @static
165 * @inheritable
166 * @property {Object}
167 */
168 mw.widgets.CategoryCapsuleItemWidget.static.pageExistenceCaches = {
169 '': new PageExistenceCache()
170 };
171 /*jshint +W024*/
172
173 /* Methods */
174
175 /**
176 * Update label link href and CSS classes to reflect page existence status.
177 *
178 * @private
179 * @param {boolean} missing Whether the page is missing (does not exist)
180 */
181 mw.widgets.CategoryCapsuleItemWidget.prototype.setMissing = function ( missing ) {
182 var
183 title = new ForeignTitle( this.title.getPrefixedText() ), // HACK
184 prefix = this.apiUrl.replace( '/w/api.php', '' ); // HACK
185
186 this.missing = missing;
187
188 if ( !missing ) {
189 this.$link
190 .attr( 'href', prefix + title.getUrl() )
191 .attr( 'title', title.getPrefixedText() )
192 .removeClass( 'new' );
193 } else {
194 this.$link
195 .attr( 'href', prefix + title.getUrl( { action: 'edit', redlink: 1 } ) )
196 .attr( 'title', mw.msg( 'red-link-title', title.getPrefixedText() ) )
197 .addClass( 'new' );
198 }
199 };
200
201 }( jQuery, mediaWiki ) );