mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
synced 2024-09-25 03:09:40 +00:00
Merge "Blurred thumbnail preview + progress bar"
This commit is contained in:
commit
62ebfd450f
9
resources/mmv/img/blur.svg
Normal file
9
resources/mmv/img/blur.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="gaussian-blur" x="0" y="0">
|
||||
<feGaussianBlur color-interpolation-filters="sRGB" stdDeviation="3" />
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 353 B |
|
@ -188,7 +188,7 @@
|
|||
*/
|
||||
MMVP.setImage = function ( ui, thumbnail, image, imageWidths ) {
|
||||
// we downscale larger images but do not scale up smaller ones, that would look ugly
|
||||
if ( thumbnail.width > imageWidths.screen ) {
|
||||
if ( thumbnail.width > imageWidths.css ) {
|
||||
image.width = imageWidths.css;
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,9 @@
|
|||
var imageWidths,
|
||||
viewer = this,
|
||||
imagePromise,
|
||||
metadataPromise;
|
||||
metadataPromise,
|
||||
start,
|
||||
$initialImage = $( initialImage );
|
||||
|
||||
// FIXME dependency injection happens in completely random order and location, needs cleanup
|
||||
this.ui = this.lightbox.iface;
|
||||
|
@ -223,7 +225,13 @@
|
|||
this.lightbox.iface.empty();
|
||||
}
|
||||
this.lightbox.iface.setupForLoad();
|
||||
this.lightbox.iface.showImage( image, initialImage );
|
||||
|
||||
// At this point we can't show the thumbnail because we don't
|
||||
// know what size it should be. We still assign it to allow for
|
||||
// size calculations in getCurrentImageWidths, which needs to know
|
||||
// the aspect ratio
|
||||
$initialImage.hide();
|
||||
this.lightbox.iface.showImage( image, $initialImage );
|
||||
|
||||
this.preloadImagesMetadata();
|
||||
this.preloadThumbnails();
|
||||
|
@ -232,11 +240,23 @@
|
|||
$( document.body ).addClass( 'mw-mlb-lightbox-open' );
|
||||
|
||||
imageWidths = this.ui.getCurrentImageWidths();
|
||||
|
||||
this.resetBlurredThumbnailStates();
|
||||
|
||||
start = $.now();
|
||||
|
||||
imagePromise = this.fetchThumbnail(
|
||||
image.filePageTitle, imageWidths.real
|
||||
).done( function( thumbnail, image ) {
|
||||
viewer.setImage( viewer.lightbox.iface, thumbnail, image, imageWidths );
|
||||
viewer.lightbox.iface.$imageDiv.removeClass( 'empty' );
|
||||
).progress( function ( thumbnailInfoResponse, imageResponse ) {
|
||||
if ( viewer.ui && viewer.ui.panel ) {
|
||||
viewer.ui.panel.percent( imageResponse[ 1 ] );
|
||||
}
|
||||
} ).done( function ( thumbnail, image ) {
|
||||
viewer.displayRealThumbnail( thumbnail, image, imageWidths, $.now() - start );
|
||||
} );
|
||||
|
||||
this.imageInfoProvider.get( image.filePageTitle ).done( function ( imageInfo ) {
|
||||
viewer.displayPlaceholderThumbnail( imageInfo, image, $initialImage, imageWidths );
|
||||
} );
|
||||
|
||||
metadataPromise = this.fetchSizeIndependentLightboxInfo(
|
||||
|
@ -278,6 +298,81 @@
|
|||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the cross-request states needed to handle the blurred thumbnail logic
|
||||
*/
|
||||
MMVP.resetBlurredThumbnailStates = function () {
|
||||
this.realThumbnailShown = false;
|
||||
this.blurredThumbnailShown = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the real, full-resolution, thumbnail that was fetched with fetchThumbnail
|
||||
* @param {mw.mmv.model.Thumbnail} thumbnail
|
||||
* @param {HTMLImageElement} image
|
||||
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
||||
* @param {number} loadTime Time it took to load the thumbnail
|
||||
*/
|
||||
MMVP.displayRealThumbnail = function ( thumbnail, image, imageWidths, loadTime ) {
|
||||
this.realThumbnailShown = true;
|
||||
|
||||
this.setImage( this.lightbox.iface, thumbnail, image, imageWidths );
|
||||
|
||||
// We only animate unblur 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.lightbox.iface.unblur();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the blurred thumbnail from the page
|
||||
* @param {mw.mmv.model.Image} imageInfo
|
||||
* @param {HTMLImageElement} image
|
||||
* @param {jQuery} $initialImage The thumbnail from the page
|
||||
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
||||
*/
|
||||
MMVP.displayPlaceholderThumbnail = function ( imageInfo, image, $initialImage, imageWidths ) {
|
||||
var ratio,
|
||||
targetWidth,
|
||||
targetHeight,
|
||||
blowupFactor;
|
||||
|
||||
// If the actual image has already been displayed, there's no point showing the blurry one
|
||||
if ( this.realThumbnailShown ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the image is smaller than the screen we need to adjust the placeholder's size
|
||||
if ( imageInfo.width < imageWidths.css ) {
|
||||
targetWidth = imageInfo.width;
|
||||
targetHeight = imageInfo.height;
|
||||
} else {
|
||||
ratio = $initialImage.height() / $initialImage.width();
|
||||
targetWidth = imageWidths.css;
|
||||
targetHeight = Math.round( imageWidths.css * ratio );
|
||||
}
|
||||
|
||||
blowupFactor = targetWidth / $initialImage.width();
|
||||
|
||||
// If the placeholder is too blown up, it's not worth showing it
|
||||
if ( blowupFactor > 11 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$initialImage.width( targetWidth );
|
||||
$initialImage.height( targetHeight );
|
||||
|
||||
// Only blur the placeholder if it's blown up significantly
|
||||
if ( blowupFactor > 2 ) {
|
||||
$initialImage.addClass( 'blurred' );
|
||||
this.blurredThumbnailShown = true;
|
||||
}
|
||||
|
||||
this.lightbox.iface.showImage( image, $initialImage.show() );
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload this many prev/next images to speed up navigation.
|
||||
* (E.g. preloadDistance = 3 means that the previous 3 and the next 3 images will be loaded.)
|
||||
|
@ -502,6 +597,7 @@
|
|||
imagePromise;
|
||||
|
||||
thumbnailPromise = this.thumbnailInfoProvider.get( fileTitle, width );
|
||||
|
||||
imagePromise = thumbnailPromise.then( function( thumbnail ) {
|
||||
return viewer.imageProvider.get( thumbnail.url );
|
||||
} );
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
@drag-height: 18px;
|
||||
@bottom-height: (@title-height + @drag-height);
|
||||
@metadata-background: rgb(251, 251, 251);
|
||||
@progress-height: 3px;
|
||||
|
||||
.mlb-image-wrapper,
|
||||
.mw-mlb-controls {
|
||||
|
@ -307,6 +308,17 @@ body.mobile .mw-mlb-controls,
|
|||
display: none;
|
||||
}
|
||||
|
||||
.mlb-image img.blurred {
|
||||
/**
|
||||
* SVG is for Firefox
|
||||
* Cannot be embedded because of the hash needed to access the filter inside the SVG
|
||||
*/
|
||||
filter: url(img/blur.svg#gaussian-blur);
|
||||
filter: blur(3px);
|
||||
-webkit-filter: blur(3px);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
body.mw-mlb-lightbox-open {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@ -351,3 +363,20 @@ body.mw-mlb-lightbox-open #content {
|
|||
.mlb-post-image:hover .mw-mlb-drag-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mw-mlb-progress {
|
||||
width: 100%;
|
||||
height: @progress-height;
|
||||
background-color: rgb( 204, 204, 204 );
|
||||
margin-top: -@progress-height;
|
||||
}
|
||||
|
||||
.mw-mlb-progress.empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mw-mlb-progress-percent {
|
||||
width: 0;
|
||||
height: @progress-height;
|
||||
background-color: rgb( 0, 113, 188 );
|
||||
}
|
||||
|
|
|
@ -107,15 +107,6 @@
|
|||
|
||||
this.panel = new mw.mmv.ui.MetadataPanel( this.$postDiv, this.$controlBar );
|
||||
this.buttons = new mw.mmv.ui.Buttons( this.$imageWrapper, this.$closeButton, this.$fullscreenButton );
|
||||
this.initializeImage();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the image element.
|
||||
*/
|
||||
LIP.initializeImage = function () {
|
||||
this.$imageDiv
|
||||
.addClass( 'empty' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -126,7 +117,6 @@
|
|||
|
||||
this.panel.empty();
|
||||
|
||||
this.$imageDiv.addClass( 'empty' );
|
||||
this.$imageDiv.empty();
|
||||
|
||||
if ( this.resizeListener ) {
|
||||
|
@ -266,15 +256,17 @@
|
|||
* Displays an already loaded image.
|
||||
* This is an alternative to load() when we have an image element with the image already loaded.
|
||||
* @param {mw.mmv.LightboxImage} image
|
||||
* @param {HTMLImageElement } imageElement
|
||||
* @param {jQuery} $imageElement
|
||||
*/
|
||||
LIP.showImage = function( image, imageElement ) {
|
||||
LIP.showImage = function( image, $imageElement ) {
|
||||
var iface = this;
|
||||
|
||||
this.currentImage = image;
|
||||
image.globalMaxWidth = imageElement.width;
|
||||
image.globalMaxHeight = imageElement.height;
|
||||
this.$image = $( imageElement );
|
||||
|
||||
image.globalMaxWidth = $imageElement.width();
|
||||
image.globalMaxHeight = $imageElement.height();
|
||||
|
||||
this.$image = $imageElement;
|
||||
|
||||
this.autoResizeImage();
|
||||
|
||||
|
@ -299,7 +291,7 @@
|
|||
this.currentImage = image;
|
||||
|
||||
image.getImageElement().done( function( image, ele ) {
|
||||
iface.showImage( image, ele );
|
||||
iface.showImage( image, $( ele ) );
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -348,9 +340,8 @@
|
|||
|
||||
var $image = $( imageEle );
|
||||
|
||||
this.currentImage.src = imageEle.src;
|
||||
|
||||
this.$image.replaceWith( $image );
|
||||
this.currentImage.src = imageEle.src;
|
||||
this.$image = $image;
|
||||
|
||||
this.$image.css( {
|
||||
|
@ -449,6 +440,36 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Animates the image into focus
|
||||
*/
|
||||
LIP.unblur = function () {
|
||||
var self = this,
|
||||
animationLength = 300;
|
||||
|
||||
// The blurred class has an opacity < 1. This animated the image to become fully opaque
|
||||
this.$image
|
||||
.addClass( 'blurred' )
|
||||
.animate( { opacity: 1.0 }, animationLength );
|
||||
|
||||
// During the same amount of time (animationLength) we animate a blur value from 3.0 to 0.0
|
||||
// We pass that value to an inline CSS Gaussian blur effect
|
||||
$( { blur: 3.0 } ).animate( { blur: 0.0 }, {
|
||||
duration: animationLength,
|
||||
step: function ( step ) {
|
||||
self.$image.css( { '-webkit-filter' : 'blur(' + step + 'px)',
|
||||
'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' );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a fullscreen change event.
|
||||
* @param {jQuery.Event} e The fullscreen change event.
|
||||
|
|
|
@ -57,10 +57,22 @@
|
|||
|
||||
try {
|
||||
request = this.newXHR();
|
||||
|
||||
request.onprogress = function ( e ) {
|
||||
var percent;
|
||||
|
||||
if ( e.lengthComputable ) {
|
||||
percent = ( e.loaded / e.total ) * 100;
|
||||
}
|
||||
|
||||
deferred.notify( request.response, percent );
|
||||
};
|
||||
|
||||
request.onreadystatechange = function () {
|
||||
var total = $.now() - start;
|
||||
|
||||
if ( request.readyState === 4 ) {
|
||||
deferred.notify( request.response, 100 );
|
||||
deferred.resolve( request.response );
|
||||
perf.recordEntryDelayed( type, total, url, request );
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@
|
|||
|
||||
this.$dragIcon.removeClass( 'pointing-down' );
|
||||
|
||||
this.$progress.addClass( 'empty' );
|
||||
|
||||
this.fileReuse.empty();
|
||||
};
|
||||
|
||||
|
@ -90,6 +92,8 @@
|
|||
MPP.initializeHeader = function () {
|
||||
var panel = this;
|
||||
|
||||
this.initializeProgress();
|
||||
|
||||
this.$dragBar = $( '<div>' )
|
||||
.addClass( 'mw-mlb-drag-affordance' )
|
||||
.appendTo( this.$controlBar )
|
||||
|
@ -322,6 +326,19 @@
|
|||
.appendTo( this.$imageMetadata );
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the progress display at the top of the panel.
|
||||
*/
|
||||
MPP.initializeProgress = function () {
|
||||
this.$progress = $( '<div>' )
|
||||
.addClass( 'mw-mlb-progress empty' )
|
||||
.appendTo( this.$controlBar );
|
||||
|
||||
this.$percent = $( '<div>' )
|
||||
.addClass( 'mw-mlb-progress-percent' )
|
||||
.appendTo( this.$progress );
|
||||
};
|
||||
|
||||
// *********************************
|
||||
// ******** Setting methods ********
|
||||
// *********************************
|
||||
|
@ -716,5 +733,19 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the progress display when a percentage of progress is received
|
||||
* @param {number} percent
|
||||
*/
|
||||
MPP.percent = function ( percent ) {
|
||||
if ( percent < 100 ) {
|
||||
this.$percent.animate( { width : percent + '%' } );
|
||||
this.$progress.removeClass( 'empty' );
|
||||
} else {
|
||||
this.$percent.css( { width : 0 } );
|
||||
this.$progress.addClass( 'empty' );
|
||||
}
|
||||
};
|
||||
|
||||
mw.mmv.ui.MetadataPanel = MetadataPanel;
|
||||
}( mediaWiki, jQuery, OO, moment ) );
|
||||
|
|
|
@ -62,9 +62,9 @@
|
|||
lightbox.originalShowImage = lightbox.showImage;
|
||||
|
||||
// Mock showImage
|
||||
lightbox.showImage = function ( image, ele ) {
|
||||
lightbox.showImage = function ( image, $ele ) {
|
||||
// Call original showImage
|
||||
this.originalShowImage( image, ele );
|
||||
this.originalShowImage( image, $ele );
|
||||
|
||||
// resizeListener should have been saved
|
||||
assert.notStrictEqual( this.resizeListener, undefined, 'Saved listener !' );
|
||||
|
@ -316,7 +316,8 @@
|
|||
keydown.which = 38; // Up arrow
|
||||
$document.trigger( keydown );
|
||||
|
||||
assert.strictEqual( Math.round( $.scrollTo().scrollTop() ), lightbox.panel.$imageMetadata.height() + 1,
|
||||
assert.strictEqual( Math.round( $.scrollTo().scrollTop() ),
|
||||
lightbox.panel.$imageMetadata.height() + 1,
|
||||
'scrollTo scrollTop should be set to the metadata height + 1 after pressing up arrow' );
|
||||
assert.ok( lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing down after pressing up arrow' );
|
||||
|
@ -333,7 +334,8 @@
|
|||
|
||||
lightbox.panel.$dragIcon.click();
|
||||
|
||||
assert.strictEqual( Math.round( $.scrollTo().scrollTop() ), lightbox.panel.$imageMetadata.height() + 1,
|
||||
assert.strictEqual( Math.round( $.scrollTo().scrollTop() ),
|
||||
lightbox.panel.$imageMetadata.height() + 1,
|
||||
'scrollTo scrollTop should be set to the metadata height + 1 after clicking the chevron once' );
|
||||
assert.ok( lightbox.panel.$dragIcon.hasClass( 'pointing-down' ),
|
||||
'Chevron pointing down after clicking the chevron once' );
|
||||
|
@ -385,4 +387,44 @@
|
|||
mw.mmv.mediaViewer.lightbox = oldMWLightbox;
|
||||
mw.mmv.mediaViewer.ui = oldMWUI;
|
||||
} );
|
||||
|
||||
QUnit.test( 'Unblur', 3, function ( assert ) {
|
||||
var lightbox = new mw.mmv.LightboxInterface( mw.mediaViewer ),
|
||||
oldAnimate = $.fn.animate;
|
||||
|
||||
$.fn.animate = function ( target, options ) {
|
||||
var self = this,
|
||||
lastValue;
|
||||
|
||||
$.each( target, function ( key, value ) {
|
||||
lastValue = self.key = value;
|
||||
} );
|
||||
|
||||
if ( options ) {
|
||||
if ( options.step ) {
|
||||
options.step.call( this, lastValue );
|
||||
}
|
||||
|
||||
if ( options.complete ) {
|
||||
options.complete.call( this );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Attach lightbox to testing fixture to avoid interference with other tests.
|
||||
lightbox.attach( '#qunit-fixture' );
|
||||
lightbox.$image = $( '<img>' );
|
||||
|
||||
lightbox.unblur();
|
||||
|
||||
assert.ok( !lightbox.$image.css( '-webkit-filter' ) || !lightbox.$image.css( '-webkit-filter' ).length,
|
||||
'Image has no filter left' );
|
||||
assert.strictEqual( parseInt( lightbox.$image.css( 'opacity' ), 10 ), 1,
|
||||
'Image is fully opaque' );
|
||||
assert.ok( !lightbox.$image.hasClass( 'blurred' ), 'Image has no "blurred" class' );
|
||||
|
||||
lightbox.unattach();
|
||||
|
||||
$.fn.animate = oldAnimate;
|
||||
} );
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
|
|
@ -152,4 +152,228 @@
|
|||
|
||||
window.location.hash = '';
|
||||
} );
|
||||
|
||||
QUnit.test( 'Progress', 1, function ( assert ) {
|
||||
var imageDeferred = $.Deferred(),
|
||||
viewer = new mw.mmv.MultimediaViewer(),
|
||||
oldImageGet = mw.mmv.provider.Image.prototype.get,
|
||||
oldImageInfoGet = mw.mmv.provider.ImageInfo.prototype.get,
|
||||
oldThumbnailInfoGet = mw.mmv.provider.ThumbnailInfo.prototype.get;
|
||||
|
||||
viewer.thumbs = [];
|
||||
viewer.displayPlaceholderThumbnail = $.noop;
|
||||
viewer.setImage = $.noop;
|
||||
viewer.scroll = $.noop;
|
||||
viewer.preloadFullscreenThumbnail = $.noop;
|
||||
viewer.fetchSizeIndependentLightboxInfo = function () { return $.Deferred().resolve(); };
|
||||
viewer.lightbox = { iface : {
|
||||
setupForLoad : $.noop,
|
||||
showImage : $.noop,
|
||||
getCurrentImageWidths : function () { return { real : 0 }; },
|
||||
panel : { setImageInfo : $.noop,
|
||||
percent : function ( percent ) {
|
||||
assert.strictEqual( percent, 45,
|
||||
'Percentage correctly funneled to panel UI' );
|
||||
} }
|
||||
},
|
||||
open : $.noop };
|
||||
|
||||
mw.mmv.provider.Image.prototype.get = function() { return imageDeferred.promise(); };
|
||||
mw.mmv.provider.ImageInfo.prototype.get = function() { return $.Deferred().resolve(); };
|
||||
mw.mmv.provider.ThumbnailInfo.prototype.get = function() { return $.Deferred().resolve( {} ); };
|
||||
|
||||
viewer.loadImage( { filePageTitle : new mw.Title( 'File:Stuff.jpg' ) }, new Image() );
|
||||
|
||||
imageDeferred.notify( 'response', 45 );
|
||||
imageDeferred.resolve();
|
||||
|
||||
viewer.close();
|
||||
|
||||
mw.mmv.provider.Image.prototype.get = oldImageGet;
|
||||
mw.mmv.provider.ImageInfo.prototype.get = oldImageInfoGet;
|
||||
mw.mmv.provider.ThumbnailInfo.prototype.get = oldThumbnailInfoGet;
|
||||
} );
|
||||
|
||||
QUnit.test( 'resetBlurredThumbnailStates', 4, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
assert.ok( !viewer.realThumbnailShown, 'Real thumbnail state is correct' );
|
||||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
|
||||
viewer.realThumbnailShown = true;
|
||||
viewer.blurredThumbnailShown = true;
|
||||
|
||||
viewer.resetBlurredThumbnailStates();
|
||||
|
||||
assert.ok( !viewer.realThumbnailShown, 'Real thumbnail state is correct' );
|
||||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Placeholder first, then real thumbnail', 4, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.lightbox = { iface : {
|
||||
showImage : $.noop
|
||||
} };
|
||||
|
||||
viewer.displayPlaceholderThumbnail(
|
||||
{ width : 300 },
|
||||
undefined,
|
||||
$( '<img>' ).width( 100 ),
|
||||
{ css : 300, real : 300 }
|
||||
);
|
||||
|
||||
assert.ok( viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
assert.ok( !viewer.realThumbnailShown, 'Real thumbnail state is correct' );
|
||||
|
||||
viewer.displayRealThumbnail();
|
||||
|
||||
assert.ok( viewer.realThumbnailShown, 'Real thumbnail state is correct' );
|
||||
assert.ok( viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Real thumbnail first, then placeholder', 4, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.lightbox = { iface : {
|
||||
showImage : $.noop
|
||||
} };
|
||||
|
||||
viewer.displayRealThumbnail();
|
||||
|
||||
assert.ok( viewer.realThumbnailShown, 'Real thumbnail state is correct' );
|
||||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
|
||||
viewer.displayPlaceholderThumbnail(
|
||||
{ width : 300 },
|
||||
undefined,
|
||||
$( '<img>' ).width( 100 ),
|
||||
{ css : 300, real : 300 }
|
||||
);
|
||||
|
||||
assert.ok( viewer.realThumbnailShown, 'Real thumbnail state is correct' );
|
||||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'displayRealThumbnail', 1, function ( assert ) {
|
||||
var viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.lightbox = { iface : {
|
||||
unblur : function () { assert.ok( false, 'Image should not be unblurred yet' ); }
|
||||
} };
|
||||
viewer.blurredThumbnailShown = true;
|
||||
|
||||
// Should not result in an unblur (image cache from cache)
|
||||
viewer.displayRealThumbnail( undefined, undefined, undefined, 5 );
|
||||
|
||||
viewer.lightbox.iface.unblur = function () {
|
||||
assert.ok( true, 'Image needs to be unblurred' );
|
||||
};
|
||||
|
||||
// Should result in an unblur (image didn't come from cache)
|
||||
viewer.displayRealThumbnail( undefined, undefined, undefined, 1000 );
|
||||
} );
|
||||
|
||||
QUnit.test( 'displayPlaceholderThumbnail: placeholder big enough that it doesn\'t need blurring, actual image bigger than the lightbox', 5, function ( assert ) {
|
||||
var $image,
|
||||
viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.lightbox = { iface : {
|
||||
showImage : function () { assert.ok ( true, 'Placeholder shown'); }
|
||||
} };
|
||||
|
||||
$image = $( '<img>' ).width( 200 ).height( 100 );
|
||||
|
||||
viewer.displayPlaceholderThumbnail(
|
||||
{ width : 1000, height : 500 },
|
||||
undefined,
|
||||
$image,
|
||||
{ css : 300, real : 300 }
|
||||
);
|
||||
|
||||
assert.strictEqual( $image.width(), 300, 'Placeholder has the right width' );
|
||||
assert.strictEqual( $image.height(), 150, 'Placeholder has the right height' );
|
||||
assert.ok( !$image.hasClass( 'blurred' ), 'Placeholder is not blurred' );
|
||||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'displayPlaceholderThumbnail: big-enough placeholder that needs blurring, actual image bigger than the lightbox', 5, function ( assert ) {
|
||||
var $image,
|
||||
viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.lightbox = { iface : {
|
||||
showImage : function () { assert.ok ( true, 'Placeholder shown'); }
|
||||
} };
|
||||
|
||||
$image = $( '<img>' ).width( 100 ).height( 50 );
|
||||
|
||||
viewer.displayPlaceholderThumbnail(
|
||||
{ width : 1000, height : 500 },
|
||||
undefined,
|
||||
$image,
|
||||
{ css : 300, real : 300 }
|
||||
);
|
||||
|
||||
assert.strictEqual( $image.width(), 300, 'Placeholder has the right width' );
|
||||
assert.strictEqual( $image.height(), 150, 'Placeholder has the right height' );
|
||||
assert.ok( $image.hasClass( 'blurred' ), 'Placeholder is blurred' );
|
||||
assert.ok( viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'displayPlaceholderThumbnail: big-enough placeholder that needs blurring, actual image smaller than the lightbox', 5, function ( assert ) {
|
||||
var $image,
|
||||
viewer = new mw.mmv.MultimediaViewer(),
|
||||
oldDevicePixelRatio = $.devicePixelRatio;
|
||||
|
||||
$.devicePixelRatio = function () { return 2; };
|
||||
|
||||
viewer.setImage = $.noop;
|
||||
viewer.lightbox = { iface : {
|
||||
showImage : function () { assert.ok ( true, 'Placeholder shown'); }
|
||||
} };
|
||||
|
||||
$image = $( '<img>' ).width( 100 ).height( 50 );
|
||||
|
||||
viewer.displayPlaceholderThumbnail(
|
||||
{ width : 1000, height : 500 },
|
||||
undefined,
|
||||
$image,
|
||||
{ css : 1200, real : 1200 }
|
||||
);
|
||||
|
||||
assert.strictEqual( $image.width(), 1000, 'Placeholder has the right width' );
|
||||
assert.strictEqual( $image.height(), 500, 'Placeholder has the right height' );
|
||||
assert.ok( $image.hasClass( 'blurred' ), 'Placeholder is blurred' );
|
||||
assert.ok( viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
|
||||
$.devicePixelRatio = oldDevicePixelRatio;
|
||||
} );
|
||||
|
||||
QUnit.test( 'displayPlaceholderThumbnail: placeholder too small to be displayed, actual image bigger than the lightbox', 4, function ( assert ) {
|
||||
var $image,
|
||||
viewer = new mw.mmv.MultimediaViewer();
|
||||
|
||||
viewer.lightbox = { iface : {
|
||||
showImage : function () { assert.ok ( false, 'Placeholder shown when it should not'); }
|
||||
} };
|
||||
|
||||
$image = $( '<img>' ).width( 10 ).height( 5 );
|
||||
|
||||
viewer.displayPlaceholderThumbnail(
|
||||
{ width : 1000, height : 500 },
|
||||
undefined,
|
||||
$image,
|
||||
{ css : 300, real : 300 }
|
||||
);
|
||||
|
||||
assert.strictEqual( $image.width(), 10, 'Placeholder has the right width' );
|
||||
assert.strictEqual( $image.height(), 5, 'Placeholder has the right height' );
|
||||
assert.ok( !$image.hasClass( 'blurred' ), 'Placeholder is not blurred' );
|
||||
assert.ok( !viewer.blurredThumbnailShown, 'Placeholder state is correct' );
|
||||
} );
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
assert.ok( imageProvider );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Image load success test', 2, function ( assert ) {
|
||||
QUnit.test( 'Image load success', 2, function ( assert ) {
|
||||
var url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0'
|
||||
+ 'iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH'
|
||||
+ '8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
|
||||
|
@ -42,7 +42,7 @@
|
|||
} );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Image caching test', 6, function ( assert ) {
|
||||
QUnit.test( 'Image caching', 6, function ( assert ) {
|
||||
var url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0'
|
||||
+ 'iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH'
|
||||
+ '8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
|
||||
|
@ -75,10 +75,78 @@
|
|||
assert.strictEqual( image.src, url2, 'image src is correct');
|
||||
QUnit.start();
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
QUnit.asyncTest( 'Image load fail test', 1, function ( assert ) {
|
||||
QUnit.test( 'Image load XHR progress funneling', 7, function ( assert ) {
|
||||
var i = 0,
|
||||
imageProvider = new mw.mmv.provider.Image(),
|
||||
oldPerformance = imageProvider.performance,
|
||||
fakeURL = 'fakeURL',
|
||||
response = 'response';
|
||||
|
||||
imageProvider.performance.delay = 0;
|
||||
imageProvider.imagePreloadingSupported = function () { return true; };
|
||||
imageProvider.rawGet = function () { return $.Deferred().resolve(); };
|
||||
|
||||
imageProvider.performance.newXHR = function () {
|
||||
return { readyState: 4,
|
||||
response: response,
|
||||
send: function () {
|
||||
var self = this;
|
||||
|
||||
// The timeout is necessary because without it notify() happens before
|
||||
// the imageProvider has time to chain its progress() to the returned deferred
|
||||
setTimeout( function () {
|
||||
self.onprogress( { lengthComputable: true, loaded : 10, total : 20 } );
|
||||
self.onreadystatechange();
|
||||
} );
|
||||
},
|
||||
|
||||
open: $.noop };
|
||||
};
|
||||
|
||||
QUnit.stop();
|
||||
|
||||
imageProvider.performance.recordEntry = function ( type, total, url ) {
|
||||
QUnit.start();
|
||||
|
||||
assert.strictEqual( type, 'image', 'Type matches' );
|
||||
assert.strictEqual( url, fakeURL, 'URL matches' );
|
||||
|
||||
imageProvider.performance = oldPerformance;
|
||||
|
||||
return $.Deferred().resolve();
|
||||
};
|
||||
|
||||
QUnit.stop();
|
||||
|
||||
imageProvider.get( fakeURL )
|
||||
.fail( function () {
|
||||
QUnit.start();
|
||||
|
||||
assert.ok( false, 'Image failed to (pretend to) load' );
|
||||
} )
|
||||
.then( function () {
|
||||
QUnit.start();
|
||||
|
||||
assert.ok( true, 'Image was pretend-loaded' );
|
||||
} )
|
||||
.progress( function ( response, percent ) {
|
||||
if ( i === 0 ) {
|
||||
assert.strictEqual( percent, 50, 'Correctly propagated a 50% progress event' );
|
||||
assert.strictEqual( response, response, 'Partial response propagated' );
|
||||
} else if ( i === 1 ) {
|
||||
assert.strictEqual( percent, 100, 'Correctly propagated a 100% progress event' );
|
||||
assert.strictEqual( response, response, 'Partial response propagated' );
|
||||
} else {
|
||||
assert.ok( false, 'Only 2 progress events should propagate' );
|
||||
}
|
||||
|
||||
i++;
|
||||
} );
|
||||
} );
|
||||
|
||||
QUnit.asyncTest( 'Image load fail', 1, function ( assert ) {
|
||||
var imageProvider = new mw.mmv.provider.Image();
|
||||
|
||||
imageProvider.imagePreloadingSupported = function () { return false; };
|
||||
|
@ -90,7 +158,7 @@
|
|||
} );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Image load test with preloading supported', 1, function ( assert ) {
|
||||
QUnit.test( 'Image load with preloading supported', 1, function ( assert ) {
|
||||
var url = mw.config.get( 'wgScriptPath' ) + '/skins/vector/images/search-ltr.png',
|
||||
imageProvider = new mw.mmv.provider.Image(),
|
||||
endsWith = function ( a, b ) { return a.indexOf( b ) === a.length - b.length; };
|
||||
|
@ -112,7 +180,7 @@
|
|||
} );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Failed image load test with preloading supported', 1, function ( assert ) {
|
||||
QUnit.test( 'Failed image load with preloading supported', 1, function ( assert ) {
|
||||
var url = 'nosuchimage.png',
|
||||
imageProvider = new mw.mmv.provider.Image();
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
'$usernameLi',
|
||||
'$locationLi',
|
||||
'$repoLi',
|
||||
'$datetimeLi'
|
||||
'$datetimeLi',
|
||||
'$progress'
|
||||
];
|
||||
|
||||
QUnit.module( 'mmv.ui.metadataPanel', QUnit.newMwEnvironment() );
|
||||
|
@ -193,4 +194,34 @@
|
|||
|
||||
assert.strictEqual( result, date1, 'Invalid date is correctly ignored' );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Progress bar', 8, function ( assert ) {
|
||||
var $qf = $( '#qunit-fixture' ),
|
||||
panel = new mw.mmv.ui.MetadataPanel( $qf, $( '<div>' ).appendTo( $qf ) ),
|
||||
oldAnimate = $.fn.animate;
|
||||
|
||||
$.fn.animate = function ( target ) {
|
||||
$( this ).css( target );
|
||||
};
|
||||
|
||||
assert.ok( panel.$progress.hasClass( 'empty' ), 'Progress bar is hidden' );
|
||||
assert.strictEqual( panel.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
|
||||
panel.percent( 0 );
|
||||
|
||||
assert.ok( !panel.$progress.hasClass( 'empty' ), 'Progress bar is visible' );
|
||||
assert.strictEqual( panel.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
|
||||
panel.percent( 50 );
|
||||
|
||||
assert.ok( !panel.$progress.hasClass( 'empty' ), 'Progress bar is visible' );
|
||||
assert.strictEqual( panel.$percent.width(), $qf.width() / 2, 'Progress bar\'s indicator is at half' );
|
||||
|
||||
panel.percent( 100 );
|
||||
|
||||
assert.ok( panel.$progress.hasClass( 'empty' ), 'Progress bar is hidden' );
|
||||
assert.strictEqual( panel.$percent.width(), 0, 'Progress bar\'s indicator is at 0' );
|
||||
|
||||
$.fn.animate = oldAnimate;
|
||||
} );
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
|
Loading…
Reference in a new issue