Index: trunk/phase3/js2/ajaxcategories.js |
— | — | @@ -1,40 +1,41 @@ |
2 | 2 | loadGM( { |
3 | | - "ajax-add-category":"[Add Category]", |
4 | | - "ajax-add-category-submit":"[Add]", |
5 | | - "ajax-confirm-prompt":"[Confirmation Text]", |
6 | | - "ajax-confirm-title":"[Confirmation Title]", |
7 | | - "ajax-confirm-save":"[Save]", |
8 | | - "ajax-add-category-summary":"[Add category $1]", |
9 | | - "ajax-remove-category-summary":"[Remove category $2]", |
10 | | - "ajax-confirm-actionsummary":"[Summary]", |
11 | | - "ajax-error-title":"Error", |
12 | | - "ajax-error-dismiss":"OK", |
13 | | - "ajax-remove-category-error":"[RemoveErr]" |
14 | | - } ); |
| 3 | + "ajax-add-category" : "[Add Category]", |
| 4 | + "ajax-add-category-submit" : "[Add]", |
| 5 | + "ajax-confirm-prompt" : "[Confirmation Text]", |
| 6 | + "ajax-confirm-title" : "[Confirmation Title]", |
| 7 | + "ajax-confirm-save" : "[Save]", |
| 8 | + "ajax-add-category-summary" : "[Add category $1]", |
| 9 | + "ajax-remove-category-summary" : "[Remove category $2]", |
| 10 | + "ajax-confirm-actionsummary" : "[Summary]", |
| 11 | + "ajax-error-title" : "Error", |
| 12 | + "ajax-error-dismiss" : "OK", |
| 13 | + "ajax-remove-category-error" : "[RemoveErr]" |
| 14 | +} ); |
15 | 15 | |
16 | 16 | var ajaxCategories = { |
17 | | - |
18 | 17 | handleAddLink : function(e) { |
19 | 18 | e.preventDefault(); |
20 | | - |
| 19 | + |
21 | 20 | // Make sure the suggestion plugin is loaded. Load everything else while we're at it |
22 | | - mvJsLoader.doLoad( ['$j.ui', '$j.ui.dialog', '$j.fn.suggestions'], |
| 21 | + mvJsLoader.doLoad( |
| 22 | + ['$j.ui', '$j.ui.dialog', '$j.fn.suggestions'], |
23 | 23 | function() { |
24 | 24 | $j('#mw-addcategory-prompt').toggle(); |
25 | | - |
| 25 | + |
26 | 26 | $j('#mw-addcategory-input').suggestions( { |
27 | | - 'fetch':ajaxCategories.fetchSuggestions, |
28 | | - 'cancel': function() { |
29 | | - var req = ajaxCategories.request; |
30 | | - if (req.abort) |
31 | | - req.abort() |
32 | | - }, |
33 | | - } ); |
34 | | - |
| 27 | + 'fetch':ajaxCategories.fetchSuggestions, |
| 28 | + 'cancel': function() { |
| 29 | + var req = ajaxCategories.request; |
| 30 | + if (req.abort) |
| 31 | + req.abort() |
| 32 | + }, |
| 33 | + } ); |
| 34 | + |
35 | 35 | $j('#mw-addcategory-input').suggestions(); |
36 | | - } ); |
| 36 | + } |
| 37 | + ); |
37 | 38 | }, |
38 | | - |
| 39 | + |
39 | 40 | fetchSuggestions : function( query ) { |
40 | 41 | var that = this; |
41 | 42 | var request = $j.ajax( { |
— | — | @@ -51,44 +52,49 @@ |
52 | 53 | // Process data.query.allpages into an array of titles |
53 | 54 | var pages = data.query.allpages; |
54 | 55 | var titleArr = []; |
55 | | - |
| 56 | + |
56 | 57 | $j.each(pages, function(i, page) { |
57 | 58 | var title = page.title.split( ':', 2 )[1]; |
58 | 59 | titleArr.push(title); |
59 | 60 | } ); |
60 | | - |
| 61 | + |
61 | 62 | $j(that).suggestions( 'suggestions', titleArr ); |
62 | 63 | } |
63 | 64 | }); |
64 | | - |
| 65 | + |
65 | 66 | ajaxCategories.request = request; |
66 | 67 | }, |
67 | | - |
| 68 | + |
68 | 69 | reloadCategoryList : function( response ) { |
69 | 70 | var holder = $j('<div/>'); |
70 | | - |
71 | | - holder.load( window.location.href+' .catlinks', function() { |
72 | | - $j('.catlinks').replaceWith( holder.find('.catlinks') ); |
73 | | - ajaxCategories.setupAJAXCategories(); |
74 | | - ajaxCategories.removeProgressIndicator( $j('.catlinks') ); |
75 | | - }); |
| 71 | + |
| 72 | + holder.load( |
| 73 | + window.location.href+' .catlinks', |
| 74 | + function() { |
| 75 | + $j('.catlinks').replaceWith( holder.find('.catlinks') ); |
| 76 | + ajaxCategories.setupAJAXCategories(); |
| 77 | + ajaxCategories.removeProgressIndicator( $j('.catlinks') ); |
| 78 | + } |
| 79 | + ); |
76 | 80 | }, |
77 | | - |
| 81 | + |
78 | 82 | confirmEdit : function( page, fn, actionSummary, doneFn ) { |
79 | 83 | // Load jQuery UI |
80 | | - mvJsLoader.doLoad( ['$j.ui', '$j.ui.dialog', '$j.fn.suggestions'], function() { |
| 84 | + mvJsLoader.doLoad( |
| 85 | + ['$j.ui', '$j.ui.dialog', '$j.fn.suggestions'], |
| 86 | + function() { |
81 | 87 | // Produce a confirmation dialog |
82 | | - |
| 88 | + |
83 | 89 | var dialog = $j('<div/>'); |
84 | | - |
| 90 | + |
85 | 91 | dialog.addClass('mw-ajax-confirm-dialog'); |
86 | 92 | dialog.attr( 'title', gM('ajax-confirm-title') ); |
87 | | - |
| 93 | + |
88 | 94 | // Intro text. |
89 | 95 | var confirmIntro = $j('<p/>'); |
90 | 96 | confirmIntro.text( gM('ajax-confirm-prompt') ); |
91 | 97 | dialog.append(confirmIntro); |
92 | | - |
| 98 | + |
93 | 99 | // Summary of the action to be taken |
94 | 100 | var summaryHolder = $j('<p/>'); |
95 | 101 | var summaryLabel = $j('<strong/>'); |
— | — | @@ -96,63 +102,70 @@ |
97 | 103 | summaryHolder.text( actionSummary ); |
98 | 104 | summaryHolder.prepend( summaryLabel ); |
99 | 105 | dialog.append(summaryHolder); |
100 | | - |
| 106 | + |
101 | 107 | // Reason textbox. |
102 | 108 | var reasonBox = $j('<input type="text" size="45" />'); |
103 | 109 | reasonBox.addClass('mw-ajax-confirm-reason'); |
104 | 110 | dialog.append(reasonBox); |
105 | | - |
| 111 | + |
106 | 112 | // Submit button |
107 | 113 | var submitButton = $j('<input type="button"/>'); |
108 | 114 | submitButton.val( gM( 'ajax-confirm-save' ) ); |
109 | | - |
| 115 | + |
110 | 116 | var submitFunction = function() { |
111 | | - ajaxCategories.addProgressIndicator( dialog ); |
112 | | - ajaxCategories.doEdit( page, fn, reasonBox.val(), |
113 | | - function() { |
114 | | - doneFn(); |
115 | | - dialog.dialog('close'); |
116 | | - ajaxCategories.removeProgressIndicator( dialog ); |
117 | | - } |
118 | | - ); |
119 | | - }; |
120 | | - |
| 117 | + ajaxCategories.addProgressIndicator( dialog ); |
| 118 | + ajaxCategories.doEdit( |
| 119 | + page, |
| 120 | + fn, |
| 121 | + reasonBox.val(), |
| 122 | + function() { |
| 123 | + doneFn(); |
| 124 | + dialog.dialog('close'); |
| 125 | + ajaxCategories.removeProgressIndicator( dialog ); |
| 126 | + } |
| 127 | + ); |
| 128 | + }; |
| 129 | + |
121 | 130 | var buttons = {}; |
122 | 131 | buttons[gM('ajax-confirm-save')] = submitFunction; |
123 | | - var dialogOptions = { |
124 | | - 'AutoOpen' : true, |
125 | | - 'buttons' : buttons, |
126 | | - 'width' : 450, |
127 | | - }; |
128 | | - |
| 132 | + var dialogOptions = { |
| 133 | + 'AutoOpen' : true, |
| 134 | + 'buttons' : buttons, |
| 135 | + 'width' : 450, |
| 136 | + }; |
| 137 | + |
129 | 138 | $j('#catlinks').prepend(dialog); |
130 | 139 | dialog.dialog( dialogOptions ); |
131 | | - } ); |
| 140 | + } |
| 141 | + ); |
132 | 142 | }, |
133 | | - |
| 143 | + |
134 | 144 | doEdit : function( page, fn, summary, doneFn ) { |
135 | 145 | // Get an edit token for the page. |
136 | 146 | var getTokenVars = { |
137 | | - 'action':'query', |
138 | | - 'prop':'info|revisions', |
139 | | - 'intoken':'edit', |
140 | | - 'titles':page, |
141 | | - 'rvprop':'content|timestamp', |
142 | | - 'format':'json', |
143 | | - }; |
144 | | - $j.get(wgScriptPath+'/api.php', getTokenVars, |
| 147 | + 'action':'query', |
| 148 | + 'prop':'info|revisions', |
| 149 | + 'intoken':'edit', |
| 150 | + 'titles':page, |
| 151 | + 'rvprop':'content|timestamp', |
| 152 | + 'format':'json', |
| 153 | + }; |
| 154 | + |
| 155 | + $j.get(wgScriptPath+'/api.php', getTokenVars, |
145 | 156 | function( reply ) { |
146 | 157 | var infos = reply.query.pages; |
147 | | - $j.each(infos, function(pageid, data) { |
148 | | - var token = data.edittoken; |
149 | | - var timestamp = data.revisions[0].timestamp; |
150 | | - var oldText = data.revisions[0]['*']; |
151 | | - |
152 | | - var newText = fn(oldText); |
153 | | - |
154 | | - if (newText === false) return; |
155 | | - |
156 | | - var postEditVars = { |
| 158 | + $j.each( |
| 159 | + infos, |
| 160 | + function(pageid, data) { |
| 161 | + var token = data.edittoken; |
| 162 | + var timestamp = data.revisions[0].timestamp; |
| 163 | + var oldText = data.revisions[0]['*']; |
| 164 | + |
| 165 | + var newText = fn(oldText); |
| 166 | + |
| 167 | + if (newText === false) return; |
| 168 | + |
| 169 | + var postEditVars = { |
157 | 170 | 'action':'edit', |
158 | 171 | 'title':page, |
159 | 172 | 'text':newText, |
— | — | @@ -160,43 +173,48 @@ |
161 | 174 | 'token':token, |
162 | 175 | 'basetimestamp':timestamp, |
163 | 176 | 'format':'json', |
164 | | - }; |
165 | | - |
166 | | - $j.post( wgScriptPath+'/api.php', postEditVars, doneFn, 'json' ); |
167 | | - } ); |
| 177 | + }; |
| 178 | + |
| 179 | + $j.post( wgScriptPath+'/api.php', postEditVars, doneFn, 'json' ); |
| 180 | + } |
| 181 | + ); |
168 | 182 | } |
169 | 183 | , 'json' ); |
170 | 184 | }, |
171 | | - |
| 185 | + |
172 | 186 | addProgressIndicator : function( elem ) { |
173 | 187 | var indicator = $j('<div/>'); |
174 | | - |
| 188 | + |
175 | 189 | indicator.addClass('mw-ajax-loader'); |
176 | | - |
| 190 | + |
177 | 191 | elem.append( indicator ); |
178 | 192 | }, |
179 | | - |
| 193 | + |
180 | 194 | removeProgressIndicator : function( elem ) { |
181 | 195 | elem.find('.mw-ajax-loader').remove(); |
182 | 196 | }, |
183 | | - |
| 197 | + |
184 | 198 | handleCategoryAdd : function(e) { |
185 | 199 | // Grab category text |
186 | 200 | var category = $j('#mw-addcategory-input').val(); |
187 | 201 | var appendText = "\n[["+wgFormattedNamespaces[14]+":"+category+"]]\n"; |
188 | 202 | var summary = gM('ajax-add-category-summary', category); |
189 | | - |
190 | | - ajaxCategories.confirmEdit( wgPageName, function(oldText) { return oldText+appendText }, |
191 | | - summary, ajaxCategories.reloadCategoryList ); |
| 203 | + |
| 204 | + ajaxCategories.confirmEdit( |
| 205 | + wgPageName, |
| 206 | + function(oldText) { return oldText+appendText }, |
| 207 | + summary, |
| 208 | + ajaxCategories.reloadCategoryList |
| 209 | + ); |
192 | 210 | }, |
193 | | - |
| 211 | + |
194 | 212 | handleDeleteLink : function(e) { |
195 | 213 | e.preventDefault(); |
196 | | - |
| 214 | + |
197 | 215 | var category = $j(this).parent().find('a').text(); |
198 | | - |
| 216 | + |
199 | 217 | // Build a regex that matches legal invocations of that category. |
200 | | - |
| 218 | + |
201 | 219 | // In theory I should escape the aliases, but there's no JS function for it |
202 | 220 | // Shouldn't have any real impact, can't be exploited or anything, so we'll |
203 | 221 | // leave it for now. |
— | — | @@ -205,46 +223,48 @@ |
206 | 224 | if (id == 14) { |
207 | 225 | // Allow the first character to be any case |
208 | 226 | var firstChar = name.charAt(0); |
209 | | - firstChar = '['+firstChar.toUpperCase()+firstChar.toLowerCase()+']' |
| 227 | + firstChar = '['+firstChar.toUpperCase()+firstChar.toLowerCase()+']'; |
210 | 228 | categoryNSFragment += '|'+firstChar+name.substr(1); |
211 | 229 | } |
212 | 230 | } ); |
213 | | - categoryNSFragment = categoryNSFragment.substr(1) // Remove leading | |
214 | | - |
215 | | - |
| 231 | + categoryNSFragment = categoryNSFragment.substr(1); // Remove leading | |
| 232 | + |
216 | 233 | // Build the regex |
217 | 234 | var titleFragment = category; |
218 | | - |
| 235 | + |
219 | 236 | firstChar = category.charAt(0); |
220 | 237 | firstChar = '['+firstChar.toUpperCase()+firstChar.toLowerCase()+']'; |
221 | 238 | titleFragment = firstChar+category.substr(1); |
222 | 239 | var categoryRegex = '\\[\\['+categoryNSFragment+':'+titleFragment+'(\\|[^\\]]*)?\\]\\]'; |
223 | 240 | categoryRegex = new RegExp( categoryRegex, 'g' ); |
224 | | - |
| 241 | + |
225 | 242 | var summary = gM('ajax-remove-category-summary', category); |
226 | | - |
227 | | - ajaxCategories.confirmEdit( wgPageName, |
228 | | - function(oldText) { |
229 | | - var newText = oldText.replace(categoryRegex, ''); |
230 | | - |
231 | | - if (newText == oldText) { |
232 | | - var error = gM('ajax-remove-category-error'); |
233 | | - ajaxCategories.showError( error ); |
234 | | - ajaxCategories.removeProgressIndicator( $j('.mw-ajax-confirm-dialog') ); |
235 | | - $j('.mw-ajax-confirm-dialog').dialog('close'); |
236 | | - return false; |
237 | | - } |
238 | | - |
239 | | - return newText; |
240 | | - }, summary, ajaxCategories.reloadCategoryList ); |
| 243 | + |
| 244 | + ajaxCategories.confirmEdit( |
| 245 | + wgPageName, |
| 246 | + function(oldText) { |
| 247 | + var newText = oldText.replace(categoryRegex, ''); |
| 248 | + |
| 249 | + if (newText == oldText) { |
| 250 | + var error = gM('ajax-remove-category-error'); |
| 251 | + ajaxCategories.showError( error ); |
| 252 | + ajaxCategories.removeProgressIndicator( $j('.mw-ajax-confirm-dialog') ); |
| 253 | + $j('.mw-ajax-confirm-dialog').dialog('close'); |
| 254 | + return false; |
| 255 | + } |
| 256 | + |
| 257 | + return newText; |
| 258 | + }, |
| 259 | + summary, ajaxCategories.reloadCategoryList |
| 260 | + ); |
241 | 261 | }, |
242 | | - |
| 262 | + |
243 | 263 | showError : function( str ) { |
244 | 264 | var dialog = $j('<div/>'); |
245 | 265 | dialog.text(str); |
246 | | - |
| 266 | + |
247 | 267 | $j('#bodyContent').append(dialog); |
248 | | - |
| 268 | + |
249 | 269 | var buttons = {}; |
250 | 270 | buttons[gM('ajax-error-dismiss')] = function(e) { dialog.dialog('close'); }; |
251 | 271 | var dialogOptions = { |
— | — | @@ -252,54 +272,53 @@ |
253 | 273 | 'AutoOpen' : true, |
254 | 274 | 'title' : gM('ajax-error-title'), |
255 | 275 | }; |
256 | | - |
| 276 | + |
257 | 277 | dialog.dialog(dialogOptions); |
258 | 278 | }, |
259 | | - |
| 279 | + |
260 | 280 | setupAJAXCategories : function() { |
261 | 281 | // Only do it for articles. |
262 | 282 | if ( !wgIsArticle ) return; |
263 | | - |
| 283 | + |
264 | 284 | var clElement = $j('.catlinks'); |
265 | | - |
| 285 | + |
266 | 286 | // Unhide hidden category holders. |
267 | 287 | clElement.removeClass( 'catlinks-allhidden' ); |
268 | | - |
| 288 | + |
269 | 289 | var addLink = $j('<a/>'); |
270 | 290 | addLink.addClass( 'mw-ajax-addcategory' ); |
271 | | - |
| 291 | + |
272 | 292 | // Create [Add Category] link |
273 | 293 | addLink.text( gM( 'ajax-add-category' ) ); |
274 | 294 | addLink.attr('href', '#'); |
275 | 295 | addLink.click( ajaxCategories.handleAddLink ); |
276 | 296 | clElement.append(addLink); |
277 | | - |
| 297 | + |
278 | 298 | // Create add category prompt |
279 | 299 | var promptContainer = $j('<div id="mw-addcategory-prompt"/>'); |
280 | 300 | var promptTextbox = $j('<input type="text" size="45" id="mw-addcategory-input"/>'); |
281 | 301 | var addButton = $j('<input type="button" id="mw-addcategory-button"/>' ); |
282 | 302 | addButton.val( gM('ajax-add-category-submit') ); |
283 | | - |
| 303 | + |
284 | 304 | promptTextbox.keypress( ajaxCategories.handleCategoryInput ); |
285 | 305 | addButton.click( ajaxCategories.handleCategoryAdd ); |
286 | | - |
| 306 | + |
287 | 307 | promptContainer.append(promptTextbox); |
288 | 308 | promptContainer.append(addButton); |
289 | 309 | promptContainer.hide(); |
290 | | - |
| 310 | + |
291 | 311 | // Create delete link for each category. |
292 | 312 | $j('.catlinks div span a').each( function(e) { |
293 | | - // Create a remove link |
294 | | - var deleteLink = $j('<a class="mw-remove-category" href="#"/>'); |
295 | | - |
296 | | - deleteLink.click(ajaxCategories.handleDeleteLink); |
297 | | - |
298 | | - $j(this).after(deleteLink); |
299 | | - } ); |
300 | | - |
| 313 | + // Create a remove link |
| 314 | + var deleteLink = $j('<a class="mw-remove-category" href="#"/>'); |
| 315 | + |
| 316 | + deleteLink.click(ajaxCategories.handleDeleteLink); |
| 317 | + |
| 318 | + $j(this).after(deleteLink); |
| 319 | + } ); |
| 320 | + |
301 | 321 | clElement.append(promptContainer); |
302 | 322 | }, |
303 | | - |
304 | 323 | }; |
305 | 324 | |
306 | 325 | js2AddOnloadHook( ajaxCategories.setupAJAXCategories ); |