mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
synced 2024-09-29 13:17:35 +00:00
Merge "Refactor progressbar & blur handling"
This commit is contained in:
commit
9e0df0dd4c
|
@ -232,11 +232,11 @@
|
|||
* Loads and sets the specified image. It also updates the controls.
|
||||
* @param {mw.mmv.LightboxInterface} ui image container
|
||||
* @param {mw.mmv.model.Thumbnail} thumbnail thumbnail information
|
||||
* @param {HTMLImageElement} image
|
||||
* @param {HTMLImageElement} imageElement
|
||||
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
||||
*/
|
||||
MMVP.setImage = function ( ui, thumbnail, image, imageWidths ) {
|
||||
ui.canvas.setImageAndMaxDimensions( thumbnail, image, imageWidths );
|
||||
MMVP.setImage = function ( ui, thumbnail, imageElement, imageWidths ) {
|
||||
ui.canvas.setImageAndMaxDimensions( thumbnail, imageElement, imageWidths );
|
||||
this.updateControls();
|
||||
};
|
||||
|
||||
|
@ -255,7 +255,6 @@
|
|||
|
||||
this.currentIndex = image.index;
|
||||
|
||||
this.currentImageFilename = image.filePageTitle.getPrefixedText();
|
||||
this.currentImageFileTitle = image.filePageTitle;
|
||||
|
||||
if ( !this.isOpen ) {
|
||||
|
@ -279,15 +278,17 @@
|
|||
|
||||
imageWidths = this.ui.canvas.getCurrentImageWidths();
|
||||
|
||||
this.resetBlurredThumbnailStates();
|
||||
|
||||
start = $.now();
|
||||
|
||||
imagePromise = this.fetchThumbnailForLightboxImage( image, imageWidths.real );
|
||||
|
||||
viewer.displayPlaceholderThumbnail( image, $initialImage, imageWidths );
|
||||
this.resetBlurredThumbnailStates();
|
||||
if ( imagePromise.state() === 'pending' ) {
|
||||
this.displayPlaceholderThumbnail( image, $initialImage, imageWidths );
|
||||
}
|
||||
|
||||
this.setupProgressBar( image, imagePromise );
|
||||
this.setupProgressBar( image, imagePromise, imageWidths.real );
|
||||
|
||||
imagePromise.done( function ( thumbnail, imageElement ) {
|
||||
if ( viewer.currentIndex !== image.index ) {
|
||||
|
@ -356,29 +357,52 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* Resets the cross-request states needed to handle the blurred thumbnail logic
|
||||
* @private
|
||||
* Image loading progress. Keyed by image (database) name + '|' + thumbnail width in pixels,
|
||||
* value is undefined, 'blurred' or 'real' (meaning respectively that no thumbnail is shown
|
||||
* yet / the thumbnail that existed on the page is shown, enlarged and blurred / the real,
|
||||
* correct-size thumbnail is shown).
|
||||
* @property {Object.<string, string>}
|
||||
*/
|
||||
MMVP.thumbnailStateCache = {};
|
||||
|
||||
/**
|
||||
* Resets the cross-request states needed to handle the blurred thumbnail logic.
|
||||
*/
|
||||
MMVP.resetBlurredThumbnailStates = function () {
|
||||
/**
|
||||
* Stores whether the real image was loaded and displayed already.
|
||||
* This is reset when paging, so it is not necessarily accurate.
|
||||
* @property {boolean}
|
||||
*/
|
||||
this.realThumbnailShown = false;
|
||||
|
||||
/**
|
||||
* Stores whether the a blurred placeholder is being displayed in place of the real image.
|
||||
* When a placeholder is displayed, but it is not blurred, this is false.
|
||||
* This is reset when paging, so it is not necessarily accurate.
|
||||
* @property {boolean}
|
||||
*/
|
||||
this.blurredThumbnailShown = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the real, full-resolution, thumbnail that was fetched with fetchThumbnail
|
||||
* @param {mw.mmv.model.Thumbnail} thumbnail
|
||||
* @param {HTMLImageElement} image
|
||||
* @param {HTMLImageElement} imageElement
|
||||
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
||||
* @param {number} loadTime Time it took to load the thumbnail
|
||||
*/
|
||||
MMVP.displayRealThumbnail = function ( thumbnail, image, imageWidths, loadTime ) {
|
||||
MMVP.displayRealThumbnail = function ( thumbnail, imageElement, imageWidths, loadTime ) {
|
||||
this.realThumbnailShown = true;
|
||||
|
||||
this.setImage( this.ui, thumbnail, image, imageWidths );
|
||||
this.setImage( this.ui, thumbnail, imageElement, imageWidths );
|
||||
|
||||
// We only animate unblur if the image wasn't loaded from the cache
|
||||
// We only animate unblurWithAnimation if the image wasn't loaded from the cache
|
||||
// A load in < 10ms is considered to be a browser cache hit
|
||||
// And of course we only unblur if there was a blur to begin with
|
||||
if ( this.blurredThumbnailShown && loadTime > 10 ) {
|
||||
this.ui.canvas.unblurWithAnimation();
|
||||
} else {
|
||||
this.ui.canvas.unblur();
|
||||
}
|
||||
|
||||
|
@ -390,13 +414,15 @@
|
|||
* @param {mw.mmv.LightboxImage} image
|
||||
* @param {jQuery} $initialImage The thumbnail from the page
|
||||
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
||||
* @param {boolean} [recursion=false] for internal use, never set this
|
||||
* @param {boolean} [recursion=false] for internal use, never set this when calling from outside
|
||||
*/
|
||||
MMVP.displayPlaceholderThumbnail = function ( image, $initialImage, imageWidths, recursion ) {
|
||||
var viewer = this,
|
||||
size = { width : image.originalWidth, height : image.originalHeight };
|
||||
|
||||
// If the actual image has already been displayed, there's no point showing the blurry one
|
||||
// If the actual image has already been displayed, there's no point showing the blurry one.
|
||||
// This can happen if the API request to get the original image size needed to show the
|
||||
// placeholder thumbnail takes longer then loading the actual thumbnail.
|
||||
if ( this.realThumbnailShown ) {
|
||||
return;
|
||||
}
|
||||
|
@ -425,58 +451,71 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Image loading progress. Keyed by image (database) name + '|' + thumbnail width in pixels,
|
||||
* value is a number between 0-100.
|
||||
* @property {Object.<string, number>}
|
||||
*/
|
||||
MMVP.progressCache = {};
|
||||
|
||||
/**
|
||||
* Displays a progress bar for the image loading, if necessary, and sets up handling of
|
||||
* all the related callbacks.
|
||||
* FIXME would be nice to pass a simple promise which only returns a single number
|
||||
* and does not fire when the image is not visible
|
||||
* @param {mw.mmv.LightboxImage} image
|
||||
* @param {jQuery.Promise.<mw.mmv.model.Thumbnail, HTMLImageElement>} imagePromise
|
||||
* @param {number} imageWidth needed for caching progress (FIXME)
|
||||
*/
|
||||
MMVP.setupProgressBar = function ( image, imagePromise ) {
|
||||
var viewer = this;
|
||||
|
||||
// Reset the progress bar, it could be at any state if we're calling loadImage
|
||||
// while another image is already loading
|
||||
// FIXME we should probably jump to the current progress instead
|
||||
viewer.ui.panel.progressBar.percent( 0 );
|
||||
MMVP.setupProgressBar = function ( image, imagePromise, imageWidth ) {
|
||||
var viewer = this,
|
||||
progressBar = viewer.ui.panel.progressBar,
|
||||
key = image.filePageTitle.getPrefixedDb() + '|' + imageWidth;
|
||||
|
||||
if ( imagePromise.state() !== 'pending' ) {
|
||||
// image has already loaded (or failed to load) - nothing to do
|
||||
// image has already loaded (or failed to load) - do not show the progress bar
|
||||
progressBar.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME this is all wrong, we might be navigating back to a half-loaded image
|
||||
if ( !this.progressCache[key] ) {
|
||||
// Animate progress bar to 5 to give a sense that something is happening, and make sure
|
||||
// the progress bar is noticeable, even if we're sitting at 0% stuck waiting for
|
||||
// server-side processing, such as thumbnail (re)generation
|
||||
progressBar.jumpTo( 0 );
|
||||
progressBar.animateTo( 5 );
|
||||
viewer.progressCache[key] = 5;
|
||||
} else {
|
||||
progressBar.jumpTo( this.progressCache[key] );
|
||||
}
|
||||
|
||||
// Animate progress bar to 5 to give a sense to something is happening, even if we're
|
||||
// stuck waiting for server-side processing, such as thumbnail (re)generation
|
||||
viewer.ui.panel.progressBar.percent( 5 );
|
||||
|
||||
imagePromise.progress( function ( thumbnailInfoResponse, imageResponse ) {
|
||||
// FIXME this should be explained in a comment
|
||||
var progress = imageResponse[1];
|
||||
|
||||
if ( viewer.currentIndex !== image.index ) {
|
||||
// FIXME would be nice to have a "filtered" promise which does not fire when the image is not visible
|
||||
imagePromise.progress( function ( progress ) {
|
||||
// We pretend progress is always at least 5%, so progress events below 5% should be ignored
|
||||
// 100 will be handled by the done handler, do not mix two animations
|
||||
if ( progress < 5 || progress === 100 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We started from 5, don't move backwards
|
||||
if ( progress > 5 ) {
|
||||
viewer.ui.panel.progressBar.percent( progress );
|
||||
viewer.progressCache[key] = progress;
|
||||
|
||||
// Touch the UI only if the user is looking at this image
|
||||
if ( viewer.currentIndex === image.index ) {
|
||||
progressBar.animateTo( progress );
|
||||
}
|
||||
} ).done( function () {
|
||||
if ( viewer.currentIndex !== image.index ) {
|
||||
return;
|
||||
}
|
||||
viewer.progressCache[key] = 100;
|
||||
|
||||
// Fallback in case the browser doesn't have fancy progress updates
|
||||
viewer.ui.panel.progressBar.percent( 100 );
|
||||
} ).fail( function () {
|
||||
if ( viewer.currentIndex !== image.index ) {
|
||||
return;
|
||||
if ( viewer.currentIndex === image.index ) {
|
||||
// Fallback in case the browser doesn't have fancy progress updates
|
||||
progressBar.animateTo( 100 );
|
||||
}
|
||||
} ).fail( function () {
|
||||
viewer.progressCache[key] = 100;
|
||||
|
||||
if ( viewer.currentIndex === image.index ) {
|
||||
// Hide progress bar on error
|
||||
progressBar.hide();
|
||||
}
|
||||
// Hide progress bar on error
|
||||
viewer.ui.panel.progressBar.percent( 0 );
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -671,7 +710,9 @@
|
|||
* @param {string} [sampleUrl] a thumbnail URL for the same file (but with different size) (might be missing)
|
||||
* @param {number} [originalWidth] the width of the original, full-sized file (might be missing)
|
||||
* @param {number} [originalHeight] the height of the original, full-sized file (might be missing)
|
||||
* @returns {jQuery.Promise.<mw.mmv.model.Thumbnail, HTMLImageElement>}
|
||||
* @returns {jQuery.Promise.<mw.mmv.model.Thumbnail, HTMLImageElement>} A promise resolving to
|
||||
* a thumbnail model and an <img> element. It might or might not have progress events which
|
||||
* return a single number.
|
||||
*/
|
||||
MMVP.fetchThumbnail = function ( fileTitle, width, sampleUrl, originalWidth, originalHeight ) {
|
||||
var viewer = this,
|
||||
|
@ -714,7 +755,10 @@
|
|||
} );
|
||||
}
|
||||
|
||||
return $.when( thumbnailPromise, imagePromise );
|
||||
return $.when( thumbnailPromise, imagePromise ).then( null, null, function ( thumbnailProgress, imageProgress ) {
|
||||
// Make progress events have a nice format.
|
||||
return imageProgress[1];
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,10 +37,12 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads an image and returns it.
|
||||
* Includes performance metrics.
|
||||
* Loads an image and returns it. Includes performance metrics via mw.mmv.Performance.
|
||||
* When the browser supports it, the image is loaded as an AJAX request.
|
||||
* @param {string} url
|
||||
* @return {jQuery.Promise.<HTMLImageElement>} a promise which resolves to the image object
|
||||
* @return {jQuery.Promise.<HTMLImageElement>} A promise which resolves to the image object.
|
||||
* When loaded via AJAX, it has progress events, which return an array with the content loaded
|
||||
* so far and with the progress as a floating-point number between 0 and 100.
|
||||
*/
|
||||
Image.prototype.get = function ( url ) {
|
||||
var provider = this,
|
||||
|
@ -59,11 +61,12 @@
|
|||
provider.performance.recordEntry( 'image', $.now() - start, url );
|
||||
} );
|
||||
}
|
||||
this.cache[cacheKey].fail( function ( error ) {
|
||||
mw.log( provider.constructor.name + ' provider failed to load: ', error );
|
||||
} );
|
||||
}
|
||||
|
||||
return this.cache[cacheKey].fail( function ( error ) {
|
||||
mw.log( provider.constructor.name + ' provider failed to load: ', error );
|
||||
} );
|
||||
return this.cache[cacheKey];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -113,11 +113,11 @@
|
|||
* Sets contained image and also the max dimensions. Called while resizing the viewer.
|
||||
* Assumes set function called before.
|
||||
* @param {mw.mmv.model.Thumbnail} thumbnail thumbnail information
|
||||
* @param {HTMLImageElement} imageEle
|
||||
* @param {HTMLImageElement} imageElement
|
||||
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
||||
*/
|
||||
C.setImageAndMaxDimensions = function( thumbnail, imageEle, imageWidths ) {
|
||||
var $image = $( imageEle );
|
||||
C.setImageAndMaxDimensions = function( thumbnail, imageElement, imageWidths ) {
|
||||
var $image = $( imageElement );
|
||||
|
||||
function makeMaxMatchParent ( $image ) {
|
||||
$image.css( {
|
||||
|
@ -128,10 +128,10 @@
|
|||
|
||||
// we downscale larger images but do not scale up smaller ones, that would look ugly
|
||||
if ( thumbnail.width > imageWidths.cssWidth ) {
|
||||
imageEle.width = imageWidths.cssWidth;
|
||||
imageElement.width = imageWidths.cssWidth;
|
||||
}
|
||||
|
||||
if ( this.$image.is( imageEle ) ) { // http://bugs.jquery.com/ticket/4087
|
||||
if ( this.$image.is( imageElement ) ) { // http://bugs.jquery.com/ticket/4087
|
||||
// We may be changing the width of the image when we resize, we should also
|
||||
// update the max dimensions otherwise the image is not scaled properly
|
||||
makeMaxMatchParent( this.$image );
|
||||
|
@ -233,7 +233,7 @@
|
|||
/**
|
||||
* Animates the image into focus
|
||||
*/
|
||||
C.unblur = function() {
|
||||
C.unblurWithAnimation = function() {
|
||||
var self = this,
|
||||
animationLength = 300;
|
||||
|
||||
|
@ -251,15 +251,19 @@
|
|||
'filter' : 'blur(' + step + 'px)' } );
|
||||
},
|
||||
complete: function () {
|
||||
// When the animation is complete, the blur value is 0
|
||||
// We apply empty CSS values to remove the inline styles applied by jQuery
|
||||
// so that they don't get in the way of styles defined in CSS
|
||||
self.$image.css( { '-webkit-filter' : '', 'opacity' : '' } )
|
||||
.removeClass( 'blurred' );
|
||||
// When the animation is complete, the blur value is 0, clean things up
|
||||
self.unblur();
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
C.unblur = function() {
|
||||
// We apply empty CSS values to remove the inline styles applied by jQuery
|
||||
// so that they don't get in the way of styles defined in CSS
|
||||
this.$image.css( { '-webkit-filter' : '', 'opacity' : '' } )
|
||||
.removeClass( 'blurred' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a message and error icon when loading the image fails.
|
||||
* @param {string} error error message
|
||||
|
|
|
@ -45,37 +45,46 @@
|
|||
};
|
||||
|
||||
PBP.empty = function () {
|
||||
this.hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the bar, resets it to 0 and stops any animation in progress.
|
||||
*/
|
||||
PBP.hide = function () {
|
||||
this.$progress.addClass( 'empty' );
|
||||
this.$percent.stop().css( { width : 0 } );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the progress display when a percentage of progress is received
|
||||
* @param {number} percent
|
||||
* @param {number} percent a number between 0 and 100
|
||||
*/
|
||||
PBP.percent = function ( percent ) {
|
||||
PBP.animateTo = function ( percent ) {
|
||||
var panel = this;
|
||||
|
||||
if ( percent === 0 ) {
|
||||
// When a 0% update comes in, we jump without animation to 0 and we hide the bar
|
||||
this.$progress.addClass( 'empty' );
|
||||
this.$percent.stop().css( { width : 0 } );
|
||||
} else if ( percent === 100 ) {
|
||||
this.$progress.removeClass( 'empty' );
|
||||
this.$percent.stop();
|
||||
|
||||
if ( percent === 100 ) {
|
||||
// When a 100% update comes in, we make sure that the bar is visible, we animate
|
||||
// fast to 100 and we hide the bar when the animation is done
|
||||
this.$progress.removeClass( 'empty' );
|
||||
this.$percent.stop().animate( { width : percent + '%' }, 50, 'swing',
|
||||
function () {
|
||||
// Reset the position for good measure
|
||||
panel.$percent.stop().css( { width : 0 } );
|
||||
panel.$progress.addClass( 'empty' );
|
||||
} );
|
||||
this.$percent.animate( { width : percent + '%' }, 50, 'swing', $.proxy( panel.hide, panel ) );
|
||||
} else {
|
||||
// When any other % update comes in, we make sure the bar is visible
|
||||
// and we animate to the right position
|
||||
this.$progress.removeClass( 'empty' );
|
||||
this.$percent.stop().animate( { width : percent + '%' } );
|
||||
this.$percent.animate( { width : percent + '%' } );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Goes to the given percent without animation
|
||||
* @param {number} percent a number between 0 and 100
|
||||
*/
|
||||
PBP.jumpTo = function ( percent ) {
|
||||
this.$progress.removeClass( 'empty' );
|
||||
this.$percent.stop().css( { width : percent + '%' } );
|
||||
};
|
||||
|
||||
mw.mmv.ui.ProgressBar = ProgressBar;
|
||||
}( mediaWiki, jQuery, OO ) );
|
||||
|
|
|
@ -102,8 +102,7 @@
|
|||
|
||||
QUnit.test( 'Progress', 4, function ( assert ) {
|
||||
var imageDeferred = $.Deferred(),
|
||||
viewer = new mw.mmv.MultimediaViewer(),
|
||||
i = 0;
|
||||
viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.thumbs = [];
|
||||
viewer.displayPlaceholderThumbnail = $.noop;
|
||||
|
@ -114,28 +113,15 @@
|
|||
viewer.ui = {
|
||||
setupForLoad : $.noop,
|
||||
canvas : { set : $.noop,
|
||||
unblurWithAnimation: $.noop,
|
||||
unblur: $.noop,
|
||||
getCurrentImageWidths : function () { return { real : 0 }; } },
|
||||
panel : {
|
||||
setImageInfo : $.noop,
|
||||
animateMetadataOnce : $.noop,
|
||||
progressBar: {
|
||||
percent : function ( percent ) {
|
||||
if ( i === 0 ) {
|
||||
assert.strictEqual( percent, 0,
|
||||
'Percentage correctly reset by loadImage' );
|
||||
} else if ( i === 1 ) {
|
||||
assert.strictEqual( percent, 5,
|
||||
'Percentage correctly animated to 5 by loadImage' );
|
||||
} else if ( i === 2 ) {
|
||||
assert.strictEqual( percent, 45,
|
||||
'Percentage correctly funneled to panel UI' );
|
||||
} else {
|
||||
assert.strictEqual( percent, 100,
|
||||
'Percentage correctly funneled to panel UI' );
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
animateTo: this.sandbox.stub(),
|
||||
jumpTo: this.sandbox.stub()
|
||||
}
|
||||
},
|
||||
open : $.noop };
|
||||
|
@ -145,9 +131,116 @@
|
|||
viewer.thumbnailInfoProvider.get = function() { return $.Deferred().resolve( {} ); };
|
||||
|
||||
viewer.loadImage( { filePageTitle : new mw.Title( 'File:Stuff.jpg' ) }, new Image() );
|
||||
assert.ok( viewer.ui.panel.progressBar.jumpTo.lastCall.calledWith( 0 ),
|
||||
'Percentage correctly reset by loadImage' );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 5 ),
|
||||
'Percentage correctly animated to 5 by loadImage' );
|
||||
|
||||
imageDeferred.notify( 'response', 45 );
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 45 ),
|
||||
'Percentage correctly funneled to panel UI' );
|
||||
|
||||
imageDeferred.resolve();
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 100 ),
|
||||
'Percentage correctly funneled to panel UI' );
|
||||
|
||||
viewer.close();
|
||||
} );
|
||||
|
||||
QUnit.test( 'Progress when switching images', 11, function ( assert ) {
|
||||
var firstImageDeferred = $.Deferred(),
|
||||
secondImageDeferred = $.Deferred(),
|
||||
firstImage = { index: 1, filePageTitle : new mw.Title( 'File:First.jpg' ) },
|
||||
secondImage = { index: 2, filePageTitle : new mw.Title( 'File:Second.jpg' ) },
|
||||
viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.thumbs = [];
|
||||
viewer.displayPlaceholderThumbnail = $.noop;
|
||||
viewer.setImage = $.noop;
|
||||
viewer.scroll = $.noop;
|
||||
viewer.preloadFullscreenThumbnail = $.noop;
|
||||
viewer.preloadImagesMetadata = $.noop;
|
||||
viewer.preloadThumbnails = $.noop;
|
||||
viewer.fetchSizeIndependentLightboxInfo = function () { return $.Deferred().resolve(); };
|
||||
viewer.ui = {
|
||||
setupForLoad : $.noop,
|
||||
canvas : { set : $.noop,
|
||||
unblurWithAnimation: $.noop,
|
||||
unblur: $.noop,
|
||||
getCurrentImageWidths : function () { return { real : 0 }; } },
|
||||
panel : {
|
||||
setImageInfo : $.noop,
|
||||
animateMetadataOnce : $.noop,
|
||||
progressBar: {
|
||||
hide: this.sandbox.stub(),
|
||||
animateTo: this.sandbox.stub(),
|
||||
jumpTo: this.sandbox.stub()
|
||||
}
|
||||
},
|
||||
open : $.noop,
|
||||
empty: $.noop };
|
||||
|
||||
viewer.imageInfoProvider.get = function() { return $.Deferred().resolve(); };
|
||||
viewer.thumbnailInfoProvider.get = function() { return $.Deferred().resolve( {} ); };
|
||||
|
||||
// load some image
|
||||
viewer.imageProvider.get = this.sandbox.stub().returns( firstImageDeferred );
|
||||
viewer.loadImage( firstImage, new Image() );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.jumpTo.lastCall.calledWith( 0 ),
|
||||
'Percentage correctly reset for new first image' );
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 5 ),
|
||||
'Percentage correctly animated to 5 for first new image' );
|
||||
|
||||
firstImageDeferred.notify( 'response', 20 );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 20 ),
|
||||
'Percentage correctly animated when active image is loading' );
|
||||
|
||||
// change to another image
|
||||
viewer.imageProvider.get = this.sandbox.stub().returns( secondImageDeferred );
|
||||
viewer.loadImage( secondImage, new Image() );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.jumpTo.lastCall.calledWith( 0 ),
|
||||
'Percentage correctly reset for second new image' );
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 5 ),
|
||||
'Percentage correctly animated to 5 for second new image' );
|
||||
|
||||
secondImageDeferred.notify( 'response', 30 );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.animateTo.lastCall.calledWith( 30 ),
|
||||
'Percentage correctly animated when active image is loading' );
|
||||
|
||||
// this is the most convenient way of checking for new calls - just reset() and check called
|
||||
viewer.ui.panel.progressBar.animateTo.reset();
|
||||
viewer.ui.panel.progressBar.jumpTo.reset();
|
||||
|
||||
firstImageDeferred.notify( 'response', 40 );
|
||||
|
||||
assert.ok( !viewer.ui.panel.progressBar.animateTo.called,
|
||||
'Percentage not animated when inactive image is loading' );
|
||||
assert.ok( !viewer.ui.panel.progressBar.jumpTo.called,
|
||||
'Percentage not changed when inactive image is loading' );
|
||||
|
||||
secondImageDeferred.notify( 'response', 50 );
|
||||
|
||||
// change back to first image
|
||||
viewer.loadImage( firstImage, new Image() );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.jumpTo.lastCall.calledWith( 40 ),
|
||||
'Percentage jumps to right value when changing images' );
|
||||
|
||||
secondImageDeferred.resolve();
|
||||
assert.ok( !viewer.ui.panel.progressBar.hide.called,
|
||||
'Progress bar not hidden when something finishes in the background' );
|
||||
|
||||
// change to second image which has finished loading
|
||||
viewer.imageProvider.get = this.sandbox.stub().returns( secondImageDeferred );
|
||||
viewer.loadImage( secondImage, new Image() );
|
||||
|
||||
assert.ok( viewer.ui.panel.progressBar.hide.called,
|
||||
'Progress bar not hidden when switching to finished image' );
|
||||
|
||||
viewer.close();
|
||||
} );
|
||||
|
@ -172,6 +265,8 @@
|
|||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.ui = { canvas : {
|
||||
unblurWithAnimation: $.noop,
|
||||
unblur: $.noop,
|
||||
maybeDisplayPlaceholder : function() { return true; }
|
||||
} };
|
||||
viewer.imageInfoProvider.get = this.sandbox.stub();
|
||||
|
@ -193,6 +288,8 @@
|
|||
viewer.currentIndex = 1;
|
||||
viewer.setImage = $.noop;
|
||||
viewer.ui = { canvas : {
|
||||
unblurWithAnimation: $.noop,
|
||||
unblur: $.noop,
|
||||
maybeDisplayPlaceholder : function() { return true; }
|
||||
} };
|
||||
viewer.imageInfoProvider.get = this.sandbox.stub().returns( $.Deferred().resolve( {width: 100, height: 100 } ) );
|
||||
|
@ -213,8 +310,11 @@
|
|||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.ui = {
|
||||
showImage : $.noop
|
||||
};
|
||||
showImage : $.noop,
|
||||
canvas : {
|
||||
unblurWithAnimation: $.noop,
|
||||
unblur: $.noop
|
||||
} };
|
||||
|
||||
viewer.displayRealThumbnail();
|
||||
|
||||
|
@ -227,94 +327,78 @@
|
|||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'displayRealThumbnail', 1, function ( assert ) {
|
||||
QUnit.test( 'displayRealThumbnail', 2, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.ui = { canvas : {
|
||||
unblur : function () { assert.ok( false, 'Image should not be unblurred yet' ); }
|
||||
unblurWithAnimation : this.sandbox.stub(),
|
||||
unblur: $.noop
|
||||
} };
|
||||
viewer.blurredThumbnailShown = true;
|
||||
|
||||
// Should not result in an unblur (image cache from cache)
|
||||
// Should not result in an unblurWithAnimation animation (image cache from cache)
|
||||
viewer.displayRealThumbnail( undefined, undefined, undefined, 5 );
|
||||
assert.ok( !viewer.ui.canvas.unblurWithAnimation.called, 'There should not be an unblurWithAnimation animation' );
|
||||
|
||||
viewer.ui.canvas.unblur = function () {
|
||||
assert.ok( true, 'Image needs to be unblurred' );
|
||||
};
|
||||
|
||||
// Should result in an unblur (image didn't come from cache)
|
||||
// Should result in an unblurWithAnimation (image didn't come from cache)
|
||||
viewer.displayRealThumbnail( undefined, undefined, undefined, 1000 );
|
||||
assert.ok( viewer.ui.canvas.unblurWithAnimation.called, 'There should be an unblurWithAnimation animation' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'New image loaded while another one is loading', 1, function ( assert ) {
|
||||
var imageDeferred,
|
||||
ligthboxInfoDeferred,
|
||||
viewer = new mw.mmv.MultimediaViewer(),
|
||||
QUnit.test( 'New image loaded while another one is loading', 5, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer(),
|
||||
firstImageDeferred = $.Deferred(),
|
||||
secondImageDeferred = $.Deferred(),
|
||||
firstLigthboxInfoDeferred = $.Deferred(),
|
||||
secondLigthboxInfoDeferred = $.Deferred();
|
||||
|
||||
viewer.preloadFullscreenThumbnail = $.noop;
|
||||
viewer.fetchSizeIndependentLightboxInfo = function () { return ligthboxInfoDeferred.promise(); };
|
||||
viewer.fetchSizeIndependentLightboxInfo = this.sandbox.stub();
|
||||
viewer.ui = {
|
||||
setupForLoad : $.noop,
|
||||
canvas : { set : $.noop,
|
||||
getCurrentImageWidths : function () { return { real : 0 }; } },
|
||||
panel : { setImageInfo : function () {
|
||||
assert.ok( false, 'Metadata of the first image should not be shown' );
|
||||
},
|
||||
panel : {
|
||||
setImageInfo : this.sandbox.stub(),
|
||||
progressBar: {
|
||||
percent : function ( response, percent ) {
|
||||
if ( percent === 45 ) {
|
||||
assert.ok( false, 'Progress of the first image should not be shown' );
|
||||
}
|
||||
}
|
||||
animateTo : this.sandbox.stub(),
|
||||
jumpTo : this.sandbox.stub()
|
||||
},
|
||||
empty: $.noop,
|
||||
animateMetadataOnce: $.noop
|
||||
},
|
||||
open : $.noop,
|
||||
empty: $.noop };
|
||||
viewer.displayRealThumbnail = this.sandbox.stub();
|
||||
viewer.eachPrealoadableLightboxIndex = $.noop;
|
||||
viewer.animateMetadataDivOnce = function () {
|
||||
assert.ok( false, 'Metadata of the first image should not be animated' );
|
||||
return $.Deferred().reject();
|
||||
};
|
||||
|
||||
viewer.imageProvider.get = function() { return imageDeferred.promise(); };
|
||||
viewer.animateMetadataDivOnce = this.sandbox.stub().returns( $.Deferred().reject() );
|
||||
viewer.imageProvider.get = this.sandbox.stub();
|
||||
viewer.imageInfoProvider.get = function() { return $.Deferred().reject(); };
|
||||
viewer.thumbnailInfoProvider.get = function() { return $.Deferred().resolve( {} ); };
|
||||
|
||||
imageDeferred = firstImageDeferred;
|
||||
ligthboxInfoDeferred = firstLigthboxInfoDeferred;
|
||||
viewer.imageProvider.get.returns( firstImageDeferred.promise() );
|
||||
viewer.fetchSizeIndependentLightboxInfo.returns( firstLigthboxInfoDeferred.promise() );
|
||||
viewer.loadImage( { filePageTitle : new mw.Title( 'File:Foo.jpg' ), index : 0 }, new Image() );
|
||||
assert.ok( !viewer.animateMetadataDivOnce.called, 'Metadata of the first image should not be animated' );
|
||||
assert.ok( !viewer.ui.panel.setImageInfo.called, 'Metadata of the first image should not be shown' );
|
||||
|
||||
imageDeferred = secondImageDeferred;
|
||||
ligthboxInfoDeferred = secondLigthboxInfoDeferred;
|
||||
viewer.imageProvider.get.returns( secondImageDeferred.promise() );
|
||||
viewer.fetchSizeIndependentLightboxInfo.returns( secondLigthboxInfoDeferred.promise() );
|
||||
viewer.loadImage( { filePageTitle : new mw.Title( 'File:Bar.jpg' ), index : 1 }, new Image() );
|
||||
|
||||
viewer.displayRealThumbnail = function () {
|
||||
assert.ok( false, 'The first image being done loading should have no effect');
|
||||
};
|
||||
|
||||
viewer.ui.panel.progressBar.animateTo.reset();
|
||||
firstImageDeferred.notify( undefined, 45 );
|
||||
assert.ok( !viewer.ui.panel.progressBar.animateTo.reset.called, 'Progress of the first image should not be shown' );
|
||||
|
||||
firstImageDeferred.resolve();
|
||||
firstLigthboxInfoDeferred.resolve();
|
||||
assert.ok( !viewer.displayRealThumbnail.called, 'The first image being done loading should have no effect');
|
||||
|
||||
viewer.ui.panel.setImageInfo = $.noop;
|
||||
viewer.animateMetadataDivOnce = function() { return $.Deferred().reject(); };
|
||||
|
||||
viewer.displayRealThumbnail = function () {
|
||||
assert.ok( true, 'The second image being done loading should result in the image being shown');
|
||||
QUnit.start();
|
||||
viewer.close();
|
||||
};
|
||||
|
||||
QUnit.stop();
|
||||
viewer.displayRealThumbnail = this.sandbox.spy( function () { viewer.close(); } );
|
||||
secondImageDeferred.resolve();
|
||||
secondLigthboxInfoDeferred.resolve();
|
||||
assert.ok( viewer.displayRealThumbnail.called, 'The second image being done loading should result in the image being shown');
|
||||
} );
|
||||
|
||||
QUnit.test( 'Events are not trapped after the viewer is closed', 0, function( assert ) {
|
||||
|
|
|
@ -287,7 +287,7 @@
|
|||
|
||||
canvas.$image = $( '<img>' );
|
||||
|
||||
canvas.unblur();
|
||||
canvas.unblurWithAnimation();
|
||||
|
||||
assert.ok( ! canvas.$image.css( '-webkit-filter' ) || !canvas.$image.css( '-webkit-filter' ).length,
|
||||
'Image has no filter left' );
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
assert.ok( progressBar.$progress.hasClass( 'empty' ), 'ProgressBar starts empty' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Progress bar', 11, function ( assert ) {
|
||||
QUnit.test( 'animateTo()', 8, function ( assert ) {
|
||||
var $qf = $( '#qunit-fixture' ),
|
||||
progress = new mw.mmv.ui.ProgressBar( $qf );
|
||||
|
||||
|
@ -35,17 +35,10 @@
|
|||
$( this ).css( target );
|
||||
assert.strictEqual( target.width, '50%', 'Animation should go to 50%' );
|
||||
} );
|
||||
progress.percent( 50 );
|
||||
progress.animateTo( 50 );
|
||||
assert.ok( !progress.$progress.hasClass( 'empty' ), 'Progress bar is visible' );
|
||||
assert.strictEqual( progress.$percent.width(), $qf.width() / 2, 'Progress bar\'s indicator is at half' );
|
||||
|
||||
$.fn.animate.restore();
|
||||
this.sandbox.stub( $.fn, 'animate' );
|
||||
progress.percent( 0 );
|
||||
assert.ok( !$.fn.animate.called, 'Going to 0% should not animate' );
|
||||
assert.ok( progress.$progress.hasClass( 'empty' ), 'Progress bar is hidden' );
|
||||
assert.strictEqual( progress.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
|
||||
$.fn.animate.restore();
|
||||
this.sandbox.stub( $.fn, 'animate', function ( target, duration, transition, callback ) {
|
||||
$( this ).css( target );
|
||||
|
@ -56,7 +49,25 @@
|
|||
callback();
|
||||
}
|
||||
} );
|
||||
progress.percent( 100 );
|
||||
progress.animateTo( 100 );
|
||||
assert.ok( progress.$progress.hasClass( 'empty' ), 'Progress bar is hidden' );
|
||||
assert.strictEqual( progress.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'jumpTo()/hide()', 6, function ( assert ) {
|
||||
var $qf = $( '#qunit-fixture' ),
|
||||
progress = new mw.mmv.ui.ProgressBar( $qf );
|
||||
|
||||
assert.ok( progress.$progress.hasClass( 'empty' ), 'Progress bar is hidden' );
|
||||
assert.strictEqual( progress.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
|
||||
progress.jumpTo( 50 );
|
||||
|
||||
assert.ok( !progress.$progress.hasClass( 'empty' ), 'Progress bar is visible' );
|
||||
assert.strictEqual( progress.$percent.width(), $qf.width() / 2, 'Progress bar\'s indicator is at half' );
|
||||
|
||||
progress.hide();
|
||||
|
||||
assert.ok( progress.$progress.hasClass( 'empty' ), 'Progress bar is hidden' );
|
||||
assert.strictEqual( progress.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
} );
|
||||
|
|
Loading…
Reference in a new issue