r87314 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r87313‎ | r87314 | r87315 >
Date:02:43, 3 May 2011
Author:neilk
Status:ok
Tags:
Comment:
retry thumbnails for several seconds before declaring failure. All similar thumbnails in layout "subscribe" interest, and then the thumbnail is "published" to them when ready. Should address bug 28782
Modified paths:
  • /trunk/extensions/UploadWizard/UploadWizardHooks.php (modified) (history)
  • /trunk/extensions/UploadWizard/resources/jquery/jquery.pubsub.js (added) (history)
  • /trunk/extensions/UploadWizard/resources/mw.UploadWizard.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UploadWizard/UploadWizardHooks.php
@@ -37,6 +37,7 @@
3838 'resources/jquery/jquery.autoEllipsis.js',
3939 'resources/jquery/jquery.suggestions.js',
4040 'resources/jquery/jquery.removeCtrl.js',
 41+ 'resources/jquery/jquery.pubsub.js',
4142
4243 // mediawiki-specific interface helper (relies on mediawiki globals)
4344 'resources/jquery/jquery.mwCoolCats.js',
Index: trunk/extensions/UploadWizard/resources/jquery/jquery.pubsub.js
@@ -0,0 +1,61 @@
 2+/**
 3+ * Minimal pubsub framework
 4+ *
 5+ * Loosely based on https://github.com/phiggins42/bloody-jquery-plugins/pubsub.js, which is itself BSD-licensed.
 6+ */
 7+
 8+( function( $ ) {
 9+ /**
 10+ * Store of events -> array of listener callbacks
 11+ */
 12+ var subs = {};
 13+
 14+ /**
 15+ * Publish an event
 16+ * Additional variadic arguments after the event name are passed as arguments to the subscriber functions
 17+ * @param {String} name of event
 18+ * @return {Number} number of subscribers
 19+ */
 20+ $.publish = function( name /* , args... */ ) {
 21+ var args = [].slice.call( arguments, 1 );
 22+ $.each( subs[name], function( i, sub ) {
 23+ sub.apply( null, args );
 24+ } );
 25+ return subs[name].length;
 26+ };
 27+
 28+ /**
 29+ * Subscribe to an event.
 30+ * @param {String} name of event to listen for
 31+ * @param {Function} callback to run when event occurs
 32+ * @return {Array} returns handle which can be used as argument to unsubscribe()
 33+ */
 34+ $.subscribe = function( name, fn ) {
 35+ if (!subs[name]) {
 36+ subs[name] = [];
 37+ }
 38+ subs[name].push(fn);
 39+ return [ name, fn ];
 40+ };
 41+
 42+ /**
 43+ * Given the handle of a particular subscription, remove it
 44+ * @param {Array} object returned by subscribe ( array of event name and callback )
 45+ * @return {Boolean} success
 46+ */
 47+ $.unsubscribe = function( nameFn ) {
 48+ var name = nameFn[0];
 49+ var fn = nameFn[1];
 50+ var success = false;
 51+ if ( subs[name].length ) {
 52+ $.each( subs[name], function( i, fni ) {
 53+ if ( fni === fn ) {
 54+ subs[name].splice( i, 1 );
 55+ success = true;
 56+ return false; /* early exit loop */
 57+ }
 58+ } );
 59+ }
 60+ return success;
 61+ };
 62+} )( jQuery );
Property changes on: trunk/extensions/UploadWizard/resources/jquery/jquery.pubsub.js
___________________________________________________________________
Added: svn:eol-style
163 + native
Index: trunk/extensions/UploadWizard/resources/mw.UploadWizard.js
@@ -11,6 +11,7 @@
1212 this.api = api;
1313 this.state = 'new';
1414 this.thumbnails = {};
 15+ this.thumbnailPublishers = {};
1516 this.imageinfo = {};
1617 this.title = undefined;
1718 this.mimetype = undefined;
@@ -29,10 +30,15 @@
3031 // this.handler = new ( mw.UploadWizard.config[ 'uploadHandlerClass' ] )( this );
3132 // this.handler = new mw.MockUploadHandler( this );
3233 this.handler = new mw.ApiUploadHandler( this, api );
 34+
 35+ this.index = mw.UploadWizardUpload.prototype.count++;
3336 };
3437
3538 mw.UploadWizardUpload.prototype = {
3639
 40+ // increments with each upload
 41+ count: 0,
 42+
3743 acceptDeed: function( deed ) {
3844 var _this = this;
3945 _this.deed.applyDeed( _this );
@@ -390,34 +396,87 @@
391397 if ( mw.isEmpty( height ) ) {
392398 height = -1;
393399 }
394 - var key = "width" + width + ',height' + height;
 400+ // this key is overspecified for this thumbnail ( we don't need to reiterate the index ) but
 401+ // we can use this as key as an event now, that might fire much later
 402+ var key = 'thumbnail.' + _this.index + '.width' + width + ',height' + height;
395403 if ( mw.isDefined( _this.thumbnails[key] ) ) {
396404 callback( _this.thumbnails[key] );
397 - } else {
398 - var apiCallback = function( thumbnails ) {
 405+ return;
 406+ }
 407+
 408+ // subscribe to the event that this thumbnail is ready -- will give null or an Image to the callback
 409+ $j.subscribe( key, callback );
 410+
 411+ // if someone else already started a thumbnail fetch & publish, then don't bother, just wait for the event.
 412+ if ( ! mw.isDefined( _this.thumbnailPublishers[ key ] ) ) {
 413+ // The thumbnail publisher accepts the result of a stashImageInfo, and then tries to get the thumbnail, and eventually
 414+ // will trigger the event we just subscribed to.
 415+ _this.thumbnailPublishers[ key ] = _this.getThumbnailPublisher( key );
 416+ _this.getStashImageInfo( _this.thumbnailPublishers[ key ], [ 'url' ], width, height );
 417+ }
 418+ },
 419+
 420+ /**
 421+ * Returns a callback that can be used with a stashImageInfo call to fetch images, and then fire off an event to
 422+ * let everyone else know the image is loaded.
 423+ *
 424+ * Will retry the thumbnail URL several times, as thumbnails are known to be slow in production.
 425+ * @param {String} name of event to publish when thumbnails received (or final failure)
 426+ */
 427+ getThumbnailPublisher: function( key ) {
 428+ var _this = this;
 429+ return function( thumbnails ) {
399430 if ( thumbnails === null ) {
400 - callback( null );
 431+ // the api call failed somehow, no thumbnail data.
 432+ $j.publish( key, null );
401433 } else {
402 - for ( var i = 0; i < thumbnails.length; i++ ) {
403 - var thumb = thumbnails[i];
 434+ // ok, the api callback has returned us information on where the thumbnail(s) ARE, but that doesn't mean
 435+ // they are actually there yet. Keep trying to set the source ( which should trigger "error" or "load" event )
 436+ // on the image. If it loads publish the event with the image. If it errors out too many times, give up and publish
 437+ // the event with a null.
 438+ $j.each( thumbnails, function( i, thumb ) {
404439 if ( thumb.thumberror || ( ! ( thumb.thumburl && thumb.thumbwidth && thumb.thumbheight ) ) ) {
405440 mw.log( "mw.UploadWizardUpload::getThumbnail> thumbnail error or missing information" );
406 - callback( null );
 441+ $j.publish( key, null );
407442 return;
408443 }
 444+
 445+ // try to load this image with exponential backoff
 446+ // if the delay goes past 8 seconds, it gives up and publishes the event with null
 447+ var timeoutMs = 100;
 448+
409449 var image = document.createElement( 'img' );
410 - $j( image ).load( function() {
411 - callback( image );
412 - } );
413450 image.width = thumb.thumbwidth;
414451 image.height = thumb.thumbheight;
415 - image.src = thumb.thumburl;
 452+ $j( image )
 453+ .load( function() {
 454+ // cache this thumbnail
416455 _this.thumbnails[key] = image;
 456+ // publish the image to anyone who wanted it
 457+ $j.publish( key, image );
 458+ } )
 459+ .error( function() {
 460+ // retry with exponential backoff
 461+ if ( timeoutMs < 8000 ) {
 462+ setTimeout( function() {
 463+ timeoutMs = timeoutMs * 2 + Math.round( Math.random() * ( timeoutMs / 10 ) );
 464+ setSrc();
 465+ }, timeoutMs )
 466+ } else {
 467+ $j.publish( key, null );
417468 }
418 - }
 469+ } );
 470+
 471+ // executing this should cause a .load() or .error() event on the image
 472+ function setSrc() {
 473+ image.src = thumb.thumburl;
419474 };
420 - _this.getStashImageInfo( apiCallback, [ 'url' ], width, height );
 475+
 476+ // and, go!
 477+ setSrc();
 478+ } );
421479 }
 480+ };
422481 },
423482
424483 /**
@@ -877,10 +936,6 @@
878937
879938 _this.uploads.push( upload );
880939
881 - /* useful for making ids unique and so on */
882 - _this.uploadsSeen++;
883 - upload.index = _this.uploadsSeen;
884 -
885940 _this.updateFileCounts();
886941
887942 upload.deedPreview = new mw.UploadWizardDeedPreview( upload );
@@ -889,9 +944,6 @@
890945 upload.details = new mw.UploadWizardDetails( upload, $j( '#mwe-upwiz-macro-files' ) );
891946 },
892947
893 - /* increments with every upload */
894 - uploadsSeen: 0,
895 -
896948 /**
897949 * Remove an upload from our array of uploads, and the HTML UI
898950 * We can remove the HTML UI directly, as jquery will just get the parent.

Status & tagging log