2 "ajax-add-category" : "[Add Category]",
3 "ajax-add-category-submit" : "[Add]",
4 "ajax-confirm-prompt" : "[Confirmation Text]",
5 "ajax-confirm-title" : "[Confirmation Title]",
6 "ajax-confirm-save" : "[Save]",
7 "ajax-add-category-summary" : "[Add category $1]",
8 "ajax-remove-category-summary" : "[Remove category $2]",
9 "ajax-confirm-actionsummary" : "[Summary]",
10 "ajax-error-title" : "Error",
11 "ajax-error-dismiss" : "OK",
12 "ajax-remove-category-error" : "[RemoveErr]"
15 var ajaxCategories
= {
16 handleAddLink : function( e
) {
19 // Make sure the suggestion plugin is loaded. Load everything else while we're at it
21 ['$j.ui', '$j.ui.dialog', '$j.fn.suggestions'],
23 $j( '#mw-addcategory-prompt' ).toggle();
25 $j( '#mw-addcategory-input' ).suggestions( {
26 'fetch':ajaxCategories
.fetchSuggestions
,
27 'cancel': function() {
28 var req
= ajaxCategories
.request
;
34 $j( '#mw-addcategory-input' ).suggestions();
39 fetchSuggestions : function( query
) {
41 var request
= $j
.ajax( {
42 url
: wgScriptPath
+ '/api.php',
47 'apprefix': $j( this ).val(),
51 success: function( data
) {
52 // Process data.query.allpages into an array of titles
53 var pages
= data
.query
.allpages
;
56 $j
.each( pages
, function( i
, page
) {
57 var title
= page
.title
.split( ':', 2 )[1];
58 titleArr
.push( title
);
61 $j( that
).suggestions( 'suggestions', titleArr
);
65 ajaxCategories
.request
= request
;
68 reloadCategoryList : function( response
) {
69 var holder
= $j( '<div/>' );
72 window
.location
.href
+ ' .catlinks',
74 $j( '.catlinks' ).replaceWith( holder
.find( '.catlinks' ) );
75 ajaxCategories
.setupAJAXCategories();
76 ajaxCategories
.removeProgressIndicator( $j( '.catlinks' ) );
81 confirmEdit : function( page
, fn
, actionSummary
, doneFn
) {
84 ['$j.ui', '$j.ui.dialog', '$j.fn.suggestions'],
86 // Produce a confirmation dialog
88 var dialog
= $j( '<div/>' );
90 dialog
.addClass( 'mw-ajax-confirm-dialog' );
91 dialog
.attr( 'title', gM( 'ajax-confirm-title' ) );
94 var confirmIntro
= $j( '<p/>' );
95 confirmIntro
.text( gM( 'ajax-confirm-prompt' ) );
96 dialog
.append( confirmIntro
);
98 // Summary of the action to be taken
99 var summaryHolder
= $j( '<p/>' );
100 var summaryLabel
= $j( '<strong/>' );
101 summaryLabel
.text( gM( 'ajax-confirm-actionsummary' ) + " " );
102 summaryHolder
.text( actionSummary
);
103 summaryHolder
.prepend( summaryLabel
);
104 dialog
.append( summaryHolder
);
107 var reasonBox
= $j( '<input type="text" size="45" />' );
108 reasonBox
.addClass( 'mw-ajax-confirm-reason' );
109 dialog
.append( reasonBox
);
112 var submitButton
= $j( '<input type="button"/>' );
113 submitButton
.val( gM( 'ajax-confirm-save' ) );
115 var submitFunction = function() {
116 ajaxCategories
.addProgressIndicator( dialog
);
117 ajaxCategories
.doEdit(
123 dialog
.dialog( 'close' );
124 ajaxCategories
.removeProgressIndicator( dialog
);
130 buttons
[gM( 'ajax-confirm-save' )] = submitFunction
;
131 var dialogOptions
= {
137 $j( '#catlinks' ).prepend( dialog
);
138 dialog
.dialog( dialogOptions
);
143 doEdit : function( page
, fn
, summary
, doneFn
) {
144 // Get an edit token for the page.
147 'prop':'info|revisions',
150 'rvprop':'content|timestamp',
154 $j
.get( wgScriptPath
+ '/api.php', getTokenVars
,
156 var infos
= reply
.query
.pages
;
159 function( pageid
, data
) {
160 var token
= data
.edittoken
;
161 var timestamp
= data
.revisions
[0].timestamp
;
162 var oldText
= data
.revisions
[0]['*'];
164 var newText
= fn( oldText
);
166 if ( newText
=== false ) return;
174 'basetimestamp':timestamp
,
178 $j
.post( wgScriptPath
+ '/api.php', postEditVars
, doneFn
, 'json' );
185 addProgressIndicator : function( elem
) {
186 var indicator
= $j( '<div/>' );
188 indicator
.addClass( 'mw-ajax-loader' );
190 elem
.append( indicator
);
193 removeProgressIndicator : function( elem
) {
194 elem
.find( '.mw-ajax-loader' ).remove();
197 handleCategoryAdd : function( e
) {
198 // Grab category text
199 var category
= $j( '#mw-addcategory-input' ).val();
200 var appendText
= "\n[[" + wgFormattedNamespaces
[14] + ":" + category
+ "]]\n";
201 var summary
= gM( 'ajax-add-category-summary', category
);
203 ajaxCategories
.confirmEdit(
205 function( oldText
) { return oldText
+ appendText
},
207 ajaxCategories
.reloadCategoryList
211 handleDeleteLink : function( e
) {
214 var category
= $j( this ).parent().find( 'a' ).text();
216 // Build a regex that matches legal invocations of that category.
218 // In theory I should escape the aliases, but there's no JS function for it
219 // Shouldn't have any real impact, can't be exploited or anything, so we'll
221 var categoryNSFragment
= '';
222 $j
.each( wgNamespaceIds
, function( name
, id
) {
224 // Allow the first character to be any case
225 var firstChar
= name
.charAt( 0 );
226 firstChar
= '[' + firstChar
.toUpperCase() + firstChar
.toLowerCase() + ']';
227 categoryNSFragment
+= '|' + firstChar
+ name
.substr( 1 );
230 categoryNSFragment
= categoryNSFragment
.substr( 1 ); // Remove leading |
233 var titleFragment
= category
;
235 firstChar
= category
.charAt( 0 );
236 firstChar
= '[' + firstChar
.toUpperCase() + firstChar
.toLowerCase() + ']';
237 titleFragment
= firstChar
+ category
.substr( 1 );
238 var categoryRegex
= '\\[\\[' + categoryNSFragment
+ ':' + titleFragment
+ '(\\|[^\\]]*)?\\]\\]';
239 categoryRegex
= new RegExp( categoryRegex
, 'g' );
241 var summary
= gM( 'ajax-remove-category-summary', category
);
243 ajaxCategories
.confirmEdit(
245 function( oldText
) {
246 var newText
= oldText
.replace( categoryRegex
, '' );
248 if ( newText
== oldText
) {
249 var error
= gM( 'ajax-remove-category-error' );
250 ajaxCategories
.showError( error
);
251 ajaxCategories
.removeProgressIndicator( $j( '.mw-ajax-confirm-dialog' ) );
252 $j( '.mw-ajax-confirm-dialog' ).dialog( 'close' );
258 summary
, ajaxCategories
.reloadCategoryList
262 showError : function( str
) {
263 var dialog
= $j( '<div/>' );
266 $j( '#bodyContent' ).append( dialog
);
269 buttons
[gM( 'ajax-error-dismiss' )] = function( e
) {
270 dialog
.dialog( 'close' );
272 var dialogOptions
= {
275 'title' : gM( 'ajax-error-title' )
278 dialog
.dialog( dialogOptions
);
281 setupAJAXCategories : function() {
282 // Only do it for articles.
283 if ( !wgIsArticle
) return;
285 var clElement
= $j( '.catlinks' );
287 // Unhide hidden category holders.
288 clElement
.removeClass( 'catlinks-allhidden' );
290 var addLink
= $j( '<a/>' );
291 addLink
.addClass( 'mw-ajax-addcategory' );
293 // Create [Add Category] link
294 addLink
.text( gM( 'ajax-add-category' ) );
295 addLink
.attr( 'href', '#' );
296 addLink
.click( ajaxCategories
.handleAddLink
);
297 clElement
.append( addLink
);
299 // Create add category prompt
300 var promptContainer
= $j( '<div id="mw-addcategory-prompt"/>' );
301 var promptTextbox
= $j( '<input type="text" size="45" id="mw-addcategory-input"/>' );
302 var addButton
= $j( '<input type="button" id="mw-addcategory-button"/>' );
303 addButton
.val( gM( 'ajax-add-category-submit' ) );
305 promptTextbox
.keypress( ajaxCategories
.handleCategoryInput
);
306 addButton
.click( ajaxCategories
.handleCategoryAdd
);
308 promptContainer
.append( promptTextbox
);
309 promptContainer
.append( addButton
);
310 promptContainer
.hide();
312 // Create delete link for each category.
313 $j( '.catlinks div span a' ).each( function( e
) {
314 // Create a remove link
315 var deleteLink
= $j( '<a class="mw-remove-category" href="#"/>' );
317 deleteLink
.click( ajaxCategories
.handleDeleteLink
);
319 $j( this ).after( deleteLink
);
322 clElement
.append( promptContainer
);
326 js2AddOnloadHook( ajaxCategories
.setupAJAXCategories
);