Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.Language.js |
— | — | @@ -2,8 +2,10 @@ |
3 | 3 | 'mwe-code-unknown': 'Unknown language' |
4 | 4 | }); |
5 | 5 | |
6 | | - |
7 | | -// TODO: make this a more common library, used by this and TimedText |
| 6 | +/** |
| 7 | + * Utility class which knows about languages, and how to construct HTML to select them |
| 8 | + * TODO: make this a more common library, used by this and TimedText |
| 9 | + */ |
8 | 10 | mw.Language = { |
9 | 11 | |
10 | 12 | defaultCode: 'en', // when we absolutely have no idea what language to preselect |
— | — | @@ -379,68 +381,84 @@ |
380 | 382 | { code: "got", text: "\ud800\udf32\ud800\udf3f\ud800\udf44\ud800\udf39\ud800\udf43\ud800\udf3a" }, |
381 | 383 | ], |
382 | 384 | |
383 | | - // cache some useful objects |
384 | | - // 1) mostly ready-to-go language HTML menu. When/if we upgrade, make it a jQuery combobox |
385 | | - // 2) dict of language code to name -- useful for testing for existence, maybe other things. |
| 385 | + /** |
| 386 | + * cache some useful objects |
| 387 | + * 1) mostly ready-to-go language HTML menu. When/if we upgrade, make it a jQuery combobox |
| 388 | + * 2) dict of language code to name -- useful for testing for existence, maybe other things. |
| 389 | + */ |
386 | 390 | initialize: function() { |
387 | | - if (mw.Language.initialized) { |
| 391 | + if ( mw.Language.initialized ) { |
388 | 392 | return; |
389 | 393 | } |
390 | 394 | mw.Language._codes = {}; |
391 | | - var select = $j('<select/>'); |
392 | | - $j.each(mw.Language.languages, function(i, language) { |
| 395 | + var select = $j( '<select/>' ); |
| 396 | + $j.each( mw.Language.languages, function( i, language ) { |
393 | 397 | select.append( |
394 | | - $j('<option>') |
395 | | - .attr('value', language.code) |
396 | | - .append(language.text) |
| 398 | + $j( '<option>' ) |
| 399 | + .attr( 'value', language.code ) |
| 400 | + .append( language.text ) |
397 | 401 | ); |
398 | 402 | mw.Language._codes[language.code] = language.text; |
399 | | - }); |
| 403 | + } ); |
400 | 404 | mw.Language.$_select = select; |
401 | 405 | mw.Language.initialized = true; |
402 | 406 | }, |
403 | 407 | |
404 | | - getMenu: function(name, code) { |
| 408 | + /** |
| 409 | + * Get an HTML select menu of all our languages. |
| 410 | + * @param name desired name of select element |
| 411 | + * @param code desired default language code |
| 412 | + * @return HTML select element configured as desired |
| 413 | + */ |
| 414 | + getMenu: function( name, code ) { |
405 | 415 | mw.Language.initialize(); |
406 | 416 | var $select = mw.Language.$_select.clone(); |
407 | | - $select.attr('name', name); |
408 | | - if (code === mw.Language.UNKNOWN) { |
| 417 | + $select.attr( 'name', name ); |
| 418 | + if ( code === mw.Language.UNKNOWN ) { |
409 | 419 | // n.b. MediaWiki LanguageHandler has ability to add custom label for 'Unknown'; possibly as pseudo-label |
410 | | - $select.prepend($j('<option>').attr('value', mw.Language.UNKNOWN).append(gM('mwe-code-unknown'))); |
411 | | - $select.val(mw.Language.UNKNOWN); |
412 | | - } else if (code !== undefined) { |
413 | | - $select.val(mw.Language.getClosest(code)); |
| 420 | + $select.prepend( $j( '<option>' ).attr( 'value', mw.Language.UNKNOWN ).append( gM( 'mwe-code-unknown' )) ); |
| 421 | + $select.val( mw.Language.UNKNOWN ); |
| 422 | + } else if ( code !== undefined ) { |
| 423 | + $select.val( mw.Language.getClosest( code )); |
414 | 424 | } |
415 | | - return $select.get(0); |
| 425 | + return $select.get( 0 ); |
416 | 426 | }, |
417 | 427 | |
418 | | - /* logic from MediaWiki:LanguageHandler.js */ |
419 | | - // handle null cases, special cases for some Chinese variants |
420 | | - // Otherwise, if handed "foo-bar-baz" language, try to match most specific language, |
421 | | - // "foo-bar-baz", then "foo-bar", then "foo" |
422 | | - getClosest: function(code) { |
| 428 | + /** |
| 429 | + * Figure out the closest language we have to a supplied language code. |
| 430 | + * It seems that people on Mediawiki set their language code as freetext, and it could be anything, even |
| 431 | + * variants we don't have a record for, or ones that are not in any ISO standard. |
| 432 | + * |
| 433 | + * Logic copied from MediaWiki:LanguageHandler.js |
| 434 | + * handle null cases, special cases for some Chinese variants |
| 435 | + * Otherwise, if handed "foo-bar-baz" language, try to match most specific language, |
| 436 | + * "foo-bar-baz", then "foo-bar", then "foo" |
| 437 | + * |
| 438 | + * @param code A string representing a language code, which we may or may not have. |
| 439 | + * Expected to be separated with dashes as codes from ISO 639, e.g. "zh-tw" for Chinese ( Traditional ) |
| 440 | + * @return a language code which is close to the supplied parameter, or fall back to mw.Language.defaultCode |
| 441 | + */ |
| 442 | + getClosest: function( code ) { |
423 | 443 | mw.Language.initialize(); |
424 | | - if (typeof (code) != 'string' || code === null || code.length === 0) { |
| 444 | + if ( typeof ( code ) != 'string' || code === null || code.length === 0 ) { |
425 | 445 | return mw.Language.defaultCode; |
426 | 446 | } |
427 | | - if (code == 'nan' || code == 'minnan') { |
| 447 | + if ( code == 'nan' || code == 'minnan' ) { |
428 | 448 | return 'zh-min-nan'; |
429 | | - } else if (mw.Language._codes[code] !== undefined) { |
| 449 | + } else if ( mw.Language._codes[code] !== undefined ) { |
430 | 450 | return code; |
431 | 451 | } |
432 | | - return mw.Language.getClosest(code.substring(0, code.indexOf('-'))); |
| 452 | + return mw.Language.getClosest( code.substring( 0, code.indexOf( '-' )) ); |
433 | 453 | }, |
434 | 454 | |
435 | | - // XXX n.b. there are a lot of "closest matching language" features in older MediaWiki:LanguageHandler; we will have to emulate |
436 | 455 | |
437 | | - |
438 | 456 | // enhance a simple text input to be an autocompleting language menu |
439 | 457 | // this will work when/if we move to jQuery 1.4. As of now the autocomplete is too underpowered for our needs without |
440 | 458 | // serious hackery |
441 | 459 | /* |
442 | | - $j.fn.languageMenu = function(options) { |
| 460 | + $j.fn.languageMenu = function( options ) { |
443 | 461 | var _this = this; |
444 | | - _this.autocomplete(null, { |
| 462 | + _this.autocomplete( null, { |
445 | 463 | minChars: 0, |
446 | 464 | width: 310, |
447 | 465 | selectFirst: true, |
— | — | @@ -450,16 +468,16 @@ |
451 | 469 | highlightItem: true, |
452 | 470 | scroll: true, |
453 | 471 | scrollHeight: 220, |
454 | | - formatItem: function(row, i, max, term) { |
| 472 | + formatItem: function( row, i, max, term ) { |
455 | 473 | return row.code + " " + row.code; |
456 | 474 | }, |
457 | | - formatMatch: function(row, i, max, term) { |
| 475 | + formatMatch: function( row, i, max, term ) { |
458 | 476 | return row.code + " " + row.code; |
459 | 477 | }, |
460 | | - formatResult: function(row) { |
| 478 | + formatResult: function( row ) { |
461 | 479 | return row.code; |
462 | 480 | } |
463 | | - }, mw.languages); |
| 481 | + }, mw.languages ); |
464 | 482 | |
465 | 483 | // and add a dropdown so we can see the thingy, too |
466 | 484 | return _this; |
— | — | @@ -467,7 +485,7 @@ |
468 | 486 | */ |
469 | 487 | |
470 | 488 | // XXX the concept of "internal language" exists in UploadForm.js -- seems to be how they handled i18n, with |
471 | | - // language codes that has underscores rather than dashes, ("en_gb" rather than the correct "en-gb"). |
| 489 | + // language codes that has underscores rather than dashes, ( "en_gb" rather than the correct "en-gb" ). |
472 | 490 | // although other info such as Information boxes was recorded correctly. |
473 | 491 | // This is presumed not to apply to the shiny new world of JS2, where i18n is handled in other ways. |
474 | 492 | |
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.UploadWizard.js |
— | — | @@ -1,4 +1,4 @@ |
2 | | -mw.addMessages({ |
| 2 | +mw.addMessages( { |
3 | 3 | 'mwe-upwiz-tab-file': 'Step 1', |
4 | 4 | 'mwe-upwiz-tab-metadata': 'Step 2', |
5 | 5 | 'mwe-upwiz-tab-thanks': 'Step 3', |
— | — | @@ -54,22 +54,22 @@ |
55 | 55 | |
56 | 56 | // available licenses should be a configuration of the MediaWiki instance, |
57 | 57 | // not hardcoded here. |
58 | | - // but, MediaWiki has no real concept of a License as a first class object -- there are templates and then specially-parsed |
| 58 | + // but, MediaWiki has no real concept of a License as a first class object -- there are templates and then specially - parsed |
59 | 59 | // texts to create menus -- hack on top of hacks -- a bit too much to deal with ATM |
60 | 60 | 'mwe-lic-pd': 'Public domain', |
61 | 61 | 'mwe-lic-cc-0': 'Creative Commons Zero waiver', |
62 | 62 | 'mwe-lic-cc-by-3.0': 'Creative Commons Attribution 3.0', |
63 | 63 | 'mwe-lic-cc-by-sa-3.0': 'Creative Commons Attribution ShareAlike 3.0', |
64 | 64 | 'mwe-lic-gfdl': 'GFDL' |
65 | | -}); |
| 65 | +} ); |
66 | 66 | |
67 | 67 | |
68 | 68 | |
69 | | -// this interface only works for the wizard... should say so in class name |
70 | | -// XXX there is a CSS/scripting trick to get X-browser consistent file input, plus one-click file input |
71 | | -// It will not work in Netscape 4, Explorer 4, or Netscape 3. |
72 | | -// We probably don't care, but... http://www.quirksmode.org/dom/inputfile.html in case we do |
73 | | -mw.UploadWizardUploadInterface = function(filenameAcceptedCb) { |
| 69 | +/** |
| 70 | + * Create an interface fragment corresponding to a file input, suitable for Upload Wizard. |
| 71 | + * @param filenameAcceptedCb Execute if good filename entered into this interface; useful for knowing if we're ready to upload |
| 72 | + */ |
| 73 | +mw.UploadWizardUploadInterface = function( filenameAcceptedCb ) { |
74 | 74 | var _this = this; |
75 | 75 | |
76 | 76 | _this.filenameAcceptedCb = filenameAcceptedCb; |
— | — | @@ -89,133 +89,160 @@ |
90 | 90 | |
91 | 91 | _this.form = $j('<form class="mwe-upwiz-form"></form>') |
92 | 92 | .append($j('<div class="mwe-upwiz-file-ctrl-container">') |
93 | | - .append(_this.fileInputCtrl) |
94 | | - .append(_this.visibleFilename) |
95 | | - ).append(_this.filenameCtrl).get(0); |
| 93 | + .append( _this.fileInputCtrl ) |
| 94 | + .append( _this.visibleFilename ) |
| 95 | + ).append( _this.filenameCtrl ).get( 0 ); |
96 | 96 | |
97 | 97 | _this.progressMessage = $j('<span class="mwe-upwiz-status-message" style="display: none"></span>').get(0); |
98 | 98 | |
99 | | - $j(_this.fileInputCtrl).change( function() { _this.fileChanged() } ); |
| 99 | + $j( _this.fileInputCtrl ).change( function() { _this.fileChanged() } ); |
100 | 100 | |
101 | 101 | _this.errorDiv = $j('<div class="mwe-upwiz-upload-error" style="display: none;"></div>').get(0); |
102 | 102 | |
103 | 103 | |
104 | | - $j(_this.div).append(_this.form) |
105 | | - .append(_this.progressMessage) |
106 | | - .append(_this.errorDiv); |
| 104 | + $j( _this.div ).append( _this.form ) |
| 105 | + .append( _this.progressMessage ) |
| 106 | + .append( _this.errorDiv ); |
107 | 107 | |
108 | | - // _this.progressBar = (no progress bar for individual uploads yet) |
| 108 | + // _this.progressBar = ( no progress bar for individual uploads yet ) |
109 | 109 | // add a metadata thing to metadata |
110 | 110 | }; |
111 | 111 | |
112 | 112 | mw.UploadWizardUploadInterface.prototype = { |
113 | | - /* start! */ |
| 113 | + /** |
| 114 | + * Things to do to this interface once we start uploading |
| 115 | + */ |
114 | 116 | start: function() { |
115 | 117 | var _this = this; |
116 | | - $j(_this.removeCtrl).hide(); |
| 118 | + $j( _this.removeCtrl ).hide(); |
117 | 119 | }, |
118 | 120 | |
119 | | - /* generically busy, but not a fraction. Encoding, or transports that don't know progress */ |
| 121 | + /** |
| 122 | + * Make this interface look "busy" (i.e. spinner) without indicating a particular percentage of file uploaded. |
| 123 | + * Will be useful for encoding phase of Firefogg, for example. |
| 124 | + */ |
120 | 125 | busy: function() { |
121 | 126 | var _this = this; |
122 | 127 | // for now we implement this as looking like "100% progress" |
123 | 128 | // e.g. an animated bar that takes up all the space |
124 | | - _this.progress(1.0); |
| 129 | + _this.progress( 1.0 ); |
125 | 130 | }, |
126 | 131 | |
127 | | - /* show progress with a certain fraction */ |
128 | | - progress: function(fraction) { |
| 132 | + /** |
| 133 | + * Show progress by a fraction |
| 134 | + * @param fraction The fraction of progress. Float between 0 and 1 |
| 135 | + */ |
| 136 | + progress: function( fraction ) { |
129 | 137 | var _this = this; |
130 | | - $j(_this.progressMessage).addClass('mwe-upwiz-status-progress') |
131 | | - .html(gM( 'mwe-upwiz-uploading' )) |
132 | | - .show(); |
| 138 | + $j( _this.progressMessage ).addClass('mwe-upwiz-status-progress') |
| 139 | + .html(gM( 'mwe-upwiz-uploading' )) |
| 140 | + .show(); |
133 | 141 | // update individual progress bar with fraction? |
134 | 142 | }, |
135 | 143 | |
136 | | - // this is just completed in the sense that it's all uploaded. There may be other errors? |
137 | | - // really need to rethink the UI / metadata separation |
138 | | - completed: function(result) { |
| 144 | + /** |
| 145 | + * Execute when this upload is completed; cleans up interface. |
| 146 | + * @param result AJAx result object |
| 147 | + */ |
| 148 | + completed: function( result ) { |
139 | 149 | var _this = this; |
140 | | - $j(_this.progressMessage).removeClass('mwe-upwiz-status-progress') |
141 | | - .addClass('mwe-upwiz-status-completed') |
142 | | - .html(gM( 'mwe-upwiz-completed' )); |
| 150 | + $j( _this.progressMessage ).removeClass( 'mwe-upwiz-status-progress' ) |
| 151 | + .addClass( 'mwe-upwiz-status-completed' ) |
| 152 | + .html( gM( 'mwe-upwiz-completed' ) ); |
143 | 153 | }, |
144 | 154 | |
| 155 | + /** |
| 156 | + * Run this when the value of the file input has changed. Check the file for various forms of goodness. |
| 157 | + */ |
145 | 158 | fileChanged: function() { |
146 | 159 | var _this = this; |
147 | 160 | _this.clearErrors(); |
148 | 161 | var ext = _this.getExtension(); |
149 | | - if (_this.isGoodExtension(ext)) { |
| 162 | + if ( _this.isGoodExtension( ext ) ) { |
150 | 163 | _this.updateFilename(); |
151 | 164 | } else { |
152 | | - _this.error('bad-filename-extension', ext); |
| 165 | + _this.error( 'bad-filename-extension', ext ); |
153 | 166 | } |
154 | 167 | }, |
155 | 168 | |
156 | | - // this does two things: |
157 | | - // 1) since the file input has been hidden with some clever CSS (to avoid x-browser styling issues), |
158 | | - // update the visible filename |
159 | | - // |
160 | | - // 2) update the filename desired when added to MediaWiki. This should be RELATED to the filename on the filesystem, |
161 | | - // but it should be silently fixed so that it does not trigger uniqueness conflicts. i.e. if server has cat.jpg we change ours to cat_2.jpg. |
162 | | - // This is hard to do in a scalable fashion on the client; we don't want to do 12 api calls to get cat_12.jpg. |
163 | | - // Ideally we should ask the SERVER for a decently unique filename related to our own. |
164 | | - // So, at the moment, this is hacked with a guaranteed-unique filename instead. |
| 169 | + /** |
| 170 | + * this does two things: |
| 171 | + * 1 ) since the file input has been hidden with some clever CSS ( to avoid x-browser styling issues ), |
| 172 | + * update the visible filename |
| 173 | + * |
| 174 | + * 2 ) update the filename desired when added to MediaWiki. This should be RELATED to the filename on the filesystem, |
| 175 | + * but it should be silently fixed so that it does not trigger uniqueness conflicts. i.e. if server has cat.jpg we change ours to cat_2.jpg. |
| 176 | + * This is hard to do in a scalable fashion on the client; we don't want to do 12 api calls to get cat_12.jpg. |
| 177 | + * Ideally we should ask the SERVER for a decently unique filename related to our own. |
| 178 | + * So, at the moment, this is hacked with a guaranteed - unique filename instead. |
| 179 | + */ |
165 | 180 | updateFilename: function() { |
166 | 181 | var _this = this; |
167 | 182 | var path = $j(_this.fileInputCtrl).attr('value'); |
168 | 183 | |
169 | 184 | // visible filename |
170 | | - $j(_this.visibleFilename).removeClass('helper').html(path); |
| 185 | + $j( _this.visibleFilename ).removeClass( 'helper' ).html( path ); |
171 | 186 | |
172 | 187 | // desired filename |
173 | | - var filename = _this.convertPathToFilename(path); |
| 188 | + var filename = _this.convertPathToFilename( path ); |
174 | 189 | // this is a hack to get a filename guaranteed unique. |
175 | | - uniqueFilename = mw.getConfig('userName') + "_" + (new Date()).getTime() + "_" + filename; |
176 | | - $j(_this.filenameCtrl).attr('value', uniqueFilename); |
| 190 | + uniqueFilename = mw.getConfig( 'userName' ) + "_" + ( new Date() ).getTime() + "_" + filename; |
| 191 | + $j(_this.filenameCtrl).attr( 'value', uniqueFilename ); |
177 | 192 | _this.filenameAcceptedCb(); |
178 | 193 | }, |
179 | 194 | |
| 195 | + /** |
| 196 | + * Remove any complaints we had about errors and such |
| 197 | + * XXX this should be changed to something Theme compatible |
| 198 | + */ |
180 | 199 | clearErrors: function() { |
181 | 200 | var _this = this; |
182 | | - // XXX this should be changed to something Theme compatible |
183 | | - $j(_this.div).removeClass('mwe-upwiz-upload-error'); |
184 | | - $j(_this.errorDiv).hide().empty(); |
| 201 | + $j( _this.div ).removeClass( 'mwe-upwiz-upload-error '); |
| 202 | + $j( _this.errorDiv ).hide().empty(); |
185 | 203 | }, |
186 | 204 | |
| 205 | + /** |
| 206 | + * Show an error with the upload |
| 207 | + */ |
187 | 208 | error: function() { |
188 | 209 | var _this = this; |
189 | | - var args = Array.prototype.slice.call(arguments); // copies arguments into a real array |
| 210 | + var args = Array.prototype.slice.call( arguments ); // copies arguments into a real array |
190 | 211 | var msg = 'mwe-upwiz-upload-error-' + args[0]; |
191 | | - $j(_this.errorDiv).append($j('<p class="mwe-upwiz-upload-error">' + gM(msg, args.slice(1)) + '</p>')); |
| 212 | + $j( _this.errorDiv ).append( $j( '<p class="mwe-upwiz-upload-error">' + gM( msg, args.slice( 1 ) ) + '</p>') ); |
192 | 213 | // apply a error style to entire did |
193 | | - $j(_this.div).addClass('mwe-upwiz-upload-error'); |
194 | | - $j(_this.errorDiv).show(); |
| 214 | + $j( _this.div ).addClass( 'mwe-upwiz-upload-error' ); |
| 215 | + $j( _this.errorDiv ).show(); |
195 | 216 | }, |
196 | 217 | |
197 | | - // arguably should be about filename, not path? |
198 | | - // may check for common bad patterns here, like DSC_NNNNN, filenames too short, too long, etc. |
199 | | - // steal that from the commons js |
| 218 | + /** |
| 219 | + * Get the extension of the path in fileInputCtrl |
| 220 | + * @return extension as string |
| 221 | + */ |
200 | 222 | getExtension: function() { |
201 | 223 | var _this = this; |
202 | 224 | var path = $j(_this.fileInputCtrl).attr('value'); |
203 | 225 | return path.substr( path.lastIndexOf( '.' ) + 1 ).toLowerCase(); |
204 | 226 | }, |
205 | 227 | |
206 | | - // XXX this is common utility code |
207 | | - // used when converting contents of a file input and coming up with a suitable "filename" for mediawiki |
208 | | - // test: what if path is length 0 |
209 | | - // what if path is all separators |
210 | | - // what if path ends with a separator character |
211 | | - // what if it ends with multiple separator characters |
212 | | - convertPathToFilename: function(path) { |
| 228 | + /** |
| 229 | + * XXX this is common utility code |
| 230 | + * used when converting contents of a file input and coming up with a suitable "filename" for mediawiki |
| 231 | + * test: what if path is length 0 |
| 232 | + * what if path is all separators |
| 233 | + * what if path ends with a separator character |
| 234 | + * what if it ends with multiple separator characters |
| 235 | + * |
| 236 | + * @param path |
| 237 | + * @return filename suitable for mediawiki as string |
| 238 | + */ |
| 239 | + convertPathToFilename: function( path ) { |
213 | 240 | if (path === undefined || path == '') { |
214 | 241 | return ''; |
215 | 242 | } |
216 | 243 | |
217 | 244 | var lastFileSeparatorIdx = Math.max(path.lastIndexOf( '/' ), path.lastIndexOf( '\\' )); |
218 | 245 | // lastFileSeparatorIdx is now -1 if no separator found, or some index in the string. |
219 | | - // so, +1, that is either 0 (beginning of string) or the character after last separator. |
| 246 | + // so, +1, that is either 0 ( beginning of string ) or the character after last separator. |
220 | 247 | // caution! could go past end of string... need to be more careful |
221 | 248 | var filename = path.substring( lastFileSeparatorIdx + 1, 10000 ); |
222 | 249 | |
— | — | @@ -225,14 +252,18 @@ |
226 | 253 | return filename; |
227 | 254 | }, |
228 | 255 | |
229 | | - // XXX this is common utility code |
230 | | - // XXX unused, copied because we'll probably need it... stripped from old doDestinationFill |
231 | | - // this is used when checking for "bad" extensions in a filename. |
232 | | - isGoodExtension: function(ext) { |
| 256 | + /** |
| 257 | + * XXX this is common utility code |
| 258 | + * copied because we'll probably need it... stripped from old doDestinationFill |
| 259 | + * this is used when checking for "bad" extensions in a filename. |
| 260 | + * @param ext |
| 261 | + * @return boolean if extension was acceptable |
| 262 | + */ |
| 263 | + isGoodExtension: function( ext ) { |
233 | 264 | var _this = this; |
234 | 265 | var found = false; |
235 | 266 | var extensions = mw.getConfig('fileExtensions'); |
236 | | - if (extensions) { |
| 267 | + if ( extensions ) { |
237 | 268 | for ( var i = 0; i < extensions.length; i++ ) { |
238 | 269 | if ( extensions[i].toLowerCase() == ext ) { |
239 | 270 | found = true; |
— | — | @@ -243,25 +274,12 @@ |
244 | 275 | } |
245 | 276 | |
246 | 277 | }; |
247 | | - |
248 | | - |
249 | | -mw.UploadWizard = function() { |
250 | | - |
251 | | - this.uploadHandlerClass = mw.getConfig('uploadHandlerClass') || this.getUploadHandlerClass(); |
252 | | - this.isCompleted = false; |
253 | 278 | |
254 | | - this.uploads = []; |
255 | | - // leading underline for privacy. DO NOT TAMPER. |
256 | | - this._uploadsQueued = []; |
257 | | - this._uploadsInProgress = []; |
258 | | - this._uploadsCompleted = []; |
259 | | - |
260 | | - this.uploadsBeginTime = null; |
261 | | - |
262 | | - return this; |
263 | | -}; |
264 | | - |
265 | | -mw.UploadWizardDescription = function(languageCode) { |
| 279 | +/** |
| 280 | + * Object that represents an indvidual language description, in the metadata portion of Upload Wizard |
| 281 | + * @param languageCode |
| 282 | + */ |
| 283 | +mw.UploadWizardDescription = function( languageCode ) { |
266 | 284 | var _this = this; |
267 | 285 | |
268 | 286 | // Logic copied from MediaWiki:UploadForm.js |
— | — | @@ -277,56 +295,64 @@ |
278 | 296 | $j(_this.languageMenu).addClass('mwe-upwiz-desc-lang-select'); |
279 | 297 | _this.description = $j('<textarea name="desc" rows="3" cols="50" class="mwe-upwiz-desc-lang-text"></textarea>').get(0); |
280 | 298 | _this.div = $j('<div class="mwe-upwiz-desc-lang-container"></div>') |
281 | | - .append(_this.languageMenu) |
282 | | - .append(_this.description) |
| 299 | + .append( _this.languageMenu ) |
| 300 | + .append( _this.description ) |
283 | 301 | |
284 | 302 | }; |
285 | 303 | |
286 | 304 | mw.UploadWizardDescription.prototype = { |
287 | 305 | |
| 306 | + /** |
| 307 | + * Obtain text of this description, suitable for including into Information template |
| 308 | + * @return wikitext as a string |
| 309 | + */ |
288 | 310 | getWikiText: function() { |
289 | 311 | var _this = this; |
290 | 312 | return '{{' + _this.languageMenu.value + '|' + _this.description.value + '}}' |
291 | 313 | } |
292 | 314 | }; |
293 | 315 | |
294 | | -mw.UploadWizardMetadata = function(containerDiv) { |
| 316 | +/** |
| 317 | + * Object that represents the Metadata (step 2) portion of the UploadWizard |
| 318 | + * @param containerDiv The div to put the interface into |
| 319 | + */ |
| 320 | +mw.UploadWizardMetadata = function( containerDiv ) { |
295 | 321 | |
296 | 322 | var _this = this; |
297 | 323 | _this.descriptions = []; |
298 | 324 | |
299 | | - _this.div = $j('<div class="mwe-upwiz-metadata-file"></div>'); |
| 325 | + _this.div = $j( '<div class="mwe-upwiz-metadata-file"></div>' ); |
300 | 326 | |
301 | | - _this.macroDiv = $j('<div class="mwe-upwiz-macro"></div>') |
302 | | - .append($j('<input type="submit" value="test edit"/>').click(function() { _this.submit() })); |
| 327 | + _this.macroDiv = $j( '<div class="mwe-upwiz-macro"></div>' ) |
| 328 | + .append( $j( '<input type="submit" value="test edit"/>' ).click( function( ) { _this.submit( ) } )); |
303 | 329 | |
304 | | - _this.thumbnailDiv = $j('<div class="mwe-upwiz-thumbnail"></div>'); |
| 330 | + _this.thumbnailDiv = $j( '<div class="mwe-upwiz-thumbnail"></div>' ); |
305 | 331 | |
306 | | - _this.errorDiv = $j('<div class="mwe-upwiz-metadata-error"></div>'); |
| 332 | + _this.errorDiv = $j( '<div class="mwe-upwiz-metadata-error"></div>' ); |
307 | 333 | |
308 | | - _this.dataDiv = $j('<div class="mwe-upwiz-metadata-data"></div>'); |
| 334 | + _this.dataDiv = $j( '<div class="mwe-upwiz-metadata-data"></div>' ); |
309 | 335 | |
310 | | - _this.descriptionsDiv = $j('<div class="mwe-upwiz-metadata-descriptions"></div>'); |
| 336 | + _this.descriptionsDiv = $j( '<div class="mwe-upwiz-metadata-descriptions"></div>' ); |
311 | 337 | |
312 | | - _this.descriptionAdder = $j('<a id="mwe-upwiz-desc-add"/>') |
313 | | - .attr('href', '#') |
314 | | - .html( gM('mwe-upwiz-desc-add-0') ) |
315 | | - .click( function() { _this.addDescription() } ); |
| 338 | + _this.descriptionAdder = $j( '<a id="mwe-upwiz-desc-add"/>' ) |
| 339 | + .attr( 'href', '#' ) |
| 340 | + .html( gM( 'mwe-upwiz-desc-add-0' ) ) |
| 341 | + .click( function( ) { _this.addDescription( ) } ); |
316 | 342 | |
317 | 343 | _this.descriptionsContainerDiv = |
318 | | - $j('<div class="mwe-upwiz-metadata-descriptions-container"></div>') |
319 | | - .append( $j('<div class="mwe-upwiz-metadata-descriptions-title">' + gM('mwe-upwiz-desc') + '</div>') ) |
320 | | - .append(_this.descriptionsDiv) |
321 | | - .append( $j('<div class="mwe-upwiz-metadata-descriptions-add"></div>') |
322 | | - .append(_this.descriptionAdder) ); |
| 344 | + $j( '<div class="mwe-upwiz-metadata-descriptions-container"></div>' ) |
| 345 | + .append( $j( '<div class="mwe-upwiz-metadata-descriptions-title">' + gM( 'mwe-upwiz-desc' ) + '</div>' ) ) |
| 346 | + .append( _this.descriptionsDiv ) |
| 347 | + .append( $j( '<div class="mwe-upwiz-metadata-descriptions-add"></div>' ) |
| 348 | + .append( _this.descriptionAdder ) ); |
323 | 349 | |
324 | 350 | |
325 | | - $j(_this.div) |
326 | | - .append(_this.macroDiv) |
327 | | - .append(_this.thumbnailDiv) |
328 | | - .append(_this.errorDiv) |
329 | | - .append($j(_this.dataDiv) |
330 | | - .append(_this.descriptionsContainerDiv)); |
| 351 | + $j( _this.div ) |
| 352 | + .append( _this.macroDiv ) |
| 353 | + .append( _this.thumbnailDiv ) |
| 354 | + .append( _this.errorDiv ) |
| 355 | + .append( $j( _this.dataDiv ) |
| 356 | + .append( _this.descriptionsContainerDiv )); |
331 | 357 | |
332 | 358 | |
333 | 359 | |
— | — | @@ -352,120 +378,152 @@ |
353 | 379 | // Other info |
354 | 380 | |
355 | 381 | _this.addDescription(); |
356 | | - $j(containerDiv).append(_this.div); |
| 382 | + $j( containerDiv ).append( _this.div ); |
357 | 383 | |
358 | 384 | |
359 | 385 | }; |
360 | 386 | |
361 | 387 | mw.UploadWizardMetadata.prototype = { |
362 | 388 | |
| 389 | + /** |
| 390 | + * Do anything related to a change in the number of descriptions |
| 391 | + */ |
363 | 392 | recountDescriptions: function() { |
364 | 393 | var _this = this; |
365 | 394 | // if there is some maximum number of descriptions, deal with that here |
366 | | - $j(_this.descriptionAdder).html( gM('mwe-upwiz-desc-add-' + (_this.descriptions.length == 0 ? '0' : 'n') ) ); |
| 395 | + $j( _this.descriptionAdder ).html( gM( 'mwe-upwiz-desc-add-' + ( _this.descriptions.length == 0 ? '0' : 'n' ) ) ); |
367 | 396 | }, |
368 | 397 | |
369 | 398 | |
| 399 | + /** |
| 400 | + * Add a new description |
| 401 | + */ |
370 | 402 | addDescription: function() { |
371 | 403 | var _this = this; |
372 | | - var languageCode = _this.descriptions.length ? mw.Language.UNKNOWN : mw.getConfig('userLanguage'); |
373 | | - var description = new mw.UploadWizardDescription(languageCode); |
| 404 | + var languageCode = _this.descriptions.length ? mw.Language.UNKNOWN : mw.getConfig('userLanguage' ); |
| 405 | + var description = new mw.UploadWizardDescription( languageCode ); |
374 | 406 | |
375 | | - description.removeCtrl = $j('<a title="' + gM( 'mwe-upwiz-remove-description') + '" href="#">x</a>') |
376 | | - .addClass('mwe-upwiz-remove') |
377 | | - .addClass('mwe-upwiz-remove-desc') |
378 | | - .click( function() { _this.removeDescription(description) } ) |
379 | | - .get(0); |
380 | | - $j(description.div).append(description.removeCtrl); |
| 407 | + description.removeCtrl = $j('<a title="' + gM( 'mwe-upwiz-remove-description' ) + '" href="#">x</a>' ) |
| 408 | + .addClass('mwe-upwiz-remove' ) |
| 409 | + .addClass('mwe-upwiz-remove-desc' ) |
| 410 | + .click( function() { _this.removeDescription( description ) } ) |
| 411 | + .get( 0 ); |
| 412 | + $j( description.div ).append( description.removeCtrl ); |
381 | 413 | |
382 | | - $j(_this.descriptionsDiv).append(description.div); |
383 | | - _this.descriptions.push(description); |
| 414 | + $j( _this.descriptionsDiv ).append( description.div ); |
| 415 | + _this.descriptions.push( description ); |
384 | 416 | _this.recountDescriptions(); |
385 | 417 | }, |
386 | 418 | |
387 | | - removeDescription: function(description) { |
| 419 | + /** |
| 420 | + * Remove a description |
| 421 | + * @param description |
| 422 | + */ |
| 423 | + removeDescription: function( description ) { |
388 | 424 | var _this = this; |
389 | | - $j(description.div).remove(); |
390 | | - mw.UploadWizardUtil.removeItem(_this.descriptions, description); |
| 425 | + $j( description.div ).remove(); |
| 426 | + mw.UploadWizardUtil.removeItem( _this.descriptions, description ); |
391 | 427 | _this.recountDescriptions(); |
392 | 428 | }, |
393 | 429 | |
394 | | - // this is a lot like upload ui's error -- should merge |
| 430 | + /** |
| 431 | + * Display an error with metadata |
| 432 | + * XXX this is a lot like upload ui's error -- should merge |
| 433 | + */ |
395 | 434 | error: function() { |
396 | 435 | var _this = this; |
397 | | - var args = Array.prototype.slice.call(arguments); // copies arguments into a real array |
| 436 | + var args = Array.prototype.slice.call( arguments ); // copies arguments into a real array |
398 | 437 | var msg = 'mwe-upwiz-upload-error-' + args[0]; |
399 | | - $j(_this.errorDiv).append($j('<p class="mwe-upwiz-upload-error">' + gM(msg, args.slice(1)) + '</p>')); |
| 438 | + $j( _this.errorDiv ).append( $j( '<p class="mwe-upwiz-upload-error">' + gM( msg, args.slice( 1 ) ) + '</p>' ) ); |
400 | 439 | // apply a error style to entire did |
401 | | - $j(_this.div).addClass('mwe-upwiz-upload-error'); |
402 | | - $j(_this.dataDiv).hide(); |
403 | | - $j(_this.errorDiv).show(); |
| 440 | + $j( _this.div ).addClass( 'mwe-upwiz-upload-error' ); |
| 441 | + $j( _this.dataDiv ).hide(); |
| 442 | + $j( _this.errorDiv ).show(); |
404 | 443 | }, |
405 | 444 | |
406 | | - // just like error but with ok/cancel |
407 | | - errorDuplicate: function(sessionKey, duplicates) { |
| 445 | + /** |
| 446 | + * Display an error but check before proceeding -- useful for cases where user can override a failed upload due to |
| 447 | + * hash collision |
| 448 | + * just like error but with ok / cancel |
| 449 | + * @param sessionKey |
| 450 | + * @param duplicates |
| 451 | + */ |
| 452 | + errorDuplicate: function( sessionKey, duplicates ) { |
408 | 453 | var _this = this; |
409 | 454 | /* |
410 | 455 | TODO - do something clever to get page URLs and image URLs |
411 | 456 | var duplicatePageTitles = result.upload.warnings.duplicate; |
412 | 457 | var duplicates = []; |
413 | | - for (var i = 0; i < duplicates.length; i++) { |
414 | | - imageInfo = mw.getJSON(undefined, |
| 458 | + for ( var i = 0; i < duplicates.length; i++ ) { |
| 459 | + imageInfo = mw.getJSON( undefined, |
415 | 460 | {'titles' : duplicatePageTitles[i], 'prop' : 'imageinfo'}) |
416 | | - function() { _this.renderUploads() }); |
417 | | - duplicates.push({ |
| 461 | + function() { _this.renderUploads() } ); |
| 462 | + duplicates.push( { |
418 | 463 | // ?? async, so we should insert later... |
419 | | - }) |
| 464 | + } ) |
420 | 465 | } |
421 | 466 | */ |
422 | | - _this.error('duplicate'); |
| 467 | + _this.error( 'duplicate' ); |
423 | 468 | // add placeholder spinners to div, and then fetch the thumbnails and so on async |
424 | 469 | // meanwhile... |
425 | | - //$j(_this.errorDiv).append( |
| 470 | + //$j( _this.errorDiv ).append( |
426 | 471 | // $j('<form></form>'); |
427 | | - // same as normal error but you get to ok/cancel, which resubmits with ignore warnings |
| 472 | + // same as normal error but you get to ok / cancel, which resubmits with ignore warnings |
428 | 473 | }, |
429 | 474 | |
430 | | - // given the API result pull some info into the form (for instance, extracted from EXIF, desired filename) |
431 | | - populateFromResult: function(result) { |
| 475 | + /** |
| 476 | + * Given the API result pull some info into the form ( for instance, extracted from EXIF, desired filename ) |
| 477 | + * @param result Upload API result object |
| 478 | + */ |
| 479 | + populateFromResult: function( result ) { |
432 | 480 | var _this = this; |
433 | 481 | var upload = result.upload; |
434 | | - mw.log("populating from result"); |
435 | | - _this.setThumbnail(upload.filename, mw.getConfig('thumbnailWidth')); |
436 | | - //_this.setSource(upload. result); |
| 482 | + mw.log( "populating from result" ); |
| 483 | + _this.setThumbnail( upload.filename, mw.getConfig( 'thumbnailWidth' )); |
| 484 | + //_this.setSource( upload. result ); |
437 | 485 | |
438 | | - //_this.setFilename(upload.filename); |
| 486 | + //_this.setFilename( upload.filename ); |
439 | 487 | |
440 | 488 | //_this.setDescription(); // is there anything worthwhile here? image comment? |
441 | | - //_this.setDate(upload.metadata); |
442 | | - //_this.setLocation(upload.metadata); // we could be VERY clever with location sensing... |
443 | | - //_this.setAuthor(_this.config.user, upload.exif.Copyright); |
| 489 | + //_this.setDate( upload.metadata ); |
| 490 | + //_this.setLocation( upload.metadata ); // we could be VERY clever with location sensing... |
| 491 | + //_this.setAuthor( _this.config.user, upload.exif.Copyright ); |
444 | 492 | }, |
445 | 493 | |
446 | | - // look up thumbnail info and set it |
447 | | - setThumbnail: function(filename, width) { |
| 494 | + /** |
| 495 | + * look up thumbnail info and set it on the form, with loading spinner |
| 496 | + * @param filename |
| 497 | + * @param width |
| 498 | + */ |
| 499 | + setThumbnail: function( filename, width ) { |
448 | 500 | var _this = this; |
449 | 501 | |
450 | | - var callback = function(imageInfo) { |
451 | | - var thumbnail = $j('<img class="mwe-upwiz-thumbnail"/>').get(0); |
| 502 | + var callback = function( imageInfo ) { |
| 503 | + var thumbnail = $j( '<img class="mwe-upwiz-thumbnail"/>' ).get( 0 ); |
452 | 504 | thumbnail.width = imageInfo.thumbwidth; |
453 | 505 | thumbnail.height = imageInfo.thumbheight; |
454 | 506 | thumbnail.src = imageInfo.thumburl; |
455 | 507 | // side effect: will replace thumbnail's loadingSpinner |
456 | | - _this.thumbnailDiv.html(thumbnail); |
| 508 | + _this.thumbnailDiv.html( thumbnail ); |
457 | 509 | }; |
458 | 510 | |
459 | 511 | _this.thumbnailDiv.loadingSpinner(); |
460 | | - _this.getThumbnail("File:" + filename, width, callback); |
| 512 | + _this.getThumbnail( "File:" + filename, width, callback ); |
461 | 513 | |
462 | 514 | }, |
463 | 515 | |
464 | | - // use iinfo to get thumbnail info |
465 | | - // this API method can be used to get a lot of thumbnails at once, but that may not be so useful for us ATM |
466 | | - // this is mostly ripped off from mw.UploadHandler's doDestCheck, but: stripped of UI, does only one, does not check for name collisions. |
467 | | - getThumbnail: function(title, width, setThumbnailCb, apiUrl) { |
| 516 | + /** |
| 517 | + * use iinfo to get thumbnail info |
| 518 | + * this API method can be used to get a lot of thumbnails at once, but that may not be so useful for us ATM |
| 519 | + * this is mostly ripped off from mw.UploadHandler's doDestCheck, but: stripped of UI, does only one, does not check for name collisions. |
| 520 | + * @param title - name of the file on mediawiki (e.g. File:Foo.jpg) |
| 521 | + * @param width - desired width of thumbnail (height will scale to match) |
| 522 | + * @param setThumbnailCb - callback to execute once info has been obtained (for instance, put it into the interface) |
| 523 | + * @param apiUrl - where to get your API results |
| 524 | + */ |
| 525 | + getThumbnail: function( title, width, setThumbnailCb, apiUrl ) { |
468 | 526 | |
469 | | - if (apiUrl === undefined) { |
| 527 | + if ( apiUrl === undefined ) { |
470 | 528 | apiUrl = mw.getLocalApiUrl(); |
471 | 529 | } |
472 | 530 | |
— | — | @@ -476,34 +534,38 @@ |
477 | 535 | 'iiprop': 'url|mime|size' |
478 | 536 | }; |
479 | 537 | |
480 | | - mw.getJSON(apiUrl, params, function( data ) { |
| 538 | + mw.getJSON( apiUrl, params, function( data ) { |
481 | 539 | if ( !data || !data.query || !data.query.pages ) { |
482 | 540 | mw.log(" No data? ") |
483 | 541 | return; |
484 | 542 | } |
485 | 543 | |
486 | | - if (data.query.pages[-1]) { |
| 544 | + if ( data.query.pages[-1] ) { |
487 | 545 | // not found ? error |
488 | 546 | } |
489 | 547 | for ( var page_id in data.query.pages ) { |
490 | 548 | var page = data.query.pages[ page_id ]; |
491 | | - if (! page.imageinfo ) { |
| 549 | + if ( ! page.imageinfo ) { |
492 | 550 | // not found? error |
493 | 551 | } else { |
494 | 552 | var imageInfo = page.imageinfo[0]; |
495 | | - setThumbnailCb(imageInfo); |
| 553 | + setThumbnailCb( imageInfo ); |
496 | 554 | } |
497 | 555 | } |
498 | | - }); |
| 556 | + } ); |
499 | 557 | }, |
500 | 558 | |
501 | 559 | |
| 560 | + /** |
| 561 | + * Convert entire metadata for this file into wikiText, which will then be posted to the file |
| 562 | + * @return wikitext representing all metadata |
| 563 | + */ |
502 | 564 | getWikiText: function() { |
503 | 565 | var _this = this; |
504 | 566 | wikiText = ''; |
505 | 567 | |
506 | 568 | |
507 | | - // http://commons.wikimedia.org/wiki/Template:Information |
| 569 | + // http://commons.wikimedia.org / wiki / Template:Information |
508 | 570 | |
509 | 571 | // can we be more slick and do this with maps, applys, joins? |
510 | 572 | var information = { |
— | — | @@ -518,16 +580,16 @@ |
519 | 581 | |
520 | 582 | // sanity check the descriptions -- do not have two in the same lang |
521 | 583 | // all should be a known lang |
522 | | - if (_this.descriptions.length === 0) { |
| 584 | + if ( _this.descriptions.length === 0 ) { |
523 | 585 | // ruh roh |
524 | | - // we should not even allow them to press the button (?) but then what about the queue... |
| 586 | + // we should not even allow them to press the button ( ? ) but then what about the queue... |
525 | 587 | } |
526 | | - $j.each(_this.descriptions, function(i, description) { |
527 | | - information.description += descriptions.getWikiText(); |
528 | | - }) |
| 588 | + $j.each( _this.descriptions, function( i, desc ) { |
| 589 | + information.description += desc.getWikiText(); |
| 590 | + } ) |
529 | 591 | |
530 | 592 | var info = ''; |
531 | | - for (var key in information) { |
| 593 | + for ( var key in information ) { |
532 | 594 | info += '|' + key + '=' + information[key] + "\n"; |
533 | 595 | } |
534 | 596 | |
— | — | @@ -536,33 +598,39 @@ |
537 | 599 | wikiText += '{{Information\n' + info + '}}\n'; |
538 | 600 | |
539 | 601 | // wikiText += "=={int:license}==\n"; |
540 | | - // XXX get the real one -- usually dual license GFDL/cc-by-sa |
| 602 | + // XXX get the real one -- usually dual license GFDL / cc - by - sa |
541 | 603 | //wikiText += "{{cc-by-sa-3.0}}\n"; |
542 | | - // http://commons.wikimedia.org/wiki/Template:Information |
| 604 | + // http://commons.wikimedia.org / wiki / Template:Information |
543 | 605 | |
544 | 606 | return wikiText; |
545 | 607 | }, |
546 | 608 | |
| 609 | + /** |
| 610 | + * Check if we are ready to post wikitext |
| 611 | + */ |
547 | 612 | isReady: function() { |
548 | 613 | // somehow, all the various issues discovered with this upload should be present in a single place |
549 | 614 | // where we can then check on |
550 | 615 | // perhaps as simple as _this.issues or _this.agenda |
551 | 616 | }, |
552 | 617 | |
| 618 | + /** |
| 619 | + * Post wikitext as edited here, to the file |
| 620 | + */ |
553 | 621 | submit: function() { |
554 | 622 | var _this = this; |
555 | 623 | // are we okay to submit? |
556 | 624 | // check descriptions |
557 | 625 | |
558 | | - // are we changing the name (moving the file?) if so, do that first, and the rest of this submission has to become |
| 626 | + // are we changing the name ( moving the file? ) if so, do that first, and the rest of this submission has to become |
559 | 627 | // a callback when that is completed? |
560 | 628 | |
561 | | - // XXX check state of metadata for okayness (license selected, at least one desc, sane filename) |
| 629 | + // XXX check state of metadata for okayness ( license selected, at least one desc, sane filename ) |
562 | 630 | var wikiText = _this.getWikiText(); |
563 | | - mw.log(wikiText); |
| 631 | + mw.log( wikiText ); |
564 | 632 | // do some api call to edit the info |
565 | 633 | |
566 | | - // api.php ? action=edit & title=Talk:Main_Page & section=new & summary=Hello%20World & text=Hello%20everyone! & watch & basetimestamp=2008-03-20T17:26:39Z & token=cecded1f35005d22904a35cc7b736e18%2B%5C |
| 634 | + // api.php ? action = edit & title = Talk:Main_Page & section = new & summary = Hello%20World & text = Hello%20everyone! & watch & basetimestamp = 2008 - 03 - 20T17:26:39Z & token = cecded1f35005d22904a35cc7b736e18%2B%5C |
567 | 635 | // caution this may result in a captcha response, which user will have to solve |
568 | 636 | // |
569 | 637 | |
— | — | @@ -572,8 +640,8 @@ |
573 | 641 | // we are presuming this File page is brand new, so let's not bother with the whole redirection deal. ('noredirect') |
574 | 642 | |
575 | 643 | /* |
576 | | - Note: In this example, all parameters are passed in a GET request just for the sake of simplicity. However, action=move requires POST requests; GET requests will cause an error. Moving Main Pgae (sic) and its talk page to Main Page, without creating a redirect |
577 | | - api.php ? action=move & from=Main%20Pgae & to=Main%20Page & reason=Oops,%20misspelling & movetalk & noredirect & token=58b54e0bab4a1d3fd3f7653af38e75cb%2B\ |
| 644 | + Note: In this example, all parameters are passed in a GET request just for the sake of simplicity. However, action = move requires POST requests; GET requests will cause an error. Moving Main Pgae ( sic ) and its talk page to Main Page, without creating a redirect |
| 645 | + api.php ? action = move & from = Main%20Pgae & to = Main%20Page & reason = Oops,%20misspelling & movetalk & noredirect & token = 58b54e0bab4a1d3fd3f7653af38e75cb%2B\ |
578 | 646 | */ |
579 | 647 | |
580 | 648 | |
— | — | @@ -581,6 +649,25 @@ |
582 | 650 | }; |
583 | 651 | |
584 | 652 | |
| 653 | + |
| 654 | +/** |
| 655 | + * Object that reperesents the entire multi-step Upload Wizard |
| 656 | + */ |
| 657 | +mw.UploadWizard = function() { |
| 658 | + |
| 659 | + this.uploadHandlerClass = mw.getConfig('uploadHandlerClass') || this.getUploadHandlerClass(); |
| 660 | + this.isCompleted = false; |
| 661 | + |
| 662 | + this.uploads = []; |
| 663 | + // leading underline for privacy. DO NOT TAMPER. |
| 664 | + this._uploadsQueued = []; |
| 665 | + this._uploadsInProgress = []; |
| 666 | + this._uploadsCompleted = []; |
| 667 | + |
| 668 | + this.uploadsBeginTime = null; |
| 669 | + |
| 670 | +}; |
| 671 | + |
585 | 672 | mw.UploadWizard.prototype = { |
586 | 673 | maxUploads: 10, // XXX get this from config |
587 | 674 | maxSimultaneousUploads: 2, // XXX get this from config |
— | — | @@ -599,30 +686,33 @@ |
600 | 687 | 'NullUploadHandler' |
601 | 688 | ], |
602 | 689 | |
603 | | - // let's figure out exactly what we can use. |
604 | | - // |
| 690 | + /* |
| 691 | + * We can use various UploadHandlers based on the browser's capabilities. Let's pick one. |
| 692 | + * For example, the ApiUploadHandler should work just about everywhere, but XhrUploadHandler |
| 693 | + * allows for more fine-grained upload progress |
| 694 | + * @return valid JS upload handler class constructor function |
| 695 | + */ |
605 | 696 | getUploadHandlerClass: function() { |
| 697 | + // return mw.MockUploadHandler; |
| 698 | + return mw.ApiUploadHandler; |
| 699 | + /* |
606 | 700 | var _this = this; |
607 | | - for (var i = 0; i < uploadHandlers.length; i++) { |
| 701 | + for ( var i = 0; i < uploadHandlers.length; i++ ) { |
608 | 702 | var klass = mw[uploadHandlers[i]]; |
609 | | - if (klass != undefined && klass.canRun(this.config)) { |
| 703 | + if ( klass != undefined && klass.canRun( this.config )) { |
610 | 704 | return klass; |
611 | 705 | } |
612 | 706 | } |
613 | 707 | // this should never happen; NullUploadHandler should always work |
614 | 708 | return null; |
| 709 | + */ |
615 | 710 | }, |
616 | | - */ |
617 | | - |
618 | | - // later we will do some testing to see if they can support more advanced UploadHandlers, like |
619 | | - // an XHR based one or Firefogg |
620 | | - getUploadHandlerClass: function() { |
621 | | - // return mw.MockUploadHandler; |
622 | | - return mw.ApiUploadHandler; |
623 | | - }, |
624 | | - |
625 | | - // create the basic interface to make an upload in this div |
626 | | - createInterface: function(div) { |
| 711 | + |
| 712 | + /** |
| 713 | + * create the basic interface to make an upload in this div |
| 714 | + * @param div The div in the DOM to put all of this into. |
| 715 | + */ |
| 716 | + createInterface: function( div ) { |
627 | 717 | var _this = this; |
628 | 718 | div.innerHTML = |
629 | 719 | |
— | — | @@ -672,7 +762,7 @@ |
673 | 763 | // Create global progress bar |
674 | 764 | $j( '#mwe-upwiz-progress-bar' ).progressbar({ |
675 | 765 | value: 0 |
676 | | - }); |
| 766 | + } ); |
677 | 767 | |
678 | 768 | // add one to start |
679 | 769 | _this.addUpload(); |
— | — | @@ -681,29 +771,38 @@ |
682 | 772 | _this.moveToTab('file'); |
683 | 773 | }, |
684 | 774 | |
685 | | - moveToTab: function(selectedTabName) { |
| 775 | + /** |
| 776 | + * Advance one "step" in the wizard interface. |
| 777 | + * @param selectedTabName |
| 778 | + */ |
| 779 | + moveToTab: function( selectedTabName ) { |
686 | 780 | var _this = this; |
687 | | - for (var i=0; i < _this.tabs.length; i++) { |
| 781 | + for ( var i = 0; i < _this.tabs.length; i++ ) { |
688 | 782 | tabName = _this.tabs[i]; |
689 | | - var tabDiv = $j('#mwe-upwiz-tabdiv-' + tabName); |
690 | | - var tab = $j('#mwe-upwiz-tab-' + tabName); |
691 | | - if (selectedTabName == tabName) { |
| 783 | + var tabDiv = $j( '#mwe-upwiz-tabdiv-' + tabName ); |
| 784 | + var tab = $j( '#mwe-upwiz-tab-' + tabName ); |
| 785 | + if ( selectedTabName == tabName ) { |
692 | 786 | tabDiv.show(); |
693 | | - tab.addClass('mwe-upwiz-tab-highlight'); |
| 787 | + tab.addClass( 'mwe-upwiz-tab-highlight' ); |
694 | 788 | } else { |
695 | 789 | tabDiv.hide(); |
696 | | - tab.removeClass('mwe-upwiz-tab-highlight'); |
| 790 | + tab.removeClass( 'mwe-upwiz-tab-highlight' ); |
697 | 791 | } |
698 | 792 | } |
699 | 793 | // XXX possibly select appropriate form field to begin work |
700 | 794 | }, |
701 | 795 | |
702 | | - // add an Upload, with controls. |
703 | | - // XXX study what Mdale is doing to create this file form controls... he has full control over CSS etc and the browsing button. |
| 796 | + /** |
| 797 | + * add an Upload |
| 798 | + * we create the upload interface, a handler to transport it to the server, |
| 799 | + * and UI for the upload itself and the "metadata" at the second step of the wizard. |
| 800 | + * Finally stuff it into an array of uploads. |
| 801 | + * @return boolean success |
| 802 | + */ |
704 | 803 | addUpload: function() { |
705 | 804 | var _this = this; |
706 | 805 | var idx = _this.uploads.length; // or? |
707 | | - if (idx == _this.maxUploads) { |
| 806 | + if ( idx == _this.maxUploads ) { |
708 | 807 | return false; |
709 | 808 | } |
710 | 809 | |
— | — | @@ -713,45 +812,45 @@ |
714 | 813 | |
715 | 814 | // API |
716 | 815 | // XXX hardcoded for now. Maybe passed through config or guessed at here. |
717 | | - // upload.api = new mw.UploadApiProcessor(function(result) { _this.uploadCompleted); |
| 816 | + // upload.api = new mw.UploadApiProcessor( function( result ) { _this.uploadCompleted ); |
718 | 817 | |
719 | 818 | |
720 | 819 | // UI |
721 | | - // originalFilename is the basename of the file on our file system. This is what we really wanted (probably). |
| 820 | + // originalFilename is the basename of the file on our file system. This is what we really wanted ( probably ). |
722 | 821 | // the system will upload with a temporary filename and we'll get that back from the API return when we upload |
723 | 822 | var filenameAcceptedCb = function() { |
724 | 823 | _this.updateFileCounts(); |
725 | 824 | }; |
726 | | - var ui = new mw.UploadWizardUploadInterface(filenameAcceptedCb); |
727 | | - ui.removeCtrl = $j('<a title="' + gM( 'mwe-upwiz-remove-upload') |
728 | | - + '" href="#" class="mwe-upwiz-remove">x</a>') |
729 | | - .click( function() { _this.removeUpload(upload) } ) |
730 | | - .get(0); |
731 | | - $j(ui.div).append(ui.removeCtrl); |
| 825 | + var ui = new mw.UploadWizardUploadInterface( filenameAcceptedCb ); |
| 826 | + ui.removeCtrl = $j( '<a title="' + gM( 'mwe-upwiz-remove-upload' ) |
| 827 | + + '" href="#" class="mwe-upwiz-remove">x</a>' ) |
| 828 | + .click( function() { _this.removeUpload( upload ) } ) |
| 829 | + .get( 0 ); |
| 830 | + $j( ui.div ).append( ui.removeCtrl ); |
732 | 831 | |
733 | 832 | upload.ui = ui; |
734 | 833 | // handler -- usually ApiUploadHandler |
735 | | - upload.handler = new _this.uploadHandlerClass(upload.ui); |
| 834 | + upload.handler = new _this.uploadHandlerClass( upload.ui ); |
736 | 835 | |
737 | 836 | // this is for UI only... |
738 | | - upload.handler.addProgressCb( function(fraction) { _this.uploadProgress(upload, fraction) } ); |
| 837 | + upload.handler.addProgressCb( function( fraction ) { _this.uploadProgress( upload, fraction ) } ); |
739 | 838 | |
740 | 839 | // this is only the UI one, so is the result even going to be there? |
741 | | - upload.handler.addCompletedCb( function(result) { _this.uploadCompleted(upload, result) } ); |
| 840 | + upload.handler.addCompletedCb( function( result ) { _this.uploadCompleted( upload, result ) } ); |
742 | 841 | |
743 | 842 | // not sure about this...UI only? |
744 | 843 | // this will tell us that at least one of our uploads has had an error -- may change messaging, |
745 | 844 | // like, please fix below |
746 | | - upload.handler.addErrorCb( function(error) { _this.uploadError(upload, error) } ); |
| 845 | + upload.handler.addErrorCb( function( error ) { _this.uploadError( upload, error ) } ); |
747 | 846 | |
748 | 847 | // metadata |
749 | | - upload.metadata = new mw.UploadWizardMetadata($j('#mwe-upwiz-metadata-files')); |
| 848 | + upload.metadata = new mw.UploadWizardMetadata( $j( '#mwe-upwiz-metadata-files' )); |
750 | 849 | |
751 | 850 | |
752 | 851 | |
753 | | - _this.uploads.push(upload); |
| 852 | + _this.uploads.push( upload ); |
754 | 853 | |
755 | | - $j("#mwe-upwiz-files").append(upload.ui.div); |
| 854 | + $j( "#mwe-upwiz-files" ).append( upload.ui.div ); |
756 | 855 | |
757 | 856 | |
758 | 857 | |
— | — | @@ -760,135 +859,165 @@ |
761 | 860 | |
762 | 861 | // update the uploadUi to add files - we may be over limit |
763 | 862 | _this.updateFileCounts(); |
764 | | - |
| 863 | + return true; |
765 | 864 | }, |
766 | 865 | |
767 | | - /* Remove an upload from our array of uploads, and the HTML UI |
768 | | - We can remove the HTML UI directly, as jquery will just get the parent. |
769 | | - We need to grep through the array of uploads, since we don't know the current index. */ |
770 | | - removeUpload: function(upload) { |
| 866 | + /** |
| 867 | + * Remove an upload from our array of uploads, and the HTML UI |
| 868 | + * We can remove the HTML UI directly, as jquery will just get the parent. |
| 869 | + * We need to grep through the array of uploads, since we don't know the current index. |
| 870 | + * |
| 871 | + * @param upload |
| 872 | + */ |
| 873 | + removeUpload: function( upload ) { |
771 | 874 | var _this = this; |
772 | | - $j(upload.ui.div).remove(); |
773 | | - $j(upload.metadata.div).remove(); |
774 | | - mw.UploadWizardUtil.removeItem(_this.uploads, upload); |
| 875 | + $j( upload.ui.div ).remove(); |
| 876 | + $j( upload.metadata.div ).remove(); |
| 877 | + mw.UploadWizardUtil.removeItem( _this.uploads, upload ); |
775 | 878 | _this.updateFileCounts(); |
776 | 879 | }, |
777 | 880 | |
778 | | - // using a second array to iterate, because we will be splicing the main one, _this.uploads |
| 881 | + /** |
| 882 | + * This is useful to clean out unused upload file inputs if the user hits GO. |
| 883 | + * We are using a second array to iterate, because we will be splicing the main one, _this.uploads |
| 884 | + */ |
779 | 885 | removeEmptyUploads: function() { |
780 | 886 | var _this = this; |
781 | 887 | var toRemove = []; |
782 | | - for (var i = 0; i < _this.uploads.length; i++) { |
783 | | - if (_this.uploads[i].ui.fileInputCtrl.value == "") { |
784 | | - toRemove.push(_this.uploads[i]); |
| 888 | + for ( var i = 0; i < _this.uploads.length; i++ ) { |
| 889 | + if ( _this.uploads[i].ui.fileInputCtrl.value == "" ) { |
| 890 | + toRemove.push( _this.uploads[i] ); |
785 | 891 | } |
786 | 892 | }; |
787 | | - for (var i = 0; i < toRemove.length; i++) { |
788 | | - _this.removeUpload(toRemove[i]); |
| 893 | + for ( var i = 0; i < toRemove.length; i++ ) { |
| 894 | + _this.removeUpload( toRemove[i] ); |
789 | 895 | } |
790 | 896 | }, |
791 | 897 | |
| 898 | + /** |
| 899 | + * Kick off the upload processes. |
| 900 | + * Does some precalculations, changes the interface to be less mutable, moves the uploads to a queue, |
| 901 | + * and kicks off a thread which will take from the queue. |
| 902 | + */ |
792 | 903 | startUploads: function() { |
793 | 904 | var _this = this; |
794 | 905 | _this.removeEmptyUploads(); |
795 | 906 | // remove the upload button, and the add file button |
796 | | - $j('#mwe-upwiz-upload-ctrl').hide(); |
797 | | - $j('#mwe-upwiz-add-file').hide(); |
| 907 | + $j( '#mwe-upwiz-upload-ctrl' ).hide(); |
| 908 | + $j( '#mwe-upwiz-add-file' ).hide(); |
798 | 909 | |
799 | 910 | // remove ability to change files |
800 | 911 | // ideally also hide the "button"... but then we require styleable file input CSS trickery |
801 | 912 | // although, we COULD do this just for files already in progress... |
802 | 913 | |
803 | 914 | // XXX we just want to VISUALLY lock it in -- disabling this seems to remove it from form post |
804 | | - // $j('.mwe-upwiz-file').attr('disabled', 'disabled'); |
| 915 | + // $j( '.mwe-upwiz-file' ).attr( 'disabled', 'disabled' ); |
805 | 916 | |
806 | 917 | // add the upload progress bar, with ETA |
807 | 918 | // add in the upload count |
808 | | - $j('#mwe-upwiz-progress').show(); |
| 919 | + $j( '#mwe-upwiz-progress' ).show(); |
809 | 920 | |
810 | | - _this.uploadsBeginTime = (new Date()).getTime(); |
| 921 | + _this.uploadsBeginTime = ( new Date() ).getTime(); |
811 | 922 | |
812 | | - var canGetFileSize = (_this.uploadHandlerClass.prototype.getFileSize !== undefined); |
| 923 | + var canGetFileSize = ( _this.uploadHandlerClass.prototype.getFileSize !== undefined ); |
813 | 924 | |
814 | 925 | // queue the uploads |
815 | 926 | _this.totalWeight = 0; |
816 | | - for (var i = 0; i < _this.uploads.length; i++) { |
| 927 | + for ( var i = 0; i < _this.uploads.length; i++ ) { |
817 | 928 | var upload = _this.uploads[i]; |
818 | 929 | |
819 | 930 | // we may want to do something clever here to detect |
820 | 931 | // whether this is a real or dummy weight |
821 | | - if (canGetFileSize) { |
| 932 | + if ( canGetFileSize ) { |
822 | 933 | upload.weight = upload.getFileSize(); |
823 | 934 | } else { |
824 | 935 | upload.weight = 1; |
825 | 936 | } |
826 | 937 | _this.totalWeight += upload.weight; |
827 | 938 | |
828 | | - _this._uploadsQueued.push(upload); |
| 939 | + _this._uploadsQueued.push( upload ); |
829 | 940 | } |
830 | 941 | setTimeout( function () { _this._startUploadsQueued(); }, 0 ); |
831 | 942 | }, |
832 | 943 | |
833 | | - // making this another thread of execution, because we want to avoid any race condition |
834 | | - // this way, this is the only "thread" that can start uploads |
835 | | - // it may miss a newly completed upload but it will get it eventually |
| 944 | + /** |
| 945 | + * Uploads must be 'queued' to be considered for uploading |
| 946 | + * making this another thread of execution, because we want to avoid any race condition |
| 947 | + * this way, this is the only "thread" that can start uploads |
| 948 | + * it may miss a newly completed upload but it will get it eventually |
| 949 | + * |
| 950 | + */ |
836 | 951 | _startUploadsQueued: function() { |
837 | 952 | var _this = this; |
838 | | - var uploadsToStart = Math.min(_this.maxSimultaneousUploads - _this._uploadsInProgress.length, _this._uploadsQueued.length); |
839 | | - mw.log("_startUploadsQueued: should start " + uploadsToStart + " uploads"); |
840 | | - while (uploadsToStart--) { |
| 953 | + var uploadsToStart = Math.min( _this.maxSimultaneousUploads - _this._uploadsInProgress.length, |
| 954 | + _this._uploadsQueued.length ); |
| 955 | + mw.log( "_startUploadsQueued: should start " + uploadsToStart + " uploads" ); |
| 956 | + while ( uploadsToStart-- ) { |
841 | 957 | var upload = _this._uploadsQueued.shift(); |
842 | | - _this._uploadsInProgress.push(upload); |
| 958 | + _this._uploadsInProgress.push( upload ); |
843 | 959 | upload.handler.start(); |
844 | 960 | } |
845 | | - if (_this._uploadsQueued.length) { |
| 961 | + if ( _this._uploadsQueued.length ) { |
846 | 962 | setTimeout( function () { _this._startUploadsQueued(); }, 1000 ); |
847 | 963 | } |
848 | 964 | }, |
849 | 965 | |
850 | 966 | |
851 | | - // could be a spinning loop by itself, but this is annoying to debug |
852 | | - // and then we'd have to be careful to note the state-transition to completed once and only once. Race conditions. |
| 967 | + /** |
| 968 | + * Show overall progress for the entire UploadWizard |
| 969 | + * The current design doesn't have individual progress bars, just one giant one. |
| 970 | + * We did some tricky calculations in startUploads to try to weight each individual file's progress against |
| 971 | + * the overall progress. |
| 972 | + */ |
853 | 973 | showProgress: function() { |
854 | 974 | var _this = this; |
855 | | - if (_this.isCompleted) { |
| 975 | + if ( _this.isCompleted ) { |
856 | 976 | return; |
857 | 977 | } |
858 | 978 | |
859 | | - //var updateFileCounts = false; |
860 | 979 | var fraction = 0; |
861 | | - for (var i = 0; i < _this.uploads.length; i++) { |
| 980 | + for ( var i = 0; i < _this.uploads.length; i++ ) { |
862 | 981 | var upload = _this.uploads[i]; |
863 | | - mw.log("progress of " + upload.ui.fileInputCtrl.value + " = " + upload.progress); |
864 | | - fraction += upload.progress * (upload.weight / _this.totalWeight); |
| 982 | + mw.log( "progress of " + upload.ui.fileInputCtrl.value + " = " + upload.progress ); |
| 983 | + fraction += upload.progress * ( upload.weight / _this.totalWeight ); |
865 | 984 | } |
866 | | - _this.showProgressBar(fraction); |
| 985 | + _this.showProgressBar( fraction ); |
867 | 986 | |
868 | | - var remainingTime = _this.getRemainingTime(_this.uploadsBeginTime, fraction); |
869 | | - if (remainingTime !== null) { |
870 | | - _this.showRemainingTime(remainingTime); |
| 987 | + var remainingTime = _this.getRemainingTime( _this.uploadsBeginTime, fraction ); |
| 988 | + if ( remainingTime !== null ) { |
| 989 | + _this.showRemainingTime( remainingTime ); |
871 | 990 | } |
872 | 991 | }, |
873 | 992 | |
874 | | - // show the progress bar |
875 | | - showProgressBar: function(fraction) { |
| 993 | + /** |
| 994 | + * Show the progress bar for the entire Upload Wizard. |
| 995 | + * @param fraction fraction completed (float between 0 and 1) |
| 996 | + */ |
| 997 | + showProgressBar: function( fraction ) { |
876 | 998 | $j( '#mwe-upwiz-progress-bar' ).progressbar( 'value', parseInt( fraction * 100 ) ); |
877 | 999 | }, |
878 | 1000 | |
879 | | - // show remaining time for all the uploads |
880 | | - // remainingTime is in milliseconds |
881 | | - // XXX should be localized - x hours, x minutes, x seconds |
882 | | - showRemainingTime: function(remainingTime) { |
| 1001 | + /** |
| 1002 | + * Show remaining time for all the uploads |
| 1003 | + * XXX should be localized - x hours, x minutes, x seconds |
| 1004 | + * @param remainingTime estimated time remaining in milliseconds |
| 1005 | + */ |
| 1006 | + showRemainingTime: function( remainingTime ) { |
883 | 1007 | $j( '#mwe-upwiz-etr' ).html( gM( 'mwe-upwiz-remaining', mw.seconds2npt(parseInt(remainingTime / 1000)) ) ); |
884 | 1008 | }, |
885 | 1009 | |
886 | 1010 | |
887 | | - // XXX should be refactored with the very similar code in mw.UploadInterface.js:updateProgress |
888 | | - // returns time in whatever units getTime() returns; presumed milliseconds |
889 | | - getRemainingTime: function (beginTime, fractionCompleted) { |
| 1011 | + /** |
| 1012 | + * Calculate remaining time for all uploads to complete. |
| 1013 | + * |
| 1014 | + * @param beginTime time in whatever unit getTime returns, presume epoch milliseconds |
| 1015 | + * @param fractionCompleted fraction completed |
| 1016 | + * @return time in whatever units getTime() returns; presumed milliseconds |
| 1017 | + */ |
| 1018 | + getRemainingTime: function ( beginTime, fractionCompleted ) { |
890 | 1019 | if ( beginTime ) { |
891 | 1020 | var elapsedTime = ( new Date() ).getTime() - beginTime; |
892 | | - if (fractionCompleted > 0.0 && elapsedTime > 0) { // or some other minimums for good data |
| 1021 | + if ( fractionCompleted > 0.0 && elapsedTime > 0 ) { // or some other minimums for good data |
893 | 1022 | var rate = fractionCompleted / elapsedTime; |
894 | 1023 | return parseInt( ( 1.0 - fractionCompleted ) / rate ); |
895 | 1024 | } |
— | — | @@ -896,35 +1025,45 @@ |
897 | 1026 | return null; |
898 | 1027 | }, |
899 | 1028 | |
900 | | - // okay we are in a confusing state here -- are we asking for progress to be stored in the uploadhandler for our perusal or |
901 | | - // to be explicitly forwarded to us |
902 | | - uploadProgress: function(upload, progress) { |
| 1029 | + /** |
| 1030 | + * Record the progress of an individual upload |
| 1031 | + * okay we are in a confusing state here -- are we asking for progress to be stored in the uploadhandler for our perusal or |
| 1032 | + * to be explicitly forwarded to us |
| 1033 | + * @param upload an Upload object |
| 1034 | + * @param progress fraction of progress (float between 0 and 1) |
| 1035 | + */ |
| 1036 | + uploadProgress: function( upload, progress ) { |
903 | 1037 | mw.log("upload progress is " + progress); |
904 | 1038 | var _this = this; |
905 | 1039 | upload.progress = progress; |
906 | 1040 | _this.showProgress(); |
907 | 1041 | }, |
908 | 1042 | |
909 | | - uploadCompleted: function(upload, result) { |
| 1043 | + /** |
| 1044 | + * To be executed when an individual upload finishes. Processes the result and updates step 2's metadata |
| 1045 | + * @param upload an Upload object |
| 1046 | + * @param result the API result in parsed JSON form |
| 1047 | + */ |
| 1048 | + uploadCompleted: function( upload, result ) { |
910 | 1049 | var _this = this; |
911 | | - _this._uploadsCompleted.push(upload); |
912 | | - mw.UploadWizardUtil.removeItem(_this._uploadsInProgress, upload); |
| 1050 | + _this._uploadsCompleted.push( upload ); |
| 1051 | + mw.UploadWizardUtil.removeItem( _this._uploadsInProgress, upload ); |
913 | 1052 | |
914 | 1053 | if ( result.upload && result.upload.imageinfo && result.upload.imageinfo.descriptionurl ) { |
915 | 1054 | // success |
916 | 1055 | setTimeout( function() { |
917 | | - upload.metadata.populateFromResult(result); }, 0 ); |
| 1056 | + upload.metadata.populateFromResult( result ); }, 0 ); |
918 | 1057 | |
919 | | - } else if (result.upload && result.upload.sessionkey) { |
920 | | - // there was a warning-type error which prevented it from adding the result to the db |
921 | | - if (result.upload.warnings.duplicate) { |
| 1058 | + } else if ( result.upload && result.upload.sessionkey ) { |
| 1059 | + // there was a warning - type error which prevented it from adding the result to the db |
| 1060 | + if ( result.upload.warnings.duplicate ) { |
922 | 1061 | var duplicates = result.upload.warnings.duplicate; |
923 | | - _this.metadata.errorDuplicate(result.upload.sessionkey, duplicates); |
| 1062 | + _this.metadata.errorDuplicate( result.upload.sessionkey, duplicates ); |
924 | 1063 | } |
925 | 1064 | |
926 | 1065 | // XXX namespace collision |
927 | 1066 | // and other errors that result in a stash |
928 | | - } else if (0 /* actual failure */) { |
| 1067 | + } else if ( 0 /* actual failure */ ) { |
929 | 1068 | // we may want to tag or otherwise queue it as an upload to retry |
930 | 1069 | } |
931 | 1070 | |
— | — | @@ -932,55 +1071,63 @@ |
933 | 1072 | }, |
934 | 1073 | |
935 | 1074 | |
936 | | - // depending on number of file upoads, change link text (and/or disable it) |
937 | | - // change button disabled/enabled |
| 1075 | + /** |
| 1076 | + * Occurs whenever we need to update the interface based on how many files are there or have completed |
| 1077 | + * Also detects if all uploads have completed and kicks off the process that eventually gets us to Step 2. |
| 1078 | + */ |
938 | 1079 | updateFileCounts: function() { |
939 | | - mw.log("update counts"); |
| 1080 | + mw.log( "update counts" ); |
940 | 1081 | var _this = this; |
941 | | - $j('#mwe-upwiz-add-file').html(gM('mwe-upwiz-add-file-' + (_this.uploads.length === 0 ? '0' : 'n'))); |
942 | | - if (_this.uploads.length < _this.maxUploads) { |
943 | | - $j('#mwe-upwiz-add-file').removeAttr('disabled'); |
| 1082 | + $j( '#mwe-upwiz-add-file' ).html( gM( 'mwe-upwiz-add-file-' + ( _this.uploads.length === 0 ? '0' : 'n' )) ); |
| 1083 | + if ( _this.uploads.length < _this.maxUploads ) { |
| 1084 | + $j( '#mwe-upwiz-add-file' ).removeAttr( 'disabled' ); |
944 | 1085 | } else { |
945 | | - $j('#mwe-upwiz-add-file').attr('disabled', true); |
| 1086 | + $j( '#mwe-upwiz-add-file' ).attr( 'disabled', true ); |
946 | 1087 | } |
947 | 1088 | |
948 | 1089 | var hasFile; |
949 | | - for (var i = 0; i < _this.uploads.length; i++) { |
| 1090 | + for ( var i = 0; i < _this.uploads.length; i++ ) { |
950 | 1091 | var upload = _this.uploads[i]; |
951 | | - if (upload.ui.fileInputCtrl.value != "") { |
| 1092 | + if ( upload.ui.fileInputCtrl.value != "" ) { |
952 | 1093 | hasFile = true; |
953 | 1094 | } |
954 | 1095 | } |
955 | | - if (hasFile) { |
956 | | - $j('#mwe-upwiz-upload-ctrl').removeAttr('disabled'); |
| 1096 | + if ( hasFile ) { |
| 1097 | + $j( '#mwe-upwiz-upload-ctrl' ).removeAttr( 'disabled' ); |
957 | 1098 | } else { |
958 | | - $j('#mwe-upwiz-upload-ctrl').attr('disabled', 'disabled'); |
| 1099 | + $j( '#mwe-upwiz-upload-ctrl' ).attr( 'disabled', 'disabled' ); |
959 | 1100 | } |
960 | 1101 | |
961 | 1102 | |
962 | | - $j('#mwe-upwiz-count').html( gM('mwe-upwiz-upload-count', [ _this._uploadsCompleted.length, _this.uploads.length ]) ); |
| 1103 | + $j( '#mwe-upwiz-count' ).html( gM( 'mwe-upwiz-upload-count', [ _this._uploadsCompleted.length, _this.uploads.length ] ) ); |
963 | 1104 | |
964 | | - if (_this.uploads.length > 0 && _this._uploadsCompleted.length == _this.uploads.length) { |
| 1105 | + if ( _this.uploads.length > 0 && _this._uploadsCompleted.length == _this.uploads.length ) { |
965 | 1106 | // is this enough to stop the progress monitor? |
966 | 1107 | _this.isCompleted = true; |
967 | 1108 | // set progress to 100% |
968 | | - _this.showProgressBar(1); |
969 | | - _this.showRemainingTime(0); |
| 1109 | + _this.showProgressBar( 1 ); |
| 1110 | + _this.showRemainingTime( 0 ); |
970 | 1111 | |
971 | 1112 | // XXX then should make the progress bar not have the animated lines when done. Solid blue, or fade away or something. |
972 | 1113 | // likewise, the remaining time should disappear, fadeout maybe. |
973 | 1114 | |
974 | 1115 | // do some sort of "all done" thing for the UI - advance to next tab maybe. |
975 | | - _this.moveToTab('metadata'); |
| 1116 | + _this.moveToTab( 'metadata' ); |
976 | 1117 | } |
977 | 1118 | |
978 | 1119 | }, |
979 | 1120 | |
980 | 1121 | |
| 1122 | + /** |
| 1123 | + * |
| 1124 | + */ |
981 | 1125 | pause: function() { |
982 | 1126 | |
983 | 1127 | }, |
984 | 1128 | |
| 1129 | + /** |
| 1130 | + * |
| 1131 | + */ |
985 | 1132 | stop: function() { |
986 | 1133 | |
987 | 1134 | }, |
— | — | @@ -989,10 +1136,16 @@ |
990 | 1137 | // entire METADATA TAB |
991 | 1138 | // |
992 | 1139 | |
| 1140 | + /** |
| 1141 | + * |
| 1142 | + */ |
993 | 1143 | createMetadata: function() { |
994 | 1144 | |
995 | 1145 | }, |
996 | 1146 | |
| 1147 | + /** |
| 1148 | + * |
| 1149 | + */ |
997 | 1150 | submitMetadata: function() { |
998 | 1151 | |
999 | 1152 | }, |
— | — | @@ -1001,13 +1154,22 @@ |
1002 | 1155 | |
1003 | 1156 | }; |
1004 | 1157 | |
1005 | | -// XXX refactor? or, reconsider approach -- this is an awful hack in some ways |
1006 | | -// The jQuery way would be to query the DOM for objects, not to keep a separate array hanging around |
| 1158 | +/** |
| 1159 | + * Miscellaneous utilities |
| 1160 | + */ |
1007 | 1161 | mw.UploadWizardUtil = { |
1008 | | - removeItem: function(items, item) { |
1009 | | - for (var i = 0; i < items.length; i++) { |
1010 | | - if (items[i] === item) { |
1011 | | - items.splice(i, 1); |
| 1162 | + /** |
| 1163 | + * remove an item from an array. Tests for === identity to remove the item |
| 1164 | + * XXX the entire rationale for this file may be wrong. |
| 1165 | + * XXX The jQuery way would be to query the DOM for objects, not to keep a separate array hanging around |
| 1166 | + * @param items the array where we want to remove an item |
| 1167 | + * @param item the item to remove |
| 1168 | + */ |
| 1169 | + removeItem: function( items, item ) { |
| 1170 | + for ( var i = 0; i < items.length; i++ ) { |
| 1171 | + if ( items[i] === item ) { |
| 1172 | + items.splice( i, 1 ); |
| 1173 | + break; |
1012 | 1174 | } |
1013 | 1175 | } |
1014 | 1176 | } |
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.ApiUploadHandler.js |
— | — | @@ -6,7 +6,11 @@ |
7 | 7 | // n.b. if there are message strings, or any assumption about HTML structure of the form. |
8 | 8 | // then we probably did it wrong |
9 | 9 | |
10 | | -mw.ApiUploadHandler = function(ui) { |
| 10 | +/** |
| 11 | + * Represents an object which configures a form to upload its files via an iframe talking to the MediaWiki API. |
| 12 | + * @param an UploadInterface object, which contains a .form property which points to a real HTML form in the DOM |
| 13 | + */ |
| 14 | +mw.ApiUploadHandler = function( ui ) { |
11 | 15 | var _this = this; |
12 | 16 | |
13 | 17 | _this.ui = ui; |
— | — | @@ -23,110 +27,145 @@ |
24 | 28 | // can also use Xhr Binary depending on config |
25 | 29 | _this.transport = new mw.IframeTransport( |
26 | 30 | _this.ui.form, |
27 | | - function(fraction){ _this.progress(fraction) }, |
28 | | - function(result) { _this.completed(result) } |
| 31 | + function( fraction ){ _this.progress( fraction ) }, |
| 32 | + function( result ) { _this.completed( result ) } |
29 | 33 | ); |
30 | 34 | |
31 | 35 | }; |
32 | 36 | |
33 | 37 | mw.ApiUploadHandler.prototype = { |
34 | | - addProgressCb: function(fn) { |
| 38 | + /** |
| 39 | + * Allow other parties to register interest in how we are progressing |
| 40 | + * @param callback which accepts a float between 0 and 1 as our current progress |
| 41 | + */ |
| 42 | + addProgressCb: function( fn ) { |
35 | 43 | var _this = this; |
36 | | - _this.progressCallbacks.push(function(progress) { fn(progress) }); |
| 44 | + _this.progressCallbacks.push( function( progress ) { fn( progress ) } ); |
37 | 45 | }, |
38 | 46 | |
39 | | - |
40 | | - addCompletedCb: function(f) { |
| 47 | + /** |
| 48 | + * Allow other parties to register interest in when we finish uploading |
| 49 | + * @param callback |
| 50 | + */ |
| 51 | + addCompletedCb: function( f ) { |
41 | 52 | var _this = this; |
42 | | - _this.completedCallbacks.push(f); |
| 53 | + _this.completedCallbacks.push( f ); |
43 | 54 | }, |
44 | 55 | |
45 | | - addErrorCb: function(f) { |
| 56 | + /** |
| 57 | + * Allow other parties to register interest in when we have an error |
| 58 | + * @param callback |
| 59 | + */ |
| 60 | + addErrorCb: function( f ) { |
46 | 61 | var _this = this; |
47 | | - _this.errorCallbacks.push(f); |
| 62 | + _this.errorCallbacks.push( f ); |
48 | 63 | }, |
49 | 64 | |
| 65 | + /** |
| 66 | + * Configure an HTML form so that it will submit its files to our transport (an iframe) |
| 67 | + * with proper params for the API |
| 68 | + * @param callback |
| 69 | + */ |
50 | 70 | configureForm: function() { |
51 | | - var apiUrl = mw.getLocalApiUrl(); // XXX or? throw new Error("configuration", "no API url"); |
52 | | - if (! (mw.getConfig('token') ) ) { |
53 | | - throw new Error("configuration", "no edit token"); |
| 71 | + var apiUrl = mw.getLocalApiUrl(); // XXX or? throw new Error( "configuration", "no API url" ); |
| 72 | + if ( ! ( mw.getConfig( 'token' ) ) ) { |
| 73 | + throw new Error( "configuration", "no edit token" ); |
54 | 74 | } |
55 | 75 | |
56 | 76 | var _this = this; |
57 | | - mw.log("configuring form for Upload API"); |
| 77 | + mw.log( "configuring form for Upload API" ); |
58 | 78 | |
59 | 79 | // Set the form action |
60 | 80 | try { |
61 | | - $j(_this.ui.form) |
62 | | - .attr('action', apiUrl) |
63 | | - .attr('method', 'POST') |
64 | | - .attr('enctype', 'multipart/form-data'); |
| 81 | + $j( _this.ui.form ) |
| 82 | + .attr( 'action', apiUrl ) |
| 83 | + .attr( 'method', 'POST' ) |
| 84 | + .attr( 'enctype', 'multipart/form-data' ); |
65 | 85 | } catch ( e ) { |
66 | | - alert("oops, form modification didn't work in ApiUploadHandler"); |
67 | | - mw.log("IE for some reason error's out when you change the action"); |
| 86 | + alert( "oops, form modification didn't work in ApiUploadHandler" ); |
| 87 | + mw.log( "IE for some reason error's out when you change the action" ); |
68 | 88 | // well, if IE fucks this up perhaps we should do something to make sure it writes correctly |
69 | 89 | // from the outset? |
70 | 90 | } |
71 | 91 | |
72 | | - _this.addFormInputIfMissing('token', mw.getConfig('token')); |
73 | | - _this.addFormInputIfMissing('action', 'upload'); |
74 | | - _this.addFormInputIfMissing('format', 'jsonfm'); |
| 92 | + _this.addFormInputIfMissing( 'token', mw.getConfig( 'token' )); |
| 93 | + _this.addFormInputIfMissing( 'action', 'upload' ); |
| 94 | + _this.addFormInputIfMissing( 'format', 'jsonfm' ); |
75 | 95 | |
76 | 96 | // XXX only for testing, so it stops complaining about dupes |
77 | | - _this.addFormInputIfMissing('ignorewarnings', '1'); |
| 97 | + if ( mw.getConfig( 'debug' )) { |
| 98 | + _this.addFormInputIfMissing( 'ignorewarnings', '1' ); |
| 99 | + } |
78 | 100 | }, |
79 | 101 | |
80 | | - addFormInputIfMissing: function(name, value) { |
| 102 | + /** |
| 103 | + * Add a hidden input to a form if it was not already there. |
| 104 | + * @param name the name of the input |
| 105 | + * @param value the value of the input |
| 106 | + */ |
| 107 | + addFormInputIfMissing: function( name, value ) { |
81 | 108 | var _this = this; |
82 | | - var $jForm = $j(_this.ui.form); |
| 109 | + var $jForm = $j( _this.ui.form ); |
83 | 110 | if ( $jForm.find( "[name='" + name + "']" ).length == 0 ) { |
84 | 111 | $jForm.append( |
85 | | - $j('<input />') |
86 | | - .attr({ |
| 112 | + $j( '<input />' ) |
| 113 | + .attr( { |
87 | 114 | 'type': "hidden", |
88 | 115 | 'name' : name, |
89 | 116 | 'value' : value |
90 | | - }) |
| 117 | + } ) |
91 | 118 | ); |
92 | 119 | } |
93 | 120 | }, |
94 | 121 | |
| 122 | + /** |
| 123 | + * Kick off the upload! |
| 124 | + */ |
95 | 125 | start: function() { |
96 | 126 | var _this = this; |
97 | | - mw.log("api: upload start!") |
98 | | - _this.beginTime = (new Date()).getTime(); |
| 127 | + mw.log( "api: upload start!" ); |
| 128 | + _this.beginTime = ( new Date() ).getTime(); |
99 | 129 | _this.ui.start(); |
100 | 130 | _this.ui.busy(); |
101 | | - $j(this.ui.form).submit(); |
| 131 | + $j( this.ui.form ).submit(); |
102 | 132 | }, |
103 | 133 | |
104 | | - progress: function(fraction) { |
105 | | - mw.log("api: upload progress!") |
| 134 | + /** |
| 135 | + * Central dispatch function for every other object interested in our progress |
| 136 | + * @param fraction float between 0 and 1, representing progress |
| 137 | + */ |
| 138 | + progress: function( fraction ) { |
| 139 | + mw.log( "api: upload progress!" ); |
106 | 140 | var _this = this; |
107 | | - _this.ui.progress(fraction); |
108 | | - for (var i = 0; i < _this.progressCallbacks.length; i++) { |
109 | | - _this.progressCallbacks[i](fraction); |
| 141 | + _this.ui.progress( fraction ); |
| 142 | + for ( var i = 0; i < _this.progressCallbacks.length; i++ ) { |
| 143 | + _this.progressCallbacks[i]( fraction ); |
110 | 144 | } |
111 | 145 | }, |
112 | 146 | |
113 | | - // this is not quite the right place for all this code |
114 | | - // perhaps should be abstract to any uploadHandler, or not |
115 | | - // in this at all |
116 | | - completed: function(result) { |
117 | | - mw.log("api: upload completed!") |
| 147 | + /** |
| 148 | + * Central dispatch function for everyone else interested if we've completed |
| 149 | + * @param result javascript object representing MediaWiki API result. |
| 150 | + */ |
| 151 | + completed: function( result ) { |
| 152 | + mw.log( "api: upload completed!" ); |
118 | 153 | var _this = this; |
119 | 154 | _this.ui.completed(); |
120 | | - for (var i = 0; i < _this.completedCallbacks.length; i++) { |
121 | | - _this.completedCallbacks[i](result); |
| 155 | + for ( var i = 0; i < _this.completedCallbacks.length; i++ ) { |
| 156 | + _this.completedCallbacks[i]( result ); |
122 | 157 | } |
123 | 158 | }, |
124 | 159 | |
125 | | - error: function(error) { |
126 | | - mw.log("api: error!"); |
| 160 | + /** |
| 161 | + * Central dispatch function for everyone else interested if we've had an error |
| 162 | + * @param error the error |
| 163 | + */ |
| 164 | + error: function( error ) { |
| 165 | + mw.log( "api: error!" ); |
127 | 166 | var _this = this; |
128 | | - _this.ui.error(error); |
129 | | - for (var i = 0; i < _this.errorCallbacks.length; i++) { |
130 | | - _this.errorCallbacks[i](error); |
| 167 | + _this.ui.error( error ); |
| 168 | + for ( var i = 0; i < _this.errorCallbacks.length; i++ ) { |
| 169 | + _this.errorCallbacks[i]( error ); |
131 | 170 | } |
132 | 171 | } |
133 | 172 | }; |
Index: branches/js2-work/phase3/js/mwEmbed/modules/UploadWizard/mw.IframeTransport.js |
— | — | @@ -1,4 +1,12 @@ |
2 | | -mw.IframeTransport = function(form, progressCb, completedCb) { |
| 2 | +/** |
| 3 | + * Represents a "transport" for files to upload; in this case an iframe. |
| 4 | + * The iframe is made to be the target of a form so that the existing page does not reload, even though it's a POST. |
| 5 | + * @param form an HTML form |
| 6 | + * @param progressCb callback to execute when we've started. (does not do float here because iframes can't |
| 7 | + * monitor fractional progress). |
| 8 | + * @param completedCb callback to execute when we've finished the upload |
| 9 | + */ |
| 10 | +mw.IframeTransport = function( form, progressCb, completedCb ) { |
3 | 11 | var _this = this; |
4 | 12 | |
5 | 13 | _this.form = form; |
— | — | @@ -7,47 +15,51 @@ |
8 | 16 | |
9 | 17 | _this.iframeId = 'f_' + ( $j( 'iframe' ).length + 1 ); |
10 | 18 | |
11 | | - //IE only works if you "create element with the name" (not jquery style) |
| 19 | + //IE only works if you "create element with the name" ( not jquery style ) |
12 | 20 | var iframe; |
13 | 21 | try { |
14 | 22 | iframe = document.createElement( '<iframe name="' + _this.iframeId + '">' ); |
15 | | - } catch (ex) { |
16 | | - iframe = document.createElement('iframe'); |
| 23 | + } catch ( ex ) { |
| 24 | + iframe = document.createElement( 'iframe' ); |
17 | 25 | } |
18 | 26 | |
19 | 27 | // we configure form on load, because the first time it loads, it's blank |
20 | 28 | // then we configure it to deal with an API submission |
21 | 29 | $j( iframe ) |
22 | | - .attr({ 'src' : 'javascript:false;', |
23 | | - 'id' : _this.iframeId, |
24 | | - 'name' : _this.iframeId }) |
25 | | - .load(function() { _this.configureForm() }) |
26 | | - .css('display', 'none'); |
| 30 | + .attr( { 'src' : 'javascript:false;', |
| 31 | + 'id' : _this.iframeId, |
| 32 | + 'name' : _this.iframeId } ) |
| 33 | + .load( function() { _this.configureForm() } ) |
| 34 | + .css( 'display', 'none' ); |
27 | 35 | |
28 | 36 | $j( "body" ).append( iframe ); |
29 | 37 | }; |
30 | 38 | |
31 | 39 | mw.IframeTransport.prototype = { |
| 40 | + /** |
| 41 | + * Configure the form we have so that it submits to the iframe |
| 42 | + * Ensure callback on completion of upload |
| 43 | + */ |
32 | 44 | configureForm: function() { |
33 | | - mw.log("configuring form for iframe transport"); |
| 45 | + mw.log( "configuring form for iframe transport" ); |
34 | 46 | var _this = this; |
35 | 47 | // Set the form target to the iframe |
36 | | - var $jForm = $j(_this.form); |
| 48 | + var $jForm = $j( _this.form ); |
37 | 49 | $jForm.attr( 'target', _this.iframeId ); |
38 | 50 | |
39 | 51 | // attach an additional handler to the form, so, when submitted, it starts showing the progress |
40 | 52 | // XXX this is lame .. there should be a generic way to indicate busy status... |
41 | 53 | $jForm.submit( function() { |
42 | | - mw.log("submitting to iframe..."); |
43 | | - _this.progressCb(1.0); |
| 54 | + mw.log( "submitting to iframe..." ); |
| 55 | + _this.progressCb( 1.0 ); |
44 | 56 | return true; |
45 | 57 | } ); |
46 | 58 | |
47 | 59 | // Set up the completion callback |
48 | 60 | $j( '#' + _this.iframeId ).load( function() { |
49 | | - mw.log("received result in iframe"); |
| 61 | + mw.log( "received result in iframe" ); |
50 | 62 | _this.processIframeResult( $j( this ).get( 0 ) ); |
51 | | - }); |
| 63 | + } ); |
52 | 64 | }, |
53 | 65 | |
54 | 66 | /** |
— | — | @@ -61,13 +73,13 @@ |
62 | 74 | var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document; |
63 | 75 | // Fix for Opera 9.26 |
64 | 76 | if ( doc.readyState && doc.readyState != 'complete' ) { |
65 | | - mw.log("not complete"); |
| 77 | + mw.log( "not complete" ); |
66 | 78 | return; |
67 | 79 | } |
68 | 80 | |
69 | 81 | // Fix for Opera 9.64 |
70 | 82 | if ( doc.body && doc.body.innerHTML == "false" ) { |
71 | | - mw.log("no innerhtml"); |
| 83 | + mw.log( "no innerhtml" ); |
72 | 84 | return; |
73 | 85 | } |
74 | 86 | var response; |
— | — | @@ -78,9 +90,9 @@ |
79 | 91 | // Get the json string |
80 | 92 | // XXX wait... why are we grepping it out of an HTML doc? We requested jsonfm, why? |
81 | 93 | json = $j( doc.body ).find( 'pre' ).text(); |
82 | | - mw.log( 'iframe:json::' + json) |
| 94 | + mw.log( 'iframe:json::' + json ) |
83 | 95 | if ( json ) { |
84 | | - response = window["eval"]( "(" + json + ")" ); |
| 96 | + response = window["eval"]( "( " + json + " )" ); |
85 | 97 | } else { |
86 | 98 | response = {}; |
87 | 99 | } |