Bug 28180: Add Chocolat to OPAC
See Bug 28179: Add the Chocolat gallery lightbox JS library Sponsored-by: Gerhard Sondermann Dialog e.K. (presseplus.de, presseshop.at, presseshop.ch) Signed-off-by: Rasmus Leißner <rasmus.leissner@solutions-factory.de> Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
This commit is contained in:
parent
e94e4476f1
commit
e309857d2f
8 changed files with 937 additions and 0 deletions
230
koha-tmpl/opac-tmpl/lib/Chocolat/css/chocolat.css
Normal file
230
koha-tmpl/opac-tmpl/lib/Chocolat/css/chocolat.css
Normal file
|
@ -0,0 +1,230 @@
|
|||
.chocolat-zoomable.chocolat-zoomed {
|
||||
cursor: zoom-out;
|
||||
}
|
||||
.chocolat-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
.chocolat-overlay {
|
||||
transition: opacity 0.4s ease, visibility 0s 0.4s ease;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background-color: #fff;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
.chocolat-overlay.chocolat-visible {
|
||||
transition: opacity 0.4s, visibility 0s;
|
||||
visibility: visible;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.chocolat-wrapper {
|
||||
transition: opacity 0.4s ease, visibility 0s 0.4s ease;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
opacity: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 16;
|
||||
color: #fff;
|
||||
visibility: hidden;
|
||||
}
|
||||
.chocolat-wrapper.chocolat-visible {
|
||||
transition: opacity 0.4s, visibility 0s;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.chocolat-zoomable .chocolat-img {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
.chocolat-loader {
|
||||
transition: opacity 0.3s;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -16px;
|
||||
margin-top: -16px;
|
||||
z-index: 11;
|
||||
background: url(../images/loader.gif);
|
||||
opacity: 0;
|
||||
}
|
||||
.chocolat-loader.chocolat-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.chocolat-image-wrapper {
|
||||
position: fixed;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
z-index: 14;
|
||||
text-align: left;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.chocolat-image-wrapper .chocolat-img {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-left {
|
||||
width: 50px;
|
||||
height: 100px;
|
||||
cursor: pointer;
|
||||
background: url(../images/left.png) 50% 50% no-repeat;
|
||||
z-index: 17;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.chocolat-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.chocolat-image-canvas {
|
||||
transition: opacity .2s;
|
||||
opacity: 0;
|
||||
flex-grow: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
.chocolat-image-canvas.chocolat-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
.chocolat-center {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.chocolat-wrapper .chocolat-right {
|
||||
width: 50px;
|
||||
height: 100px;
|
||||
cursor: pointer;
|
||||
background: url(../images/right.png) 50% 50% no-repeat;
|
||||
z-index: 17;
|
||||
visibility: hidden;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-right.active {
|
||||
visibility: visible;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-left.active {
|
||||
visibility: visible;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-top {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
z-index: 17;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-close {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: url(../images/close.png) 50% 50% no-repeat;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-bottom {
|
||||
height: 40px;
|
||||
font-size: 12px;
|
||||
z-index: 17;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
.chocolat-wrapper .chocolat-set-title {
|
||||
display: inline-block;
|
||||
padding-right: 15px;
|
||||
line-height: 1;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.chocolat-wrapper .chocolat-pagination {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
margin-right: 15px;
|
||||
/*border-right: 1px solid rgba(255, 255, 255, 0.2);*/
|
||||
}
|
||||
.chocolat-wrapper .chocolat-fullscreen {
|
||||
width: 16px;
|
||||
height: 40px;
|
||||
background: url(../images/fullscreen.png) 50% 50% no-repeat;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
.chocolat-wrapper .chocolat-description {
|
||||
display: inline-block;
|
||||
flex-grow: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* no container mode*/
|
||||
body.chocolat-open > .chocolat-overlay {
|
||||
z-index: 15;
|
||||
}
|
||||
body.chocolat-open > .chocolat-loader {
|
||||
z-index: 15;
|
||||
}
|
||||
body.chocolat-open > .chocolat-image-wrapper {
|
||||
z-index: 17;
|
||||
}
|
||||
|
||||
/* container mode*/
|
||||
.chocolat-in-container .chocolat-wrapper,
|
||||
.chocolat-in-container .chocolat-image-wrapper,
|
||||
.chocolat-in-container .chocolat-overlay {
|
||||
position: absolute;
|
||||
}
|
||||
.chocolat-in-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chocolat-zoomable.chocolat-zooming-in .chocolat-image-wrapper,
|
||||
.chocolat-zoomable.chocolat-zooming-out .chocolat-image-wrapper {
|
||||
transition: width .2s ease, height .2s ease;
|
||||
}
|
||||
.chocolat-zoomable.chocolat-zooming-in .chocolat-img,
|
||||
.chocolat-zoomable.chocolat-zooming-out .chocolat-img {
|
||||
transition: margin .2s ease;
|
||||
}
|
||||
|
||||
/* uncomment to hide controls when zoomed-in*/
|
||||
/*
|
||||
.chocolat-zoomable .chocolat-top,
|
||||
.chocolat-zoomable .chocolat-bottom,
|
||||
.chocolat-zoomable .chocolat-right,
|
||||
.chocolat-zoomable .chocolat-left {
|
||||
transition: opacity .3s ease, visibility 0s .3s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.chocolat-zoomable.chocolat-zoomed .chocolat-top,
|
||||
.chocolat-zoomable.chocolat-zoomed .chocolat-bottom,
|
||||
.chocolat-zoomable.chocolat-zoomed .chocolat-right,
|
||||
.chocolat-zoomable.chocolat-zoomed .chocolat-left {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
*/
|
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/close.png
Normal file
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen-black.png
Normal file
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen-black.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 658 B |
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen.png
Normal file
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 663 B |
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/left.png
Normal file
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/left.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/loader.gif
Normal file
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/loader.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/right.png
Normal file
BIN
koha-tmpl/opac-tmpl/lib/Chocolat/images/right.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
707
koha-tmpl/opac-tmpl/lib/Chocolat/js/chocolat.js
Normal file
707
koha-tmpl/opac-tmpl/lib/Chocolat/js/chocolat.js
Normal file
|
@ -0,0 +1,707 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
let timerDebounce = undefined;
|
||||
function debounce(duration, callback) {
|
||||
clearTimeout(timerDebounce);
|
||||
timerDebounce = setTimeout(function () {
|
||||
callback();
|
||||
}, duration);
|
||||
return timerDebounce;
|
||||
}
|
||||
function transitionAsPromise(triggeringFunc, el) {
|
||||
return new Promise(resolve => {
|
||||
const handleTransitionEnd = () => {
|
||||
el.removeEventListener('transitionend', handleTransitionEnd);
|
||||
resolve();
|
||||
};
|
||||
|
||||
el.addEventListener('transitionend', handleTransitionEnd);
|
||||
const classesBefore = el.getAttribute('class');
|
||||
const stylesBefore = el.getAttribute('style');
|
||||
triggeringFunc();
|
||||
|
||||
if (classesBefore === el.getAttribute('class') && stylesBefore === el.getAttribute('style')) {
|
||||
handleTransitionEnd();
|
||||
}
|
||||
|
||||
if (parseFloat(getComputedStyle(el)['transitionDuration']) === 0) {
|
||||
handleTransitionEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
function loadImage({
|
||||
src,
|
||||
srcset,
|
||||
sizes
|
||||
}) {
|
||||
const image = new Image();
|
||||
image.src = src;
|
||||
|
||||
if (srcset) {
|
||||
image.srcset = srcset;
|
||||
}
|
||||
|
||||
if (sizes) {
|
||||
image.sizes = sizes;
|
||||
}
|
||||
|
||||
if ('decode' in image) {
|
||||
return new Promise((resolve, reject) => {
|
||||
image.decode().then(() => {
|
||||
resolve(image);
|
||||
}).catch(() => {
|
||||
reject(image);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
image.onload = resolve(image);
|
||||
image.onerror = reject(image);
|
||||
});
|
||||
}
|
||||
}
|
||||
function fit(options) {
|
||||
let height;
|
||||
let width;
|
||||
const {
|
||||
imgHeight,
|
||||
imgWidth,
|
||||
containerHeight,
|
||||
containerWidth,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
imageSize
|
||||
} = options;
|
||||
const canvasRatio = canvasHeight / canvasWidth;
|
||||
const containerRatio = containerHeight / containerWidth;
|
||||
const imgRatio = imgHeight / imgWidth;
|
||||
|
||||
if (imageSize == 'cover') {
|
||||
if (imgRatio < containerRatio) {
|
||||
height = containerHeight;
|
||||
width = height / imgRatio;
|
||||
} else {
|
||||
width = containerWidth;
|
||||
height = width * imgRatio;
|
||||
}
|
||||
} else if (imageSize == 'native') {
|
||||
height = imgHeight;
|
||||
width = imgWidth;
|
||||
} else {
|
||||
if (imgRatio > canvasRatio) {
|
||||
height = canvasHeight;
|
||||
width = height / imgRatio;
|
||||
} else {
|
||||
width = canvasWidth;
|
||||
height = width * imgRatio;
|
||||
}
|
||||
|
||||
if (imageSize === 'scale-down' && (width >= imgWidth || height >= imgHeight)) {
|
||||
width = imgWidth;
|
||||
height = imgHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
height: height,
|
||||
width: width
|
||||
};
|
||||
}
|
||||
function openFullScreen(wrapper) {
|
||||
if (wrapper.requestFullscreen) {
|
||||
return wrapper.requestFullscreen();
|
||||
} else if (wrapper.webkitRequestFullscreen) {
|
||||
return wrapper.webkitRequestFullscreen();
|
||||
} else if (wrapper.msRequestFullscreen) {
|
||||
return wrapper.msRequestFullscreen();
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
function exitFullScreen() {
|
||||
if (document.exitFullscreen) {
|
||||
return document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
return document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
return document.msExitFullscreen();
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
container: document.body,
|
||||
// window or element
|
||||
className: undefined,
|
||||
imageSize: 'scale-down',
|
||||
// 'scale-down', 'contain', 'cover' or 'native'
|
||||
fullScreen: false,
|
||||
loop: false,
|
||||
linkImages: true,
|
||||
setIndex: 0,
|
||||
firstImageIndex: 0,
|
||||
lastImageIndex: false,
|
||||
currentImageIndex: undefined,
|
||||
allowZoom: true,
|
||||
closeOnBackgroundClick: true,
|
||||
setTitle: function () {
|
||||
return '';
|
||||
},
|
||||
description: function () {
|
||||
return this.images[this.settings.currentImageIndex].title;
|
||||
},
|
||||
pagination: function () {
|
||||
const last = this.settings.lastImageIndex + 1;
|
||||
const position = this.settings.currentImageIndex + 1;
|
||||
return position + '/' + last;
|
||||
},
|
||||
|
||||
afterInitialize() {},
|
||||
|
||||
afterMarkup() {},
|
||||
|
||||
afterImageLoad() {},
|
||||
|
||||
afterClose() {},
|
||||
|
||||
zoomedPaddingX: function (canvasWidth, imgWidth) {
|
||||
return 0;
|
||||
},
|
||||
zoomedPaddingY: function (canvasHeight, imgHeight) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
class Chocolat {
|
||||
constructor(elements, settings) {
|
||||
this.settings = settings;
|
||||
this.elems = {};
|
||||
this.images = [];
|
||||
this.events = [];
|
||||
this.state = {
|
||||
fullScreenOpen: false,
|
||||
initialZoomState: null,
|
||||
initialized: false,
|
||||
timer: false,
|
||||
visible: false
|
||||
};
|
||||
this._cssClasses = ['chocolat-open', 'chocolat-in-container', 'chocolat-cover', 'chocolat-zoomable', 'chocolat-zoomed', 'chocolat-zooming-in', 'chocolat-zooming-out'];
|
||||
|
||||
if (NodeList.prototype.isPrototypeOf(elements) || HTMLCollection.prototype.isPrototypeOf(elements)) {
|
||||
elements.forEach((el, i) => {
|
||||
this.images.push({
|
||||
title: el.getAttribute('title'),
|
||||
src: el.getAttribute('href'),
|
||||
srcset: el.getAttribute('data-srcset'),
|
||||
sizes: el.getAttribute('data-sizes')
|
||||
});
|
||||
this.off(el, 'click.chocolat');
|
||||
this.on(el, 'click.chocolat', e => {
|
||||
this.init(i);
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.images = elements;
|
||||
}
|
||||
|
||||
if (this.settings.container instanceof Element || this.settings.container instanceof HTMLElement) {
|
||||
this.elems.container = this.settings.container;
|
||||
} else {
|
||||
this.elems.container = document.body;
|
||||
}
|
||||
|
||||
this.api = {
|
||||
open: i => {
|
||||
i = parseInt(i) || 0;
|
||||
return this.init(i);
|
||||
},
|
||||
close: () => {
|
||||
return this.close();
|
||||
},
|
||||
next: () => {
|
||||
return this.change(1);
|
||||
},
|
||||
prev: () => {
|
||||
return this.change(-1);
|
||||
},
|
||||
goto: i => {
|
||||
return this.open(i);
|
||||
},
|
||||
current: () => {
|
||||
return this.settings.currentImageIndex;
|
||||
},
|
||||
position: () => {
|
||||
return this.position(this.elems.img);
|
||||
},
|
||||
destroy: () => {
|
||||
return this.destroy();
|
||||
},
|
||||
set: (property, value) => {
|
||||
this.settings[property] = value;
|
||||
return value;
|
||||
},
|
||||
get: property => {
|
||||
return this.settings[property];
|
||||
},
|
||||
getElem: name => {
|
||||
return this.elems[name];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
init(i) {
|
||||
if (!this.state.initialized) {
|
||||
this.markup();
|
||||
this.attachListeners();
|
||||
this.settings.lastImageIndex = this.images.length - 1;
|
||||
this.state.initialized = true;
|
||||
}
|
||||
|
||||
this.settings.afterInitialize.call(this);
|
||||
return this.load(i);
|
||||
}
|
||||
|
||||
load(index) {
|
||||
if (!this.state.visible) {
|
||||
this.state.visible = true;
|
||||
setTimeout(() => {
|
||||
this.elems.overlay.classList.add('chocolat-visible');
|
||||
this.elems.wrapper.classList.add('chocolat-visible');
|
||||
}, 0);
|
||||
this.elems.container.classList.add('chocolat-open');
|
||||
}
|
||||
|
||||
if (this.settings.fullScreen) {
|
||||
openFullScreen(this.elems.wrapper);
|
||||
}
|
||||
|
||||
if (this.settings.currentImageIndex === index) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let loaderTimer = setTimeout(() => {
|
||||
this.elems.loader.classList.add('chocolat-visible');
|
||||
}, 1000);
|
||||
let fadeOutPromise;
|
||||
let image;
|
||||
let fadeOutTimer = setTimeout(() => {
|
||||
fadeOutTimer = undefined;
|
||||
fadeOutPromise = transitionAsPromise(() => {
|
||||
this.elems.imageCanvas.classList.remove('chocolat-visible');
|
||||
}, this.elems.imageCanvas);
|
||||
}, 80);
|
||||
return loadImage(this.images[index]).then(loadedImage => {
|
||||
image = loadedImage;
|
||||
|
||||
if (fadeOutTimer) {
|
||||
clearTimeout(fadeOutTimer);
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return fadeOutPromise;
|
||||
}
|
||||
}).then(() => {
|
||||
const nextIndex = index + 1;
|
||||
|
||||
if (this.images[nextIndex] != undefined) {
|
||||
loadImage(this.images[nextIndex]);
|
||||
}
|
||||
|
||||
this.settings.currentImageIndex = index;
|
||||
this.elems.description.innerHTML = this.settings.description.call(this);
|
||||
this.elems.pagination.textContent = this.settings.pagination.call(this);
|
||||
this.arrows();
|
||||
return this.position(image).then(() => {
|
||||
this.elems.loader.classList.remove('chocolat-visible');
|
||||
clearTimeout(loaderTimer);
|
||||
return this.appear(image);
|
||||
});
|
||||
}).then(() => {
|
||||
this.elems.container.classList.toggle('chocolat-zoomable', this.zoomable(image, this.elems.wrapper));
|
||||
this.settings.afterImageLoad.call(this);
|
||||
});
|
||||
}
|
||||
|
||||
position({
|
||||
naturalHeight,
|
||||
naturalWidth
|
||||
}) {
|
||||
const fitOptions = {
|
||||
imgHeight: naturalHeight,
|
||||
imgWidth: naturalWidth,
|
||||
containerHeight: this.elems.container.clientHeight,
|
||||
containerWidth: this.elems.container.clientWidth,
|
||||
canvasWidth: this.elems.imageCanvas.clientWidth,
|
||||
canvasHeight: this.elems.imageCanvas.clientHeight,
|
||||
imageSize: this.settings.imageSize
|
||||
};
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = fit(fitOptions);
|
||||
return transitionAsPromise(() => {
|
||||
Object.assign(this.elems.imageWrapper.style, {
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
});
|
||||
}, this.elems.imageWrapper);
|
||||
}
|
||||
|
||||
appear(image) {
|
||||
this.elems.imageWrapper.removeChild(this.elems.img);
|
||||
this.elems.img = image;
|
||||
this.elems.img.setAttribute('class', 'chocolat-img');
|
||||
this.elems.imageWrapper.appendChild(this.elems.img);
|
||||
const fadeInPromise = transitionAsPromise(() => {
|
||||
this.elems.imageCanvas.classList.add('chocolat-visible');
|
||||
}, this.elems.imageCanvas);
|
||||
return fadeInPromise;
|
||||
}
|
||||
|
||||
change(step) {
|
||||
if (!this.state.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.settings.linkImages) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.zoomOut();
|
||||
const requestedImage = this.settings.currentImageIndex + parseInt(step);
|
||||
|
||||
if (requestedImage > this.settings.lastImageIndex) {
|
||||
if (this.settings.loop) {
|
||||
return this.load(this.settings.firstImageIndex);
|
||||
}
|
||||
} else if (requestedImage < this.settings.firstImageIndex) {
|
||||
if (this.settings.loop) {
|
||||
return this.load(this.settings.lastImageIndex);
|
||||
}
|
||||
} else {
|
||||
return this.load(requestedImage);
|
||||
}
|
||||
}
|
||||
|
||||
arrows() {
|
||||
if (this.settings.loop) {
|
||||
this.elems.left.classList.add('active');
|
||||
this.elems.right.classList.add('active');
|
||||
} else if (this.settings.linkImages) {
|
||||
this.elems.right.classList.toggle('active', this.settings.currentImageIndex !== this.settings.lastImageIndex);
|
||||
this.elems.left.classList.toggle('active', this.settings.currentImageIndex !== this.settings.firstImageIndex);
|
||||
} else {
|
||||
this.elems.left.classList.remove('active');
|
||||
this.elems.right.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.state.fullScreenOpen) {
|
||||
exitFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.visible = false;
|
||||
const promiseOverlay = transitionAsPromise(() => {
|
||||
this.elems.overlay.classList.remove('chocolat-visible');
|
||||
}, this.elems.overlay);
|
||||
const promiseWrapper = transitionAsPromise(() => {
|
||||
this.elems.wrapper.classList.remove('chocolat-visible');
|
||||
}, this.elems.wrapper);
|
||||
return Promise.all([promiseOverlay, promiseWrapper]).then(() => {
|
||||
this.elems.container.classList.remove('chocolat-open');
|
||||
this.settings.afterClose.call(this);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (let i = this.events.length - 1; i >= 0; i--) {
|
||||
const {
|
||||
element,
|
||||
eventName
|
||||
} = this.events[i];
|
||||
this.off(element, eventName);
|
||||
}
|
||||
|
||||
if (!this.state.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.fullScreenOpen) {
|
||||
exitFullScreen();
|
||||
}
|
||||
|
||||
this.settings.currentImageIndex = undefined;
|
||||
this.state.visible = false;
|
||||
this.state.initialized = false;
|
||||
this.elems.container.classList.remove(...this._cssClasses);
|
||||
this.elems.wrapper.parentNode.removeChild(this.elems.wrapper);
|
||||
}
|
||||
|
||||
markup() {
|
||||
this.elems.container.classList.add('chocolat-open', this.settings.className);
|
||||
|
||||
if (this.settings.imageSize == 'cover') {
|
||||
this.elems.container.classList.add('chocolat-cover');
|
||||
}
|
||||
|
||||
if (this.elems.container !== document.body) {
|
||||
this.elems.container.classList.add('chocolat-in-container');
|
||||
}
|
||||
|
||||
this.elems.wrapper = document.createElement('div');
|
||||
this.elems.wrapper.setAttribute('id', 'chocolat-content-' + this.settings.setIndex);
|
||||
this.elems.wrapper.setAttribute('class', 'chocolat-wrapper');
|
||||
this.elems.container.appendChild(this.elems.wrapper);
|
||||
this.elems.overlay = document.createElement('div');
|
||||
this.elems.overlay.setAttribute('class', 'chocolat-overlay');
|
||||
this.elems.wrapper.appendChild(this.elems.overlay);
|
||||
this.elems.loader = document.createElement('div');
|
||||
this.elems.loader.setAttribute('class', 'chocolat-loader');
|
||||
this.elems.wrapper.appendChild(this.elems.loader);
|
||||
this.elems.layout = document.createElement('div');
|
||||
this.elems.layout.setAttribute('class', 'chocolat-layout');
|
||||
this.elems.wrapper.appendChild(this.elems.layout);
|
||||
this.elems.top = document.createElement('div');
|
||||
this.elems.top.setAttribute('class', 'chocolat-top');
|
||||
this.elems.layout.appendChild(this.elems.top);
|
||||
this.elems.center = document.createElement('div');
|
||||
this.elems.center.setAttribute('class', 'chocolat-center');
|
||||
this.elems.layout.appendChild(this.elems.center);
|
||||
this.elems.left = document.createElement('div');
|
||||
this.elems.left.setAttribute('class', 'chocolat-left');
|
||||
this.elems.center.appendChild(this.elems.left);
|
||||
this.elems.imageCanvas = document.createElement('div');
|
||||
this.elems.imageCanvas.setAttribute('class', 'chocolat-image-canvas');
|
||||
this.elems.center.appendChild(this.elems.imageCanvas);
|
||||
this.elems.imageWrapper = document.createElement('div');
|
||||
this.elems.imageWrapper.setAttribute('class', 'chocolat-image-wrapper');
|
||||
this.elems.imageCanvas.appendChild(this.elems.imageWrapper);
|
||||
this.elems.img = document.createElement('img');
|
||||
this.elems.img.setAttribute('class', 'chocolat-img');
|
||||
this.elems.imageWrapper.appendChild(this.elems.img);
|
||||
this.elems.right = document.createElement('div');
|
||||
this.elems.right.setAttribute('class', 'chocolat-right');
|
||||
this.elems.center.appendChild(this.elems.right);
|
||||
this.elems.bottom = document.createElement('div');
|
||||
this.elems.bottom.setAttribute('class', 'chocolat-bottom');
|
||||
this.elems.layout.appendChild(this.elems.bottom);
|
||||
this.elems.close = document.createElement('span');
|
||||
this.elems.close.setAttribute('class', 'chocolat-close');
|
||||
this.elems.top.appendChild(this.elems.close);
|
||||
this.elems.description = document.createElement('span');
|
||||
this.elems.description.setAttribute('class', 'chocolat-description');
|
||||
this.elems.bottom.appendChild(this.elems.description);
|
||||
this.elems.pagination = document.createElement('span');
|
||||
this.elems.pagination.setAttribute('class', 'chocolat-pagination');
|
||||
this.elems.bottom.appendChild(this.elems.pagination);
|
||||
this.elems.setTitle = document.createElement('span');
|
||||
this.elems.setTitle.setAttribute('class', 'chocolat-set-title');
|
||||
this.elems.setTitle.textContent = this.settings.setTitle();
|
||||
this.elems.bottom.appendChild(this.elems.setTitle);
|
||||
this.elems.fullscreen = document.createElement('span');
|
||||
this.elems.fullscreen.setAttribute('class', 'chocolat-fullscreen');
|
||||
this.elems.bottom.appendChild(this.elems.fullscreen);
|
||||
this.settings.afterMarkup.call(this);
|
||||
}
|
||||
|
||||
attachListeners() {
|
||||
this.off(document, 'keydown.chocolat');
|
||||
this.on(document, 'keydown.chocolat', e => {
|
||||
if (this.state.initialized) {
|
||||
if (e.keyCode == 37) {
|
||||
this.change(-1);
|
||||
} else if (e.keyCode == 39) {
|
||||
this.change(1);
|
||||
} else if (e.keyCode == 27) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
const right = this.elems.wrapper.querySelector('.chocolat-right');
|
||||
this.off(right, 'click.chocolat');
|
||||
this.on(right, 'click.chocolat', () => {
|
||||
this.change(+1);
|
||||
});
|
||||
const left = this.elems.wrapper.querySelector('.chocolat-left');
|
||||
this.off(left, 'click.chocolat');
|
||||
this.on(left, 'click.chocolat', () => {
|
||||
this.change(-1);
|
||||
});
|
||||
this.off(this.elems.close, 'click.chocolat');
|
||||
this.on(this.elems.close, 'click.chocolat', this.close.bind(this));
|
||||
this.off(this.elems.fullscreen, 'click.chocolat');
|
||||
this.on(this.elems.fullscreen, 'click.chocolat', () => {
|
||||
if (this.state.fullScreenOpen) {
|
||||
exitFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
openFullScreen(this.elems.wrapper);
|
||||
});
|
||||
this.off(document, 'fullscreenchange.chocolat');
|
||||
this.on(document, 'fullscreenchange.chocolat', () => {
|
||||
if (document.fullscreenElement || document.webkitCurrentFullScreenElement || document.webkitFullscreenElement) {
|
||||
this.state.fullScreenOpen = true;
|
||||
} else {
|
||||
this.state.fullScreenOpen = false;
|
||||
}
|
||||
});
|
||||
this.off(document, 'webkitfullscreenchange.chocolat');
|
||||
this.on(document, 'webkitfullscreenchange.chocolat', () => {
|
||||
if (document.fullscreenElement || document.webkitCurrentFullScreenElement || document.webkitFullscreenElement) {
|
||||
this.state.fullScreenOpen = true;
|
||||
} else {
|
||||
this.state.fullScreenOpen = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (this.settings.closeOnBackgroundClick) {
|
||||
this.off(this.elems.overlay, 'click.chocolat');
|
||||
this.on(this.elems.overlay, 'click.chocolat', this.close.bind(this));
|
||||
}
|
||||
|
||||
this.off(this.elems.wrapper, 'click.chocolat');
|
||||
this.on(this.elems.wrapper, 'click.chocolat', () => {
|
||||
if (this.state.initialZoomState === null || !this.state.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.elems.container.classList.add('chocolat-zooming-out');
|
||||
this.zoomOut().then(() => {
|
||||
this.elems.container.classList.remove('chocolat-zoomed');
|
||||
this.elems.container.classList.remove('chocolat-zooming-out');
|
||||
});
|
||||
});
|
||||
this.off(this.elems.imageWrapper, 'click.chocolat');
|
||||
this.on(this.elems.imageWrapper, 'click.chocolat', e => {
|
||||
if (this.state.initialZoomState === null && this.elems.container.classList.contains('chocolat-zoomable')) {
|
||||
e.stopPropagation();
|
||||
this.elems.container.classList.add('chocolat-zooming-in');
|
||||
this.zoomIn(e).then(() => {
|
||||
this.elems.container.classList.add('chocolat-zoomed');
|
||||
this.elems.container.classList.remove('chocolat-zooming-in');
|
||||
});
|
||||
}
|
||||
});
|
||||
this.on(this.elems.wrapper, 'mousemove.chocolat', e => {
|
||||
if (this.state.initialZoomState === null || !this.state.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = this.elems.wrapper.getBoundingClientRect();
|
||||
const pos = {
|
||||
top: rect.top + window.scrollY,
|
||||
left: rect.left + window.scrollX
|
||||
};
|
||||
const height = this.elems.wrapper.clientHeight;
|
||||
const width = this.elems.wrapper.clientWidth;
|
||||
const imgWidth = this.elems.img.width;
|
||||
const imgHeight = this.elems.img.height;
|
||||
const coord = [e.pageX - width / 2 - pos.left, e.pageY - height / 2 - pos.top];
|
||||
let mvtX = 0;
|
||||
|
||||
if (imgWidth > width) {
|
||||
const paddingX = this.settings.zoomedPaddingX(imgWidth, width);
|
||||
mvtX = coord[0] / (width / 2);
|
||||
mvtX = ((imgWidth - width) / 2 + paddingX) * mvtX;
|
||||
}
|
||||
|
||||
let mvtY = 0;
|
||||
|
||||
if (imgHeight > height) {
|
||||
const paddingY = this.settings.zoomedPaddingY(imgHeight, height);
|
||||
mvtY = coord[1] / (height / 2);
|
||||
mvtY = ((imgHeight - height) / 2 + paddingY) * mvtY;
|
||||
}
|
||||
|
||||
this.elems.img.style.marginLeft = -mvtX + 'px';
|
||||
this.elems.img.style.marginTop = -mvtY + 'px';
|
||||
});
|
||||
this.on(window, 'resize.chocolat', e => {
|
||||
if (!this.state.initialized || !this.state.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
debounce(50, () => {
|
||||
const fitOptions = {
|
||||
imgHeight: this.elems.img.naturalHeight,
|
||||
imgWidth: this.elems.img.naturalWidth,
|
||||
containerHeight: this.elems.wrapper.clientHeight,
|
||||
containerWidth: this.elems.wrapper.clientWidth,
|
||||
canvasWidth: this.elems.imageCanvas.clientWidth,
|
||||
canvasHeight: this.elems.imageCanvas.clientHeight,
|
||||
imageSize: this.settings.imageSize
|
||||
};
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = fit(fitOptions);
|
||||
this.position(this.elems.img).then(() => {
|
||||
this.elems.container.classList.toggle('chocolat-zoomable', this.zoomable(this.elems.img, this.elems.wrapper));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
zoomable(image, wrapper) {
|
||||
const wrapperWidth = wrapper.clientWidth;
|
||||
const wrapperHeight = wrapper.clientHeight;
|
||||
const isImageZoomable = this.settings.allowZoom && (image.naturalWidth > wrapperWidth || image.naturalHeight > wrapperHeight) ? true : false;
|
||||
const isImageStretched = image.clientWidth > image.naturalWidth || image.clientHeight > image.naturalHeight;
|
||||
return isImageZoomable && !isImageStretched;
|
||||
}
|
||||
|
||||
zoomIn(e) {
|
||||
this.state.initialZoomState = this.settings.imageSize;
|
||||
this.settings.imageSize = 'native';
|
||||
return this.position(this.elems.img);
|
||||
}
|
||||
|
||||
zoomOut(e) {
|
||||
this.settings.imageSize = this.state.initialZoomState || this.settings.imageSize;
|
||||
this.state.initialZoomState = null;
|
||||
this.elems.img.style.margin = 0;
|
||||
return this.position(this.elems.img);
|
||||
}
|
||||
|
||||
on(element, eventName, cb) {
|
||||
// const eventName = this.settings.setIndex + '-' + eventName
|
||||
const length = this.events.push({
|
||||
element,
|
||||
eventName,
|
||||
cb
|
||||
});
|
||||
element.addEventListener(eventName.split('.')[0], this.events[length - 1].cb);
|
||||
}
|
||||
|
||||
off(element, eventName) {
|
||||
// const eventName = this.settings.setIndex + '-' + eventName
|
||||
const index = this.events.findIndex(event => {
|
||||
return event.element === element && event.eventName === eventName;
|
||||
});
|
||||
|
||||
if (this.events[index]) {
|
||||
element.removeEventListener(eventName.split('.')[0], this.events[index].cb);
|
||||
this.events.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const instances = [];
|
||||
|
||||
window.Chocolat = function (elements, options) {
|
||||
const settings = Object.assign({}, defaults, {
|
||||
images: []
|
||||
}, options, {
|
||||
setIndex: instances.length
|
||||
});
|
||||
const instance = new Chocolat(elements, settings);
|
||||
instances.push(instance);
|
||||
return instance;
|
||||
};
|
||||
|
||||
}());
|
Loading…
Reference in a new issue