Index: trunk/extensions/UploadWizard/UploadWizardHooks.php |
— | — | @@ -46,6 +46,8 @@ |
47 | 47 | 'resources/jquery/jquery.validate.wmCommonsBlacklist.js', |
48 | 48 | |
49 | 49 | // common utilities |
| 50 | + 'resources/mw.fileApi.js', |
| 51 | + 'resources/mw.units.js', |
50 | 52 | 'resources/mw.Log.js', |
51 | 53 | 'resources/mw.Utilities.js', |
52 | 54 | 'resources/mw.UtilitiesTime.js', |
— | — | @@ -333,7 +335,12 @@ |
334 | 336 | 'mwe-upwiz-feedback-adding', |
335 | 337 | 'mwe-upwiz-feedback-error1', |
336 | 338 | 'mwe-upwiz-feedback-error2', |
337 | | - 'mwe-upwiz-feedback-error3' |
| 339 | + 'mwe-upwiz-feedback-error3', |
| 340 | + 'size-terabytes', |
| 341 | + 'size-gigabytes', |
| 342 | + 'size-megabytes', |
| 343 | + 'size-kilobytes', |
| 344 | + 'size-bytes' |
338 | 345 | ), |
339 | 346 | 'group' => 'ext.uploadWizard' |
340 | 347 | ), |
Index: trunk/extensions/UploadWizard/resources/mw.UploadWizardUploadInterface.js |
— | — | @@ -15,7 +15,13 @@ |
16 | 16 | _this.isFilled = false; |
17 | 17 | |
18 | 18 | _this.$fileInputCtrl = $j('<input size="1" class="mwe-upwiz-file-input" name="file" type="file"/>') |
19 | | - .change( function() { _this.fileChanged(); } ); |
| 19 | + .change( function() { |
| 20 | + _this.clearErrors(); |
| 21 | + _this.upload.extractLocalFileInfo( |
| 22 | + this, // the file input |
| 23 | + function() { _this.fileChanged(); } |
| 24 | + ); |
| 25 | + } ); |
20 | 26 | |
21 | 27 | _this.$indicator = $j( '<div class="mwe-upwiz-file-indicator"></div>' ); |
22 | 28 | |
— | — | @@ -82,6 +88,14 @@ |
83 | 89 | $j( _this.div ).bind( 'transportProgressEvent', function(e) { _this.showTransportProgress(); } ); |
84 | 90 | // $j( _this.div ).bind( 'transportedEvent', function(e) { _this.showStashed(); } ); |
85 | 91 | |
| 92 | + // XXX feature envy |
| 93 | + var $preview = $j( this.div ).find( '.mwe-upwiz-file-preview' ); |
| 94 | + _this.upload.setThumbnail( |
| 95 | + $preview, |
| 96 | + mw.UploadWizard.config[ 'thumbnailWidth' ], |
| 97 | + mw.UploadWizard.config[ 'thumbnailMaxHeight' ] |
| 98 | + ); |
| 99 | + |
86 | 100 | }; |
87 | 101 | |
88 | 102 | |
— | — | @@ -143,7 +157,6 @@ |
144 | 158 | * Set the status line for this upload with an internationalized message string. |
145 | 159 | * @param String msgKey: key for the message |
146 | 160 | * @param Array args: array of values, in case any need to be fed to the image. |
147 | | - * @param Boolean error: if true, show an error |
148 | 161 | */ |
149 | 162 | setStatus: function( msgKey, args ) { |
150 | 163 | if ( !mw.isDefined( args ) ) { |
— | — | @@ -155,6 +168,14 @@ |
156 | 169 | }, |
157 | 170 | |
158 | 171 | /** |
| 172 | + * Set status line directly with a string |
| 173 | + * @param {String} |
| 174 | + */ |
| 175 | + setStatusString: function( s ) { |
| 176 | + $j( this.div ).find( '.mwe-upwiz-file-status' ).html( s ).show(); |
| 177 | + }, |
| 178 | + |
| 179 | + /** |
159 | 180 | * Clear the status line for this upload (hide it, in case there are paddings and such which offset other things.) |
160 | 181 | */ |
161 | 182 | clearStatus: function() { |
— | — | @@ -209,10 +230,10 @@ |
210 | 231 | */ |
211 | 232 | fileChanged: function() { |
212 | 233 | var _this = this; |
213 | | - _this.clearErrors(); |
214 | | - _this.upload.extractLocalFileInfo( _this.$fileInputCtrl.val() ); |
215 | 234 | var extension = _this.upload.title.getExtension(); |
216 | 235 | var hasExtension = ! mw.isEmpty( extension ); |
| 236 | + |
| 237 | + /* this should not be in the interface */ |
217 | 238 | var isGoodExtension = false; |
218 | 239 | if ( hasExtension ) { |
219 | 240 | isGoodExtension = $j.inArray( extension.toLowerCase(), mw.UploadWizard.config[ 'fileExtensions' ] ) !== -1; |
— | — | @@ -220,19 +241,20 @@ |
221 | 242 | if ( hasExtension && isGoodExtension ) { |
222 | 243 | _this.updateFilename(); |
223 | 244 | } else { |
| 245 | + var $errorMessage; |
224 | 246 | // Check if firefogg should be recommended to be installed ( user selects an extension that can be converted) |
225 | 247 | if( mw.UploadWizard.config['enableFirefogg'] |
226 | 248 | && |
227 | 249 | $j.inArray( extension.toLowerCase(), mw.UploadWizard.config['transcodeExtensionList'] ) !== -1 |
228 | 250 | ){ |
229 | | - var $errorMessage = $j( '<p>' ).msg('mwe-upwiz-upload-error-bad-extension-video-firefogg', |
| 251 | + $errorMessage = $j( '<p>' ).msg('mwe-upwiz-upload-error-bad-extension-video-firefogg', |
230 | 252 | mw.Firefogg.getFirefoggInstallUrl(), |
231 | 253 | 'http://commons.wikimedia.org/wiki/Help:Converting_video' |
232 | 254 | ); |
233 | 255 | |
234 | 256 | } else { |
235 | 257 | var errorKey = hasExtension ? 'mwe-upwiz-upload-error-bad-filename-extension' : 'mwe-upwiz-upload-error-bad-filename-no-extension'; |
236 | | - var $errorMessage = $j( '<p>' ).msg( errorKey, extension ); |
| 258 | + $errorMessage = $j( '<p>' ).msg( errorKey, extension ); |
237 | 259 | } |
238 | 260 | $( '<div></div>' ) |
239 | 261 | .append( |
— | — | @@ -250,7 +272,11 @@ |
251 | 273 | modal: true |
252 | 274 | }); |
253 | 275 | } |
| 276 | + |
254 | 277 | this.clearStatus(); |
| 278 | + if ( this.upload.file ) { |
| 279 | + this.setStatusString( mw.units.bytes( this.upload.file.size ) ); |
| 280 | + } |
255 | 281 | }, |
256 | 282 | |
257 | 283 | /** |
— | — | @@ -326,6 +352,8 @@ |
327 | 353 | // But for UploadWizard, at this stage, it's the reverse. We want to stop same-content dead, but for now we ignore same-filename |
328 | 354 | $j( _this.filenameCtrl ).val( ( new Date() ).getTime().toString() +_this.upload.title.getMain() ); |
329 | 355 | |
| 356 | + |
| 357 | + // deal with styling the file inputs and making it react to mouse |
330 | 358 | if ( ! _this.isFilled ) { |
331 | 359 | var $div = $j( _this.div ); |
332 | 360 | _this.isFilled = true; |
Index: trunk/extensions/UploadWizard/resources/mw.UploadWizard.js |
— | — | @@ -8,6 +8,10 @@ |
9 | 9 | ( function( $j ) { |
10 | 10 | |
11 | 11 | mw.UploadWizardUpload = function( api, filesDiv ) { |
| 12 | + |
| 13 | + this.index = mw.UploadWizardUpload.prototype.count; |
| 14 | + mw.UploadWizardUpload.prototype.count++; |
| 15 | + |
12 | 16 | this.api = api; |
13 | 17 | this.state = 'new'; |
14 | 18 | this.thumbnails = {}; |
— | — | @@ -20,8 +24,8 @@ |
21 | 25 | this.sessionKey = undefined; |
22 | 26 | |
23 | 27 | // this should be moved to the interface, if we even keep this |
24 | | - this.transportWeight = 1; // default |
25 | | - this.detailsWeight = 1; // default |
| 28 | + this.transportWeight = 1; // default all same |
| 29 | + this.detailsWeight = 1; // default all same |
26 | 30 | |
27 | 31 | // details |
28 | 32 | this.ui = new mw.UploadWizardUploadInterface( this, filesDiv ); |
— | — | @@ -32,8 +36,6 @@ |
33 | 37 | this.handler = this.getUploadHandler(); |
34 | 38 | |
35 | 39 | |
36 | | - this.index = mw.UploadWizardUpload.prototype.count; |
37 | | - mw.UploadWizardUpload.prototype.count++; |
38 | 40 | }; |
39 | 41 | |
40 | 42 | mw.UploadWizardUpload.prototype = { |
— | — | @@ -231,32 +233,18 @@ |
232 | 234 | * @param {Mixed} result -- result of AJAX call |
233 | 235 | */ |
234 | 236 | setSuccess: function( result ) { |
235 | | - var _this = this; // was a triumph |
| 237 | + var _this = this; |
236 | 238 | _this.state = 'transported'; |
237 | 239 | _this.transportProgress = 1; |
238 | 240 | |
239 | | - // I'm making a note here |
240 | 241 | _this.ui.setStatus( 'mwe-upwiz-getting-metadata' ); |
241 | 242 | if ( result.upload ) { |
242 | 243 | _this.extractUploadInfo( result.upload ); |
243 | | - _this.getThumbnail( |
244 | | - function( image ) { |
245 | | - // n.b. if server returns a URL, which is a 404, we do NOT get broken image |
246 | | - _this.ui.setPreview( image ); // make the thumbnail the preview image |
247 | | - }, |
248 | | - mw.UploadWizard.config[ 'thumbnailWidth' ], |
249 | | - mw.UploadWizard.config[ 'thumbnailMaxHeight' ] |
250 | | - ); |
251 | | - // create the large thumbnail that the other thumbnails link to |
252 | | - _this.getThumbnail( |
253 | | - function( image ) {}, |
254 | | - mw.UploadWizard.config[ 'largeThumbnailWidth' ], |
255 | | - mw.UploadWizard.config[ 'largeThumbnailMaxHeight' ] |
256 | | - ); |
257 | 244 | _this.deedPreview.setup(); |
258 | 245 | _this.details.populate(); |
259 | 246 | _this.state = 'stashed'; |
260 | 247 | _this.ui.showStashed(); |
| 248 | + $.publishReady( 'thumbnails.' + _this.index, 'api' ); |
261 | 249 | } else { |
262 | 250 | _this.setError( 'noimageinfo' ); |
263 | 251 | } |
— | — | @@ -265,18 +253,46 @@ |
266 | 254 | |
267 | 255 | /** |
268 | 256 | * Called when the file is entered into the file input |
269 | | - * Get as much data as possible -- maybe exif, even thumbnail maybe |
| 257 | + * Get as much data as possible with this browser, including thumbnail. |
| 258 | + * TODO exif |
| 259 | + * @param {HTMLFileInput} file input field |
| 260 | + * @param {Function} callback when ready |
270 | 261 | */ |
271 | | - extractLocalFileInfo: function( filename ) { |
272 | | - if ( false ) { // FileAPI, one day |
273 | | - this.transportWeight = getFileSize(); |
274 | | - } |
275 | | - // XXX sanitize filename |
| 262 | + extractLocalFileInfo: function( fileInput, callback ) { |
| 263 | + var _this = this; |
| 264 | + if ( mw.fileApi.isAvailable() ) { |
| 265 | + if ( fileInput.files && fileInput.files.length ) { |
| 266 | + // TODO multiple files in an input |
| 267 | + this.file = fileInput.files[0]; |
| 268 | + } |
| 269 | + // TODO check max upload size, alert user if too big |
| 270 | + this.transportWeight = this.file.size; |
| 271 | + |
| 272 | + if ( mw.fileApi.isPreviewableFile( this.file ) ) { |
| 273 | + // TODO all that other complicated file rotation / binary stuff from mediawiki.special.upload.js |
| 274 | + var image = document.createElement( 'img' ); |
| 275 | + image.onload = function() { |
| 276 | + $.publishReady( 'thumbnails.' + _this.index, image ); |
| 277 | + }; |
| 278 | + var reader = new FileReader(); |
| 279 | + reader.onload = function( progressEvent ) { |
| 280 | + _this.thumbnails['*'] = reader.result; |
| 281 | + image.src = reader.result; |
| 282 | + }; |
| 283 | + reader.readAsDataURL( _this.file ); |
| 284 | + } |
| 285 | + |
| 286 | + } |
| 287 | + // TODO sanitize filename |
| 288 | + var filename = fileInput.value; |
276 | 289 | try { |
277 | 290 | this.title = new mw.Title( mw.UploadWizardUtil.getBasename( filename ).replace( /:/g, '_' ), 'file' ); |
278 | 291 | } catch ( e ) { |
279 | 292 | this.setError( 'mwe-upwiz-unparseable-filename', filename ); |
280 | 293 | } |
| 294 | + if ( this.title ) { |
| 295 | + callback(); |
| 296 | + } |
281 | 297 | }, |
282 | 298 | |
283 | 299 | /** |
— | — | @@ -285,16 +301,17 @@ |
286 | 302 | * @param result The JSON object from a successful API upload result. |
287 | 303 | */ |
288 | 304 | extractUploadInfo: function( resultUpload ) { |
| 305 | + |
289 | 306 | if ( resultUpload.sessionkey ) { |
290 | 307 | this.sessionKey = resultUpload.sessionkey; |
291 | 308 | } |
| 309 | + |
292 | 310 | if ( resultUpload.imageinfo ) { |
293 | 311 | this.extractImageInfo( resultUpload.imageinfo ); |
294 | 312 | } else if ( resultUpload.stashimageinfo ) { |
295 | 313 | this.extractImageInfo( resultUpload.stashimageinfo ); |
296 | 314 | } |
297 | 315 | |
298 | | - |
299 | 316 | }, |
300 | 317 | |
301 | 318 | /** |
— | — | @@ -335,6 +352,10 @@ |
336 | 353 | } |
337 | 354 | */ |
338 | 355 | } |
| 356 | + |
| 357 | + |
| 358 | + |
| 359 | + |
339 | 360 | }, |
340 | 361 | |
341 | 362 | /** |
— | — | @@ -386,7 +407,69 @@ |
387 | 408 | |
388 | 409 | this.api.get( params, { ok: ok, err: err } ); |
389 | 410 | }, |
| 411 | + |
| 412 | + |
390 | 413 | /** |
| 414 | + * Get information about published images |
| 415 | + * (There is some overlap with getStashedImageInfo, but it's different at every stage so it's clearer to have separate functions) |
| 416 | + * See API documentation for prop=imageinfo for what 'props' can contain |
| 417 | + * @param {Function} callback -- called with null if failure, with imageinfo data structure if success |
| 418 | + * @param {Array} properties to extract |
| 419 | + * @param {Number} optional, width of thumbnail. Will force 'url' to be added to props |
| 420 | + * @param {Number} optional, height of thumbnail. Will force 'url' to be added to props |
| 421 | + */ |
| 422 | + getImageInfo: function( callback, props, width, height ) { |
| 423 | + var _this = this; |
| 424 | + if (!mw.isDefined( props ) ) { |
| 425 | + props = []; |
| 426 | + } |
| 427 | + var requestedTitle = _this.title.getPrefixedText(); |
| 428 | + var params = { |
| 429 | + 'prop': 'imageinfo', |
| 430 | + 'titles': requestedTitle, |
| 431 | + 'iiprop': props.join( '|' ) |
| 432 | + }; |
| 433 | + |
| 434 | + if ( mw.isDefined( width ) || mw.isDefined( height ) ) { |
| 435 | + if ( ! $j.inArray( 'url', props ) ) { |
| 436 | + props.push( 'url' ); |
| 437 | + } |
| 438 | + if ( mw.isDefined( width ) ) { |
| 439 | + params['iiurlwidth'] = width; |
| 440 | + } |
| 441 | + if ( mw.isDefined( height ) ) { |
| 442 | + params['iiurlheight'] = height; |
| 443 | + } |
| 444 | + } |
| 445 | + |
| 446 | + var ok = function( data ) { |
| 447 | + if ( data && data.query && data.query.pages ) { |
| 448 | + var found = false; |
| 449 | + $j.each( data.query.pages, function( pageId, page ) { |
| 450 | + if ( page.title && page.title === requestedTitle && page.imageinfo ) { |
| 451 | + found = true; |
| 452 | + callback( page.imageinfo ); |
| 453 | + return false; |
| 454 | + } |
| 455 | + } ); |
| 456 | + if ( found ) { |
| 457 | + return; |
| 458 | + } |
| 459 | + } |
| 460 | + mw.log("mw.UploadWizardUpload::getImageInfo> No data matching " + requestedTitle + " ? "); |
| 461 | + callback( null ); |
| 462 | + }; |
| 463 | + |
| 464 | + var err = function( code, result ) { |
| 465 | + mw.log( 'mw.UploadWizardUpload::getImageInfo> error: ' + code, 'debug' ); |
| 466 | + callback( null ); |
| 467 | + }; |
| 468 | + |
| 469 | + this.api.get( params, { ok: ok, err: err } ); |
| 470 | + }, |
| 471 | + |
| 472 | + |
| 473 | + /** |
391 | 474 | * Get the upload handler per browser capabilities |
392 | 475 | */ |
393 | 476 | getUploadHandler: function(){ |
— | — | @@ -405,173 +488,167 @@ |
406 | 489 | } |
407 | 490 | return this.uploadHandler; |
408 | 491 | }, |
| 492 | + |
409 | 493 | /** |
410 | | - * Fetch a thumbnail for a stashed upload of the desired width. |
411 | | - * It is assumed you don't call this until it's been transported. |
| 494 | + * Explicitly fetch a thumbnail for a stashed upload of the desired width. |
| 495 | + * Publishes to any event listeners that might have wanted it. |
412 | 496 | * |
413 | | - * @param callback - callback to execute once thumbnail has been obtained -- must accept Image object for success, null for error |
414 | 497 | * @param width - desired width of thumbnail (height will scale to match) |
415 | 498 | * @param height - (optional) maximum height of thumbnail |
416 | 499 | */ |
417 | | - getThumbnail: function( callback, width, height ) { |
| 500 | + getAndPublishApiThumbnail: function( key, width, height ) { |
418 | 501 | var _this = this; |
| 502 | + |
419 | 503 | if ( mw.isEmpty( height ) ) { |
420 | 504 | height = -1; |
421 | 505 | } |
422 | | - // this key is overspecified for this thumbnail ( we don't need to reiterate the index ) but |
423 | | - // we can use this as key as an event now, that might fire much later |
424 | | - var key = 'thumbnail.' + _this.index + '.width' + width + ',height' + height; |
425 | | - if ( mw.isDefined( _this.thumbnails[key] ) ) { |
426 | | - callback( _this.thumbnails[key] ); |
427 | | - return; |
428 | | - } |
429 | 506 | |
430 | | - // subscribe to the event that this thumbnail is ready -- will give null or an Image to the callback |
431 | | - $j.subscribe( key, callback ); |
432 | | - |
433 | | - // if someone else already started a thumbnail fetch & publish, then don't bother, just wait for the event. |
434 | | - if ( ! mw.isDefined( _this.thumbnailPublishers[ key ] ) ) { |
435 | | - // The thumbnail publisher accepts the result of a stashImageInfo, and then tries to get the thumbnail, and eventually |
436 | | - // will trigger the event we just subscribed to. |
437 | | - _this.thumbnailPublishers[ key ] = _this.getThumbnailPublisher( key ); |
438 | | - _this.getStashImageInfo( _this.thumbnailPublishers[ key ], [ 'url' ], width, height ); |
439 | | - } |
440 | | - }, |
441 | | - |
442 | | - /** |
443 | | - * Returns a callback that can be used with a stashImageInfo call to fetch images, and then fire off an event to |
444 | | - * let everyone else know the image is loaded. |
445 | | - * |
446 | | - * Will retry the thumbnail URL several times, as thumbnails are known to be slow in production. |
447 | | - * @param {String} name of event to publish when thumbnails received (or final failure) |
448 | | - */ |
449 | | - getThumbnailPublisher: function( key ) { |
450 | | - var _this = this; |
451 | | - return function( thumbnails ) { |
| 507 | + if ( !mw.isDefined( _this.thumbnailPublishers[key] ) ) { |
| 508 | + var thumbnailPublisher = function( thumbnails ) { |
452 | 509 | if ( thumbnails === null ) { |
453 | | - // the api call failed somehow, no thumbnail data. |
454 | | - $j.publish( key, null ); |
| 510 | + // the api call failed somehow, no thumbnail data. |
| 511 | + $j.publishReady( key, null ); |
455 | 512 | } else { |
456 | | - // ok, the api callback has returned us information on where the thumbnail(s) ARE, but that doesn't mean |
457 | | - // they are actually there yet. Keep trying to set the source ( which should trigger "error" or "load" event ) |
458 | | - // on the image. If it loads publish the event with the image. If it errors out too many times, give up and publish |
459 | | - // the event with a null. |
460 | | - $j.each( thumbnails, function( i, thumb ) { |
| 513 | + // ok, the api callback has returned us information on where the thumbnail(s) ARE, but that doesn't mean |
| 514 | + // they are actually there yet. Keep trying to set the source ( which should trigger "error" or "load" event ) |
| 515 | + // on the image. If it loads publish the event with the image. If it errors out too many times, give up and publish |
| 516 | + // the event with a null. |
| 517 | + $j.each( thumbnails, function( i, thumb ) { |
461 | 518 | if ( thumb.thumberror || ( ! ( thumb.thumburl && thumb.thumbwidth && thumb.thumbheight ) ) ) { |
462 | 519 | mw.log( "mw.UploadWizardUpload::getThumbnail> thumbnail error or missing information" ); |
463 | | - $j.publish( key, null ); |
| 520 | + $j.publishReady( key, null ); |
464 | 521 | return; |
465 | 522 | } |
466 | 523 | |
467 | | - // try to load this image with exponential backoff |
468 | | - // if the delay goes past 8 seconds, it gives up and publishes the event with null |
469 | | - var timeoutMs = 100; |
470 | | - |
| 524 | + // try to load this image with exponential backoff |
| 525 | + // if the delay goes past 8 seconds, it gives up and publishes the event with null |
| 526 | + var timeoutMs = 100; |
471 | 527 | var image = document.createElement( 'img' ); |
472 | 528 | image.width = thumb.thumbwidth; |
473 | 529 | image.height = thumb.thumbheight; |
474 | | - $j( image ) |
475 | | - .load( function() { |
476 | | - // cache this thumbnail |
477 | | - _this.thumbnails[key] = image; |
478 | | - // publish the image to anyone who wanted it |
479 | | - $j.publish( key, image ); |
480 | | - } ) |
481 | | - .error( function() { |
482 | | - // retry with exponential backoff |
483 | | - if ( timeoutMs < 8000 ) { |
484 | | - setTimeout( function() { |
485 | | - timeoutMs = timeoutMs * 2 + Math.round( Math.random() * ( timeoutMs / 10 ) ); |
486 | | - setSrc(); |
487 | | - }, timeoutMs ); |
488 | | - } else { |
489 | | - $j.publish( key, null ); |
490 | | - } |
491 | | - } ); |
| 530 | + $j( image ) |
| 531 | + .load( function() { |
| 532 | + // cache this thumbnail |
| 533 | + _this.thumbnails[key] = image; |
| 534 | + // publish the image to anyone who wanted it |
| 535 | + $j.publishReady( key, image ); |
| 536 | + } ) |
| 537 | + .error( function() { |
| 538 | + // retry with exponential backoff |
| 539 | + if ( timeoutMs < 8000 ) { |
| 540 | + setTimeout( function() { |
| 541 | + timeoutMs = timeoutMs * 2 + Math.round( Math.random() * ( timeoutMs / 10 ) ); |
| 542 | + setSrc(); |
| 543 | + }, timeoutMs ); |
| 544 | + } else { |
| 545 | + $j.publishReady( key, null ); |
| 546 | + } |
| 547 | + } ); |
492 | 548 | |
493 | | - // executing this should cause a .load() or .error() event on the image |
494 | | - function setSrc() { |
495 | | - image.src = thumb.thumburl; |
496 | | - } |
| 549 | + // executing this should cause a .load() or .error() event on the image |
| 550 | + function setSrc() { |
| 551 | + image.src = thumb.thumburl; |
| 552 | + } |
497 | 553 | |
498 | | - // and, go! |
499 | | - setSrc(); |
500 | | - } ); |
| 554 | + // and, go! |
| 555 | + setSrc(); |
| 556 | + } ); |
| 557 | + } |
| 558 | + }; |
| 559 | + |
| 560 | + _this.thumbnailPublishers[key] = thumbnailPublisher; |
| 561 | + if ( _this.state !== 'complete' ) { |
| 562 | + _this.getStashImageInfo( thumbnailPublisher, [ 'url' ], width, height ); |
| 563 | + } else { |
| 564 | + _this.getImageInfo( thumbnailPublisher, [ 'url' ], width, height ); |
| 565 | + } |
| 566 | + |
501 | 567 | } |
502 | | - }; |
503 | 568 | }, |
504 | 569 | |
505 | 570 | /** |
506 | | - * Look up thumbnail info and set it in HTML, with loading spinner |
| 571 | + * Given a jQuery selector, subscribe to the "ready" event that fills the thumbnail |
| 572 | + * This will trigger if the thumbnail is added in the future or if it already has been |
507 | 573 | * |
508 | 574 | * @param selector |
509 | | - * @param width |
510 | | - * @param height (optional) |
| 575 | + * @param width Width constraint |
| 576 | + * @param height Height constraint (optional) |
511 | 577 | */ |
512 | 578 | setThumbnail: function( selector, width, height ) { |
513 | 579 | var _this = this; |
514 | 580 | if ( typeof width === 'undefined' || width === null || width <= 0 ) { |
515 | | - width = mw.UploadWizard.config[ 'thumbnailWidth' ]; |
| 581 | + width = mw.UploadWizard.config['thumbnailWidth']; |
516 | 582 | } |
517 | | - width = parseInt( width, 10 ); |
518 | | - height = null; |
519 | | - if ( !mw.isEmpty( height ) ) { |
520 | | - height = parseInt( height, 10 ); |
521 | | - } |
| 583 | + var constraints = { |
| 584 | + width: parseInt( width, 10 ), |
| 585 | + height: ( mw.isDefined( height ) ? parseInt( height, 10 ) : null ) |
| 586 | + }; |
522 | 587 | |
523 | | - var callback = function( image ) { |
| 588 | + /** |
| 589 | + * This callback will add an image to the selector, using in-browser scaling if necessary |
| 590 | + * @param {HTMLImageElement} |
| 591 | + */ |
| 592 | + var placed = false; |
| 593 | + var placeImageCallback = function( image ) { |
524 | 594 | if ( image === null ) { |
525 | 595 | $j( selector ).addClass( 'mwe-upwiz-file-preview-broken' ); |
526 | 596 | _this.ui.setStatus( 'mwe-upwiz-thumbnail-failed' ); |
527 | | - } else { |
528 | | - var $thumbnailLink = $j( '<a class="mwe-upwiz-thumbnail-link"></a>' ); |
529 | | - if ( _this.state != 'complete' ) { // don't use lightbox for thank you page thumbnail |
530 | | - $thumbnailLink |
| 597 | + return; |
| 598 | + } |
| 599 | + |
| 600 | + // if this debugger isn't here, it's okay?? |
| 601 | + // figure out what scaling is needed, if any |
| 602 | + var scaling = 1; |
| 603 | + $j.each( [ 'width', 'height' ], function( i, dim ) { |
| 604 | + if ( constraints[dim] && image[dim] > constraints[dim] ) { |
| 605 | + var s = constraints[dim] / image[dim]; |
| 606 | + if ( s < scaling ) { |
| 607 | + scaling = s; |
| 608 | + } |
| 609 | + } |
| 610 | + } ); |
| 611 | + |
| 612 | + // add the image to the DOM, finally |
| 613 | + $j( selector ).html( |
| 614 | + $j( '<a class="mwe-upwiz-thumbnail-link"></a>' ).append( |
| 615 | + $j( '<img/>' ) |
531 | 616 | .attr( { |
532 | | - 'href': '#', |
533 | | - 'target' : '_new' |
| 617 | + width: parseInt( image.width * scaling, 10 ), |
| 618 | + height: parseInt( image.height * scaling, 10 ), |
| 619 | + src: image.src |
534 | 620 | } ) |
535 | | - // set up lightbox behavior for thumbnail |
536 | | - .click( function() { |
537 | | - // get large preview image |
538 | | - _this.getThumbnail( |
539 | | - // open large preview in modal dialog box |
540 | | - function( image ) { |
541 | | - var dialogWidth = ( image.width > 200 ) ? image.width : 200; |
542 | | - $( '<div class="mwe-upwiz-lightbox"></div>' ) |
543 | | - .append( image ) |
544 | | - .dialog( { |
545 | | - 'width': dialogWidth, |
546 | | - 'autoOpen': true, |
547 | | - 'title': gM( 'mwe-upwiz-image-preview' ), |
548 | | - 'modal': true, |
549 | | - 'resizable': false |
550 | | - } ); |
551 | | - }, |
552 | | - mw.UploadWizard.config[ 'largeThumbnailWidth' ], |
553 | | - mw.UploadWizard.config[ 'largeThumbnailMaxHeight' ] |
554 | | - ); |
555 | | - return false; |
556 | | - } ); // close thumbnail click function |
557 | | - } // close if |
558 | | - |
559 | | - $j( selector ).html( |
560 | | - // insert the thumbnail into the anchor |
561 | | - $thumbnailLink.append( |
562 | | - $j( '<img/>' ) |
563 | | - .attr( { |
564 | | - 'width': image.width, |
565 | | - 'height': image.height, |
566 | | - 'src': image.src |
567 | | - } ) |
568 | | - ) // close append |
569 | | - ); // close html |
570 | | - } // close image !== null else condition |
| 621 | + ) |
| 622 | + ); |
| 623 | + placed = true; |
571 | 624 | }; |
572 | 625 | |
573 | | - _this.getThumbnail( callback, width, height ); |
| 626 | + // Listen for even which says some kind of thumbnail is available. |
| 627 | + // The argument is an either an ImageHtmlElement ( if we could get the thumbnail locally ) or the string 'api' indicating you |
| 628 | + // now need to get the scaled thumbnail via the API |
| 629 | + $.subscribeReady( |
| 630 | + 'thumbnails.' + _this.index, |
| 631 | + function ( x ) { |
| 632 | + if ( !placed ) { |
| 633 | + if ( x === 'api' ) { |
| 634 | + // get the thumbnail via API. This also works with an async pub/sub model; if this thumbnail was already |
| 635 | + // fetched for some reason, we'll get it immediately |
| 636 | + var key = 'apiThumbnail.' + _this.index + ',width=' + width + ',height=' + height; |
| 637 | + $.subscribeReady( key, placeImageCallback ); |
| 638 | + _this.getAndPublishApiThumbnail( key, width, height ); |
| 639 | + } else if ( x instanceof HTMLImageElement ) { |
| 640 | + placeImageCallback( x ); |
| 641 | + } else { |
| 642 | + // something else went wrong, place broken image |
| 643 | + mw.log( 'unexpected argument to thumbnails event: ' + x ); |
| 644 | + placeImageCallback( null ); |
| 645 | + } |
| 646 | + } |
| 647 | + } |
| 648 | + ); |
| 649 | + |
574 | 650 | }, |
575 | 651 | |
| 652 | + |
576 | 653 | /** |
577 | 654 | * Given a filename like "Foo.jpg", get the URL to that filename, assuming the browser is on the same wiki. |
578 | 655 | * Candidate for a utility function... |