Skip to content

Commit

Permalink
Fix: IE11 image size default (#333)
Browse files Browse the repository at this point in the history
  • Loading branch information
Minh-Ng authored Aug 24, 2017
1 parent 886bd33 commit bebdcec
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 49 deletions.
64 changes: 40 additions & 24 deletions src/lib/viewers/image/ImageBaseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Controls from '../../Controls';
import BaseViewer from '../BaseViewer';
import Browser from '../../Browser';
import { ICON_ZOOM_IN, ICON_ZOOM_OUT } from '../../icons/icons';
import { get } from '../../util';

import { CLASS_INVISIBLE } from '../../constants';

Expand Down Expand Up @@ -43,13 +44,17 @@ class ImageBaseViewer extends BaseViewer {
return;
}

this.setOriginalImageSize(this.imageEl);
this.loadUI();
this.zoom();

this.imageEl.classList.remove(CLASS_INVISIBLE);
this.loaded = true;
this.emit('load');
const loadOriginalDimensions = this.setOriginalImageSize(this.imageEl);
loadOriginalDimensions
.then(() => {
this.loadUI();
this.zoom();

this.imageEl.classList.remove(CLASS_INVISIBLE);
this.loaded = true;
this.emit('load');
})
.catch(this.errorHandler);
}

/**
Expand Down Expand Up @@ -177,27 +182,38 @@ class ImageBaseViewer extends BaseViewer {
* @return {Promise} A promise that is resolved if the original image dimensions were set.
*/
setOriginalImageSize(imageEl) {
const image = imageEl;
const promise = new Promise((resolve, reject) => {
// Do not bother loading a new image when the natural size attributes exist
if (imageEl.naturalWidth > 0 && imageEl.naturalHeight > 0) {
image.originalWidth = imageEl.naturalWidth;
image.originalHeight = imageEl.naturalHeight;
if (imageEl.naturalWidth && imageEl.naturalHeight) {
imageEl.setAttribute('originalWidth', imageEl.naturalWidth);
imageEl.setAttribute('originalHeight', imageEl.naturalHeight);
resolve();
} else {
const originalImg = new Image();
image.originalWidth = 1;
image.originalHeight = 1;

originalImg.error = () => {
reject();
};
originalImg.onload = () => {
image.originalWidth = originalImg.width || 1;
image.originalHeight = originalImg.height || 1;
resolve();
};
originalImg.src = imageEl.src;
// Case when natural dimensions are not assigned
// By default, assigned width and height in Chrome/Safari/Firefox will be 300x150.
// IE11 workaround. Dimensions only displayed if the image is attached to the document.
get(imageEl.src, {}, 'text')
.then((imageAsText) => {
const parser = new DOMParser();
const svgEl = parser.parseFromString(imageAsText, 'image/svg+xml');

try {
// Assume svgEl is an instanceof an SVG with a viewBox and preserveAspectRatio of meet
// where the height is the limiting axis
const viewBox = svgEl.documentElement.getAttribute('viewBox');
const [, , w, h] = viewBox.split(' ');
const aspectRatio = h ? w / h : w;
imageEl.setAttribute('originalWidth', Math.round(aspectRatio * 150));
imageEl.setAttribute('originalHeight', 150);
resolve();
} catch (e) {
// Assume 300x150 that chrome does by default
imageEl.setAttribute('originalWidth', 300);
imageEl.setAttribute('originalHeight', 150);
resolve(e);
}
})
.catch(reject);
}
});

Expand Down
14 changes: 10 additions & 4 deletions src/lib/viewers/image/ImageViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,14 @@ class ImageViewer extends ImageBaseViewer {
// If the image is smaller than the new viewport, zoom up to a
// max of the original file size
} else if (modifyWidthInsteadOfHeight) {
const originalWidth = this.isRotated() ? this.imageEl.originalHeight : this.imageEl.originalWidth;
const originalWidth = this.isRotated()
? this.imageEl.getAttribute('originalHeight')
: this.imageEl.getAttribute('originalWidth');
newWidth = Math.min(viewport.width, originalWidth);
} else {
const originalHeight = this.isRotated() ? this.imageEl.originalWidth : this.imageEl.originalHeight;
const originalHeight = this.isRotated()
? this.imageEl.getAttribute('originalWidth')
: this.imageEl.getAttribute('originalHeight');
newHeight = Math.min(viewport.height, originalHeight);
}
}
Expand Down Expand Up @@ -231,7 +235,9 @@ class ImageViewer extends ImageBaseViewer {
* @return {void}
*/
setScale(width, height) {
this.scale = width ? width / this.imageEl.originalWidth : height / this.imageEl.originalHeight;
this.scale = width
? width / this.imageEl.getAttribute('originalWidth')
: height / this.imageEl.getAttribute('originalHeight');
this.rotationAngle = this.currentRotationAngle % 3600 % 360;
this.emit('scale', {
scale: this.scale,
Expand Down Expand Up @@ -390,7 +396,7 @@ class ImageViewer extends ImageBaseViewer {
handleOrientationChange() {
this.adjustImageZoomPadding();

this.scale = this.imageEl.clientWidth / this.imageEl.originalWidth;
this.scale = this.imageEl.clientWidth / this.imageEl.getAttribute('originalWidth');
this.rotationAngle = this.currentRotationAngle % 3600 % 360;
this.emit('scale', {
scale: this.scale,
Expand Down
57 changes: 36 additions & 21 deletions src/lib/viewers/image/__tests__/ImageBaseViewer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ImageBaseViewer from '../ImageBaseViewer';
import BaseViewer from '../../BaseViewer';
import Browser from '../../../Browser';
import fullscreen from '../../../Fullscreen';
import * as util from '../../../util';
import { ICON_ZOOM_IN, ICON_ZOOM_OUT } from '../../../icons/icons';

const CSS_CLASS_PANNING = 'panning';
Expand All @@ -11,8 +12,6 @@ const CSS_CLASS_PANNABLE = 'pannable';

let stubs = {};

const imageUrl = 'data:image/png;base64,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';

const sandbox = sinon.sandbox.create();
let imageBase;
let containerEl;
Expand Down Expand Up @@ -224,31 +223,41 @@ describe('lib/viewers/image/ImageBaseViewer', () => {
it('should use the naturalHeight and naturalWidth when available', (done) => {
const imageEl = {
naturalWidth: 100,
naturalHeight: 100
naturalHeight: 100,
setAttribute: (name, value) => {
imageEl[name] = value;
},
getAttribute: (name) => imageEl[name]
};

const promise = imageBase.setOriginalImageSize(imageEl);
promise.should.be.fulfilled.then(() => {
expect(imageEl.originalWidth).to.equal(imageEl.naturalWidth);
expect(imageEl.originalHeight).to.equal(imageEl.naturalHeight);
expect(imageEl.getAttribute('originalWidth')).to.equal(imageEl.naturalWidth);
expect(imageEl.getAttribute('originalHeight')).to.equal(imageEl.naturalHeight);
done();
}).catch(() => {
Assert.fail();
});
});

it('should work when naturalHeight and naturalWidth are undefined', (done) => {
it('should default to 300x150 when naturalHeight and naturalWidth are 0x0', (done) => {
const imageEl = {
naturalWidth: undefined,
naturalHeight: undefined,
src: imageUrl
naturalWidth: 0,
naturalHeight: 0,
setAttribute: (name, value) => {
imageEl[name] = value;
},
getAttribute: (name) => imageEl[name]
};

const imageUrlWidth = 12;
const imageUrlHeight = 12;
const getStub = sandbox.stub(util, 'get').returns(Promise.resolve('not real a image'));
const promise = imageBase.setOriginalImageSize(imageEl);
promise.should.be.fulfilled.then(() => {
expect(imageEl.originalWidth).to.equal(imageUrlWidth);
expect(imageEl.originalHeight).to.equal(imageUrlHeight);
expect(imageEl.getAttribute('originalWidth')).to.equal(300);
expect(imageEl.getAttribute('originalHeight')).to.equal(150);
done();
}).catch(() => {
Assert.fail();
});
});
});
Expand Down Expand Up @@ -532,32 +541,38 @@ describe('lib/viewers/image/ImageBaseViewer', () => {
describe('finishLoading()', () => {
beforeEach(() => {
imageBase.loaded = false;
stubs.emit = sandbox.stub(imageBase, 'emit');
stubs.zoom = sandbox.stub(imageBase, 'zoom');
stubs.loadUI = sandbox.stub(imageBase, 'loadUI');
stubs.setOriginalImageSize = sandbox.stub(imageBase, 'setOriginalImageSize');
stubs.errorHandler = sandbox.stub(imageBase, 'errorHandler');
});

it('should do nothing if already destroyed', () => {
imageBase.destroyed = true;
stubs.emit = sandbox.stub(imageBase, 'emit');

imageBase.finishLoading();
expect(imageBase.loaded).to.be.false;
expect(stubs.emit).to.not.have.been.called;
expect(stubs.zoom).to.not.have.been.called;
expect(stubs.setOriginalImageSize).to.not.have.been.called;
expect(stubs.loadUI).to.not.have.been.called;
expect(stubs.errorHandler).to.not.have.been.called;
});

it('should load UI if not destroyed', () => {
imageBase.finishLoading();
it('should load UI if not destroyed', (done) => {
imageBase.on('load', () => {
expect(stubs.errorHandler).to.not.have.been.called;
expect(imageBase.loaded).to.be.true;
expect(stubs.zoom).to.have.been.called;
expect(stubs.loadUI).to.have.been.called;
done();
});
stubs.setOriginalImageSize.returns(Promise.resolve());
imageBase.destroyed = false;

expect(imageBase.loaded).to.be.true;
expect(stubs.emit).to.have.been.calledWith('load');
expect(stubs.emit).to.have.been.calledWith('load');
imageBase.finishLoading();
expect(stubs.setOriginalImageSize).to.have.been.called;
expect(stubs.zoom).to.have.been.called;
expect(stubs.loadUI).to.have.been.called;
});
});

Expand Down

0 comments on commit bebdcec

Please sign in to comment.