viewerjs
Version:
JavaScript image viewer.
266 lines (220 loc) • 6.31 kB
JavaScript
import {
ACTION_MOVE,
ACTION_SWITCH,
ACTION_ZOOM,
CLASS_HIDE,
CLASS_OPEN,
EVENT_FOCUSIN,
EVENT_HIDDEN,
EVENT_SHOWN,
} from './constants';
import {
addClass,
addListener,
dispatchEvent,
forEach,
getMaxZoomRatio,
isFunction,
isPlainObject,
isString,
removeClass,
removeListener,
} from './utilities';
export default {
getImageURL(image) {
let { url } = this.options;
if (isString(url)) {
url = image.getAttribute(url);
} else if (isFunction(url)) {
url = url.call(this, image);
} else {
url = '';
}
return url;
},
enforceFocus() {
this.clearEnforceFocus();
addListener(document, EVENT_FOCUSIN, (this.onFocusin = (event) => {
const { viewer } = this;
let { target } = event;
if (target === document || target === viewer || viewer.contains(target)) {
return;
}
while (target) {
// Avoid conflicts with other modals (#474, #540)
if ((target.getAttribute('tabindex') !== null || target.getAttribute('aria-modal') === 'true')) {
return;
}
target = target.parentElement;
}
viewer.focus();
}));
},
clearEnforceFocus() {
if (this.onFocusin) {
removeListener(document, EVENT_FOCUSIN, this.onFocusin);
this.onFocusin = null;
}
},
open() {
const { body } = this;
addClass(body, CLASS_OPEN);
if (this.scrollbarWidth > 0) {
body.style.paddingRight = `${this.scrollbarWidth + (parseFloat(this.initialBodyComputedPaddingRight) || 0)}px`;
}
},
close() {
const { body } = this;
removeClass(body, CLASS_OPEN);
if (this.scrollbarWidth > 0) {
body.style.paddingRight = this.initialBodyPaddingRight;
}
},
shown() {
const { element, options, viewer } = this;
this.fulled = true;
this.isShown = true;
this.render();
this.bind();
this.showing = false;
if (options.focus) {
viewer.focus();
this.enforceFocus();
}
if (isFunction(options.shown)) {
addListener(element, EVENT_SHOWN, options.shown, {
once: true,
});
}
if (dispatchEvent(element, EVENT_SHOWN) === false) {
return;
}
if (this.ready && this.isShown && !this.hiding) {
this.view(this.index);
}
},
hidden() {
const { element, options, viewer } = this;
if (options.fucus) {
this.clearEnforceFocus();
}
this.close();
this.unbind();
addClass(viewer, CLASS_HIDE);
viewer.removeAttribute('role');
viewer.removeAttribute('aria-labelledby');
viewer.removeAttribute('aria-modal');
viewer.setAttribute('aria-hidden', true);
this.resetList();
this.resetImage();
this.fulled = false;
this.viewed = false;
this.isShown = false;
this.hiding = false;
if (!this.destroyed) {
if (isFunction(options.hidden)) {
addListener(element, EVENT_HIDDEN, options.hidden, {
once: true,
});
}
dispatchEvent(element, EVENT_HIDDEN, null, {
cancelable: false,
});
}
},
requestFullscreen(options) {
const document = this.element.ownerDocument;
if (this.fulled && !(
document.fullscreenElement
|| document.webkitFullscreenElement
|| document.mozFullScreenElement
|| document.msFullscreenElement
)) {
const { documentElement } = document;
// Element.requestFullscreen()
if (documentElement.requestFullscreen) {
// Avoid TypeError when convert `options` to dictionary
if (isPlainObject(options)) {
documentElement.requestFullscreen(options);
} else {
documentElement.requestFullscreen();
}
} else if (documentElement.webkitRequestFullscreen) {
documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
} else if (documentElement.mozRequestFullScreen) {
documentElement.mozRequestFullScreen();
} else if (documentElement.msRequestFullscreen) {
documentElement.msRequestFullscreen();
}
}
},
exitFullscreen() {
const document = this.element.ownerDocument;
if (this.fulled && (
document.fullscreenElement
|| document.webkitFullscreenElement
|| document.mozFullScreenElement
|| document.msFullscreenElement
)) {
// Document.exitFullscreen()
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
},
change(event) {
const { options, pointers } = this;
const pointer = pointers[Object.keys(pointers)[0]];
// In the case of the `pointers` object is empty (#421)
if (!pointer) {
return;
}
const offsetX = pointer.endX - pointer.startX;
const offsetY = pointer.endY - pointer.startY;
switch (this.action) {
// Move the current image
case ACTION_MOVE:
if (offsetX !== 0 || offsetY !== 0) {
this.pointerMoved = true;
this.move(offsetX, offsetY, event);
}
break;
// Zoom the current image
case ACTION_ZOOM:
this.zoom(getMaxZoomRatio(pointers), false, null, event);
break;
case ACTION_SWITCH: {
this.action = 'switched';
const absoluteOffsetX = Math.abs(offsetX);
if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
// Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
this.pointers = {};
if (offsetX > 1) {
this.prev(options.loop);
} else if (offsetX < -1) {
this.next(options.loop);
}
}
break;
}
default:
}
// Override
forEach(pointers, (p) => {
p.startX = p.endX;
p.startY = p.endY;
});
},
isSwitchable() {
const { imageData, viewerData } = this;
return this.length > 1 && imageData.x >= 0 && imageData.y >= 0
&& imageData.width <= viewerData.width
&& imageData.height <= viewerData.height;
},
};