2 * MediaWiki Widgets - CategoryCapsuleItemWidget class.
4 * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
5 * @license The MIT License (MIT); see LICENSE.txt
9 var hasOwn
= Object
.prototype.hasOwnProperty
;
12 * @class mw.widgets.PageExistenceCache
14 * @param {mw.Api} [api]
16 function PageExistenceCache( api
) {
17 this.api
= api
|| new mw
.Api();
18 this.processExistenceCheckQueueDebounced
= OO
.ui
.debounce( this.processExistenceCheckQueue
);
19 this.currentRequest
= null;
20 this.existenceCache
= {};
21 this.existenceCheckQueue
= {};
25 * Check for existence of pages in the queue.
29 PageExistenceCache
.prototype.processExistenceCheckQueue = function () {
32 if ( this.currentRequest
) {
33 // Don't fire off a million requests at the same time
34 this.currentRequest
.always( function () {
35 cache
.currentRequest
= null;
36 cache
.processExistenceCheckQueueDebounced();
40 queue
= this.existenceCheckQueue
;
41 this.existenceCheckQueue
= {};
42 titles
= Object
.keys( queue
).filter( function ( title
) {
43 if ( hasOwn
.call( cache
.existenceCache
, title
) ) {
44 queue
[ title
].resolve( cache
.existenceCache
[ title
] );
46 return !hasOwn
.call( cache
.existenceCache
, title
);
48 if ( !titles
.length
) {
51 this.currentRequest
= this.api
.get( {
56 } ).done( function ( response
) {
60 $.each( response
.query
.normalized
|| [], function ( index
, data
) {
61 normalized
[ data
.fromencoded
? decodeURIComponent( data
.from ) : data
.from ] = data
.to
;
63 $.each( response
.query
.pages
, function ( index
, page
) {
64 pages
[ page
.title
] = !page
.missing
;
66 titles
.forEach( function ( title
) {
67 var normalizedTitle
= title
;
68 while ( hasOwn
.call( normalized
, normalizedTitle
) ) {
69 normalizedTitle
= normalized
[ normalizedTitle
];
71 cache
.existenceCache
[ title
] = pages
[ normalizedTitle
];
72 queue
[ title
].resolve( cache
.existenceCache
[ title
] );
78 * Register a request to check whether a page exists.
81 * @param {mw.Title} title
82 * @return {jQuery.Promise} Promise resolved with true if the page exists or false otherwise
84 PageExistenceCache
.prototype.checkPageExistence = function ( title
) {
85 var key
= title
.getPrefixedText();
86 if ( !hasOwn
.call( this.existenceCheckQueue
, key
) ) {
87 this.existenceCheckQueue
[ key
] = $.Deferred();
89 this.processExistenceCheckQueueDebounced();
90 return this.existenceCheckQueue
[ key
].promise();
94 * @class mw.widgets.ForeignTitle
99 * @param {string} title
100 * @param {number} [namespace]
102 function ForeignTitle( title
, namespace ) {
103 // We only need to handle categories here... but we don't know the target language.
104 // So assume that any namespace-like prefix is the 'Category' namespace...
105 title
= title
.replace( /^(.+?)_*:_*(.*)$/, 'Category:$2' ); // HACK
106 ForeignTitle
.parent
.call( this, title
, namespace );
108 OO
.inheritClass( ForeignTitle
, mw
.Title
);
109 ForeignTitle
.prototype.getNamespacePrefix = function () {
110 // We only need to handle categories here...
111 return 'Category:'; // HACK
115 * Category selector capsule item widget. Extends OO.ui.CapsuleItemWidget with the ability to link
116 * to the given page, and to show its existence status (i.e., whether it is a redlink).
118 * @class mw.widgets.CategoryCapsuleItemWidget
120 * @extends OO.ui.CapsuleItemWidget
123 * @param {Object} config Configuration options
124 * @cfg {mw.Title} title Page title to use (required)
125 * @cfg {string} [apiUrl] API URL, if not the current wiki's API
127 mw
.widgets
.CategoryCapsuleItemWidget
= function MWWCategoryCapsuleItemWidget( config
) {
129 // Parent constructor
130 mw
.widgets
.CategoryCapsuleItemWidget
.parent
.call( this, $.extend( {
131 data
: config
.title
.getMainText(),
132 label
: config
.title
.getMainText()
136 this.title
= config
.title
;
137 this.apiUrl
= config
.apiUrl
|| '';
138 this.$link
= $( '<a>' )
140 .attr( 'target', '_blank' )
141 .on( 'click', function ( e
) {
142 // CapsuleMultiselectWidget really wants to prevent you from clicking the link, don't let it
147 this.setMissing( false );
148 this.$label
.replaceWith( this.$link
);
149 this.setLabelElement( this.$link
);
151 if ( !this.constructor.static.pageExistenceCaches
[ this.apiUrl
] ) {
152 this.constructor.static.pageExistenceCaches
[ this.apiUrl
] =
153 new PageExistenceCache( new mw
.ForeignApi( this.apiUrl
) );
155 this.constructor.static.pageExistenceCaches
[ this.apiUrl
]
156 .checkPageExistence( new ForeignTitle( this.title
.getPrefixedText() ) )
157 .done( function ( exists
) {
158 widget
.setMissing( !exists
);
164 OO
.inheritClass( mw
.widgets
.CategoryCapsuleItemWidget
, OO
.ui
.CapsuleItemWidget
);
166 /* Static Properties */
169 * Map of API URLs to PageExistenceCache objects.
175 mw
.widgets
.CategoryCapsuleItemWidget
.static.pageExistenceCaches
= {
176 '': new PageExistenceCache()
182 * Update label link href and CSS classes to reflect page existence status.
185 * @param {boolean} missing Whether the page is missing (does not exist)
187 mw
.widgets
.CategoryCapsuleItemWidget
.prototype.setMissing = function ( missing
) {
189 title
= new ForeignTitle( this.title
.getPrefixedText() ), // HACK
190 prefix
= this.apiUrl
.replace( '/w/api.php', '' ); // HACK
192 this.missing
= missing
;
196 .attr( 'href', prefix
+ title
.getUrl() )
197 .attr( 'title', title
.getPrefixedText() )
198 .removeClass( 'new' );
201 .attr( 'href', prefix
+ title
.getUrl( { action
: 'edit', redlink
: 1 } ) )
202 .attr( 'title', mw
.msg( 'red-link-title', title
.getPrefixedText() ) )
207 }( jQuery
, mediaWiki
) );