From e309857d2fe1d6947f39d265190a239df39b5a9a Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Mon, 19 Apr 2021 14:31:53 +0200 Subject: [PATCH] Bug 28180: Add Chocolat to OPAC MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Katrin Fischer Signed-off-by: Jonathan Druart --- .../opac-tmpl/lib/Chocolat/css/chocolat.css | 230 ++++++ .../opac-tmpl/lib/Chocolat/images/close.png | Bin 0 -> 1119 bytes .../lib/Chocolat/images/fullscreen-black.png | Bin 0 -> 658 bytes .../lib/Chocolat/images/fullscreen.png | Bin 0 -> 663 bytes .../opac-tmpl/lib/Chocolat/images/left.png | Bin 0 -> 1237 bytes .../opac-tmpl/lib/Chocolat/images/loader.gif | Bin 0 -> 3208 bytes .../opac-tmpl/lib/Chocolat/images/right.png | Bin 0 -> 1224 bytes .../opac-tmpl/lib/Chocolat/js/chocolat.js | 707 ++++++++++++++++++ 8 files changed, 937 insertions(+) create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/css/chocolat.css create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/images/close.png create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen-black.png create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen.png create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/images/left.png create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/images/loader.gif create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/images/right.png create mode 100644 koha-tmpl/opac-tmpl/lib/Chocolat/js/chocolat.js diff --git a/koha-tmpl/opac-tmpl/lib/Chocolat/css/chocolat.css b/koha-tmpl/opac-tmpl/lib/Chocolat/css/chocolat.css new file mode 100644 index 0000000000..e5ad53cd1a --- /dev/null +++ b/koha-tmpl/opac-tmpl/lib/Chocolat/css/chocolat.css @@ -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; +} +*/ diff --git a/koha-tmpl/opac-tmpl/lib/Chocolat/images/close.png b/koha-tmpl/opac-tmpl/lib/Chocolat/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..87631d1e68275986eff00bc4193f18ed81444a9b GIT binary patch literal 1119 zcmV-l1fctgP)KLZ*U+0v@6o#Kk zBy~|skq9maHzpVb!A%szeAFnH;fC8TlHlg{&NR8$`@v*xZFhqH4E+Z#x^m|~@B=Bh z_D6`z#6^szN*C?3dS~Xmobz%9%J006+AeKEk~$mo+Hx=)$+gd{Be=?au7%MwSKE(w z06f3j3Vfybi-4cs5CA8)JipuGe5^;gbs(z16K$puh-bj77cO@oz5`Xe6LdiQ05--e zdUr+J!EgknOF(tJ(ARjGpDr!D|DP|)O230GU?V`^?9Mg`TI*47*Ot|)F34wi|x6(8{ z6_MexaS<7&X?p50<-XgA%Cgo@;y4}^fr!X3j^mNFcH&bML#>9Dl_bfDF=m_xaRA1c zagrn_9#ifFz?&uMyHwdl0crvO%md(U-CLy~V*q2!s0P4YlA6=5=8-z0RgPoijzdJK zu^VZm`MX>9KDb3iq%O!T^znjaQLrp(D(EWEJn_mWSy^i*%f41bhJ(T2eH2CSDo**) z*}K;*rS5CsgDYU)TZ)YHY!&^XuDD6vsQJBvxK&%;q|RJFlFbh@H5$DJ0H)b1kWW&- z)M;KLZ*U+0v@6o#Kk zBy~|skq9maHzpVb!A%szeAFnH;fC8TlHlg{&NR8$`@v*xZFhqH4E+Z#x^m|~@B=Bh z_D6`z#6^szN*C?3dS~Xmobz%9%J006+AeKEk~$mo+Hx=)$+gd{Be=?au7%MwSKE(w z06f3j3Vfybi-4cs5CA8)JipuGe5^;gbs(z16K$puh-bj77cO@oz5`Xe6LdiQ05--e zdUr+J!EgknOF(tJ(ARjGpDr!D|DP|)O23rd6951J literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen.png b/koha-tmpl/opac-tmpl/lib/Chocolat/images/fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..7be7be9be8ac4f4c30acb77f654057ead2deffad GIT binary patch literal 663 zcmV;I0%-k-P)KLZ*U+0v@6o#Kk zBy~|skq9maHzpVb!A%szeAFnH;fC8TlHlg{&NR8$`@v*xZFhqH4E+Z#x^m|~@B=Bh z_D6`z#6^szN*C?3dS~Xmobz%9%J006+AeKEk~$mo+Hx=)$+gd{Be=?au7%MwSKE(w z06f3j3Vfybi-4cs5CA8)JipuGe5^;gbs(z16K$puh-bj77cO@oz5`Xe6LdiQ05--e zdUr+J!EgknOF(tJ(ARjGpDr!D|DP|)O23(&4O002ovPDHLkV1jM|ACCY4 literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/lib/Chocolat/images/left.png b/koha-tmpl/opac-tmpl/lib/Chocolat/images/left.png new file mode 100644 index 0000000000000000000000000000000000000000..185f77bd1c596a0c88dbfa1f7af82b7a077fdf9c GIT binary patch literal 1237 zcmV;`1SKLZ*U+0v@6o#Kk zBy~|skq9maHzpVb!A%szeAFnH;fC8TlHlg{&NR8$`@v*xZFhqH4E+Z#x^m|~@B=Bh z_D6`z#6^szN*C?3dS~Xmobz%9%J006+AeKEk~$mo+Hx=)$+gd{Be=?au7%MwSKE(w z06f3j3Vfybi-4cs5CA8)JipuGe5^;gbs(z16K$puh-bj77cO@oz5`Xe6LdiQ05--e zdUr+J!EgknOF(tJ(ARjGpDr!D|DP|)O23EfCXR) zn9%`z0vrMrpfw7F18@S<46jq*0I2DOFacMAMT}Dg_y)AQADDXtn}O@VMU0gU%!wUo z_k%DA06XWS8F9I*{UENL!w&QUv4{-%55fUlVLXz7KO&`(I?~X|c;!^TuR<)0n1;h> zTFpKcVu{Jj+-~=lftPd21l-mVsVK^})oNYV^Y&)5S+iIy_Xbi3Lt-8jn8{?G1cSk6 z`}_N^c6WE*YJsR$t91=wXH&uv4TK1VLLcmQ`-8Q$wN0? z(U8XgQ0cq^-9_rBaN6hTvVH?!2F)F{VU3Ok{Q^FBkA=M;+F~xS7<@Dppww$F?bkDo zge{-b(pv^(!PkCeK*`aOO|`};l05I0veErPzymNWmO?SfPgFlg5pAq3huH91c9)G1Y`|i z4JZjDhG<1Z5!{L(f?6D`8``H2sCB`;j(xVT^L)Wphk5_LnZEPqnRDmP=X1Wl@66!` z`n$Ttvj7(G763kc_y7RFrAwCz3JSWqx*8f9xLj^@boA)x=);E(&z?OyXU-f5f{bVW zSl0ix;3aK}PuV15r6r~$u;RDIr*GdCFLF%Wxp^00{VN2}j0dehpey_$SMt2W{1!QK zKojHE!m014ehNU3O@{&#81Ry?oX#6DZ$$v0J3e>A35z_WBvJ<_#BKo;WU| zlhe}qUa=5u3mwW&3lJ7s?M1x36dY=FTw|QvgGz$IR&C=!53NBJpfA=OKGM`_RmbT% znZm9NNG{r+6zds~vIJC01Jq2Sfd~xI=Y0{MfaQy zn2ZzlCpfx2_e$RKF6Y3;lDC^Ctng6>y!>|IX5edIqlS+PO-?8+ z`B&W3L?HdjNFjrNI!Jn^_xX`vLz8IS;`MxK?2dHilQLyLW(Kk1FgksCojERsJ!?iEkw+`1cDYq6akXxle%?Jr<{{=0nz`Kk-S^@n0J8?VXMIkDd80qP5Zm)#`}B9q`aYD-x25 zc@QMAn3TmSh+$G`MJqYrrZlSzXqzXwkxe}q+R{=~MXl6{TMe0tZ;lxDwHaEwS~Tn) z%Z4-bbN=m#CC+_Hj=V@B(_K9qdqPDt^t)b6FaB0hLKPppyu1i6y5o8OFfai$3|@Hf z;}f9$JoCBho5!)9?In}=Wi7?^t?W>oEX>UIsE7wEM6JuV|urBCNX|~_fosA>efw^cee6+8#zdilg;yU=9%o2Tr8vKU(UXB z3kWh_IQ#Dlz2mDX28*Vsv~^2N0@-2rA5dndqT#a_FD7Mja*;&mNGTuQl4hBa#RlbU zyNJdn0&7;IFttot;-xlVx#2#Rt0hHS8Yc?$hTuI$Ax^85FTg>Ou?^asn^v zc4TamL;dN)1SL|wK5J+}IUv2WZek)s&{URu5`W(KbZO#xJ-h7I%bmY@-Nh&FUD-3b zWYh3hA$_f%(+^E&|9Jfl`pIECdq1scZFL2~(DjE!P`xQick6HdB~DW0RW%CJs%Egc z5*vQ&0+H<+8=2yLP{*8J|AcQU5HKERhC^Yc8+NlT`wE?W{KMilM$MR*u`F^Vg|y0P zH$vvm4^8ofIt;5X%DqHWn*2F7FBENb*Qjev#6oN7p$rX0Wr+o zs`8{oPY+ryQp?#Sq!&YSG)vgY_Gs^!%G7))-)}L!8*2e#qa^10fs}hSj~-QC@-4P~ z6qFe9!gDNk%%gbp7$K<>c~-GPNqH$TKYQ-6`*N1g%+J>kPgn4EssJL|j0Ip5#AY?s zRM6Erzwp(Dilg}V_^V)%qWGU*#U9ns-X-MKYl| zwFePZV^uR!FKtm8+&~Gt)DlKfaDSp(XD8Bx>sdSsxd$cN6#7_!m=A>Xob*j5%IRbb zL+IeOburN9EZZ>Z9V|2W!Ll&m3Wh3Gp-TYt&PcD{jknNG3RUzoTSoVzE3-^Q04Zo> zo;@!8+wSODeZ97yngE&Z;n_3~QezZYX6lH()hmh|!W>Kvk9*v*4a;;;uE^_s5$88j z@v}80$2lr=(S2WP{rV(s;4ea&y7i}<7XxY=T&X^_9@OJUZ0sn8#??REOF5?yT1o`- zcy532%O{1)9c9x=V!U)kdGqd6mgst zjK)D-dV{YE!y_F;(H;WUcZBDP7GSpl>Q%HuunND8;a5kUr6+R98O-cNL&bM=ik$%oZJ^bN~{`Ou$DNS@CB>aXDEiy1~>dAVzrxJXf|%q~{3 zV+sT$5OlN3ch~51Ia#f2Dy#?LDRKz$p>(uvXKchk3lKrb!5U$BE`ni$=yiZPfK&CDbpRi{y#a8x>Lvn-cH8Z2YFcxCWPvAg{g4_(vBgWOcI!oCDiIr*tgFD z0>S>ZbG=}lo*<*B9x-NM2+WPPzk!bHFPppF5E{UBX{72*x15C{|HfBzB=y)?!u4((=0EgFLA_ z6`T@*qVPu%h`}%=g4~IcPci+B9@-2D7oZGStf5opdO-$lH-c!vJHV>+`Sv#v^E=-M zy2;5mj{xJ#ck$qxWMVRMnc%^tr=x`E2j(mK&uiab@cCNZ3*; z{}ciWc1dFPu?S2#l*O}QL#Hy~RyUEaitnx6%8J5aG?N#&&2ooOFi*BoP^rKruGE6e zcty2q{Z3UiqprS6E6a4e(ctyDh^*`q;E_{?+fE^2WEl1@`Khci${^T>BfB-uBvB zWRm+Rso1^=^H?Vo|byTTbgxVWRzkrjj8ud(@m}8ax_s zY?YdiajB#$UkG9tIz0b*bBDr_s}UX3GqXvExGLdpADx_i0KLZ*U+0v@6o#Kk zBy~|skq9maHzpVb!A%szeAFnH;fC8TlHlg{&NR8$`@v*xZFhqH4E+Z#x^m|~@B=Bh z_D6`z#6^szN*C?3dS~Xmobz%9%J006+AeKEk~$mo+Hx=)$+gd{Be=?au7%MwSKE(w z06f3j3Vfybi-4cs5CA8)JipuGe5^;gbs(z16K$puh-bj77cO@oz5`Xe6LdiQ05--e zdUr+J!EgknOF(tJ(ARjGpDr!D|DP|)O23o|CPYF= zf)L>$=rVZ-R6KPGgwBH3Ja~{U9SULDQ&5n{(2%)wFbRUdV~3C~tu0voKXqknOfD6C|(^C`p1oVeLaEw^&04@P%n64CHRh&qF z9E2nQPB9xTiO2nQ9E4{ECol?RK~&H`5Np7B?2!VrMM)zMl%X4Y<<@^+hgi2*hLdPf zuRa}OllkbZ4zXgg6aIa%d3K2*g;})gU>ANnM%h0t& zqtTmQulG)&Pss;Z_nO}iJ1#oibVKeFDxt*Tfo-cVIFT`rgJ zn*adl$Y!$O|CGz+u0$94B>Kcv6qae;Wt{o=vd*gC6gu z_AX4S3%u72?DL!qY;Vv-zlc<9rw&E<{R43$6N*KY*Q+=8l%tpfg6_fseyu;V6i{mvD4okxZp;4@zXt$Vo1tE0yQLWb0000 { + 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; + }; + +}()); -- 2.39.5