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];
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(
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
);