diff --git a/files/pswp/default-skin/default-skin.css b/files/pswp/default-skin/default-skin.css
index a4676cc..5b1d563 100644
--- a/files/pswp/default-skin/default-skin.css
+++ b/files/pswp/default-skin/default-skin.css
@@ -35,8 +35,7 @@
transition: opacity 0.2s;
-webkit-box-shadow: none;
box-shadow: none; }
- .pswp__button:focus,
- .pswp__button:hover {
+ .pswp__button:focus, .pswp__button:hover {
opacity: 1; }
.pswp__button:active {
outline: none;
@@ -272,14 +271,13 @@ a.pswp__share--download:hover {
color: #BBB; }
.pswp__caption__center {
- text-align: center;
+ text-align: left;
max-width: 420px;
margin: 0 auto;
font-size: 13px;
padding: 10px;
line-height: 20px;
- color: #CCC;
- font-weight: bold; }
+ color: #CCC; }
.pswp__caption--empty {
display: none; }
@@ -481,4 +479,4 @@ a.pswp__share--download:hover {
display: none !important; }
.pswp--minimal--dark .pswp__top-bar {
- background: none; }
+ background: none; }
\ No newline at end of file
diff --git a/files/pswp/default-skin/default-skin.svg b/files/pswp/default-skin/default-skin.svg
index e235a2e..9d5f0c6 100644
--- a/files/pswp/default-skin/default-skin.svg
+++ b/files/pswp/default-skin/default-skin.svg
@@ -1,131 +1 @@
-
-
+
\ No newline at end of file
diff --git a/files/pswp/photoswipe-ui-default.min.js b/files/pswp/photoswipe-ui-default.min.js
index 67e3604..ab7bedc 100644
--- a/files/pswp/photoswipe-ui-default.min.js
+++ b/files/pswp/photoswipe-ui-default.min.js
@@ -1,4 +1,4 @@
-/*! PhotoSwipe Default UI - 4.1.1 - 2015-12-24
+/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08
* http://photoswipe.com
-* Copyright (c) 2015 Dmitry Semenov; */
-!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.PhotoSwipeUI_Default=b()}(this,function(){"use strict";var a=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=this,w=!1,x=!0,y=!0,z={barsSize:{top:44,bottom:"auto"},closeElClasses:["item","caption","zoom-wrap","ui","top-bar"],timeToIdle:4e3,timeToIdleOutside:1e3,loadingIndicatorDelay:1e3,addCaptionHTMLFn:function(a,b){return a.title?(b.children[0].innerHTML=a.title,!0):(b.children[0].innerHTML="",!1)},closeEl:!0,captionEl:!0,fullscreenEl:!0,zoomEl:!0,shareEl:!0,counterEl:!0,arrowEl:!0,preloaderEl:!0,tapToClose:!1,tapToToggleControls:!0,clickToCloseNonZoomable:!0,shareButtons:[{id:"download",label:"Download image",url:"{{raw_image_url}}",download:!0}],getImageURLForShare:function(){return a.currItem.src||""},getPageURLForShare:function(){return window.location.href},getTextForShare:function(){return a.currItem.title||""},indexIndicatorSep:" / ",fitControlsWidth:1200},A=function(a){if(r)return!0;a=a||window.event,q.timeToIdle&&q.mouseUsed&&!k&&K();for(var c,d,e=a.target||a.srcElement,f=e.getAttribute("class")||"",g=0;g-1&&(c.onTap(),d=!0);if(d){a.stopPropagation&&a.stopPropagation(),r=!0;var h=b.features.isOldAndroid?600:30;s=setTimeout(function(){r=!1},h)}},B=function(){return!a.likelyTouchDevice||q.mouseUsed||screen.width>q.fitControlsWidth},C=function(a,c,d){b[(d?"add":"remove")+"Class"](a,"pswp__"+c)},D=function(){var a=1===q.getNumItemsFn();a!==p&&(C(d,"ui--one-slide",a),p=a)},E=function(){C(i,"share-modal--hidden",y)},F=function(){return y=!y,y?(b.removeClass(i,"pswp__share-modal--fade-in"),setTimeout(function(){y&&E()},300)):(E(),setTimeout(function(){y||b.addClass(i,"pswp__share-modal--fade-in")},30)),y||H(),!1},G=function(b){b=b||window.event;var c=b.target||b.srcElement;return a.shout("shareLinkClick",b,c),c.href?c.hasAttribute("download")?!0:(window.open(c.href,"pswp_share","scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,top=100,left="+(window.screen?Math.round(screen.width/2-275):100)),y||F(),!1):!1},H=function(){for(var a,b,c,d,e,f="",g=0;g"+a.label+"",q.parseShareButtonOut&&(f=q.parseShareButtonOut(a,f));i.children[0].innerHTML=f,i.children[0].onclick=G},I=function(a){for(var c=0;ca?v.hideControls():!x&&a>=.95&&v.showControls()});var a;l("onPinchClose",function(b){x&&.9>b?(v.hideControls(),a=!0):a&&!x&&b>.9&&v.showControls()}),l("zoomGestureEnded",function(){a=!1,a&&!x&&v.showControls()})},S=[{name:"caption",option:"captionEl",onInit:function(a){e=a}},{name:"share-modal",option:"shareEl",onInit:function(a){i=a},onTap:function(){F()}},{name:"button--share",option:"shareEl",onInit:function(a){h=a},onTap:function(){F()}},{name:"button--zoom",option:"zoomEl",onTap:a.toggleDesktopZoom},{name:"counter",option:"counterEl",onInit:function(a){g=a}},{name:"button--close",option:"closeEl",onTap:a.close},{name:"button--arrow--left",option:"arrowEl",onTap:a.prev},{name:"button--arrow--right",option:"arrowEl",onTap:a.next},{name:"button--fs",option:"fullscreenEl",onTap:function(){c.isFullscreen()?c.exit():c.enter()}},{name:"preloader",option:"preloaderEl",onInit:function(a){m=a}}],T=function(){var a,c,e,f=function(d){if(d)for(var f=d.length,g=0;f>g;g++){a=d[g],c=a.className;for(var h=0;h-1&&(q[e.option]?(b.removeClass(a,"pswp__element--disabled"),e.onInit&&e.onInit(a)):b.addClass(a,"pswp__element--disabled"))}};f(d.children);var g=b.getChildByClass(d,"pswp__top-bar");g&&f(g.children)};v.init=function(){b.extend(a.options,z,!0),q=a.options,d=b.getChildByClass(a.scrollWrap,"pswp__ui"),l=a.listen,R(),l("beforeChange",v.update),l("doubleTap",function(b){var c=a.currItem.initialZoomLevel;a.getZoomLevel()!==c?a.zoomTo(c,b,333):a.zoomTo(q.getDoubleTapZoom(!1,a.currItem),b,333)}),l("preventDragEvent",function(a,b,c){var d=a.target||a.srcElement;d&&d.getAttribute("class")&&a.type.indexOf("mouse")>-1&&(d.getAttribute("class").indexOf("__caption")>0||/(SMALL|STRONG|EM)/i.test(d.tagName))&&(c.prevent=!1)}),l("bindEvents",function(){b.bind(d,"pswpTap click",A),b.bind(a.scrollWrap,"pswpTap",v.onGlobalTap),a.likelyTouchDevice||b.bind(a.scrollWrap,"mouseover",v.onMouseOver)}),l("unbindEvents",function(){y||F(),t&&clearInterval(t),b.unbind(document,"mouseout",L),b.unbind(document,"mousemove",K),b.unbind(d,"pswpTap click",A),b.unbind(a.scrollWrap,"pswpTap",v.onGlobalTap),b.unbind(a.scrollWrap,"mouseover",v.onMouseOver),c&&(b.unbind(document,c.eventK,v.updateFullscreen),c.isFullscreen()&&(q.hideAnimationDuration=0,c.exit()),c=null)}),l("destroy",function(){q.captionEl&&(f&&d.removeChild(f),b.removeClass(e,"pswp__caption--empty")),i&&(i.children[0].onclick=null),b.removeClass(d,"pswp__ui--over-close"),b.addClass(d,"pswp__ui--hidden"),v.setIdle(!1)}),q.showAnimationDuration||b.removeClass(d,"pswp__ui--hidden"),l("initialZoomIn",function(){q.showAnimationDuration&&b.removeClass(d,"pswp__ui--hidden")}),l("initialZoomOut",function(){b.addClass(d,"pswp__ui--hidden")}),l("parseVerticalMargin",P),T(),q.shareEl&&h&&i&&(y=!0),D(),Q(),M(),N()},v.setIdle=function(a){k=a,C(d,"ui--idle",a)},v.update=function(){x&&a.currItem?(v.updateIndexIndicator(),q.captionEl&&(q.addCaptionHTMLFn(a.currItem,e),C(e,"caption--empty",!a.currItem.title)),w=!0):w=!1,y||F(),D()},v.updateFullscreen=function(d){d&&setTimeout(function(){a.setScrollOffset(0,b.getScrollY())},50),b[(c.isFullscreen()?"add":"remove")+"Class"](a.template,"pswp--fs")},v.updateIndexIndicator=function(){q.counterEl&&(g.innerHTML=a.getCurrentIndex()+1+q.indexIndicatorSep+q.getNumItemsFn())},v.onGlobalTap=function(c){c=c||window.event;var d=c.target||c.srcElement;if(!r)if(c.detail&&"mouse"===c.detail.pointerType){if(I(d))return void a.close();b.hasClass(d,"pswp__img")&&(1===a.getZoomLevel()&&a.getZoomLevel()<=a.currItem.fitRatio?q.clickToCloseNonZoomable&&a.close():a.toggleDesktopZoom(c.detail.releasePoint))}else if(q.tapToToggleControls&&(x?v.hideControls():v.showControls()),q.tapToClose&&(b.hasClass(d,"pswp__img")||I(d)))return void a.close()},v.onMouseOver=function(a){a=a||window.event;var b=a.target||a.srcElement;C(d,"ui--over-close",I(b))},v.hideControls=function(){b.addClass(d,"pswp__ui--hidden"),x=!1},v.showControls=function(){x=!0,w||v.update(),b.removeClass(d,"pswp__ui--hidden")},v.supportsFullscreen=function(){var a=document;return!!(a.exitFullscreen||a.mozCancelFullScreen||a.webkitExitFullscreen||a.msExitFullscreen)},v.getFullscreenAPI=function(){var b,c=document.documentElement,d="fullscreenchange";return c.requestFullscreen?b={enterK:"requestFullscreen",exitK:"exitFullscreen",elementK:"fullscreenElement",eventK:d}:c.mozRequestFullScreen?b={enterK:"mozRequestFullScreen",exitK:"mozCancelFullScreen",elementK:"mozFullScreenElement",eventK:"moz"+d}:c.webkitRequestFullscreen?b={enterK:"webkitRequestFullscreen",exitK:"webkitExitFullscreen",elementK:"webkitFullscreenElement",eventK:"webkit"+d}:c.msRequestFullscreen&&(b={enterK:"msRequestFullscreen",exitK:"msExitFullscreen",elementK:"msFullscreenElement",eventK:"MSFullscreenChange"}),b&&(b.enter=function(){return j=q.closeOnScroll,q.closeOnScroll=!1,"webkitRequestFullscreen"!==this.enterK?a.template[this.enterK]():void a.template[this.enterK](Element.ALLOW_KEYBOARD_INPUT)},b.exit=function(){return q.closeOnScroll=j,document[this.exitK]()},b.isFullscreen=function(){return document[this.elementK]}),b}};return a});
+* Copyright (c) 2019 Dmitry Semenov; */
+!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.PhotoSwipeUI_Default=b()}(this,function(){"use strict";var a=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=this,w=!1,x=!0,y=!0,z={barsSize:{top:44,bottom:"auto"},closeElClasses:["item","caption","zoom-wrap","ui","top-bar"],timeToIdle:4e3,timeToIdleOutside:1e3,loadingIndicatorDelay:1e3,addCaptionHTMLFn:function(a,b){return a.title?(b.children[0].innerHTML=a.title,!0):(b.children[0].innerHTML="",!1)},closeEl:!0,captionEl:!0,fullscreenEl:!0,zoomEl:!0,shareEl:!0,counterEl:!0,arrowEl:!0,preloaderEl:!0,tapToClose:!1,tapToToggleControls:!0,clickToCloseNonZoomable:!0,shareButtons:[{id:"facebook",label:"Share on Facebook",url:"https://www.facebook.com/sharer/sharer.php?u={{url}}"},{id:"twitter",label:"Tweet",url:"https://twitter.com/intent/tweet?text={{text}}&url={{url}}"},{id:"pinterest",label:"Pin it",url:"http://www.pinterest.com/pin/create/button/?url={{url}}&media={{image_url}}&description={{text}}"},{id:"download",label:"Download image",url:"{{raw_image_url}}",download:!0}],getImageURLForShare:function(){return a.currItem.src||""},getPageURLForShare:function(){return window.location.href},getTextForShare:function(){return a.currItem.title||""},indexIndicatorSep:" / ",fitControlsWidth:1200},A=function(a){if(r)return!0;a=a||window.event,q.timeToIdle&&q.mouseUsed&&!k&&K();for(var c,d,e=a.target||a.srcElement,f=e.getAttribute("class")||"",g=0;g-1&&(c.onTap(),d=!0);if(d){a.stopPropagation&&a.stopPropagation(),r=!0;var h=b.features.isOldAndroid?600:30;s=setTimeout(function(){r=!1},h)}},B=function(){return!a.likelyTouchDevice||q.mouseUsed||screen.width>q.fitControlsWidth},C=function(a,c,d){b[(d?"add":"remove")+"Class"](a,"pswp__"+c)},D=function(){var a=1===q.getNumItemsFn();a!==p&&(C(d,"ui--one-slide",a),p=a)},E=function(){C(i,"share-modal--hidden",y)},F=function(){return y=!y,y?(b.removeClass(i,"pswp__share-modal--fade-in"),setTimeout(function(){y&&E()},300)):(E(),setTimeout(function(){y||b.addClass(i,"pswp__share-modal--fade-in")},30)),y||H(),!1},G=function(b){b=b||window.event;var c=b.target||b.srcElement;return a.shout("shareLinkClick",b,c),!!c.href&&(!!c.hasAttribute("download")||(window.open(c.href,"pswp_share","scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,top=100,left="+(window.screen?Math.round(screen.width/2-275):100)),y||F(),!1))},H=function(){for(var a,b,c,d,e,f="",g=0;g"+a.label+"",q.parseShareButtonOut&&(f=q.parseShareButtonOut(a,f));i.children[0].innerHTML=f,i.children[0].onclick=G},I=function(a){for(var c=0;c=.95&&v.showControls()});var a;l("onPinchClose",function(b){x&&b<.9?(v.hideControls(),a=!0):a&&!x&&b>.9&&v.showControls()}),l("zoomGestureEnded",function(){a=!1,a&&!x&&v.showControls()})},S=[{name:"caption",option:"captionEl",onInit:function(a){e=a}},{name:"share-modal",option:"shareEl",onInit:function(a){i=a},onTap:function(){F()}},{name:"button--share",option:"shareEl",onInit:function(a){h=a},onTap:function(){F()}},{name:"button--zoom",option:"zoomEl",onTap:a.toggleDesktopZoom},{name:"counter",option:"counterEl",onInit:function(a){g=a}},{name:"button--close",option:"closeEl",onTap:a.close},{name:"button--arrow--left",option:"arrowEl",onTap:a.prev},{name:"button--arrow--right",option:"arrowEl",onTap:a.next},{name:"button--fs",option:"fullscreenEl",onTap:function(){c.isFullscreen()?c.exit():c.enter()}},{name:"preloader",option:"preloaderEl",onInit:function(a){m=a}}],T=function(){var a,c,e,f=function(d){if(d)for(var f=d.length,g=0;g-1&&(q[e.option]?(b.removeClass(a,"pswp__element--disabled"),e.onInit&&e.onInit(a)):b.addClass(a,"pswp__element--disabled"))}};f(d.children);var g=b.getChildByClass(d,"pswp__top-bar");g&&f(g.children)};v.init=function(){b.extend(a.options,z,!0),q=a.options,d=b.getChildByClass(a.scrollWrap,"pswp__ui"),l=a.listen,R(),l("beforeChange",v.update),l("doubleTap",function(b){var c=a.currItem.initialZoomLevel;a.getZoomLevel()!==c?a.zoomTo(c,b,333):a.zoomTo(q.getDoubleTapZoom(!1,a.currItem),b,333)}),l("preventDragEvent",function(a,b,c){var d=a.target||a.srcElement;d&&d.getAttribute("class")&&a.type.indexOf("mouse")>-1&&(d.getAttribute("class").indexOf("__caption")>0||/(SMALL|STRONG|EM)/i.test(d.tagName))&&(c.prevent=!1)}),l("bindEvents",function(){b.bind(d,"pswpTap click",A),b.bind(a.scrollWrap,"pswpTap",v.onGlobalTap),a.likelyTouchDevice||b.bind(a.scrollWrap,"mouseover",v.onMouseOver)}),l("unbindEvents",function(){y||F(),t&&clearInterval(t),b.unbind(document,"mouseout",L),b.unbind(document,"mousemove",K),b.unbind(d,"pswpTap click",A),b.unbind(a.scrollWrap,"pswpTap",v.onGlobalTap),b.unbind(a.scrollWrap,"mouseover",v.onMouseOver),c&&(b.unbind(document,c.eventK,v.updateFullscreen),c.isFullscreen()&&(q.hideAnimationDuration=0,c.exit()),c=null)}),l("destroy",function(){q.captionEl&&(f&&d.removeChild(f),b.removeClass(e,"pswp__caption--empty")),i&&(i.children[0].onclick=null),b.removeClass(d,"pswp__ui--over-close"),b.addClass(d,"pswp__ui--hidden"),v.setIdle(!1)}),q.showAnimationDuration||b.removeClass(d,"pswp__ui--hidden"),l("initialZoomIn",function(){q.showAnimationDuration&&b.removeClass(d,"pswp__ui--hidden")}),l("initialZoomOut",function(){b.addClass(d,"pswp__ui--hidden")}),l("parseVerticalMargin",P),T(),q.shareEl&&h&&i&&(y=!0),D(),Q(),M(),N()},v.setIdle=function(a){k=a,C(d,"ui--idle",a)},v.update=function(){x&&a.currItem?(v.updateIndexIndicator(),q.captionEl&&(q.addCaptionHTMLFn(a.currItem,e),C(e,"caption--empty",!a.currItem.title)),w=!0):w=!1,y||F(),D()},v.updateFullscreen=function(d){d&&setTimeout(function(){a.setScrollOffset(0,b.getScrollY())},50),b[(c.isFullscreen()?"add":"remove")+"Class"](a.template,"pswp--fs")},v.updateIndexIndicator=function(){q.counterEl&&(g.innerHTML=a.getCurrentIndex()+1+q.indexIndicatorSep+q.getNumItemsFn())},v.onGlobalTap=function(c){c=c||window.event;var d=c.target||c.srcElement;if(!r)if(c.detail&&"mouse"===c.detail.pointerType){if(I(d))return void a.close();b.hasClass(d,"pswp__img")&&(1===a.getZoomLevel()&&a.getZoomLevel()<=a.currItem.fitRatio?q.clickToCloseNonZoomable&&a.close():a.toggleDesktopZoom(c.detail.releasePoint))}else if(q.tapToToggleControls&&(x?v.hideControls():v.showControls()),q.tapToClose&&(b.hasClass(d,"pswp__img")||I(d)))return void a.close()},v.onMouseOver=function(a){a=a||window.event;var b=a.target||a.srcElement;C(d,"ui--over-close",I(b))},v.hideControls=function(){b.addClass(d,"pswp__ui--hidden"),x=!1},v.showControls=function(){x=!0,w||v.update(),b.removeClass(d,"pswp__ui--hidden")},v.supportsFullscreen=function(){var a=document;return!!(a.exitFullscreen||a.mozCancelFullScreen||a.webkitExitFullscreen||a.msExitFullscreen)},v.getFullscreenAPI=function(){var b,c=document.documentElement,d="fullscreenchange";return c.requestFullscreen?b={enterK:"requestFullscreen",exitK:"exitFullscreen",elementK:"fullscreenElement",eventK:d}:c.mozRequestFullScreen?b={enterK:"mozRequestFullScreen",exitK:"mozCancelFullScreen",elementK:"mozFullScreenElement",eventK:"moz"+d}:c.webkitRequestFullscreen?b={enterK:"webkitRequestFullscreen",exitK:"webkitExitFullscreen",elementK:"webkitFullscreenElement",eventK:"webkit"+d}:c.msRequestFullscreen&&(b={enterK:"msRequestFullscreen",exitK:"msExitFullscreen",elementK:"msFullscreenElement",eventK:"MSFullscreenChange"}),b&&(b.enter=function(){return j=q.closeOnScroll,q.closeOnScroll=!1,"webkitRequestFullscreen"!==this.enterK?a.template[this.enterK]():void a.template[this.enterK](Element.ALLOW_KEYBOARD_INPUT)},b.exit=function(){return q.closeOnScroll=j,document[this.exitK]()},b.isFullscreen=function(){return document[this.elementK]}),b}};return a});
\ No newline at end of file
diff --git a/files/pswp/photoswipe.css b/files/pswp/photoswipe.css
index e83dc3e..2b85cbf 100644
--- a/files/pswp/photoswipe.css
+++ b/files/pswp/photoswipe.css
@@ -15,21 +15,14 @@
touch-action: none;
z-index: 1500;
-webkit-text-size-adjust: 100%;
- text-size-adjust: 100%;
/* create separate layer, to avoid paint on window.onscroll in webkit/blink */
-webkit-backface-visibility: hidden;
- backface-visibility: hidden;
- outline: none;
-}
-
-.pswp * {
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
-}
-
-.pswp img {
- max-width: none;
-}
+ outline: none; }
+ .pswp * {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box; }
+ .pswp img {
+ max-width: none; }
/* style is added when JS option showHideOpacity is set to true */
.pswp--animate_opacity {
@@ -38,33 +31,28 @@
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
- transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
-}
+ transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp--open {
- display: block;
-}
+ display: block; }
.pswp--zoom-allowed .pswp__img {
/* autoprefixer: off */
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
- cursor: zoom-in;
-}
+ cursor: zoom-in; }
.pswp--zoomed-in .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grab;
cursor: -moz-grab;
- cursor: grab;
-}
+ cursor: grab; }
.pswp--dragging .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
- cursor: grabbing;
-}
+ cursor: grabbing; }
/*
Background is added as a separate element.
@@ -78,10 +66,10 @@
height: 100%;
background: #000;
opacity: 0;
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
-webkit-backface-visibility: hidden;
- backface-visibility: hidden;
- will-change: opacity;
-}
+ will-change: opacity; }
.pswp__scroll-wrap {
position: absolute;
@@ -89,8 +77,7 @@
top: 0;
width: 100%;
height: 100%;
- overflow: hidden;
-}
+ overflow: hidden; }
.pswp__container,
.pswp__zoom-wrap {
@@ -100,8 +87,7 @@
left: 0;
right: 0;
top: 0;
- bottom: 0;
-}
+ bottom: 0; }
/* Prevent selection and tap highlights */
.pswp__container,
@@ -109,10 +95,9 @@
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
- user-select: none;
+ user-select: none;
-webkit-tap-highlight-color: transparent;
- -webkit-touch-callout: none;
-}
+ -webkit-touch-callout: none; }
.pswp__zoom-wrap {
position: absolute;
@@ -122,27 +107,22 @@
transform-origin: left top;
/* for open/close transition */
-webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
- transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
-}
+ transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp__bg {
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
- transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
-}
+ transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp--animated-in .pswp__bg,
.pswp--animated-in .pswp__zoom-wrap {
-webkit-transition: none;
- transition: none;
-}
+ transition: none; }
.pswp__container,
.pswp__zoom-wrap {
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden;
-}
+ -webkit-backface-visibility: hidden; }
.pswp__item {
position: absolute;
@@ -150,40 +130,34 @@
right: 0;
top: 0;
bottom: 0;
- overflow: hidden;
-}
+ overflow: hidden; }
.pswp__img {
position: absolute;
width: auto;
height: auto;
top: 0;
- left: 0;
-}
+ left: 0; }
/*
stretched thumbnail or div placeholder element (see below)
style is added to avoid flickering in webkit/blink when layers overlap
*/
.pswp__img--placeholder {
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden;
-}
+ -webkit-backface-visibility: hidden; }
/*
div element that matches size of large image
large image loads on top of it
*/
.pswp__img--placeholder--blank {
- background: #222;
-}
+ background: #222; }
.pswp--ie .pswp__img {
width: 100% !important;
height: auto !important;
left: 0;
- top: 0;
-}
+ top: 0; }
/*
Error message appears when image is not loaded
@@ -198,10 +172,8 @@
font-size: 14px;
line-height: 16px;
margin-top: -8px;
- color: #CCC;
-}
+ color: #CCC; }
.pswp__error-msg a {
color: #CCC;
- text-decoration: underline;
-}
\ No newline at end of file
+ text-decoration: underline; }
\ No newline at end of file
diff --git a/files/pswp/photoswipe.js b/files/pswp/photoswipe.js
new file mode 100644
index 0000000..0a762f8
--- /dev/null
+++ b/files/pswp/photoswipe.js
@@ -0,0 +1,3788 @@
+/*! PhotoSwipe - v4.1.3 - 2019-01-08
+ * http://photoswipe.com
+ * Copyright (c) 2019 Dmitry Semenov;
+ * Heavily Modified (c) 2025 Florian Greistorfer */
+(function (root, factory) {
+ if (typeof define === "function" && define.amd) {
+ define(factory);
+ } else if (typeof exports === "object") {
+ module.exports = factory();
+ } else {
+ root.PhotoSwipe = factory();
+ }
+})(this, function () {
+ "use strict";
+ var PhotoSwipe = function (template, UiClass, items, options) {
+ /*>>framework-bridge*/
+ /**
+ *
+ * Set of generic functions used by gallery.
+ *
+ * You're free to modify anything here as long as functionality is kept.
+ *
+ */
+ var framework = {
+ features: null,
+ bind: function (target, type, listener, unbind) {
+ var methodName = (unbind ? "remove" : "add") + "EventListener";
+ type = type.split(" ");
+ for (var i = 0; i < type.length; i++) {
+ if (type[i]) {
+ target[methodName](type[i], listener, false);
+ }
+ }
+ },
+ isArray: function (obj) {
+ return obj instanceof Array;
+ },
+ createEl: function (classes, tag) {
+ var el = document.createElement(tag || "div");
+ if (classes) {
+ el.className = classes;
+ }
+ return el;
+ },
+ getScrollY: function () {
+ var yOffset = window.pageYOffset;
+ return yOffset !== undefined
+ ? yOffset
+ : document.documentElement.scrollTop;
+ },
+ unbind: function (target, type, listener) {
+ framework.bind(target, type, listener, true);
+ },
+ removeClass: function (el, className) {
+ var reg = new RegExp("(\\s|^)" + className + "(\\s|$)");
+ el.className = el.className
+ .replace(reg, " ")
+ .replace(/^\s\s*/, "")
+ .replace(/\s\s*$/, "");
+ },
+ addClass: function (el, className) {
+ if (!framework.hasClass(el, className)) {
+ el.className += (el.className ? " " : "") + className;
+ }
+ },
+ hasClass: function (el, className) {
+ return (
+ el.className &&
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(el.className)
+ );
+ },
+ getChildByClass: function (parentEl, childClassName) {
+ var node = parentEl.firstChild;
+ while (node) {
+ if (framework.hasClass(node, childClassName)) {
+ return node;
+ }
+ node = node.nextSibling;
+ }
+ },
+ arraySearch: function (array, value, key) {
+ var i = array.length;
+ while (i--) {
+ if (array[i][key] === value) {
+ return i;
+ }
+ }
+ return -1;
+ },
+ extend: function (o1, o2, preventOverwrite) {
+ for (var prop in o2) {
+ if (o2.hasOwnProperty(prop)) {
+ if (preventOverwrite && o1.hasOwnProperty(prop)) {
+ continue;
+ }
+ o1[prop] = o2[prop];
+ }
+ }
+ },
+ easing: {
+ sine: {
+ out: function (k) {
+ return Math.sin(k * (Math.PI / 2));
+ },
+ inOut: function (k) {
+ return -(Math.cos(Math.PI * k) - 1) / 2;
+ },
+ },
+ cubic: {
+ out: function (k) {
+ return --k * k * k + 1;
+ },
+ },
+ /*
+ elastic: {
+ out: function ( k ) {
+
+ var s, a = 0.1, p = 0.4;
+ if ( k === 0 ) return 0;
+ if ( k === 1 ) return 1;
+ if ( !a || a < 1 ) { a = 1; s = p / 4; }
+ else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
+ return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
+
+ },
+ },
+ back: {
+ out: function ( k ) {
+ var s = 1.70158;
+ return --k * k * ( ( s + 1 ) * k + s ) + 1;
+ }
+ }
+ */
+ },
+
+ /**
+ *
+ * @return {object}
+ *
+ * {
+ * raf : request animation frame function
+ * caf : cancel animation frame function
+ * transfrom : transform property key (with vendor), or null if not supported
+ * oldIE : IE8 or below
+ * }
+ *
+ */
+ detectFeatures: function () {
+ if (framework.features) {
+ return framework.features;
+ }
+ var helperEl = framework.createEl(),
+ helperStyle = helperEl.style,
+ vendor = "",
+ features = {};
+
+ // IE8 and below
+ features.oldIE = document.all && !document.addEventListener;
+
+ features.touch = "ontouchstart" in window;
+
+ if (window.requestAnimationFrame) {
+ features.raf = window.requestAnimationFrame;
+ features.caf = window.cancelAnimationFrame;
+ }
+
+ features.pointerEvent =
+ !!window.PointerEvent || navigator.msPointerEnabled;
+
+ // fix false-positive detection of old Android in new IE
+ // (IE11 ua string contains "Android 4.0")
+
+ if (!features.pointerEvent) {
+ var ua = navigator.userAgent;
+
+ // Detect if device is iPhone or iPod and if it's older than iOS 8
+ // http://stackoverflow.com/a/14223920
+ //
+ // This detection is made because of buggy top/bottom toolbars
+ // that don't trigger window.resize event.
+ // For more info refer to _isFixedPosition variable in core.js
+
+ if (/iP(hone|od)/.test(navigator.platform)) {
+ var v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
+ if (v && v.length > 0) {
+ v = parseInt(v[1], 10);
+ if (v >= 1 && v < 8) {
+ features.isOldIOSPhone = true;
+ }
+ }
+ }
+
+ // Detect old Android (before KitKat)
+ // due to bugs related to position:fixed
+ // http://stackoverflow.com/questions/7184573/pick-up-the-android-version-in-the-browser-by-javascript
+
+ var match = ua.match(/Android\s([0-9\.]*)/);
+ var androidversion = match ? match[1] : 0;
+ androidversion = parseFloat(androidversion);
+ if (androidversion >= 1) {
+ if (androidversion < 4.4) {
+ features.isOldAndroid = true; // for fixed position bug & performance
+ }
+ features.androidVersion = androidversion; // for touchend bug
+ }
+ features.isMobileOpera = /opera mini|opera mobi/i.test(ua);
+
+ // p.s. yes, yes, UA sniffing is bad, propose your solution for above bugs.
+ }
+
+ var styleChecks = ["transform", "perspective", "animationName"],
+ vendors = ["", "webkit", "Moz", "ms", "O"],
+ styleCheckItem,
+ styleName;
+
+ for (var i = 0; i < 4; i++) {
+ vendor = vendors[i];
+
+ for (var a = 0; a < 3; a++) {
+ styleCheckItem = styleChecks[a];
+
+ // uppercase first letter of property name, if vendor is present
+ styleName =
+ vendor +
+ (vendor
+ ? styleCheckItem.charAt(0).toUpperCase() +
+ styleCheckItem.slice(1)
+ : styleCheckItem);
+
+ if (!features[styleCheckItem] && styleName in helperStyle) {
+ features[styleCheckItem] = styleName;
+ }
+ }
+
+ if (vendor && !features.raf) {
+ vendor = vendor.toLowerCase();
+ features.raf = window[vendor + "RequestAnimationFrame"];
+ if (features.raf) {
+ features.caf =
+ window[vendor + "CancelAnimationFrame"] ||
+ window[vendor + "CancelRequestAnimationFrame"];
+ }
+ }
+ }
+
+ if (!features.raf) {
+ var lastTime = 0;
+ features.raf = function (fn) {
+ var currTime = new Date().getTime();
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+ var id = window.setTimeout(function () {
+ fn(currTime + timeToCall);
+ }, timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+ };
+ features.caf = function (id) {
+ clearTimeout(id);
+ };
+ }
+
+ // Detect SVG support
+ features.svg =
+ !!document.createElementNS &&
+ !!document.createElementNS("http://www.w3.org/2000/svg", "svg")
+ .createSVGRect;
+
+ framework.features = features;
+
+ return features;
+ },
+ };
+
+ framework.detectFeatures();
+
+ // Override addEventListener for old versions of IE
+ if (framework.features.oldIE) {
+ framework.bind = function (target, type, listener, unbind) {
+ type = type.split(" ");
+
+ var methodName = (unbind ? "detach" : "attach") + "Event",
+ evName,
+ _handleEv = function () {
+ listener.handleEvent.call(listener);
+ };
+
+ for (var i = 0; i < type.length; i++) {
+ evName = type[i];
+ if (evName) {
+ if (typeof listener === "object" && listener.handleEvent) {
+ if (!unbind) {
+ listener["oldIE" + evName] = _handleEv;
+ } else {
+ if (!listener["oldIE" + evName]) {
+ return false;
+ }
+ }
+
+ target[methodName]("on" + evName, listener["oldIE" + evName]);
+ } else {
+ target[methodName]("on" + evName, listener);
+ }
+ }
+ }
+ };
+ }
+
+ /*>>framework-bridge*/
+
+ /*>>core*/
+ //function(template, UiClass, items, options)
+
+ var self = this;
+
+ /**
+ * Static vars, don't change unless you know what you're doing.
+ */
+ var DOUBLE_TAP_RADIUS = 25,
+ NUM_HOLDERS = 3;
+
+ /**
+ * Options
+ */
+ var _options = {
+ allowPanToNext: true,
+ spacing: 0.12,
+ bgOpacity: 1,
+ mouseUsed: false,
+ loop: true,
+ pinchToClose: true,
+ closeOnScroll: true,
+ closeOnVerticalDrag: true,
+ verticalDragRange: 0.75,
+ hideAnimationDuration: 333,
+ showAnimationDuration: 333,
+ showHideOpacity: false,
+ focus: true,
+ escKey: true,
+ arrowKeys: true,
+ mainScrollEndFriction: 0.35,
+ panEndFriction: 0.35,
+ isClickableElement: function (el) {
+ return el.tagName === "A";
+ },
+ getDoubleTapZoom: function (isMouseClick, item) {
+ if (isMouseClick) {
+ return 1;
+ } else {
+ return item.initialZoomLevel < 0.7 ? 1 : 1.33;
+ }
+ },
+ maxSpreadZoom: 1.33,
+ modal: true,
+
+ // not fully implemented yet
+ scaleMode: "fit", // TODO
+ };
+ framework.extend(_options, options);
+
+ /**
+ * Private helper variables & functions
+ */
+
+ var _getEmptyPoint = function () {
+ return { x: 0, y: 0 };
+ };
+
+ var _isOpen,
+ _isDestroying,
+ _closedByScroll,
+ _currentItemIndex,
+ _containerStyle,
+ _containerShiftIndex,
+ _currPanDist = _getEmptyPoint(),
+ _startPanOffset = _getEmptyPoint(),
+ _panOffset = _getEmptyPoint(),
+ _upMoveEvents, // drag move, drag end & drag cancel events array
+ _downEvents, // drag start events array
+ _globalEventHandlers,
+ _viewportSize = {},
+ _currZoomLevel,
+ _startZoomLevel,
+ _translatePrefix,
+ _translateSufix,
+ _updateSizeInterval,
+ _itemsNeedUpdate,
+ _currPositionIndex = 0,
+ _offset = {},
+ _slideSize = _getEmptyPoint(), // size of slide area, including spacing
+ _itemHolders,
+ _prevItemIndex,
+ _indexDiff = 0, // difference of indexes since last content update
+ _dragStartEvent,
+ _dragMoveEvent,
+ _dragEndEvent,
+ _dragCancelEvent,
+ _transformKey,
+ _pointerEventEnabled,
+ _isFixedPosition = true,
+ _likelyTouchDevice,
+ _modules = [],
+ _requestAF,
+ _cancelAF,
+ _initalClassName,
+ _initalWindowScrollY,
+ _oldIE,
+ _currentWindowScrollY,
+ _features,
+ _windowVisibleSize = {},
+ _renderMaxResolution = false,
+ _orientationChangeTimeout,
+ // Registers PhotoSWipe module (History, Controller ...)
+ _registerModule = function (name, module) {
+ framework.extend(self, module.publicMethods);
+ _modules.push(name);
+ },
+ _getLoopedId = function (index) {
+ var numSlides = _getNumItems();
+ if (index > numSlides - 1) {
+ return index - numSlides;
+ } else if (index < 0) {
+ return numSlides + index;
+ }
+ return index;
+ },
+ // Micro bind/trigger
+ _listeners = {},
+ _listen = function (name, fn) {
+ if (!_listeners[name]) {
+ _listeners[name] = [];
+ }
+ return _listeners[name].push(fn);
+ },
+ _shout = function (name) {
+ var listeners = _listeners[name];
+
+ if (listeners) {
+ var args = Array.prototype.slice.call(arguments);
+ args.shift();
+
+ for (var i = 0; i < listeners.length; i++) {
+ listeners[i].apply(self, args);
+ }
+ }
+ },
+ _getCurrentTime = function () {
+ return new Date().getTime();
+ },
+ _applyBgOpacity = function (opacity) {
+ _bgOpacity = opacity;
+ self.bg.style.opacity = opacity * _options.bgOpacity;
+ },
+ _applyZoomTransform = function (styleObj, x, y, zoom, item) {
+ if (!_renderMaxResolution || (item && item !== self.currItem)) {
+ zoom = zoom / (item ? item.fitRatio : self.currItem.fitRatio);
+ }
+
+ styleObj[_transformKey] =
+ _translatePrefix +
+ x +
+ "px, " +
+ y +
+ "px" +
+ _translateSufix +
+ " scale(" +
+ zoom +
+ ")";
+ },
+ _applyCurrentZoomPan = function (allowRenderResolution) {
+ if (_currZoomElementStyle) {
+ if (allowRenderResolution) {
+ if (_currZoomLevel > self.currItem.fitRatio) {
+ if (!_renderMaxResolution) {
+ _setImageSize(self.currItem, false, true);
+ _renderMaxResolution = true;
+ }
+ } else {
+ if (_renderMaxResolution) {
+ _setImageSize(self.currItem);
+ _renderMaxResolution = false;
+ }
+ }
+ }
+
+ _applyZoomTransform(
+ _currZoomElementStyle,
+ _panOffset.x,
+ _panOffset.y,
+ _currZoomLevel
+ );
+ }
+ },
+ _applyZoomPanToItem = function (item) {
+ if (item.container) {
+ _applyZoomTransform(
+ item.container.style,
+ item.initialPosition.x,
+ item.initialPosition.y,
+ item.initialZoomLevel,
+ item
+ );
+ }
+ },
+ _setTranslateX = function (x, elStyle) {
+ elStyle[_transformKey] =
+ _translatePrefix + x + "px, 0px" + _translateSufix;
+ },
+ _moveMainScroll = function (x, dragging) {
+ if (!_options.loop && dragging) {
+ var newSlideIndexOffset =
+ _currentItemIndex +
+ (_slideSize.x * _currPositionIndex - x) / _slideSize.x,
+ delta = Math.round(x - _mainScrollPos.x);
+
+ if (
+ (newSlideIndexOffset < 0 && delta > 0) ||
+ (newSlideIndexOffset >= _getNumItems() - 1 && delta < 0)
+ ) {
+ x = _mainScrollPos.x + delta * _options.mainScrollEndFriction;
+ }
+ }
+
+ _mainScrollPos.x = x;
+ _setTranslateX(x, _containerStyle);
+ },
+ _calculatePanOffset = function (axis, zoomLevel) {
+ var m = _midZoomPoint[axis] - _offset[axis];
+ return (
+ _startPanOffset[axis] +
+ _currPanDist[axis] +
+ m -
+ m * (zoomLevel / _startZoomLevel)
+ );
+ },
+ _equalizePoints = function (p1, p2) {
+ p1.x = p2.x;
+ p1.y = p2.y;
+ if (p2.id) {
+ p1.id = p2.id;
+ }
+ },
+ _roundPoint = function (p) {
+ p.x = Math.round(p.x);
+ p.y = Math.round(p.y);
+ },
+ _mouseMoveTimeout = null,
+ _onFirstMouseMove = function () {
+ // Wait until mouse move event is fired at least twice during 100ms
+ // We do this, because some mobile browsers trigger it on touchstart
+ if (_mouseMoveTimeout) {
+ framework.unbind(document, "mousemove", _onFirstMouseMove);
+ framework.addClass(template, "pswp--has_mouse");
+ _options.mouseUsed = true;
+ _shout("mouseUsed");
+ }
+ _mouseMoveTimeout = setTimeout(function () {
+ _mouseMoveTimeout = null;
+ }, 100);
+ },
+ _bindEvents = function () {
+ framework.bind(document, "keydown", self);
+
+ if (_features.transform) {
+ // don't bind click event in browsers that don't support transform (mostly IE8)
+ framework.bind(self.scrollWrap, "click", self);
+ }
+
+ if (!_options.mouseUsed) {
+ framework.bind(document, "mousemove", _onFirstMouseMove);
+ }
+
+ framework.bind(window, "resize scroll orientationchange", self);
+
+ _shout("bindEvents");
+ },
+ _unbindEvents = function () {
+ framework.unbind(window, "resize scroll orientationchange", self);
+ framework.unbind(window, "scroll", _globalEventHandlers.scroll);
+ framework.unbind(document, "keydown", self);
+ framework.unbind(document, "mousemove", _onFirstMouseMove);
+
+ if (_features.transform) {
+ framework.unbind(self.scrollWrap, "click", self);
+ }
+
+ if (_isDragging) {
+ framework.unbind(window, _upMoveEvents, self);
+ }
+
+ clearTimeout(_orientationChangeTimeout);
+
+ _shout("unbindEvents");
+ },
+ _calculatePanBounds = function (zoomLevel, update) {
+ var bounds = _calculateItemSize(
+ self.currItem,
+ _viewportSize,
+ zoomLevel
+ );
+ if (update) {
+ _currPanBounds = bounds;
+ }
+ return bounds;
+ },
+ _getMinZoomLevel = function (item) {
+ if (!item) {
+ item = self.currItem;
+ }
+ return item.initialZoomLevel;
+ },
+ _getMaxZoomLevel = function (item) {
+ if (!item) {
+ item = self.currItem;
+ }
+ return item.w > 0 ? _options.maxSpreadZoom : 1;
+ },
+ // Return true if offset is out of the bounds
+ _modifyDestPanOffset = function (
+ axis,
+ destPanBounds,
+ destPanOffset,
+ destZoomLevel
+ ) {
+ if (destZoomLevel === self.currItem.initialZoomLevel) {
+ destPanOffset[axis] = self.currItem.initialPosition[axis];
+ return true;
+ } else {
+ destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel);
+
+ if (destPanOffset[axis] > destPanBounds.min[axis]) {
+ destPanOffset[axis] = destPanBounds.min[axis];
+ return true;
+ } else if (destPanOffset[axis] < destPanBounds.max[axis]) {
+ destPanOffset[axis] = destPanBounds.max[axis];
+ return true;
+ }
+ }
+ return false;
+ },
+ _setupTransforms = function () {
+ if (_transformKey) {
+ // setup 3d transforms
+ var allow3dTransform = _features.perspective && !_likelyTouchDevice;
+ _translatePrefix = "translate" + (allow3dTransform ? "3d(" : "(");
+ _translateSufix = _features.perspective ? ", 0px)" : ")";
+ return;
+ }
+
+ // Override zoom/pan/move functions in case old browser is used (most likely IE)
+ // (so they use left/top/width/height, instead of CSS transform)
+
+ _transformKey = "left";
+ framework.addClass(template, "pswp--ie");
+
+ _setTranslateX = function (x, elStyle) {
+ elStyle.left = x + "px";
+ };
+ _applyZoomPanToItem = function (item) {
+ var zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
+ s = item.container.style,
+ w = zoomRatio * item.w,
+ h = zoomRatio * item.h;
+
+ s.width = w + "px";
+ s.height = h + "px";
+ s.left = item.initialPosition.x + "px";
+ s.top = item.initialPosition.y + "px";
+ };
+ _applyCurrentZoomPan = function () {
+ if (_currZoomElementStyle) {
+ var s = _currZoomElementStyle,
+ item = self.currItem,
+ zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
+ w = zoomRatio * item.w,
+ h = zoomRatio * item.h;
+
+ s.width = w + "px";
+ s.height = h + "px";
+
+ s.left = _panOffset.x + "px";
+ s.top = _panOffset.y + "px";
+ }
+ };
+ },
+ _onKeyDown = function (e) {
+ var keydownAction = "";
+ if (_options.escKey && e.keyCode === 27) {
+ keydownAction = "close";
+ } else if (_options.arrowKeys) {
+ if (e.keyCode === 37) {
+ keydownAction = "prev";
+ } else if (e.keyCode === 39) {
+ keydownAction = "next";
+ }
+ }
+
+ if (keydownAction) {
+ // don't do anything if special key pressed to prevent from overriding default browser actions
+ // e.g. in Chrome on Mac cmd+arrow-left returns to previous page
+ if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ } else {
+ e.returnValue = false;
+ }
+ self[keydownAction]();
+ }
+ }
+ },
+ _onGlobalClick = function (e) {
+ if (!e) {
+ return;
+ }
+
+ // don't allow click event to pass through when triggering after drag or some other gesture
+ if (
+ _moved ||
+ _zoomStarted ||
+ _mainScrollAnimating ||
+ _verticalDragInitiated
+ ) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ },
+ _updatePageScrollOffset = function () {
+ self.setScrollOffset(0, framework.getScrollY());
+ };
+
+ // Micro animation engine
+ var _animations = {},
+ _numAnimations = 0,
+ _stopAnimation = function (name) {
+ if (_animations[name]) {
+ if (_animations[name].raf) {
+ _cancelAF(_animations[name].raf);
+ }
+ _numAnimations--;
+ delete _animations[name];
+ }
+ },
+ _registerStartAnimation = function (name) {
+ if (_animations[name]) {
+ _stopAnimation(name);
+ }
+ if (!_animations[name]) {
+ _numAnimations++;
+ _animations[name] = {};
+ }
+ },
+ _stopAllAnimations = function () {
+ for (var prop in _animations) {
+ if (_animations.hasOwnProperty(prop)) {
+ _stopAnimation(prop);
+ }
+ }
+ },
+ _animateProp = function (
+ name,
+ b,
+ endProp,
+ d,
+ easingFn,
+ onUpdate,
+ onComplete
+ ) {
+ var startAnimTime = _getCurrentTime(),
+ t;
+ _registerStartAnimation(name);
+
+ var animloop = function () {
+ if (_animations[name]) {
+ t = _getCurrentTime() - startAnimTime; // time diff
+ //b - beginning (start prop)
+ //d - anim duration
+
+ if (t >= d) {
+ _stopAnimation(name);
+ onUpdate(endProp);
+ if (onComplete) {
+ onComplete();
+ }
+ return;
+ }
+ onUpdate((endProp - b) * easingFn(t / d) + b);
+
+ _animations[name].raf = _requestAF(animloop);
+ }
+ };
+ animloop();
+ };
+
+ var publicMethods = {
+ // make a few local variables and functions public
+ shout: _shout,
+ listen: _listen,
+ viewportSize: _viewportSize,
+ options: _options,
+
+ isMainScrollAnimating: function () {
+ return _mainScrollAnimating;
+ },
+ getZoomLevel: function () {
+ return _currZoomLevel;
+ },
+ getCurrentIndex: function () {
+ return _currentItemIndex;
+ },
+ isDragging: function () {
+ return _isDragging;
+ },
+ isZooming: function () {
+ return _isZooming;
+ },
+ setScrollOffset: function (x, y) {
+ _offset.x = x;
+ _currentWindowScrollY = _offset.y = y;
+ _shout("updateScrollOffset", _offset);
+ },
+ applyZoomPan: function (zoomLevel, panX, panY, allowRenderResolution) {
+ _panOffset.x = panX;
+ _panOffset.y = panY;
+ _currZoomLevel = zoomLevel;
+ _applyCurrentZoomPan(allowRenderResolution);
+ },
+
+ init: function () {
+ if (_isOpen || _isDestroying) {
+ return;
+ }
+
+ var i;
+
+ self.framework = framework; // basic functionality
+ self.template = template; // root DOM element of PhotoSwipe
+ self.bg = framework.getChildByClass(template, "pswp__bg");
+
+ _initalClassName = template.className;
+ _isOpen = true;
+
+ _features = framework.detectFeatures();
+ _requestAF = _features.raf;
+ _cancelAF = _features.caf;
+ _transformKey = _features.transform;
+ _oldIE = _features.oldIE;
+
+ self.scrollWrap = framework.getChildByClass(
+ template,
+ "pswp__scroll-wrap"
+ );
+ self.container = framework.getChildByClass(
+ self.scrollWrap,
+ "pswp__container"
+ );
+
+ _containerStyle = self.container.style; // for fast access
+
+ // Objects that hold slides (there are only 3 in DOM)
+ self.itemHolders = _itemHolders = [
+ { el: self.container.children[0], wrap: 0, index: -1 },
+ { el: self.container.children[1], wrap: 0, index: -1 },
+ { el: self.container.children[2], wrap: 0, index: -1 },
+ ];
+
+ // hide nearby item holders until initial zoom animation finishes (to avoid extra Paints)
+ _itemHolders[0].el.style.display = _itemHolders[2].el.style.display =
+ "none";
+
+ _setupTransforms();
+
+ // Setup global events
+ _globalEventHandlers = {
+ resize: self.updateSize,
+
+ // Fixes: iOS 10.3 resize event
+ // does not update scrollWrap.clientWidth instantly after resize
+ // https://github.com/dimsemenov/PhotoSwipe/issues/1315
+ orientationchange: function () {
+ clearTimeout(_orientationChangeTimeout);
+ _orientationChangeTimeout = setTimeout(function () {
+ if (_viewportSize.x !== self.scrollWrap.clientWidth) {
+ self.updateSize();
+ }
+ }, 500);
+ },
+ scroll: _updatePageScrollOffset,
+ keydown: _onKeyDown,
+ click: _onGlobalClick,
+ };
+
+ // disable show/hide effects on old browsers that don't support CSS animations or transforms,
+ // old IOS, Android and Opera mobile. Blackberry seems to work fine, even older models.
+ var oldPhone =
+ _features.isOldIOSPhone ||
+ _features.isOldAndroid ||
+ _features.isMobileOpera;
+ if (!_features.animationName || !_features.transform || oldPhone) {
+ _options.showAnimationDuration = _options.hideAnimationDuration = 0;
+ }
+
+ // init modules
+ for (i = 0; i < _modules.length; i++) {
+ self["init" + _modules[i]]();
+ }
+
+ // init
+ if (UiClass) {
+ var ui = (self.ui = new UiClass(self, framework));
+ ui.init();
+ }
+
+ _shout("firstUpdate");
+ _currentItemIndex = _currentItemIndex || _options.index || 0;
+ // validate index
+ if (
+ isNaN(_currentItemIndex) ||
+ _currentItemIndex < 0 ||
+ _currentItemIndex >= _getNumItems()
+ ) {
+ _currentItemIndex = 0;
+ }
+ self.currItem = _getItemAt(_currentItemIndex);
+
+ if (_features.isOldIOSPhone || _features.isOldAndroid) {
+ _isFixedPosition = false;
+ }
+
+ template.setAttribute("aria-hidden", "false");
+ if (_options.modal) {
+ if (!_isFixedPosition) {
+ template.style.position = "absolute";
+ template.style.top = framework.getScrollY() + "px";
+ } else {
+ template.style.position = "fixed";
+ }
+ }
+
+ if (_currentWindowScrollY === undefined) {
+ _shout("initialLayout");
+ _currentWindowScrollY = _initalWindowScrollY = framework.getScrollY();
+ }
+
+ // add classes to root element of PhotoSwipe
+ var rootClasses = "pswp--open ";
+ if (_options.mainClass) {
+ rootClasses += _options.mainClass + " ";
+ }
+ if (_options.showHideOpacity) {
+ rootClasses += "pswp--animate_opacity ";
+ }
+ rootClasses += _likelyTouchDevice ? "pswp--touch" : "pswp--notouch";
+ rootClasses += _features.animationName ? " pswp--css_animation" : "";
+ rootClasses += _features.svg ? " pswp--svg" : "";
+ framework.addClass(template, rootClasses);
+
+ self.updateSize();
+
+ // initial update
+ _containerShiftIndex = -1;
+ _indexDiff = null;
+ for (i = 0; i < NUM_HOLDERS; i++) {
+ _setTranslateX(
+ (i + _containerShiftIndex) * _slideSize.x,
+ _itemHolders[i].el.style
+ );
+ }
+
+ if (!_oldIE) {
+ framework.bind(self.scrollWrap, _downEvents, self); // no dragging for old IE
+ }
+
+ _listen("initialZoomInEnd", function () {
+ self.setContent(_itemHolders[0], _currentItemIndex - 1);
+ self.setContent(_itemHolders[2], _currentItemIndex + 1);
+
+ _itemHolders[0].el.style.display = _itemHolders[2].el.style.display =
+ "block";
+
+ if (_options.focus) {
+ // focus causes layout,
+ // which causes lag during the animation,
+ // that's why we delay it untill the initial zoom transition ends
+ template.focus();
+ }
+
+ _bindEvents();
+ });
+
+ // set content for center slide (first time)
+ self.setContent(_itemHolders[1], _currentItemIndex);
+
+ self.updateCurrItem();
+
+ _shout("afterInit");
+
+ if (!_isFixedPosition) {
+ // On all versions of iOS lower than 8.0, we check size of viewport every second.
+ //
+ // This is done to detect when Safari top & bottom bars appear,
+ // as this action doesn't trigger any events (like resize).
+ //
+ // On iOS8 they fixed this.
+ //
+ // 10 Nov 2014: iOS 7 usage ~40%. iOS 8 usage 56%.
+
+ _updateSizeInterval = setInterval(function () {
+ if (
+ !_numAnimations &&
+ !_isDragging &&
+ !_isZooming &&
+ _currZoomLevel === self.currItem.initialZoomLevel
+ ) {
+ self.updateSize();
+ }
+ }, 1000);
+ }
+
+ framework.addClass(template, "pswp--visible");
+ },
+
+ // Close the gallery, then destroy it
+ close: function () {
+ if (!_isOpen) {
+ return;
+ }
+
+ _isOpen = false;
+ _isDestroying = true;
+ _shout("close");
+ _unbindEvents();
+
+ _showOrHide(self.currItem, null, true, self.destroy);
+ },
+
+ // destroys the gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks)
+ destroy: function () {
+ _shout("destroy");
+
+ if (_showOrHideTimeout) {
+ clearTimeout(_showOrHideTimeout);
+ }
+
+ template.setAttribute("aria-hidden", "true");
+ template.className = _initalClassName;
+
+ if (_updateSizeInterval) {
+ clearInterval(_updateSizeInterval);
+ }
+
+ framework.unbind(self.scrollWrap, _downEvents, self);
+
+ // we unbind scroll event at the end, as closing animation may depend on it
+ framework.unbind(window, "scroll", self);
+
+ _stopDragUpdateLoop();
+
+ _stopAllAnimations();
+
+ _listeners = null;
+ },
+
+ /**
+ * Pan image to position
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Boolean} force Will ignore bounds if set to true.
+ */
+ panTo: function (x, y, force) {
+ if (!force) {
+ if (x > _currPanBounds.min.x) {
+ x = _currPanBounds.min.x;
+ } else if (x < _currPanBounds.max.x) {
+ x = _currPanBounds.max.x;
+ }
+
+ if (y > _currPanBounds.min.y) {
+ y = _currPanBounds.min.y;
+ } else if (y < _currPanBounds.max.y) {
+ y = _currPanBounds.max.y;
+ }
+ }
+
+ _panOffset.x = x;
+ _panOffset.y = y;
+ _applyCurrentZoomPan();
+ },
+
+ handleEvent: function (e) {
+ e = e || window.event;
+ if (_globalEventHandlers[e.type]) {
+ _globalEventHandlers[e.type](e);
+ }
+ },
+
+ goTo: function (index) {
+ index = _getLoopedId(index);
+
+ var diff = index - _currentItemIndex;
+ _indexDiff = diff;
+
+ _currentItemIndex = index;
+ self.currItem = _getItemAt(_currentItemIndex);
+ _currPositionIndex -= diff;
+
+ _moveMainScroll(_slideSize.x * _currPositionIndex);
+
+ _stopAllAnimations();
+ _mainScrollAnimating = false;
+
+ self.updateCurrItem();
+ },
+ next: function () {
+ self.goTo(_currentItemIndex + 1);
+ },
+ prev: function () {
+ self.goTo(_currentItemIndex - 1);
+ },
+
+ // update current zoom/pan objects
+ updateCurrZoomItem: function (emulateSetContent) {
+ if (emulateSetContent) {
+ _shout("beforeChange", 0);
+ }
+
+ // itemHolder[1] is middle (current) item
+ if (_itemHolders[1].el.children.length) {
+ var zoomElement = _itemHolders[1].el.children[0];
+ if (framework.hasClass(zoomElement, "pswp__zoom-wrap")) {
+ _currZoomElementStyle = zoomElement.style;
+ } else {
+ _currZoomElementStyle = null;
+ }
+ } else {
+ _currZoomElementStyle = null;
+ }
+
+ _currPanBounds = self.currItem.bounds;
+ _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
+
+ _panOffset.x = _currPanBounds.center.x;
+ _panOffset.y = _currPanBounds.center.y;
+
+ if (emulateSetContent) {
+ _shout("afterChange");
+ }
+ },
+
+ invalidateCurrItems: function () {
+ _itemsNeedUpdate = true;
+ for (var i = 0; i < NUM_HOLDERS; i++) {
+ if (_itemHolders[i].item) {
+ _itemHolders[i].item.needsUpdate = true;
+ }
+ }
+ },
+
+ updateCurrItem: function (beforeAnimation) {
+ if (_indexDiff === 0) {
+ return;
+ }
+
+ var diffAbs = Math.abs(_indexDiff),
+ tempHolder;
+
+ if (beforeAnimation && diffAbs < 2) {
+ return;
+ }
+
+ self.currItem = _getItemAt(_currentItemIndex);
+ _renderMaxResolution = false;
+
+ _shout("beforeChange", _indexDiff);
+
+ if (diffAbs >= NUM_HOLDERS) {
+ _containerShiftIndex +=
+ _indexDiff + (_indexDiff > 0 ? -NUM_HOLDERS : NUM_HOLDERS);
+ diffAbs = NUM_HOLDERS;
+ }
+ for (var i = 0; i < diffAbs; i++) {
+ if (_indexDiff > 0) {
+ tempHolder = _itemHolders.shift();
+ _itemHolders[NUM_HOLDERS - 1] = tempHolder; // move first to last
+
+ _containerShiftIndex++;
+ _setTranslateX(
+ (_containerShiftIndex + 2) * _slideSize.x,
+ tempHolder.el.style
+ );
+ self.setContent(
+ tempHolder,
+ _currentItemIndex - diffAbs + i + 1 + 1
+ );
+ } else {
+ tempHolder = _itemHolders.pop();
+ _itemHolders.unshift(tempHolder); // move last to first
+
+ _containerShiftIndex--;
+ _setTranslateX(
+ _containerShiftIndex * _slideSize.x,
+ tempHolder.el.style
+ );
+ self.setContent(
+ tempHolder,
+ _currentItemIndex + diffAbs - i - 1 - 1
+ );
+ }
+ }
+
+ // reset zoom/pan on previous item
+ if (_currZoomElementStyle && Math.abs(_indexDiff) === 1) {
+ var prevItem = _getItemAt(_prevItemIndex);
+ if (prevItem.initialZoomLevel !== _currZoomLevel) {
+ _calculateItemSize(prevItem, _viewportSize);
+ _setImageSize(prevItem);
+ _applyZoomPanToItem(prevItem);
+ }
+ }
+
+ // reset diff after update
+ _indexDiff = 0;
+
+ self.updateCurrZoomItem();
+
+ _prevItemIndex = _currentItemIndex;
+
+ _shout("afterChange");
+ },
+
+ updateSize: function (force) {
+ if (!_isFixedPosition && _options.modal) {
+ var windowScrollY = framework.getScrollY();
+ if (_currentWindowScrollY !== windowScrollY) {
+ template.style.top = windowScrollY + "px";
+ _currentWindowScrollY = windowScrollY;
+ }
+ if (
+ !force &&
+ _windowVisibleSize.x === window.innerWidth &&
+ _windowVisibleSize.y === window.innerHeight
+ ) {
+ return;
+ }
+ _windowVisibleSize.x = window.innerWidth;
+ _windowVisibleSize.y = window.innerHeight;
+
+ //template.style.width = _windowVisibleSize.x + 'px';
+ template.style.height = _windowVisibleSize.y + "px";
+ }
+
+ _viewportSize.x = self.scrollWrap.clientWidth;
+ _viewportSize.y = self.scrollWrap.clientHeight;
+
+ _updatePageScrollOffset();
+
+ _slideSize.x =
+ _viewportSize.x + Math.round(_viewportSize.x * _options.spacing);
+ _slideSize.y = _viewportSize.y;
+
+ _moveMainScroll(_slideSize.x * _currPositionIndex);
+
+ _shout("beforeResize"); // even may be used for example to switch image sources
+
+ // don't re-calculate size on inital size update
+ if (_containerShiftIndex !== undefined) {
+ var holder, item, hIndex;
+
+ for (var i = 0; i < NUM_HOLDERS; i++) {
+ holder = _itemHolders[i];
+ _setTranslateX(
+ (i + _containerShiftIndex) * _slideSize.x,
+ holder.el.style
+ );
+
+ hIndex = _currentItemIndex + i - 1;
+
+ if (_options.loop && _getNumItems() > 2) {
+ hIndex = _getLoopedId(hIndex);
+ }
+
+ // update zoom level on items and refresh source (if needsUpdate)
+ item = _getItemAt(hIndex);
+
+ // re-render gallery item if `needsUpdate`,
+ // or doesn't have `bounds` (entirely new slide object)
+ if (
+ item &&
+ (_itemsNeedUpdate || item.needsUpdate || !item.bounds)
+ ) {
+ self.cleanSlide(item);
+
+ self.setContent(holder, hIndex);
+
+ // if "center" slide
+ if (i === 1) {
+ self.currItem = item;
+ self.updateCurrZoomItem(true);
+ }
+
+ item.needsUpdate = false;
+ } else if (holder.index === -1 && hIndex >= 0) {
+ // add content first time
+ self.setContent(holder, hIndex);
+ }
+ if (item && item.container) {
+ _calculateItemSize(item, _viewportSize);
+ _setImageSize(item);
+ _applyZoomPanToItem(item);
+ }
+ }
+ _itemsNeedUpdate = false;
+ }
+
+ _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
+ _currPanBounds = self.currItem.bounds;
+
+ if (_currPanBounds) {
+ _panOffset.x = _currPanBounds.center.x;
+ _panOffset.y = _currPanBounds.center.y;
+ _applyCurrentZoomPan(true);
+ }
+
+ _shout("resize");
+ },
+
+ // Zoom current item to
+ zoomTo: function (destZoomLevel, centerPoint, speed, easingFn, updateFn) {
+ /*
+ if(destZoomLevel === 'fit') {
+ destZoomLevel = self.currItem.fitRatio;
+ } else if(destZoomLevel === 'fill') {
+ destZoomLevel = self.currItem.fillRatio;
+ }
+ */
+
+ if (centerPoint) {
+ _startZoomLevel = _currZoomLevel;
+ _midZoomPoint.x = Math.abs(centerPoint.x) - _panOffset.x;
+ _midZoomPoint.y = Math.abs(centerPoint.y) - _panOffset.y;
+ _equalizePoints(_startPanOffset, _panOffset);
+ }
+
+ var destPanBounds = _calculatePanBounds(destZoomLevel, false),
+ destPanOffset = {};
+
+ _modifyDestPanOffset("x", destPanBounds, destPanOffset, destZoomLevel);
+ _modifyDestPanOffset("y", destPanBounds, destPanOffset, destZoomLevel);
+
+ var initialZoomLevel = _currZoomLevel;
+ var initialPanOffset = {
+ x: _panOffset.x,
+ y: _panOffset.y,
+ };
+
+ _roundPoint(destPanOffset);
+
+ var onUpdate = function (now) {
+ if (now === 1) {
+ _currZoomLevel = destZoomLevel;
+ _panOffset.x = destPanOffset.x;
+ _panOffset.y = destPanOffset.y;
+ } else {
+ _currZoomLevel =
+ (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
+ _panOffset.x =
+ (destPanOffset.x - initialPanOffset.x) * now + initialPanOffset.x;
+ _panOffset.y =
+ (destPanOffset.y - initialPanOffset.y) * now + initialPanOffset.y;
+ }
+
+ if (updateFn) {
+ updateFn(now);
+ }
+
+ _applyCurrentZoomPan(now === 1);
+ };
+
+ if (speed) {
+ _animateProp(
+ "customZoomTo",
+ 0,
+ 1,
+ speed,
+ easingFn || framework.easing.sine.inOut,
+ onUpdate
+ );
+ } else {
+ onUpdate(1);
+ }
+ },
+ };
+
+ /*>>core*/
+
+ /*>>gestures*/
+ /**
+ * Mouse/touch/pointer event handlers.
+ *
+ * separated from @core.js for readability
+ */
+
+ var MIN_SWIPE_DISTANCE = 30,
+ DIRECTION_CHECK_OFFSET = 10; // amount of pixels to drag to determine direction of swipe
+
+ var _gestureStartTime,
+ _gestureCheckSpeedTime,
+ // pool of objects that are used during dragging of zooming
+ p = {}, // first point
+ p2 = {}, // second point (for zoom gesture)
+ delta = {},
+ _currPoint = {},
+ _startPoint = {},
+ _currPointers = [],
+ _startMainScrollPos = {},
+ _releaseAnimData,
+ _posPoints = [], // array of points during dragging, used to determine type of gesture
+ _tempPoint = {},
+ _isZoomingIn,
+ _verticalDragInitiated,
+ _oldAndroidTouchEndTimeout,
+ _currZoomedItemIndex = 0,
+ _centerPoint = _getEmptyPoint(),
+ _lastReleaseTime = 0,
+ _isDragging, // at least one pointer is down
+ _isMultitouch, // at least two _pointers are down
+ _zoomStarted, // zoom level changed during zoom gesture
+ _moved,
+ _dragAnimFrame,
+ _mainScrollShifted,
+ _currentPoints, // array of current touch points
+ _isZooming,
+ _currPointsDistance,
+ _startPointsDistance,
+ _currPanBounds,
+ _mainScrollPos = _getEmptyPoint(),
+ _currZoomElementStyle,
+ _mainScrollAnimating, // true, if animation after swipe gesture is running
+ _midZoomPoint = _getEmptyPoint(),
+ _currCenterPoint = _getEmptyPoint(),
+ _direction,
+ _isFirstMove,
+ _opacityChanged,
+ _bgOpacity,
+ _wasOverInitialZoom,
+ _isEqualPoints = function (p1, p2) {
+ return p1.x === p2.x && p1.y === p2.y;
+ },
+ _isNearbyPoints = function (touch0, touch1) {
+ return (
+ Math.abs(touch0.x - touch1.x) < DOUBLE_TAP_RADIUS &&
+ Math.abs(touch0.y - touch1.y) < DOUBLE_TAP_RADIUS
+ );
+ },
+ _calculatePointsDistance = function (p1, p2) {
+ _tempPoint.x = Math.abs(p1.x - p2.x);
+ _tempPoint.y = Math.abs(p1.y - p2.y);
+ return Math.sqrt(
+ _tempPoint.x * _tempPoint.x + _tempPoint.y * _tempPoint.y
+ );
+ },
+ _stopDragUpdateLoop = function () {
+ if (_dragAnimFrame) {
+ _cancelAF(_dragAnimFrame);
+ _dragAnimFrame = null;
+ }
+ },
+ _dragUpdateLoop = function () {
+ if (_isDragging) {
+ _dragAnimFrame = _requestAF(_dragUpdateLoop);
+ _renderMovement();
+ }
+ },
+ _canPan = function () {
+ return !(
+ _options.scaleMode === "fit" &&
+ _currZoomLevel === self.currItem.initialZoomLevel
+ );
+ },
+ // find the closest parent DOM element
+ _closestElement = function (el, fn) {
+ if (!el || el === document) {
+ return false;
+ }
+
+ // don't search elements above pswp__scroll-wrap
+ if (
+ el.getAttribute("class") &&
+ el.getAttribute("class").indexOf("pswp__scroll-wrap") > -1
+ ) {
+ return false;
+ }
+
+ if (fn(el)) {
+ return el;
+ }
+
+ return _closestElement(el.parentNode, fn);
+ },
+ _preventObj = {},
+ _preventDefaultEventBehaviour = function (e, isDown) {
+ _preventObj.prevent = !_closestElement(
+ e.target,
+ _options.isClickableElement
+ );
+
+ _shout("preventDragEvent", e, isDown, _preventObj);
+ return _preventObj.prevent;
+ },
+ _convertTouchToPoint = function (touch, p) {
+ p.x = touch.pageX;
+ p.y = touch.pageY;
+ p.id = touch.identifier;
+ return p;
+ },
+ _findCenterOfPoints = function (p1, p2, pCenter) {
+ pCenter.x = (p1.x + p2.x) * 0.5;
+ pCenter.y = (p1.y + p2.y) * 0.5;
+ },
+ _pushPosPoint = function (time, x, y) {
+ if (time - _gestureCheckSpeedTime > 50) {
+ var o = _posPoints.length > 2 ? _posPoints.shift() : {};
+ o.x = x;
+ o.y = y;
+ _posPoints.push(o);
+ _gestureCheckSpeedTime = time;
+ }
+ },
+ _calculateVerticalDragOpacityRatio = function () {
+ var yOffset = _panOffset.y - self.currItem.initialPosition.y; // difference between initial and current position
+ return 1 - Math.abs(yOffset / (_viewportSize.y / 2));
+ },
+ // points pool, reused during touch events
+ _ePoint1 = {},
+ _ePoint2 = {},
+ _tempPointsArr = [],
+ _tempCounter,
+ _getTouchPoints = function (e) {
+ // clean up previous points, without recreating array
+ while (_tempPointsArr.length > 0) {
+ _tempPointsArr.pop();
+ }
+
+ if (!_pointerEventEnabled) {
+ if (e.type.indexOf("touch") > -1) {
+ if (e.touches && e.touches.length > 0) {
+ _tempPointsArr[0] = _convertTouchToPoint(e.touches[0], _ePoint1);
+ if (e.touches.length > 1) {
+ _tempPointsArr[1] = _convertTouchToPoint(
+ e.touches[1],
+ _ePoint2
+ );
+ }
+ }
+ } else {
+ _ePoint1.x = e.pageX;
+ _ePoint1.y = e.pageY;
+ _ePoint1.id = "";
+ _tempPointsArr[0] = _ePoint1; //_ePoint1;
+ }
+ } else {
+ _tempCounter = 0;
+ // we can use forEach, as pointer events are supported only in modern browsers
+ _currPointers.forEach(function (p) {
+ if (_tempCounter === 0) {
+ _tempPointsArr[0] = p;
+ } else if (_tempCounter === 1) {
+ _tempPointsArr[1] = p;
+ }
+ _tempCounter++;
+ });
+ }
+ return _tempPointsArr;
+ },
+ _panOrMoveMainScroll = function (axis, delta) {
+ var panFriction,
+ overDiff = 0,
+ newOffset = _panOffset[axis] + delta[axis],
+ startOverDiff,
+ dir = delta[axis] > 0,
+ newMainScrollPosition = _mainScrollPos.x + delta.x,
+ mainScrollDiff = _mainScrollPos.x - _startMainScrollPos.x,
+ newPanPos,
+ newMainScrollPos;
+
+ // calculate fdistance over the bounds and friction
+ if (
+ newOffset > _currPanBounds.min[axis] ||
+ newOffset < _currPanBounds.max[axis]
+ ) {
+ panFriction = _options.panEndFriction;
+ // Linear increasing of friction, so at 1/4 of viewport it's at max value.
+ // Looks not as nice as was expected. Left for history.
+ // panFriction = (1 - (_panOffset[axis] + delta[axis] + panBounds.min[axis]) / (_viewportSize[axis] / 4) );
+ } else {
+ panFriction = 1;
+ }
+
+ newOffset = _panOffset[axis] + delta[axis] * panFriction;
+
+ // move main scroll or start panning
+ if (
+ _options.allowPanToNext ||
+ _currZoomLevel === self.currItem.initialZoomLevel
+ ) {
+ if (!_currZoomElementStyle) {
+ newMainScrollPos = newMainScrollPosition;
+ } else if (_direction === "h" && axis === "x" && !_zoomStarted) {
+ if (dir) {
+ if (newOffset > _currPanBounds.min[axis]) {
+ panFriction = _options.panEndFriction;
+ overDiff = _currPanBounds.min[axis] - newOffset;
+ startOverDiff =
+ _currPanBounds.min[axis] - _startPanOffset[axis];
+ }
+
+ // drag right
+ if (
+ (startOverDiff <= 0 || mainScrollDiff < 0) &&
+ _getNumItems() > 1
+ ) {
+ newMainScrollPos = newMainScrollPosition;
+ if (
+ mainScrollDiff < 0 &&
+ newMainScrollPosition > _startMainScrollPos.x
+ ) {
+ newMainScrollPos = _startMainScrollPos.x;
+ }
+ } else {
+ if (_currPanBounds.min.x !== _currPanBounds.max.x) {
+ newPanPos = newOffset;
+ }
+ }
+ } else {
+ if (newOffset < _currPanBounds.max[axis]) {
+ panFriction = _options.panEndFriction;
+ overDiff = newOffset - _currPanBounds.max[axis];
+ startOverDiff =
+ _startPanOffset[axis] - _currPanBounds.max[axis];
+ }
+
+ if (
+ (startOverDiff <= 0 || mainScrollDiff > 0) &&
+ _getNumItems() > 1
+ ) {
+ newMainScrollPos = newMainScrollPosition;
+
+ if (
+ mainScrollDiff > 0 &&
+ newMainScrollPosition < _startMainScrollPos.x
+ ) {
+ newMainScrollPos = _startMainScrollPos.x;
+ }
+ } else {
+ if (_currPanBounds.min.x !== _currPanBounds.max.x) {
+ newPanPos = newOffset;
+ }
+ }
+ }
+
+ //
+ }
+
+ if (axis === "x") {
+ if (newMainScrollPos !== undefined) {
+ _moveMainScroll(newMainScrollPos, true);
+ if (newMainScrollPos === _startMainScrollPos.x) {
+ _mainScrollShifted = false;
+ } else {
+ _mainScrollShifted = true;
+ }
+ }
+
+ if (_currPanBounds.min.x !== _currPanBounds.max.x) {
+ if (newPanPos !== undefined) {
+ _panOffset.x = newPanPos;
+ } else if (!_mainScrollShifted) {
+ _panOffset.x += delta.x * panFriction;
+ }
+ }
+
+ return newMainScrollPos !== undefined;
+ }
+ }
+
+ if (!_mainScrollAnimating) {
+ if (!_mainScrollShifted) {
+ if (_currZoomLevel > self.currItem.fitRatio) {
+ _panOffset[axis] += delta[axis] * panFriction;
+ }
+ }
+ }
+ },
+ // Pointerdown/touchstart/mousedown handler
+ _onDragStart = function (e) {
+ // Allow dragging only via left mouse button.
+ // As this handler is not added in IE8 - we ignore e.which
+ //
+ // http://www.quirksmode.org/js/events_properties.html
+ // https://developer.mozilla.org/en-US/docs/Web/API/event.button
+ if (e.type === "mousedown" && e.button > 0) {
+ return;
+ }
+
+ if (_initialZoomRunning) {
+ e.preventDefault();
+ return;
+ }
+
+ if (_oldAndroidTouchEndTimeout && e.type === "mousedown") {
+ return;
+ }
+
+ if (_preventDefaultEventBehaviour(e, true)) {
+ e.preventDefault();
+ }
+
+ _shout("pointerDown");
+
+ if (_pointerEventEnabled) {
+ var pointerIndex = framework.arraySearch(
+ _currPointers,
+ e.pointerId,
+ "id"
+ );
+ if (pointerIndex < 0) {
+ pointerIndex = _currPointers.length;
+ }
+ _currPointers[pointerIndex] = {
+ x: e.pageX,
+ y: e.pageY,
+ id: e.pointerId,
+ };
+ }
+
+ var startPointsList = _getTouchPoints(e),
+ numPoints = startPointsList.length;
+
+ _currentPoints = null;
+
+ _stopAllAnimations();
+
+ // init drag
+ if (!_isDragging || numPoints === 1) {
+ _isDragging = _isFirstMove = true;
+ framework.bind(window, _upMoveEvents, self);
+
+ _isZoomingIn =
+ _wasOverInitialZoom =
+ _opacityChanged =
+ _verticalDragInitiated =
+ _mainScrollShifted =
+ _moved =
+ _isMultitouch =
+ _zoomStarted =
+ false;
+
+ _direction = null;
+
+ _shout("firstTouchStart", startPointsList);
+
+ _equalizePoints(_startPanOffset, _panOffset);
+
+ _currPanDist.x = _currPanDist.y = 0;
+ _equalizePoints(_currPoint, startPointsList[0]);
+ _equalizePoints(_startPoint, _currPoint);
+
+ //_equalizePoints(_startMainScrollPos, _mainScrollPos);
+ _startMainScrollPos.x = _slideSize.x * _currPositionIndex;
+
+ _posPoints = [
+ {
+ x: _currPoint.x,
+ y: _currPoint.y,
+ },
+ ];
+
+ _gestureCheckSpeedTime = _gestureStartTime = _getCurrentTime();
+
+ //_mainScrollAnimationEnd(true);
+ _calculatePanBounds(_currZoomLevel, true);
+
+ // Start rendering
+ _stopDragUpdateLoop();
+ _dragUpdateLoop();
+ }
+
+ // init zoom
+ if (
+ !_isZooming &&
+ numPoints > 1 &&
+ !_mainScrollAnimating &&
+ !_mainScrollShifted
+ ) {
+ _startZoomLevel = _currZoomLevel;
+ _zoomStarted = false; // true if zoom changed at least once
+
+ _isZooming = _isMultitouch = true;
+ _currPanDist.y = _currPanDist.x = 0;
+
+ _equalizePoints(_startPanOffset, _panOffset);
+
+ _equalizePoints(p, startPointsList[0]);
+ _equalizePoints(p2, startPointsList[1]);
+
+ _findCenterOfPoints(p, p2, _currCenterPoint);
+
+ _midZoomPoint.x = Math.abs(_currCenterPoint.x) - _panOffset.x;
+ _midZoomPoint.y = Math.abs(_currCenterPoint.y) - _panOffset.y;
+ _currPointsDistance = _startPointsDistance = _calculatePointsDistance(
+ p,
+ p2
+ );
+ }
+ },
+ // Pointermove/touchmove/mousemove handler
+ _onDragMove = function (e) {
+ e.preventDefault();
+
+ if (_pointerEventEnabled) {
+ var pointerIndex = framework.arraySearch(
+ _currPointers,
+ e.pointerId,
+ "id"
+ );
+ if (pointerIndex > -1) {
+ var p = _currPointers[pointerIndex];
+ p.x = e.pageX;
+ p.y = e.pageY;
+ }
+ }
+
+ if (_isDragging) {
+ var touchesList = _getTouchPoints(e);
+ if (!_direction && !_moved && !_isZooming) {
+ if (_mainScrollPos.x !== _slideSize.x * _currPositionIndex) {
+ // if main scroll position is shifted – direction is always horizontal
+ _direction = "h";
+ } else {
+ var diff =
+ Math.abs(touchesList[0].x - _currPoint.x) -
+ Math.abs(touchesList[0].y - _currPoint.y);
+ // check the direction of movement
+ if (Math.abs(diff) >= DIRECTION_CHECK_OFFSET) {
+ _direction = diff > 0 ? "h" : "v";
+ _currentPoints = touchesList;
+ }
+ }
+ } else {
+ _currentPoints = touchesList;
+ }
+ }
+ },
+ //
+ _renderMovement = function () {
+ if (!_currentPoints) {
+ return;
+ }
+
+ var numPoints = _currentPoints.length;
+
+ if (numPoints === 0) {
+ return;
+ }
+
+ _equalizePoints(p, _currentPoints[0]);
+
+ delta.x = p.x - _currPoint.x;
+ delta.y = p.y - _currPoint.y;
+
+ if (_isZooming && numPoints > 1) {
+ // Handle behaviour for more than 1 point
+
+ _currPoint.x = p.x;
+ _currPoint.y = p.y;
+
+ // check if one of two points changed
+ if (!delta.x && !delta.y && _isEqualPoints(_currentPoints[1], p2)) {
+ return;
+ }
+
+ _equalizePoints(p2, _currentPoints[1]);
+
+ if (!_zoomStarted) {
+ _zoomStarted = true;
+ _shout("zoomGestureStarted");
+ }
+
+ // Distance between two points
+ var pointsDistance = _calculatePointsDistance(p, p2);
+
+ var zoomLevel = _calculateZoomLevel(pointsDistance);
+
+ // slightly over the of initial zoom level
+ if (
+ zoomLevel >
+ self.currItem.initialZoomLevel + self.currItem.initialZoomLevel / 15
+ ) {
+ _wasOverInitialZoom = true;
+ }
+
+ // Apply the friction if zoom level is out of the bounds
+ var zoomFriction = 1,
+ minZoomLevel = _getMinZoomLevel(),
+ maxZoomLevel = _getMaxZoomLevel();
+
+ if (zoomLevel < minZoomLevel) {
+ if (
+ _options.pinchToClose &&
+ !_wasOverInitialZoom &&
+ _startZoomLevel <= self.currItem.initialZoomLevel
+ ) {
+ // fade out background if zooming out
+ var minusDiff = minZoomLevel - zoomLevel;
+ var percent = 1 - minusDiff / (minZoomLevel / 1.2);
+
+ _applyBgOpacity(percent);
+ _shout("onPinchClose", percent);
+ _opacityChanged = true;
+ } else {
+ zoomFriction = (minZoomLevel - zoomLevel) / minZoomLevel;
+ if (zoomFriction > 1) {
+ zoomFriction = 1;
+ }
+ zoomLevel = minZoomLevel - zoomFriction * (minZoomLevel / 3);
+ }
+ } else if (zoomLevel > maxZoomLevel) {
+ // 1.5 - extra zoom level above the max. E.g. if max is x6, real max 6 + 1.5 = 7.5
+ zoomFriction = (zoomLevel - maxZoomLevel) / (minZoomLevel * 6);
+ if (zoomFriction > 1) {
+ zoomFriction = 1;
+ }
+ zoomLevel = maxZoomLevel + zoomFriction * minZoomLevel;
+ }
+
+ if (zoomFriction < 0) {
+ zoomFriction = 0;
+ }
+
+ // distance between touch points after friction is applied
+ _currPointsDistance = pointsDistance;
+
+ // _centerPoint - The point in the middle of two pointers
+ _findCenterOfPoints(p, p2, _centerPoint);
+
+ // paning with two pointers pressed
+ _currPanDist.x += _centerPoint.x - _currCenterPoint.x;
+ _currPanDist.y += _centerPoint.y - _currCenterPoint.y;
+ _equalizePoints(_currCenterPoint, _centerPoint);
+
+ _panOffset.x = _calculatePanOffset("x", zoomLevel);
+ _panOffset.y = _calculatePanOffset("y", zoomLevel);
+
+ _isZoomingIn = zoomLevel > _currZoomLevel;
+ _currZoomLevel = zoomLevel;
+ _applyCurrentZoomPan();
+ } else {
+ // handle behaviour for one point (dragging or panning)
+
+ if (!_direction) {
+ return;
+ }
+
+ if (_isFirstMove) {
+ _isFirstMove = false;
+
+ // subtract drag distance that was used during the detection direction
+
+ if (Math.abs(delta.x) >= DIRECTION_CHECK_OFFSET) {
+ delta.x -= _currentPoints[0].x - _startPoint.x;
+ }
+
+ if (Math.abs(delta.y) >= DIRECTION_CHECK_OFFSET) {
+ delta.y -= _currentPoints[0].y - _startPoint.y;
+ }
+ }
+
+ _currPoint.x = p.x;
+ _currPoint.y = p.y;
+
+ // do nothing if pointers position hasn't changed
+ if (delta.x === 0 && delta.y === 0) {
+ return;
+ }
+
+ if (_direction === "v" && _options.closeOnVerticalDrag) {
+ if (!_canPan()) {
+ _currPanDist.y += delta.y;
+ _panOffset.y += delta.y;
+
+ var opacityRatio = _calculateVerticalDragOpacityRatio();
+
+ _verticalDragInitiated = true;
+ _shout("onVerticalDrag", opacityRatio);
+
+ _applyBgOpacity(opacityRatio);
+ _applyCurrentZoomPan();
+ return;
+ }
+ }
+
+ _pushPosPoint(_getCurrentTime(), p.x, p.y);
+
+ _moved = true;
+ _currPanBounds = self.currItem.bounds;
+
+ var mainScrollChanged = _panOrMoveMainScroll("x", delta);
+ if (!mainScrollChanged) {
+ _panOrMoveMainScroll("y", delta);
+
+ _roundPoint(_panOffset);
+ _applyCurrentZoomPan();
+ }
+ }
+ },
+ // Pointerup/pointercancel/touchend/touchcancel/mouseup event handler
+ _onDragRelease = function (e) {
+ if (_features.isOldAndroid) {
+ if (_oldAndroidTouchEndTimeout && e.type === "mouseup") {
+ return;
+ }
+
+ // on Android (v4.1, 4.2, 4.3 & possibly older)
+ // ghost mousedown/up event isn't preventable via e.preventDefault,
+ // which causes fake mousedown event
+ // so we block mousedown/up for 600ms
+ if (e.type.indexOf("touch") > -1) {
+ clearTimeout(_oldAndroidTouchEndTimeout);
+ _oldAndroidTouchEndTimeout = setTimeout(function () {
+ _oldAndroidTouchEndTimeout = 0;
+ }, 600);
+ }
+ }
+
+ _shout("pointerUp");
+
+ if (_preventDefaultEventBehaviour(e, false)) {
+ e.preventDefault();
+ }
+
+ var releasePoint;
+
+ if (_pointerEventEnabled) {
+ var pointerIndex = framework.arraySearch(
+ _currPointers,
+ e.pointerId,
+ "id"
+ );
+
+ if (pointerIndex > -1) {
+ releasePoint = _currPointers.splice(pointerIndex, 1)[0];
+
+ if (navigator.msPointerEnabled) {
+ var MSPOINTER_TYPES = {
+ 4: "mouse", // event.MSPOINTER_TYPE_MOUSE
+ 2: "touch", // event.MSPOINTER_TYPE_TOUCH
+ 3: "pen", // event.MSPOINTER_TYPE_PEN
+ };
+ releasePoint.type = MSPOINTER_TYPES[e.pointerType];
+
+ if (!releasePoint.type) {
+ releasePoint.type = e.pointerType || "mouse";
+ }
+ } else {
+ releasePoint.type = e.pointerType || "mouse";
+ }
+ }
+ }
+
+ var touchList = _getTouchPoints(e),
+ gestureType,
+ numPoints = touchList.length;
+
+ if (e.type === "mouseup") {
+ numPoints = 0;
+ }
+
+ // Do nothing if there were 3 touch points or more
+ if (numPoints === 2) {
+ _currentPoints = null;
+ return true;
+ }
+
+ // if second pointer released
+ if (numPoints === 1) {
+ _equalizePoints(_startPoint, touchList[0]);
+ }
+
+ // pointer hasn't moved, send "tap release" point
+ if (numPoints === 0 && !_direction && !_mainScrollAnimating) {
+ if (!releasePoint) {
+ if (e.type === "mouseup") {
+ releasePoint = { x: e.pageX, y: e.pageY, type: "mouse" };
+ } else if (e.changedTouches && e.changedTouches[0]) {
+ releasePoint = {
+ x: e.changedTouches[0].pageX,
+ y: e.changedTouches[0].pageY,
+ type: "touch",
+ };
+ }
+ }
+
+ _shout("touchRelease", e, releasePoint);
+ }
+
+ // Difference in time between releasing of two last touch points (zoom gesture)
+ var releaseTimeDiff = -1;
+
+ // Gesture completed, no pointers left
+ if (numPoints === 0) {
+ _isDragging = false;
+ framework.unbind(window, _upMoveEvents, self);
+
+ _stopDragUpdateLoop();
+
+ if (_isZooming) {
+ // Two points released at the same time
+ releaseTimeDiff = 0;
+ } else if (_lastReleaseTime !== -1) {
+ releaseTimeDiff = _getCurrentTime() - _lastReleaseTime;
+ }
+ }
+ _lastReleaseTime = numPoints === 1 ? _getCurrentTime() : -1;
+
+ if (releaseTimeDiff !== -1 && releaseTimeDiff < 150) {
+ gestureType = "zoom";
+ } else {
+ gestureType = "swipe";
+ }
+
+ if (_isZooming && numPoints < 2) {
+ _isZooming = false;
+
+ // Only second point released
+ if (numPoints === 1) {
+ gestureType = "zoomPointerUp";
+ }
+ _shout("zoomGestureEnded");
+ }
+
+ _currentPoints = null;
+ if (
+ !_moved &&
+ !_zoomStarted &&
+ !_mainScrollAnimating &&
+ !_verticalDragInitiated
+ ) {
+ // nothing to animate
+ return;
+ }
+
+ _stopAllAnimations();
+
+ if (!_releaseAnimData) {
+ _releaseAnimData = _initDragReleaseAnimationData();
+ }
+
+ _releaseAnimData.calculateSwipeSpeed("x");
+
+ if (_verticalDragInitiated) {
+ var opacityRatio = _calculateVerticalDragOpacityRatio();
+
+ if (opacityRatio < _options.verticalDragRange) {
+ self.close();
+ } else {
+ var initalPanY = _panOffset.y,
+ initialBgOpacity = _bgOpacity;
+
+ _animateProp(
+ "verticalDrag",
+ 0,
+ 1,
+ 300,
+ framework.easing.cubic.out,
+ function (now) {
+ _panOffset.y =
+ (self.currItem.initialPosition.y - initalPanY) * now +
+ initalPanY;
+
+ _applyBgOpacity(
+ (1 - initialBgOpacity) * now + initialBgOpacity
+ );
+ _applyCurrentZoomPan();
+ }
+ );
+
+ _shout("onVerticalDrag", 1);
+ }
+
+ return;
+ }
+
+ // main scroll
+ if ((_mainScrollShifted || _mainScrollAnimating) && numPoints === 0) {
+ var itemChanged = _finishSwipeMainScrollGesture(
+ gestureType,
+ _releaseAnimData
+ );
+ if (itemChanged) {
+ return;
+ }
+ gestureType = "zoomPointerUp";
+ }
+
+ // prevent zoom/pan animation when main scroll animation runs
+ if (_mainScrollAnimating) {
+ return;
+ }
+
+ // Complete simple zoom gesture (reset zoom level if it's out of the bounds)
+ if (gestureType !== "swipe") {
+ _completeZoomGesture();
+ return;
+ }
+
+ // Complete pan gesture if main scroll is not shifted, and it's possible to pan current image
+ if (!_mainScrollShifted && _currZoomLevel > self.currItem.fitRatio) {
+ _completePanGesture(_releaseAnimData);
+ }
+ },
+ // Returns object with data about gesture
+ // It's created only once and then reused
+ _initDragReleaseAnimationData = function () {
+ // temp local vars
+ var lastFlickDuration, tempReleasePos;
+
+ // s = this
+ var s = {
+ lastFlickOffset: {},
+ lastFlickDist: {},
+ lastFlickSpeed: {},
+ slowDownRatio: {},
+ slowDownRatioReverse: {},
+ speedDecelerationRatio: {},
+ speedDecelerationRatioAbs: {},
+ distanceOffset: {},
+ backAnimDestination: {},
+ backAnimStarted: {},
+ calculateSwipeSpeed: function (axis) {
+ if (_posPoints.length > 1) {
+ lastFlickDuration =
+ _getCurrentTime() - _gestureCheckSpeedTime + 50;
+ tempReleasePos = _posPoints[_posPoints.length - 2][axis];
+ } else {
+ lastFlickDuration = _getCurrentTime() - _gestureStartTime; // total gesture duration
+ tempReleasePos = _startPoint[axis];
+ }
+ s.lastFlickOffset[axis] = _currPoint[axis] - tempReleasePos;
+ s.lastFlickDist[axis] = Math.abs(s.lastFlickOffset[axis]);
+ if (s.lastFlickDist[axis] > 20) {
+ s.lastFlickSpeed[axis] =
+ s.lastFlickOffset[axis] / lastFlickDuration;
+ } else {
+ s.lastFlickSpeed[axis] = 0;
+ }
+ if (Math.abs(s.lastFlickSpeed[axis]) < 0.1) {
+ s.lastFlickSpeed[axis] = 0;
+ }
+
+ s.slowDownRatio[axis] = 0.95;
+ s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
+ s.speedDecelerationRatio[axis] = 1;
+ },
+
+ calculateOverBoundsAnimOffset: function (axis, speed) {
+ if (!s.backAnimStarted[axis]) {
+ if (_panOffset[axis] > _currPanBounds.min[axis]) {
+ s.backAnimDestination[axis] = _currPanBounds.min[axis];
+ } else if (_panOffset[axis] < _currPanBounds.max[axis]) {
+ s.backAnimDestination[axis] = _currPanBounds.max[axis];
+ }
+
+ if (s.backAnimDestination[axis] !== undefined) {
+ s.slowDownRatio[axis] = 0.7;
+ s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
+ if (s.speedDecelerationRatioAbs[axis] < 0.05) {
+ s.lastFlickSpeed[axis] = 0;
+ s.backAnimStarted[axis] = true;
+
+ _animateProp(
+ "bounceZoomPan" + axis,
+ _panOffset[axis],
+ s.backAnimDestination[axis],
+ speed || 300,
+ framework.easing.sine.out,
+ function (pos) {
+ _panOffset[axis] = pos;
+ _applyCurrentZoomPan();
+ }
+ );
+ }
+ }
+ }
+ },
+
+ // Reduces the speed by slowDownRatio (per 10ms)
+ calculateAnimOffset: function (axis) {
+ if (!s.backAnimStarted[axis]) {
+ s.speedDecelerationRatio[axis] =
+ s.speedDecelerationRatio[axis] *
+ (s.slowDownRatio[axis] +
+ s.slowDownRatioReverse[axis] -
+ (s.slowDownRatioReverse[axis] * s.timeDiff) / 10);
+
+ s.speedDecelerationRatioAbs[axis] = Math.abs(
+ s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis]
+ );
+ s.distanceOffset[axis] =
+ s.lastFlickSpeed[axis] *
+ s.speedDecelerationRatio[axis] *
+ s.timeDiff;
+ _panOffset[axis] += s.distanceOffset[axis];
+ }
+ },
+
+ panAnimLoop: function () {
+ if (_animations.zoomPan) {
+ _animations.zoomPan.raf = _requestAF(s.panAnimLoop);
+
+ s.now = _getCurrentTime();
+ s.timeDiff = s.now - s.lastNow;
+ s.lastNow = s.now;
+
+ s.calculateAnimOffset("x");
+ s.calculateAnimOffset("y");
+
+ _applyCurrentZoomPan();
+
+ s.calculateOverBoundsAnimOffset("x");
+ s.calculateOverBoundsAnimOffset("y");
+
+ if (
+ s.speedDecelerationRatioAbs.x < 0.05 &&
+ s.speedDecelerationRatioAbs.y < 0.05
+ ) {
+ // round pan position
+ _panOffset.x = Math.round(_panOffset.x);
+ _panOffset.y = Math.round(_panOffset.y);
+ _applyCurrentZoomPan();
+
+ _stopAnimation("zoomPan");
+ return;
+ }
+ }
+ },
+ };
+ return s;
+ },
+ _completePanGesture = function (animData) {
+ // calculate swipe speed for Y axis (paanning)
+ animData.calculateSwipeSpeed("y");
+
+ _currPanBounds = self.currItem.bounds;
+
+ animData.backAnimDestination = {};
+ animData.backAnimStarted = {};
+
+ // Avoid acceleration animation if speed is too low
+ if (
+ Math.abs(animData.lastFlickSpeed.x) <= 0.05 &&
+ Math.abs(animData.lastFlickSpeed.y) <= 0.05
+ ) {
+ animData.speedDecelerationRatioAbs.x =
+ animData.speedDecelerationRatioAbs.y = 0;
+
+ // Run pan drag release animation. E.g. if you drag image and release finger without momentum.
+ animData.calculateOverBoundsAnimOffset("x");
+ animData.calculateOverBoundsAnimOffset("y");
+ return true;
+ }
+
+ // Animation loop that controls the acceleration after pan gesture ends
+ _registerStartAnimation("zoomPan");
+ animData.lastNow = _getCurrentTime();
+ animData.panAnimLoop();
+ },
+ _finishSwipeMainScrollGesture = function (gestureType, _releaseAnimData) {
+ var itemChanged;
+ if (!_mainScrollAnimating) {
+ _currZoomedItemIndex = _currentItemIndex;
+ }
+
+ var itemsDiff;
+
+ if (gestureType === "swipe") {
+ var totalShiftDist = _currPoint.x - _startPoint.x,
+ isFastLastFlick = _releaseAnimData.lastFlickDist.x < 10;
+
+ // if container is shifted for more than MIN_SWIPE_DISTANCE,
+ // and last flick gesture was in right direction
+ if (
+ totalShiftDist > MIN_SWIPE_DISTANCE &&
+ (isFastLastFlick || _releaseAnimData.lastFlickOffset.x > 20)
+ ) {
+ // go to prev item
+ itemsDiff = -1;
+ } else if (
+ totalShiftDist < -MIN_SWIPE_DISTANCE &&
+ (isFastLastFlick || _releaseAnimData.lastFlickOffset.x < -20)
+ ) {
+ // go to next item
+ itemsDiff = 1;
+ }
+ }
+
+ var nextCircle;
+
+ if (itemsDiff) {
+ _currentItemIndex += itemsDiff;
+
+ if (_currentItemIndex < 0) {
+ _currentItemIndex = _options.loop ? _getNumItems() - 1 : 0;
+ nextCircle = true;
+ } else if (_currentItemIndex >= _getNumItems()) {
+ _currentItemIndex = _options.loop ? 0 : _getNumItems() - 1;
+ nextCircle = true;
+ }
+
+ if (!nextCircle || _options.loop) {
+ _indexDiff += itemsDiff;
+ _currPositionIndex -= itemsDiff;
+ itemChanged = true;
+ }
+ }
+
+ var animateToX = _slideSize.x * _currPositionIndex;
+ var animateToDist = Math.abs(animateToX - _mainScrollPos.x);
+ var finishAnimDuration;
+
+ if (
+ !itemChanged &&
+ animateToX > _mainScrollPos.x !==
+ _releaseAnimData.lastFlickSpeed.x > 0
+ ) {
+ // "return to current" duration, e.g. when dragging from slide 0 to -1
+ finishAnimDuration = 333;
+ } else {
+ finishAnimDuration =
+ Math.abs(_releaseAnimData.lastFlickSpeed.x) > 0
+ ? animateToDist / Math.abs(_releaseAnimData.lastFlickSpeed.x)
+ : 333;
+
+ finishAnimDuration = Math.min(finishAnimDuration, 400);
+ finishAnimDuration = Math.max(finishAnimDuration, 250);
+ }
+
+ if (_currZoomedItemIndex === _currentItemIndex) {
+ itemChanged = false;
+ }
+
+ _mainScrollAnimating = true;
+
+ _shout("mainScrollAnimStart");
+
+ _animateProp(
+ "mainScroll",
+ _mainScrollPos.x,
+ animateToX,
+ finishAnimDuration,
+ framework.easing.cubic.out,
+ _moveMainScroll,
+ function () {
+ _stopAllAnimations();
+ _mainScrollAnimating = false;
+ _currZoomedItemIndex = -1;
+
+ if (itemChanged || _currZoomedItemIndex !== _currentItemIndex) {
+ self.updateCurrItem();
+ }
+
+ _shout("mainScrollAnimComplete");
+ }
+ );
+
+ if (itemChanged) {
+ self.updateCurrItem(true);
+ }
+
+ return itemChanged;
+ },
+ _calculateZoomLevel = function (touchesDistance) {
+ return (1 / _startPointsDistance) * touchesDistance * _startZoomLevel;
+ },
+ // Resets zoom if it's out of bounds
+ _completeZoomGesture = function () {
+ var destZoomLevel = _currZoomLevel,
+ minZoomLevel = _getMinZoomLevel(),
+ maxZoomLevel = _getMaxZoomLevel();
+
+ if (_currZoomLevel < minZoomLevel) {
+ destZoomLevel = minZoomLevel;
+ } else if (_currZoomLevel > maxZoomLevel) {
+ destZoomLevel = maxZoomLevel;
+ }
+
+ var destOpacity = 1,
+ onUpdate,
+ initialOpacity = _bgOpacity;
+
+ if (
+ _opacityChanged &&
+ !_isZoomingIn &&
+ !_wasOverInitialZoom &&
+ _currZoomLevel < minZoomLevel
+ ) {
+ //_closedByScroll = true;
+ self.close();
+ return true;
+ }
+
+ if (_opacityChanged) {
+ onUpdate = function (now) {
+ _applyBgOpacity(
+ (destOpacity - initialOpacity) * now + initialOpacity
+ );
+ };
+ }
+
+ self.zoomTo(
+ destZoomLevel,
+ 0,
+ 200,
+ framework.easing.cubic.out,
+ onUpdate
+ );
+ return true;
+ };
+
+ _registerModule("Gestures", {
+ publicMethods: {
+ initGestures: function () {
+ // helper function that builds touch/pointer/mouse events
+ var addEventNames = function (pref, down, move, up, cancel) {
+ _dragStartEvent = pref + down;
+ _dragMoveEvent = pref + move;
+ _dragEndEvent = pref + up;
+ if (cancel) {
+ _dragCancelEvent = pref + cancel;
+ } else {
+ _dragCancelEvent = "";
+ }
+ };
+
+ _pointerEventEnabled = _features.pointerEvent;
+ if (_pointerEventEnabled && _features.touch) {
+ // we don't need touch events, if browser supports pointer events
+ _features.touch = false;
+ }
+
+ if (_pointerEventEnabled) {
+ if (navigator.msPointerEnabled) {
+ // IE10 pointer events are case-sensitive
+ addEventNames("MSPointer", "Down", "Move", "Up", "Cancel");
+ } else {
+ addEventNames("pointer", "down", "move", "up", "cancel");
+ }
+ } else if (_features.touch) {
+ addEventNames("touch", "start", "move", "end", "cancel");
+ _likelyTouchDevice = true;
+ } else {
+ addEventNames("mouse", "down", "move", "up");
+ }
+
+ _upMoveEvents =
+ _dragMoveEvent + " " + _dragEndEvent + " " + _dragCancelEvent;
+ _downEvents = _dragStartEvent;
+
+ if (_pointerEventEnabled && !_likelyTouchDevice) {
+ _likelyTouchDevice =
+ navigator.maxTouchPoints > 1 || navigator.msMaxTouchPoints > 1;
+ }
+ // make variable public
+ self.likelyTouchDevice = _likelyTouchDevice;
+
+ _globalEventHandlers[_dragStartEvent] = _onDragStart;
+ _globalEventHandlers[_dragMoveEvent] = _onDragMove;
+ _globalEventHandlers[_dragEndEvent] = _onDragRelease; // the Kraken
+
+ if (_dragCancelEvent) {
+ _globalEventHandlers[_dragCancelEvent] =
+ _globalEventHandlers[_dragEndEvent];
+ }
+
+ // Bind mouse events on device with detected hardware touch support, in case it supports multiple types of input.
+ if (_features.touch) {
+ _downEvents += " mousedown";
+ _upMoveEvents += " mousemove mouseup";
+ _globalEventHandlers.mousedown =
+ _globalEventHandlers[_dragStartEvent];
+ _globalEventHandlers.mousemove =
+ _globalEventHandlers[_dragMoveEvent];
+ _globalEventHandlers.mouseup = _globalEventHandlers[_dragEndEvent];
+ }
+
+ if (!_likelyTouchDevice) {
+ // don't allow pan to next slide from zoomed state on Desktop
+ _options.allowPanToNext = false;
+ }
+ },
+ },
+ });
+
+ /*>>gestures*/
+
+ /*>>show-hide-transition*/
+ /**
+ * show-hide-transition.js:
+ *
+ * Manages initial opening or closing transition.
+ *
+ * If you're not planning to use transition for gallery at all,
+ * you may set options hideAnimationDuration and showAnimationDuration to 0,
+ * and just delete startAnimation function.
+ *
+ */
+
+ var _showOrHideTimeout,
+ _showOrHide = function (item, img, out, completeFn) {
+ if (_showOrHideTimeout) {
+ clearTimeout(_showOrHideTimeout);
+ }
+
+ _initialZoomRunning = true;
+ _initialContentSet = true;
+
+ // dimensions of small thumbnail {x:,y:,w:}.
+ // Height is optional, as calculated based on large image.
+ var thumbBounds;
+ if (item.initialLayout) {
+ thumbBounds = item.initialLayout;
+ item.initialLayout = null;
+ } else {
+ thumbBounds =
+ _options.getThumbBoundsFn &&
+ _options.getThumbBoundsFn(_currentItemIndex);
+ }
+
+ var duration = out
+ ? _options.hideAnimationDuration
+ : _options.showAnimationDuration;
+
+ var onComplete = function () {
+ _stopAnimation("initialZoom");
+ if (!out) {
+ _applyBgOpacity(1);
+ if (img) {
+ img.style.display = "block";
+ }
+ framework.addClass(template, "pswp--animated-in");
+ _shout("initialZoom" + (out ? "OutEnd" : "InEnd"));
+ } else {
+ self.template.removeAttribute("style");
+ self.bg.removeAttribute("style");
+ }
+
+ if (completeFn) {
+ completeFn();
+ }
+ _initialZoomRunning = false;
+ };
+
+ // if bounds aren't provided, just open gallery without animation
+ if (!duration || !thumbBounds || thumbBounds.x === undefined) {
+ _shout("initialZoom" + (out ? "Out" : "In"));
+
+ _currZoomLevel = item.initialZoomLevel;
+ _equalizePoints(_panOffset, item.initialPosition);
+ _applyCurrentZoomPan();
+
+ template.style.opacity = out ? 0 : 1;
+ _applyBgOpacity(1);
+
+ if (duration) {
+ setTimeout(function () {
+ onComplete();
+ }, duration);
+ } else {
+ onComplete();
+ }
+
+ return;
+ }
+
+ var startAnimation = function () {
+ var closeWithRaf = _closedByScroll,
+ fadeEverything =
+ !self.currItem.src ||
+ self.currItem.loadError ||
+ _options.showHideOpacity;
+
+ // apply hw-acceleration to image
+ if (item.miniImg) {
+ item.miniImg.style.webkitBackfaceVisibility = "hidden";
+ }
+
+ if (!out) {
+ _currZoomLevel = thumbBounds.w / item.w;
+ _panOffset.x = thumbBounds.x;
+ _panOffset.y = thumbBounds.y - _initalWindowScrollY;
+
+ self[fadeEverything ? "template" : "bg"].style.opacity = 0.001;
+ _applyCurrentZoomPan();
+ }
+
+ _registerStartAnimation("initialZoom");
+
+ if (out && !closeWithRaf) {
+ framework.removeClass(template, "pswp--animated-in");
+ }
+
+ if (fadeEverything) {
+ if (out) {
+ framework[(closeWithRaf ? "remove" : "add") + "Class"](
+ template,
+ "pswp--animate_opacity"
+ );
+ } else {
+ setTimeout(function () {
+ framework.addClass(template, "pswp--animate_opacity");
+ }, 30);
+ }
+ }
+
+ _showOrHideTimeout = setTimeout(
+ function () {
+ _shout("initialZoom" + (out ? "Out" : "In"));
+
+ if (!out) {
+ // "in" animation always uses CSS transitions (instead of rAF).
+ // CSS transition work faster here,
+ // as developer may also want to animate other things,
+ // like ui on top of sliding area, which can be animated just via CSS
+
+ _currZoomLevel = item.initialZoomLevel;
+ _equalizePoints(_panOffset, item.initialPosition);
+ _applyCurrentZoomPan();
+ _applyBgOpacity(1);
+
+ if (fadeEverything) {
+ template.style.opacity = 1;
+ } else {
+ _applyBgOpacity(1);
+ }
+
+ _showOrHideTimeout = setTimeout(onComplete, duration + 20);
+ } else {
+ // "out" animation uses rAF only when PhotoSwipe is closed by browser scroll, to recalculate position
+ var destZoomLevel = thumbBounds.w / item.w,
+ initialPanOffset = {
+ x: _panOffset.x,
+ y: _panOffset.y,
+ },
+ initialZoomLevel = _currZoomLevel,
+ initalBgOpacity = _bgOpacity,
+ onUpdate = function (now) {
+ if (now === 1) {
+ _currZoomLevel = destZoomLevel;
+ _panOffset.x = thumbBounds.x;
+ _panOffset.y = thumbBounds.y - _currentWindowScrollY;
+ } else {
+ _currZoomLevel =
+ (destZoomLevel - initialZoomLevel) * now +
+ initialZoomLevel;
+ _panOffset.x =
+ (thumbBounds.x - initialPanOffset.x) * now +
+ initialPanOffset.x;
+ _panOffset.y =
+ (thumbBounds.y -
+ _currentWindowScrollY -
+ initialPanOffset.y) *
+ now +
+ initialPanOffset.y;
+ }
+
+ _applyCurrentZoomPan();
+ if (fadeEverything) {
+ template.style.opacity = 1 - now;
+ } else {
+ _applyBgOpacity(initalBgOpacity - now * initalBgOpacity);
+ }
+ };
+
+ if (closeWithRaf) {
+ _animateProp(
+ "initialZoom",
+ 0,
+ 1,
+ duration,
+ framework.easing.cubic.out,
+ onUpdate,
+ onComplete
+ );
+ } else {
+ onUpdate(1);
+ _showOrHideTimeout = setTimeout(onComplete, duration + 20);
+ }
+ }
+ },
+ out ? 25 : 90
+ ); // Main purpose of this delay is to give browser time to paint and
+ // create composite layers of PhotoSwipe UI parts (background, controls, caption, arrows).
+ // Which avoids lag at the beginning of scale transition.
+ };
+ startAnimation();
+ };
+
+ /*>>show-hide-transition*/
+
+ /*>>items-controller*/
+ /**
+ *
+ * Controller manages gallery items, their dimensions, and their content.
+ *
+ */
+
+ var _items,
+ _tempPanAreaSize = {},
+ _imagesToAppendPool = [],
+ _initialContentSet,
+ _initialZoomRunning,
+ _controllerDefaultOptions = {
+ index: 0,
+ errorMsg:
+ '',
+ forceProgressiveLoading: false, // TODO
+ preload: [1, 1],
+ getNumItemsFn: function () {
+ return _items.length;
+ },
+ };
+
+ var _getItemAt,
+ _getNumItems,
+ _initialIsLoop,
+ _getZeroBounds = function () {
+ return {
+ center: { x: 0, y: 0 },
+ max: { x: 0, y: 0 },
+ min: { x: 0, y: 0 },
+ };
+ },
+ _calculateSingleItemPanBounds = function (
+ item,
+ realPanElementW,
+ realPanElementH
+ ) {
+ var bounds = item.bounds;
+
+ // position of element when it's centered
+ bounds.center.x = Math.round(
+ (_tempPanAreaSize.x - realPanElementW) / 2
+ );
+ bounds.center.y =
+ Math.round((_tempPanAreaSize.y - realPanElementH) / 2) +
+ item.vGap.top;
+
+ // maximum pan position
+ bounds.max.x =
+ realPanElementW > _tempPanAreaSize.x
+ ? Math.round(_tempPanAreaSize.x - realPanElementW)
+ : bounds.center.x;
+
+ bounds.max.y =
+ realPanElementH > _tempPanAreaSize.y
+ ? Math.round(_tempPanAreaSize.y - realPanElementH) + item.vGap.top
+ : bounds.center.y;
+
+ // minimum pan position
+ bounds.min.x =
+ realPanElementW > _tempPanAreaSize.x ? 0 : bounds.center.x;
+ bounds.min.y =
+ realPanElementH > _tempPanAreaSize.y
+ ? item.vGap.top
+ : bounds.center.y;
+ },
+ _calculateItemSize = function (item, viewportSize, zoomLevel) {
+ if (item.src && !item.loadError) {
+ var isInitial = !zoomLevel;
+
+ if (isInitial) {
+ if (!item.vGap) {
+ item.vGap = { top: 0, bottom: 0 };
+ }
+ // allows overriding vertical margin for individual items
+ _shout("parseVerticalMargin", item);
+ }
+
+ _tempPanAreaSize.x = viewportSize.x;
+ _tempPanAreaSize.y =
+ viewportSize.y - item.vGap.top - item.vGap.bottom;
+
+ if (isInitial) {
+ var hRatio = _tempPanAreaSize.x / item.w;
+ var vRatio = _tempPanAreaSize.y / item.h;
+
+ item.fitRatio = hRatio < vRatio ? hRatio : vRatio;
+ //item.fillRatio = hRatio > vRatio ? hRatio : vRatio;
+
+ var scaleMode = _options.scaleMode;
+
+ if (scaleMode === "orig") {
+ zoomLevel = 1;
+ } else if (scaleMode === "fit") {
+ zoomLevel = item.fitRatio;
+ }
+
+ if (zoomLevel > 1) {
+ zoomLevel = 1;
+ }
+
+ item.initialZoomLevel = zoomLevel;
+
+ if (!item.bounds) {
+ // reuse bounds object
+ item.bounds = _getZeroBounds();
+ }
+ }
+
+ if (!zoomLevel) {
+ return;
+ }
+
+ _calculateSingleItemPanBounds(
+ item,
+ item.w * zoomLevel,
+ item.h * zoomLevel
+ );
+
+ if (isInitial && zoomLevel === item.initialZoomLevel) {
+ item.initialPosition = item.bounds.center;
+ }
+
+ return item.bounds;
+ } else {
+ item.w = item.h = 0;
+ item.initialZoomLevel = item.fitRatio = 1;
+ item.bounds = _getZeroBounds();
+ item.initialPosition = item.bounds.center;
+
+ // if it's not image, we return zero bounds (content is not zoomable)
+ return item.bounds;
+ }
+ },
+ _appendImage = function (
+ index,
+ item,
+ baseDiv,
+ img,
+ preventAnimation,
+ keepPlaceholder
+ ) {
+ if (item.loadError) {
+ return;
+ }
+
+ if (img) {
+ item.imageAppended = true;
+ _setImageSize(
+ item,
+ img,
+ item === self.currItem && _renderMaxResolution
+ );
+
+ baseDiv.appendChild(img);
+
+ if (keepPlaceholder) {
+ setTimeout(function () {
+ if (item && item.loaded && item.placeholder) {
+ item.placeholder.style.display = "none";
+ item.placeholder = null;
+ }
+ }, 500);
+ }
+ }
+ },
+ _preloadImage = function (item) {
+ item.loading = true;
+ item.loaded = false;
+ var img = (item.img = framework.createEl("pswp__img", "img"));
+ var onComplete = function () {
+ item.loading = false;
+ item.loaded = true;
+
+ if (item.loadComplete) {
+ item.loadComplete(item);
+ } else {
+ item.img = null; // no need to store image object
+ }
+ img.onload = img.onerror = null;
+ img = null;
+ };
+ img.onload = onComplete;
+ img.onerror = function () {
+ item.loadError = true;
+ onComplete();
+ };
+
+ img.src = item.src; // + '?a=' + Math.random();
+
+ return img;
+ },
+ _checkForError = function (item, cleanUp) {
+ if (item.src && item.loadError && item.container) {
+ if (cleanUp) {
+ item.container.innerHTML = "";
+ }
+
+ item.container.innerHTML = _options.errorMsg.replace(
+ "%url%",
+ item.src
+ );
+ return true;
+ }
+ },
+ _setImageSize = function (item, img, maxRes) {
+ if (!item.src) {
+ return;
+ }
+
+ if (!img) {
+ img = item.container.lastChild;
+ }
+
+ var w = maxRes ? item.w : Math.round(item.w * item.fitRatio),
+ h = maxRes ? item.h : Math.round(item.h * item.fitRatio);
+
+ if (item.placeholder && !item.loaded) {
+ item.placeholder.style.width = w + "px";
+ item.placeholder.style.height = h + "px";
+ }
+
+ img.style.width = w + "px";
+ img.style.height = h + "px";
+ },
+ _appendImagesPool = function () {
+ if (_imagesToAppendPool.length) {
+ var poolItem;
+
+ for (var i = 0; i < _imagesToAppendPool.length; i++) {
+ poolItem = _imagesToAppendPool[i];
+ if (poolItem.holder.index === poolItem.index) {
+ _appendImage(
+ poolItem.index,
+ poolItem.item,
+ poolItem.baseDiv,
+ poolItem.img,
+ false,
+ poolItem.clearPlaceholder
+ );
+ }
+ }
+ _imagesToAppendPool = [];
+ }
+ };
+
+ _registerModule("Controller", {
+ publicMethods: {
+ lazyLoadItem: function (index) {
+ index = _getLoopedId(index);
+ var item = _getItemAt(index);
+
+ if (!item || ((item.loaded || item.loading) && !_itemsNeedUpdate)) {
+ return;
+ }
+
+ _shout("gettingData", index, item);
+
+ if (!item.src) {
+ return;
+ }
+
+ _preloadImage(item);
+ },
+ initController: function () {
+ framework.extend(_options, _controllerDefaultOptions, true);
+ self.items = _items = items;
+ _getItemAt = self.getItemAt;
+ _getNumItems = _options.getNumItemsFn; //self.getNumItems;
+
+ _initialIsLoop = _options.loop;
+ if (_getNumItems() < 3) {
+ _options.loop = false; // disable loop if less then 3 items
+ }
+
+ _listen("beforeChange", function (diff) {
+ var p = _options.preload,
+ isNext = diff === null ? true : diff >= 0,
+ preloadBefore = Math.min(p[0], _getNumItems()),
+ preloadAfter = Math.min(p[1], _getNumItems()),
+ i;
+
+ for (i = 1; i <= (isNext ? preloadAfter : preloadBefore); i++) {
+ self.lazyLoadItem(_currentItemIndex + i);
+ }
+ for (i = 1; i <= (isNext ? preloadBefore : preloadAfter); i++) {
+ self.lazyLoadItem(_currentItemIndex - i);
+ }
+ });
+
+ _listen("initialLayout", function () {
+ self.currItem.initialLayout =
+ _options.getThumbBoundsFn &&
+ _options.getThumbBoundsFn(_currentItemIndex);
+ });
+
+ _listen("mainScrollAnimComplete", _appendImagesPool);
+ _listen("initialZoomInEnd", _appendImagesPool);
+
+ _listen("destroy", function () {
+ var item;
+ for (var i = 0; i < _items.length; i++) {
+ item = _items[i];
+ // remove reference to DOM elements, for GC
+ if (item.container) {
+ item.container = null;
+ }
+ if (item.placeholder) {
+ item.placeholder = null;
+ }
+ if (item.img) {
+ item.img = null;
+ }
+ if (item.preloader) {
+ item.preloader = null;
+ }
+ if (item.loadError) {
+ item.loaded = item.loadError = false;
+ }
+ }
+ _imagesToAppendPool = null;
+ });
+ },
+
+ getItemAt: function (index) {
+ if (index >= 0) {
+ return _items[index] !== undefined ? _items[index] : false;
+ }
+ return false;
+ },
+
+ allowProgressiveImg: function () {
+ // 1. Progressive image loading isn't working on webkit/blink
+ // when hw-acceleration (e.g. translateZ) is applied to IMG element.
+ // That's why in PhotoSwipe parent element gets zoom transform, not image itself.
+ //
+ // 2. Progressive image loading sometimes blinks in webkit/blink when applying animation to parent element.
+ // That's why it's disabled on touch devices (mainly because of swipe transition)
+ //
+ // 3. Progressive image loading sometimes doesn't work in IE (up to 11).
+
+ // Don't allow progressive loading on non-large touch devices
+ return (
+ _options.forceProgressiveLoading ||
+ !_likelyTouchDevice ||
+ _options.mouseUsed ||
+ screen.width > 1200
+ );
+ // 1200 - to eliminate touch devices with large screen (like Chromebook Pixel)
+ },
+
+ setContent: function (holder, index) {
+ if (_options.loop) {
+ index = _getLoopedId(index);
+ }
+
+ var prevItem = self.getItemAt(holder.index);
+ if (prevItem) {
+ prevItem.container = null;
+ }
+
+ var item = self.getItemAt(index),
+ img;
+
+ if (!item) {
+ holder.el.innerHTML = "";
+ return;
+ }
+
+ // allow to override data
+ _shout("gettingData", index, item);
+
+ holder.index = index;
+ holder.item = item;
+
+ // base container DIV is created only once for each of 3 holders
+ var baseDiv = (item.container =
+ framework.createEl("pswp__zoom-wrap"));
+
+ if (!item.src && item.html) {
+ if (item.html.tagName) {
+ baseDiv.appendChild(item.html);
+ } else {
+ baseDiv.innerHTML = item.html;
+ }
+ }
+
+ _checkForError(item);
+
+ _calculateItemSize(item, _viewportSize);
+
+ if (item.src && !item.loadError && !item.loaded) {
+ item.loadComplete = function (item) {
+ // gallery closed before image finished loading
+ if (!_isOpen) {
+ return;
+ }
+
+ // check if holder hasn't changed while image was loading
+ if (holder && holder.index === index) {
+ if (_checkForError(item, true)) {
+ item.loadComplete = item.img = null;
+ _calculateItemSize(item, _viewportSize);
+ _applyZoomPanToItem(item);
+
+ if (holder.index === _currentItemIndex) {
+ // recalculate dimensions
+ self.updateCurrZoomItem();
+ }
+ return;
+ }
+ if (!item.imageAppended) {
+ if (
+ _features.transform &&
+ (_mainScrollAnimating || _initialZoomRunning)
+ ) {
+ _imagesToAppendPool.push({
+ item: item,
+ baseDiv: baseDiv,
+ img: item.img,
+ index: index,
+ holder: holder,
+ clearPlaceholder: true,
+ });
+ } else {
+ _appendImage(
+ index,
+ item,
+ baseDiv,
+ item.img,
+ _mainScrollAnimating || _initialZoomRunning,
+ true
+ );
+ }
+ } else {
+ // remove preloader & mini-img
+ if (!_initialZoomRunning && item.placeholder) {
+ item.placeholder.style.display = "none";
+ item.placeholder = null;
+ }
+ }
+ }
+
+ item.loadComplete = null;
+ item.img = null; // no need to store image element after it's added
+
+ _shout("imageLoadComplete", index, item);
+ };
+
+ if (framework.features.transform) {
+ var placeholderClassName = "pswp__img pswp__img--placeholder";
+ placeholderClassName += item.msrc
+ ? ""
+ : " pswp__img--placeholder--blank";
+
+ var placeholder = framework.createEl(
+ placeholderClassName,
+ item.msrc ? "img" : ""
+ );
+ if (item.msrc) {
+ placeholder.src = item.msrc;
+ }
+
+ _setImageSize(item, placeholder);
+
+ baseDiv.appendChild(placeholder);
+ item.placeholder = placeholder;
+ }
+
+ if (!item.loading) {
+ _preloadImage(item);
+ }
+
+ if (self.allowProgressiveImg()) {
+ // just append image
+ if (!_initialContentSet && _features.transform) {
+ _imagesToAppendPool.push({
+ item: item,
+ baseDiv: baseDiv,
+ img: item.img,
+ index: index,
+ holder: holder,
+ });
+ } else {
+ _appendImage(index, item, baseDiv, item.img, true, true);
+ }
+ }
+ } else if (item.src && !item.loadError) {
+ // image object is created every time, due to bugs of image loading & delay when switching images
+ img = framework.createEl("pswp__img", "img");
+ img.style.opacity = 1;
+ img.src = item.src;
+ _setImageSize(item, img);
+ _appendImage(index, item, baseDiv, img, true);
+ }
+
+ if (!_initialContentSet && index === _currentItemIndex) {
+ _currZoomElementStyle = baseDiv.style;
+ _showOrHide(item, img || item.img);
+ } else {
+ _applyZoomPanToItem(item);
+ }
+
+ holder.el.innerHTML = "";
+ holder.el.appendChild(baseDiv);
+ },
+
+ cleanSlide: function (item) {
+ if (item.img) {
+ item.img.onload = item.img.onerror = null;
+ }
+ item.loaded = item.loading = item.img = item.imageAppended = false;
+ },
+ },
+ });
+
+ /*>>items-controller*/
+
+ /*>>tap*/
+ /**
+ * tap.js:
+ *
+ * Displatches tap and double-tap events.
+ *
+ */
+
+ var tapTimer,
+ tapReleasePoint = {},
+ _dispatchTapEvent = function (origEvent, releasePoint, pointerType) {
+ var e = document.createEvent("CustomEvent"),
+ eDetail = {
+ origEvent: origEvent,
+ target: origEvent.target,
+ releasePoint: releasePoint,
+ pointerType: pointerType || "touch",
+ };
+
+ e.initCustomEvent("pswpTap", true, true, eDetail);
+ origEvent.target.dispatchEvent(e);
+ };
+
+ _registerModule("Tap", {
+ publicMethods: {
+ initTap: function () {
+ _listen("firstTouchStart", self.onTapStart);
+ _listen("touchRelease", self.onTapRelease);
+ _listen("destroy", function () {
+ tapReleasePoint = {};
+ tapTimer = null;
+ });
+ },
+ onTapStart: function (touchList) {
+ if (touchList.length > 1) {
+ clearTimeout(tapTimer);
+ tapTimer = null;
+ }
+ },
+ onTapRelease: function (e, releasePoint) {
+ if (!releasePoint) {
+ return;
+ }
+
+ if (!_moved && !_isMultitouch && !_numAnimations) {
+ var p0 = releasePoint;
+ if (tapTimer) {
+ clearTimeout(tapTimer);
+ tapTimer = null;
+
+ // Check if taped on the same place
+ if (_isNearbyPoints(p0, tapReleasePoint)) {
+ _shout("doubleTap", p0);
+ return;
+ }
+ }
+
+ if (releasePoint.type === "mouse") {
+ _dispatchTapEvent(e, releasePoint, "mouse");
+ return;
+ }
+
+ var clickedTagName = e.target.tagName.toUpperCase();
+ // avoid double tap delay on buttons and elements that have class pswp__single-tap
+ if (
+ clickedTagName === "BUTTON" ||
+ framework.hasClass(e.target, "pswp__single-tap")
+ ) {
+ _dispatchTapEvent(e, releasePoint);
+ return;
+ }
+
+ _equalizePoints(tapReleasePoint, p0);
+
+ tapTimer = setTimeout(function () {
+ _dispatchTapEvent(e, releasePoint);
+ tapTimer = null;
+ }, 300);
+ }
+ },
+ },
+ });
+
+ /*>>tap*/
+
+ /*>>desktop-zoom*/
+ /**
+ *
+ * desktop-zoom.js:
+ *
+ * - Binds mousewheel event for paning zoomed image.
+ * - Manages "dragging", "zoomed-in", "zoom-out" classes.
+ * (which are used for cursors and zoom icon)
+ * - Adds toggleDesktopZoom function.
+ *
+ */
+
+ var _wheelDelta;
+
+ _registerModule("DesktopZoom", {
+ publicMethods: {
+ initDesktopZoom: function () {
+ if (_oldIE) {
+ // no zoom for old IE (<=8)
+ return;
+ }
+
+ if (_likelyTouchDevice) {
+ // if detected hardware touch support, we wait until mouse is used,
+ // and only then apply desktop-zoom features
+ _listen("mouseUsed", function () {
+ self.setupDesktopZoom();
+ });
+ } else {
+ self.setupDesktopZoom(true);
+ }
+ },
+
+ setupDesktopZoom: function (onInit) {
+ _wheelDelta = {};
+
+ var events = "wheel mousewheel DOMMouseScroll";
+
+ _listen("bindEvents", function () {
+ framework.bind(template, events, self.handleMouseWheel);
+ });
+
+ _listen("unbindEvents", function () {
+ if (_wheelDelta) {
+ framework.unbind(template, events, self.handleMouseWheel);
+ }
+ });
+
+ self.mouseZoomedIn = false;
+
+ var hasDraggingClass,
+ updateZoomable = function () {
+ if (self.mouseZoomedIn) {
+ framework.removeClass(template, "pswp--zoomed-in");
+ self.mouseZoomedIn = false;
+ }
+ if (_currZoomLevel < 1) {
+ framework.addClass(template, "pswp--zoom-allowed");
+ } else {
+ framework.removeClass(template, "pswp--zoom-allowed");
+ }
+ removeDraggingClass();
+ },
+ removeDraggingClass = function () {
+ if (hasDraggingClass) {
+ framework.removeClass(template, "pswp--dragging");
+ hasDraggingClass = false;
+ }
+ };
+
+ _listen("resize", updateZoomable);
+ _listen("afterChange", updateZoomable);
+ _listen("pointerDown", function () {
+ if (self.mouseZoomedIn) {
+ hasDraggingClass = true;
+ framework.addClass(template, "pswp--dragging");
+ }
+ });
+ _listen("pointerUp", removeDraggingClass);
+
+ if (!onInit) {
+ updateZoomable();
+ }
+ },
+
+ handleMouseWheel: function (e) {
+ if (_currZoomLevel <= self.currItem.fitRatio) {
+ if (_options.modal) {
+ if (!_options.closeOnScroll || _numAnimations || _isDragging) {
+ e.preventDefault();
+ } else if (_transformKey && Math.abs(e.deltaY) > 2) {
+ // close PhotoSwipe
+ // if browser supports transforms & scroll changed enough
+ _closedByScroll = true;
+ self.close();
+ }
+ }
+ return true;
+ }
+
+ // allow just one event to fire
+ e.stopPropagation();
+
+ // https://developer.mozilla.org/en-US/docs/Web/Events/wheel
+ _wheelDelta.x = 0;
+
+ if ("deltaX" in e) {
+ if (e.deltaMode === 1 /* DOM_DELTA_LINE */) {
+ // 18 - average line height
+ _wheelDelta.x = e.deltaX * 18;
+ _wheelDelta.y = e.deltaY * 18;
+ } else {
+ _wheelDelta.x = e.deltaX;
+ _wheelDelta.y = e.deltaY;
+ }
+ } else if ("wheelDelta" in e) {
+ if (e.wheelDeltaX) {
+ _wheelDelta.x = -0.16 * e.wheelDeltaX;
+ }
+ if (e.wheelDeltaY) {
+ _wheelDelta.y = -0.16 * e.wheelDeltaY;
+ } else {
+ _wheelDelta.y = -0.16 * e.wheelDelta;
+ }
+ } else if ("detail" in e) {
+ _wheelDelta.y = e.detail;
+ } else {
+ return;
+ }
+
+ _calculatePanBounds(_currZoomLevel, true);
+
+ var newPanX = _panOffset.x - _wheelDelta.x,
+ newPanY = _panOffset.y - _wheelDelta.y;
+
+ // only prevent scrolling in nonmodal mode when not at edges
+ if (
+ _options.modal ||
+ (newPanX <= _currPanBounds.min.x &&
+ newPanX >= _currPanBounds.max.x &&
+ newPanY <= _currPanBounds.min.y &&
+ newPanY >= _currPanBounds.max.y)
+ ) {
+ e.preventDefault();
+ }
+
+ // TODO: use rAF instead of mousewheel?
+ self.panTo(newPanX, newPanY);
+ },
+
+ toggleDesktopZoom: function (centerPoint) {
+ centerPoint = centerPoint || {
+ x: _viewportSize.x / 2 + _offset.x,
+ y: _viewportSize.y / 2 + _offset.y,
+ };
+
+ var doubleTapZoomLevel = _options.getDoubleTapZoom(
+ true,
+ self.currItem
+ );
+ var zoomOut = _currZoomLevel === doubleTapZoomLevel;
+
+ self.mouseZoomedIn = !zoomOut;
+
+ self.zoomTo(
+ zoomOut ? self.currItem.initialZoomLevel : doubleTapZoomLevel,
+ centerPoint,
+ 333
+ );
+ framework[(!zoomOut ? "add" : "remove") + "Class"](
+ template,
+ "pswp--zoomed-in"
+ );
+ },
+ },
+ });
+
+ /*>>desktop-zoom*/
+
+ /*>>history*/
+ /**
+ *
+ * history.js:
+ *
+ * - Back button to close gallery.
+ *
+ * - Unique URL for each slide: example.com/&pid=1&gid=3
+ * (where PID is picture index, and GID and gallery index)
+ *
+ * - Switch URL when slides change.
+ *
+ */
+
+ var _historyDefaultOptions = {
+ history: true,
+ galleryUID: 1,
+ };
+
+ var _historyUpdateTimeout,
+ _hashChangeTimeout,
+ _hashAnimCheckTimeout,
+ _searchChangedByScript,
+ _searchChangedByHistory,
+ _hashReseted,
+ _historyChanged,
+ _closedFromURL,
+ _urlChangedOnce,
+ _url,
+ _supportsPushState,
+ _getSearch = function () {
+ return new URLSearchParams(window.location.search);
+ },
+ _cleanHistoryTimeouts = function () {
+ if (_historyUpdateTimeout) {
+ clearTimeout(_historyUpdateTimeout);
+ }
+
+ if (_hashAnimCheckTimeout) {
+ clearTimeout(_hashAnimCheckTimeout);
+ }
+ },
+ // pid - Picture index
+ // gid - Gallery index
+ _parseItemIndexFromURL = function () {
+ var searchParams = _getSearch(),
+ params = {};
+
+ params.pid = searchParams.get("pid") - 1;
+ if (params.pid < 0) {
+ params.pid = 0;
+ }
+ params.gid = searchParams.get("gid");
+
+ return params;
+ },
+ _updateSearch = function () {
+ if (_hashAnimCheckTimeout) {
+ clearTimeout(_hashAnimCheckTimeout);
+ }
+
+ if (_numAnimations || _isDragging) {
+ // changing browser URL forces layout/paint in some browsers, which causes noticable lag during animation
+ // that's why we update hash only when no animations running
+ _hashAnimCheckTimeout = setTimeout(_updateSearch, 500);
+ return;
+ }
+
+ if (_searchChangedByScript) {
+ clearTimeout(_hashChangeTimeout);
+ } else {
+ _searchChangedByScript = true;
+ }
+
+ var pid = _currentItemIndex + 1;
+ var item = _getItemAt(_currentItemIndex);
+ if (item.hasOwnProperty("pid")) {
+ // carry forward any custom pid assigned to the item
+ pid = item.pid;
+ }
+ var url = new URL(window.location.href);
+ url.searchParams.delete("pid");
+ url.searchParams.delete("gid");
+ url.searchParams.append("gid", _options.galleryUID);
+ url.searchParams.append("pid", pid);
+
+ if (_supportsPushState) {
+ history[_historyChanged ? "replaceState" : "pushState"](
+ "",
+ document.title,
+ url
+ );
+ } else {
+ if (_historyChanged) {
+ window.location.replace(url.href);
+ } else {
+ window.location = url.href;
+ }
+ }
+
+ _historyChanged = true;
+ _hashChangeTimeout = setTimeout(function () {
+ _searchChangedByScript = false;
+ }, 60);
+ };
+
+ _registerModule("History", {
+ publicMethods: {
+ initHistory: function () {
+ framework.extend(_options, _historyDefaultOptions, true);
+
+ if (!_options.history) {
+ return;
+ }
+
+ _urlChangedOnce = false;
+ _closedFromURL = false;
+ _historyChanged = false;
+ _supportsPushState = "pushState" in history;
+ _url = new URL(window.location.href);
+
+ _listen("afterChange", self.updateURL);
+ _listen("unbindEvents", function () {
+ framework.unbind(window, "searchchange", self.onSearchChange);
+ });
+
+ var returnToOriginal = function () {
+ _hashReseted = true;
+ if (!_closedFromURL) {
+ if (_urlChangedOnce) {
+ history.back();
+ } else {
+ _url.searchParams.delete("gid");
+ _url.searchParams.delete("pid");
+ if (_supportsPushState) {
+ history.pushState("", document.title, _url);
+ } else {
+ window.location = _url.href;
+ }
+ }
+ }
+
+ _cleanHistoryTimeouts();
+ };
+
+ _listen("unbindEvents", function () {
+ if (_closedByScroll) {
+ // if PhotoSwipe is closed by scroll, we go "back" before the closing animation starts
+ // this is done to keep the scroll position
+ returnToOriginal();
+ }
+ });
+ _listen("destroy", function () {
+ if (!_hashReseted) {
+ returnToOriginal();
+ }
+ });
+ _listen("firstUpdate", function () {
+ _currentItemIndex = _parseItemIndexFromURL().pid;
+ });
+
+ setTimeout(function () {
+ if (_isOpen) {
+ // hasn't destroyed yet
+ framework.bind(window, "searchchange", self.onSearchChange);
+ }
+ }, 40);
+ },
+ onSearchChange: function () {
+ if (!_searchChangedByScript) {
+ _searchChangedByHistory = true;
+ self.goTo(_parseItemIndexFromURL().pid);
+ _searchChangedByHistory = false;
+ }
+ },
+ updateURL: function () {
+ // Delay the update of URL, to avoid lag during transition,
+ // and to not to trigger actions like "refresh page sound" or "blinking favicon" to often
+
+ _cleanHistoryTimeouts();
+
+ if (_searchChangedByHistory) {
+ return;
+ }
+
+ if (!_historyChanged) {
+ _updateSearch(); // first time
+ } else {
+ _historyUpdateTimeout = setTimeout(_updateSearch, 800);
+ }
+ },
+ },
+ });
+
+ /*>>history*/
+ framework.extend(self, publicMethods);
+ };
+ return PhotoSwipe;
+});
diff --git a/files/pswp/photoswipe.min.js b/files/pswp/photoswipe.min.js
index 913e783..3e3ea59 100644
--- a/files/pswp/photoswipe.min.js
+++ b/files/pswp/photoswipe.min.js
@@ -1,4 +1,5 @@
-/*! PhotoSwipe - v4.1.1 - 2015-12-24
-* http://photoswipe.com
-* Copyright (c) 2015 Dmitry Semenov; */
-!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.PhotoSwipe=b()}(this,function(){"use strict";var a=function(a,b,c,d){var e={features:null,bind:function(a,b,c,d){var e=(d?"remove":"add")+"EventListener";b=b.split(" ");for(var f=0;f0&&(g=parseInt(g[1],10),g>=1&&8>g&&(d.isOldIOSPhone=!0))}var h=f.match(/Android\s([0-9\.]*)/),i=h?h[1]:0;i=parseFloat(i),i>=1&&(4.4>i&&(d.isOldAndroid=!0),d.androidVersion=i),d.isMobileOpera=/opera mini|opera mobi/i.test(f)}for(var j,k,l=["transform","perspective","animationName"],m=["","webkit","Moz","ms","O"],n=0;4>n;n++){c=m[n];for(var o=0;3>o;o++)j=l[o],k=c+(c?j.charAt(0).toUpperCase()+j.slice(1):j),!d[j]&&k in b&&(d[j]=k);c&&!d.raf&&(c=c.toLowerCase(),d.raf=window[c+"RequestAnimationFrame"],d.raf&&(d.caf=window[c+"CancelAnimationFrame"]||window[c+"CancelRequestAnimationFrame"]))}if(!d.raf){var p=0;d.raf=function(a){var b=(new Date).getTime(),c=Math.max(0,16-(b-p)),d=window.setTimeout(function(){a(b+c)},c);return p=b+c,d},d.caf=function(a){clearTimeout(a)}}return d.svg=!!document.createElementNS&&!!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,e.features=d,d}};e.detectFeatures(),e.features.oldIE&&(e.bind=function(a,b,c,d){b=b.split(" ");for(var e,f=(d?"detach":"attach")+"Event",g=function(){c.handleEvent.call(c)},h=0;hb-1?a-b:0>a?b+a:a},Aa={},Ba=function(a,b){return Aa[a]||(Aa[a]=[]),Aa[a].push(b)},Ca=function(a){var b=Aa[a];if(b){var c=Array.prototype.slice.call(arguments);c.shift();for(var d=0;df.currItem.fitRatio?xa||(lc(f.currItem,!1,!0),xa=!0):xa&&(lc(f.currItem),xa=!1)),Fa(da,oa.x,oa.y,s))},Ha=function(a){a.container&&Fa(a.container.style,a.initialPosition.x,a.initialPosition.y,a.initialZoomLevel,a)},Ia=function(a,b){b[E]=u+a+"px, 0px"+v},Ja=function(a,b){if(!i.loop&&b){var c=m+(sa.x*qa-a)/sa.x,d=Math.round(a-sb.x);(0>c&&d>0||c>=_b()-1&&0>d)&&(a=sb.x+d*i.mainScrollEndFriction)}sb.x=a,Ia(a,n)},Ka=function(a,b){var c=tb[a]-ra[a];return na[a]+ma[a]+c-c*(b/t)},La=function(a,b){a.x=b.x,a.y=b.y,b.id&&(a.id=b.id)},Ma=function(a){a.x=Math.round(a.x),a.y=Math.round(a.y)},Na=null,Oa=function(){Na&&(e.unbind(document,"mousemove",Oa),e.addClass(a,"pswp--has_mouse"),i.mouseUsed=!0,Ca("mouseUsed")),Na=setTimeout(function(){Na=null},100)},Pa=function(){e.bind(document,"keydown",f),N.transform&&e.bind(f.scrollWrap,"click",f),i.mouseUsed||e.bind(document,"mousemove",Oa),e.bind(window,"resize scroll",f),Ca("bindEvents")},Qa=function(){e.unbind(window,"resize",f),e.unbind(window,"scroll",r.scroll),e.unbind(document,"keydown",f),e.unbind(document,"mousemove",Oa),N.transform&&e.unbind(f.scrollWrap,"click",f),U&&e.unbind(window,p,f),Ca("unbindEvents")},Ra=function(a,b){var c=hc(f.currItem,pa,a);return b&&(ca=c),c},Sa=function(a){return a||(a=f.currItem),a.initialZoomLevel},Ta=function(a){return a||(a=f.currItem),a.w>0?i.maxSpreadZoom:1},Ua=function(a,b,c,d){return d===f.currItem.initialZoomLevel?(c[a]=f.currItem.initialPosition[a],!0):(c[a]=Ka(a,d),c[a]>b.min[a]?(c[a]=b.min[a],!0):c[a]1?1:a.fitRatio,c=a.container.style,d=b*a.w,e=b*a.h;c.width=d+"px",c.height=e+"px",c.left=a.initialPosition.x+"px",c.top=a.initialPosition.y+"px"},Ga=function(){if(da){var a=da,b=f.currItem,c=b.fitRatio>1?1:b.fitRatio,d=c*b.w,e=c*b.h;a.width=d+"px",a.height=e+"px",a.left=oa.x+"px",a.top=oa.y+"px"}}},Wa=function(a){var b="";i.escKey&&27===a.keyCode?b="close":i.arrowKeys&&(37===a.keyCode?b="prev":39===a.keyCode&&(b="next")),b&&(a.ctrlKey||a.altKey||a.shiftKey||a.metaKey||(a.preventDefault?a.preventDefault():a.returnValue=!1,f[b]()))},Xa=function(a){a&&(X||W||ea||S)&&(a.preventDefault(),a.stopPropagation())},Ya=function(){f.setScrollOffset(0,e.getScrollY())},Za={},$a=0,_a=function(a){Za[a]&&(Za[a].raf&&I(Za[a].raf),$a--,delete Za[a])},ab=function(a){Za[a]&&_a(a),Za[a]||($a++,Za[a]={})},bb=function(){for(var a in Za)Za.hasOwnProperty(a)&&_a(a)},cb=function(a,b,c,d,e,f,g){var h,i=Da();ab(a);var j=function(){if(Za[a]){if(h=Da()-i,h>=d)return _a(a),f(c),void(g&&g());f((c-b)*e(h/d)+b),Za[a].raf=H(j)}};j()},db={shout:Ca,listen:Ba,viewportSize:pa,options:i,isMainScrollAnimating:function(){return ea},getZoomLevel:function(){return s},getCurrentIndex:function(){return m},isDragging:function(){return U},isZooming:function(){return _},setScrollOffset:function(a,b){ra.x=a,M=ra.y=b,Ca("updateScrollOffset",ra)},applyZoomPan:function(a,b,c,d){oa.x=b,oa.y=c,s=a,Ga(d)},init:function(){if(!j&&!k){var c;f.framework=e,f.template=a,f.bg=e.getChildByClass(a,"pswp__bg"),J=a.className,j=!0,N=e.detectFeatures(),H=N.raf,I=N.caf,E=N.transform,L=N.oldIE,f.scrollWrap=e.getChildByClass(a,"pswp__scroll-wrap"),f.container=e.getChildByClass(f.scrollWrap,"pswp__container"),n=f.container.style,f.itemHolders=y=[{el:f.container.children[0],wrap:0,index:-1},{el:f.container.children[1],wrap:0,index:-1},{el:f.container.children[2],wrap:0,index:-1}],y[0].el.style.display=y[2].el.style.display="none",Va(),r={resize:f.updateSize,scroll:Ya,keydown:Wa,click:Xa};var d=N.isOldIOSPhone||N.isOldAndroid||N.isMobileOpera;for(N.animationName&&N.transform&&!d||(i.showAnimationDuration=i.hideAnimationDuration=0),c=0;cm||m>=_b())&&(m=0),f.currItem=$b(m),(N.isOldIOSPhone||N.isOldAndroid)&&(ua=!1),a.setAttribute("aria-hidden","false"),i.modal&&(ua?a.style.position="fixed":(a.style.position="absolute",a.style.top=e.getScrollY()+"px")),void 0===M&&(Ca("initialLayout"),M=K=e.getScrollY());var l="pswp--open ";for(i.mainClass&&(l+=i.mainClass+" "),i.showHideOpacity&&(l+="pswp--animate_opacity "),l+=G?"pswp--touch":"pswp--notouch",l+=N.animationName?" pswp--css_animation":"",l+=N.svg?" pswp--svg":"",e.addClass(a,l),f.updateSize(),o=-1,ta=null,c=0;h>c;c++)Ia((c+o)*sa.x,y[c].el.style);L||e.bind(f.scrollWrap,q,f),Ba("initialZoomInEnd",function(){f.setContent(y[0],m-1),f.setContent(y[2],m+1),y[0].el.style.display=y[2].el.style.display="block",i.focus&&a.focus(),Pa()}),f.setContent(y[1],m),f.updateCurrItem(),Ca("afterInit"),ua||(w=setInterval(function(){$a||U||_||s!==f.currItem.initialZoomLevel||f.updateSize()},1e3)),e.addClass(a,"pswp--visible")}},close:function(){j&&(j=!1,k=!0,Ca("close"),Qa(),bc(f.currItem,null,!0,f.destroy))},destroy:function(){Ca("destroy"),Wb&&clearTimeout(Wb),a.setAttribute("aria-hidden","true"),a.className=J,w&&clearInterval(w),e.unbind(f.scrollWrap,q,f),e.unbind(window,"scroll",f),yb(),bb(),Aa=null},panTo:function(a,b,c){c||(a>ca.min.x?a=ca.min.x:aca.min.y?b=ca.min.y:ba;a++)y[a].item&&(y[a].item.needsUpdate=!0)},updateCurrItem:function(a){if(0!==ta){var b,c=Math.abs(ta);if(!(a&&2>c)){f.currItem=$b(m),xa=!1,Ca("beforeChange",ta),c>=h&&(o+=ta+(ta>0?-h:h),c=h);for(var d=0;c>d;d++)ta>0?(b=y.shift(),y[h-1]=b,o++,Ia((o+2)*sa.x,b.el.style),f.setContent(b,m-c+d+1+1)):(b=y.pop(),y.unshift(b),o--,Ia(o*sa.x,b.el.style),f.setContent(b,m+c-d-1-1));if(da&&1===Math.abs(ta)){var e=$b(z);e.initialZoomLevel!==s&&(hc(e,pa),lc(e),Ha(e))}ta=0,f.updateCurrZoomItem(),z=m,Ca("afterChange")}}},updateSize:function(b){if(!ua&&i.modal){var c=e.getScrollY();if(M!==c&&(a.style.top=c+"px",M=c),!b&&wa.x===window.innerWidth&&wa.y===window.innerHeight)return;wa.x=window.innerWidth,wa.y=window.innerHeight,a.style.height=wa.y+"px"}if(pa.x=f.scrollWrap.clientWidth,pa.y=f.scrollWrap.clientHeight,Ya(),sa.x=pa.x+Math.round(pa.x*i.spacing),sa.y=pa.y,Ja(sa.x*qa),Ca("beforeResize"),void 0!==o){for(var d,g,j,k=0;h>k;k++)d=y[k],Ia((k+o)*sa.x,d.el.style),j=m+k-1,i.loop&&_b()>2&&(j=za(j)),g=$b(j),g&&(x||g.needsUpdate||!g.bounds)?(f.cleanSlide(g),f.setContent(d,j),1===k&&(f.currItem=g,f.updateCurrZoomItem(!0)),g.needsUpdate=!1):-1===d.index&&j>=0&&f.setContent(d,j),g&&g.container&&(hc(g,pa),lc(g),Ha(g));x=!1}t=s=f.currItem.initialZoomLevel,ca=f.currItem.bounds,ca&&(oa.x=ca.center.x,oa.y=ca.center.y,Ga(!0)),Ca("resize")},zoomTo:function(a,b,c,d,f){b&&(t=s,tb.x=Math.abs(b.x)-oa.x,tb.y=Math.abs(b.y)-oa.y,La(na,oa));var g=Ra(a,!1),h={};Ua("x",g,h,a),Ua("y",g,h,a);var i=s,j={x:oa.x,y:oa.y};Ma(h);var k=function(b){1===b?(s=a,oa.x=h.x,oa.y=h.y):(s=(a-i)*b+i,oa.x=(h.x-j.x)*b+j.x,oa.y=(h.y-j.y)*b+j.y),f&&f(b),Ga(1===b)};c?cb("customZoomTo",0,1,c,d||e.easing.sine.inOut,k):k(1)}},eb=30,fb=10,gb={},hb={},ib={},jb={},kb={},lb=[],mb={},nb=[],ob={},pb=0,qb=la(),rb=0,sb=la(),tb=la(),ub=la(),vb=function(a,b){return a.x===b.x&&a.y===b.y},wb=function(a,b){return Math.abs(a.x-b.x)-1?!1:b(a)?a:Bb(a.parentNode,b):!1},Cb={},Db=function(a,b){return Cb.prevent=!Bb(a.target,i.isClickableElement),Ca("preventDragEvent",a,b,Cb),Cb.prevent},Eb=function(a,b){return b.x=a.pageX,b.y=a.pageY,b.id=a.identifier,b},Fb=function(a,b,c){c.x=.5*(a.x+b.x),c.y=.5*(a.y+b.y)},Gb=function(a,b,c){if(a-P>50){var d=nb.length>2?nb.shift():{};d.x=b,d.y=c,nb.push(d),P=a}},Hb=function(){var a=oa.y-f.currItem.initialPosition.y;return 1-Math.abs(a/(pa.y/2))},Ib={},Jb={},Kb=[],Lb=function(a){for(;Kb.length>0;)Kb.pop();return F?(ka=0,lb.forEach(function(a){0===ka?Kb[0]=a:1===ka&&(Kb[1]=a),ka++})):a.type.indexOf("touch")>-1?a.touches&&a.touches.length>0&&(Kb[0]=Eb(a.touches[0],Ib),a.touches.length>1&&(Kb[1]=Eb(a.touches[1],Jb))):(Ib.x=a.pageX,Ib.y=a.pageY,Ib.id="",Kb[0]=Ib),Kb},Mb=function(a,b){var c,d,e,g,h=0,j=oa[a]+b[a],k=b[a]>0,l=sb.x+b.x,m=sb.x-mb.x;return c=j>ca.min[a]||jca.min[a]&&(c=i.panEndFriction,h=ca.min[a]-j,d=ca.min[a]-na[a]),(0>=d||0>m)&&_b()>1?(g=l,0>m&&l>mb.x&&(g=mb.x)):ca.min.x!==ca.max.x&&(e=j)):(j=d||m>0)&&_b()>1?(g=l,m>0&&lf.currItem.fitRatio&&(oa[a]+=b[a]*c)):(void 0!==g&&(Ja(g,!0),Z=g===mb.x?!1:!0),ca.min.x!==ca.max.x&&(void 0!==e?oa.x=e:Z||(oa.x+=b.x*c)),void 0!==g)},Nb=function(a){if(!("mousedown"===a.type&&a.button>0)){if(Zb)return void a.preventDefault();if(!T||"mousedown"!==a.type){if(Db(a,!0)&&a.preventDefault(),Ca("pointerDown"),F){var b=e.arraySearch(lb,a.pointerId,"id");0>b&&(b=lb.length),lb[b]={x:a.pageX,y:a.pageY,id:a.pointerId}}var c=Lb(a),d=c.length;$=null,bb(),U&&1!==d||(U=ga=!0,e.bind(window,p,f),R=ja=ha=S=Z=X=V=W=!1,fa=null,Ca("firstTouchStart",c),La(na,oa),ma.x=ma.y=0,La(jb,c[0]),La(kb,jb),mb.x=sa.x*qa,nb=[{x:jb.x,y:jb.y}],P=O=Da(),Ra(s,!0),yb(),zb()),!_&&d>1&&!ea&&!Z&&(t=s,W=!1,_=V=!0,ma.y=ma.x=0,La(na,oa),La(gb,c[0]),La(hb,c[1]),Fb(gb,hb,ub),tb.x=Math.abs(ub.x)-oa.x,tb.y=Math.abs(ub.y)-oa.y,aa=ba=xb(gb,hb))}}},Ob=function(a){if(a.preventDefault(),F){var b=e.arraySearch(lb,a.pointerId,"id");if(b>-1){var c=lb[b];c.x=a.pageX,c.y=a.pageY}}if(U){var d=Lb(a);if(fa||X||_)$=d;else if(sb.x!==sa.x*qa)fa="h";else{var f=Math.abs(d[0].x-jb.x)-Math.abs(d[0].y-jb.y);Math.abs(f)>=fb&&(fa=f>0?"h":"v",$=d)}}},Pb=function(){if($){var a=$.length;if(0!==a)if(La(gb,$[0]),ib.x=gb.x-jb.x,ib.y=gb.y-jb.y,_&&a>1){if(jb.x=gb.x,jb.y=gb.y,!ib.x&&!ib.y&&vb($[1],hb))return;La(hb,$[1]),W||(W=!0,Ca("zoomGestureStarted"));var b=xb(gb,hb),c=Ub(b);c>f.currItem.initialZoomLevel+f.currItem.initialZoomLevel/15&&(ja=!0);var d=1,e=Sa(),g=Ta();if(e>c)if(i.pinchToClose&&!ja&&t<=f.currItem.initialZoomLevel){var h=e-c,j=1-h/(e/1.2);Ea(j),Ca("onPinchClose",j),ha=!0}else d=(e-c)/e,d>1&&(d=1),c=e-d*(e/3);else c>g&&(d=(c-g)/(6*e),d>1&&(d=1),c=g+d*e);0>d&&(d=0),aa=b,Fb(gb,hb,qb),ma.x+=qb.x-ub.x,ma.y+=qb.y-ub.y,La(ub,qb),oa.x=Ka("x",c),oa.y=Ka("y",c),R=c>s,s=c,Ga()}else{if(!fa)return;if(ga&&(ga=!1,Math.abs(ib.x)>=fb&&(ib.x-=$[0].x-kb.x),Math.abs(ib.y)>=fb&&(ib.y-=$[0].y-kb.y)),jb.x=gb.x,jb.y=gb.y,0===ib.x&&0===ib.y)return;if("v"===fa&&i.closeOnVerticalDrag&&!Ab()){ma.y+=ib.y,oa.y+=ib.y;var k=Hb();return S=!0,Ca("onVerticalDrag",k),Ea(k),void Ga()}Gb(Da(),gb.x,gb.y),X=!0,ca=f.currItem.bounds;var l=Mb("x",ib);l||(Mb("y",ib),Ma(oa),Ga())}}},Qb=function(a){if(N.isOldAndroid){if(T&&"mouseup"===a.type)return;a.type.indexOf("touch")>-1&&(clearTimeout(T),T=setTimeout(function(){T=0},600))}Ca("pointerUp"),Db(a,!1)&&a.preventDefault();var b;if(F){var c=e.arraySearch(lb,a.pointerId,"id");if(c>-1)if(b=lb.splice(c,1)[0],navigator.pointerEnabled)b.type=a.pointerType||"mouse";else{var d={4:"mouse",2:"touch",3:"pen"};b.type=d[a.pointerType],b.type||(b.type=a.pointerType||"mouse")}}var g,h=Lb(a),j=h.length;if("mouseup"===a.type&&(j=0),2===j)return $=null,!0;1===j&&La(kb,h[0]),0!==j||fa||ea||(b||("mouseup"===a.type?b={x:a.pageX,y:a.pageY,type:"mouse"}:a.changedTouches&&a.changedTouches[0]&&(b={x:a.changedTouches[0].pageX,y:a.changedTouches[0].pageY,type:"touch"})),Ca("touchRelease",a,b));var k=-1;if(0===j&&(U=!1,e.unbind(window,p,f),yb(),_?k=0:-1!==rb&&(k=Da()-rb)),rb=1===j?Da():-1,g=-1!==k&&150>k?"zoom":"swipe",_&&2>j&&(_=!1,1===j&&(g="zoomPointerUp"),Ca("zoomGestureEnded")),$=null,X||W||ea||S)if(bb(),Q||(Q=Rb()),Q.calculateSwipeSpeed("x"),S){var l=Hb();if(lf.currItem.fitRatio&&Sb(Q))}},Rb=function(){var a,b,c={lastFlickOffset:{},lastFlickDist:{},lastFlickSpeed:{},slowDownRatio:{},slowDownRatioReverse:{},speedDecelerationRatio:{},speedDecelerationRatioAbs:{},distanceOffset:{},backAnimDestination:{},backAnimStarted:{},calculateSwipeSpeed:function(d){nb.length>1?(a=Da()-P+50,b=nb[nb.length-2][d]):(a=Da()-O,b=kb[d]),c.lastFlickOffset[d]=jb[d]-b,c.lastFlickDist[d]=Math.abs(c.lastFlickOffset[d]),c.lastFlickDist[d]>20?c.lastFlickSpeed[d]=c.lastFlickOffset[d]/a:c.lastFlickSpeed[d]=0,Math.abs(c.lastFlickSpeed[d])<.1&&(c.lastFlickSpeed[d]=0),c.slowDownRatio[d]=.95,c.slowDownRatioReverse[d]=1-c.slowDownRatio[d],c.speedDecelerationRatio[d]=1},calculateOverBoundsAnimOffset:function(a,b){c.backAnimStarted[a]||(oa[a]>ca.min[a]?c.backAnimDestination[a]=ca.min[a]:oa[a]eb&&(h||b.lastFlickOffset.x>20)?d=-1:-eb>g&&(h||b.lastFlickOffset.x<-20)&&(d=1)}var j;d&&(m+=d,0>m?(m=i.loop?_b()-1:0,j=!0):m>=_b()&&(m=i.loop?0:_b()-1,j=!0),(!j||i.loop)&&(ta+=d,qa-=d,c=!0));var k,l=sa.x*qa,n=Math.abs(l-sb.x);return c||l>sb.x==b.lastFlickSpeed.x>0?(k=Math.abs(b.lastFlickSpeed.x)>0?n/Math.abs(b.lastFlickSpeed.x):333,k=Math.min(k,400),k=Math.max(k,250)):k=333,pb===m&&(c=!1),ea=!0,Ca("mainScrollAnimStart"),cb("mainScroll",sb.x,l,k,e.easing.cubic.out,Ja,function(){bb(),ea=!1,pb=-1,(c||pb!==m)&&f.updateCurrItem(),Ca("mainScrollAnimComplete")}),c&&f.updateCurrItem(!0),c},Ub=function(a){return 1/ba*a*t},Vb=function(){var a=s,b=Sa(),c=Ta();b>s?a=b:s>c&&(a=c);var d,g=1,h=ia;return ha&&!R&&!ja&&b>s?(f.close(),!0):(ha&&(d=function(a){Ea((g-h)*a+h)}),f.zoomTo(a,0,200,e.easing.cubic.out,d),!0)};ya("Gestures",{publicMethods:{initGestures:function(){var a=function(a,b,c,d,e){A=a+b,B=a+c,C=a+d,D=e?a+e:""};F=N.pointerEvent,F&&N.touch&&(N.touch=!1),F?navigator.pointerEnabled?a("pointer","down","move","up","cancel"):a("MSPointer","Down","Move","Up","Cancel"):N.touch?(a("touch","start","move","end","cancel"),G=!0):a("mouse","down","move","up"),p=B+" "+C+" "+D,q=A,F&&!G&&(G=navigator.maxTouchPoints>1||navigator.msMaxTouchPoints>1),f.likelyTouchDevice=G,r[A]=Nb,r[B]=Ob,r[C]=Qb,D&&(r[D]=r[C]),N.touch&&(q+=" mousedown",p+=" mousemove mouseup",r.mousedown=r[A],r.mousemove=r[B],r.mouseup=r[C]),G||(i.allowPanToNext=!1)}}});var Wb,Xb,Yb,Zb,$b,_b,ac,bc=function(b,c,d,g){Wb&&clearTimeout(Wb),Zb=!0,Yb=!0;var h;b.initialLayout?(h=b.initialLayout,b.initialLayout=null):h=i.getThumbBoundsFn&&i.getThumbBoundsFn(m);var j=d?i.hideAnimationDuration:i.showAnimationDuration,k=function(){_a("initialZoom"),d?(f.template.removeAttribute("style"),f.bg.removeAttribute("style")):(Ea(1),c&&(c.style.display="block"),e.addClass(a,"pswp--animated-in"),Ca("initialZoom"+(d?"OutEnd":"InEnd"))),g&&g(),Zb=!1};if(!j||!h||void 0===h.x)return Ca("initialZoom"+(d?"Out":"In")),s=b.initialZoomLevel,La(oa,b.initialPosition),Ga(),a.style.opacity=d?0:1,Ea(1),void(j?setTimeout(function(){k()},j):k());var n=function(){var c=l,g=!f.currItem.src||f.currItem.loadError||i.showHideOpacity;b.miniImg&&(b.miniImg.style.webkitBackfaceVisibility="hidden"),d||(s=h.w/b.w,oa.x=h.x,oa.y=h.y-K,f[g?"template":"bg"].style.opacity=.001,Ga()),ab("initialZoom"),d&&!c&&e.removeClass(a,"pswp--animated-in"),g&&(d?e[(c?"remove":"add")+"Class"](a,"pswp--animate_opacity"):setTimeout(function(){e.addClass(a,"pswp--animate_opacity")},30)),Wb=setTimeout(function(){if(Ca("initialZoom"+(d?"Out":"In")),d){var f=h.w/b.w,i={x:oa.x,y:oa.y},l=s,m=ia,n=function(b){1===b?(s=f,oa.x=h.x,oa.y=h.y-M):(s=(f-l)*b+l,oa.x=(h.x-i.x)*b+i.x,oa.y=(h.y-M-i.y)*b+i.y),Ga(),g?a.style.opacity=1-b:Ea(m-b*m)};c?cb("initialZoom",0,1,j,e.easing.cubic.out,n,k):(n(1),Wb=setTimeout(k,j+20))}else s=b.initialZoomLevel,La(oa,b.initialPosition),Ga(),Ea(1),g?a.style.opacity=1:Ea(1),Wb=setTimeout(k,j+20)},d?25:90)};n()},cc={},dc=[],ec={index:0,errorMsg:'',forceProgressiveLoading:!1,preload:[1,1],getNumItemsFn:function(){return Xb.length}},fc=function(){return{center:{x:0,y:0},max:{x:0,y:0},min:{x:0,y:0}}},gc=function(a,b,c){var d=a.bounds;d.center.x=Math.round((cc.x-b)/2),d.center.y=Math.round((cc.y-c)/2)+a.vGap.top,d.max.x=b>cc.x?Math.round(cc.x-b):d.center.x,d.max.y=c>cc.y?Math.round(cc.y-c)+a.vGap.top:d.center.y,d.min.x=b>cc.x?0:d.center.x,d.min.y=c>cc.y?a.vGap.top:d.center.y},hc=function(a,b,c){if(a.src&&!a.loadError){var d=!c;if(d&&(a.vGap||(a.vGap={top:0,bottom:0}),Ca("parseVerticalMargin",a)),cc.x=b.x,cc.y=b.y-a.vGap.top-a.vGap.bottom,d){var e=cc.x/a.w,f=cc.y/a.h;a.fitRatio=f>e?e:f;var g=i.scaleMode;"orig"===g?c=1:"fit"===g&&(c=a.fitRatio),c>1&&(c=1),a.initialZoomLevel=c,a.bounds||(a.bounds=fc())}if(!c)return;return gc(a,a.w*c,a.h*c),d&&c===a.initialZoomLevel&&(a.initialPosition=a.bounds.center),a.bounds}return a.w=a.h=0,a.initialZoomLevel=a.fitRatio=1,a.bounds=fc(),a.initialPosition=a.bounds.center,a.bounds},ic=function(a,b,c,d,e,g){b.loadError||d&&(b.imageAppended=!0,lc(b,d,b===f.currItem&&xa),c.appendChild(d),g&&setTimeout(function(){b&&b.loaded&&b.placeholder&&(b.placeholder.style.display="none",b.placeholder=null)},500))},jc=function(a){a.loading=!0,a.loaded=!1;var b=a.img=e.createEl("pswp__img","img"),c=function(){a.loading=!1,a.loaded=!0,a.loadComplete?a.loadComplete(a):a.img=null,b.onload=b.onerror=null,b=null};return b.onload=c,b.onerror=function(){a.loadError=!0,c()},b.src=a.src,b},kc=function(a,b){return a.src&&a.loadError&&a.container?(b&&(a.container.innerHTML=""),a.container.innerHTML=i.errorMsg.replace("%url%",a.src),!0):void 0},lc=function(a,b,c){if(a.src){b||(b=a.container.lastChild);var d=c?a.w:Math.round(a.w*a.fitRatio),e=c?a.h:Math.round(a.h*a.fitRatio);a.placeholder&&!a.loaded&&(a.placeholder.style.width=d+"px",a.placeholder.style.height=e+"px"),b.style.width=d+"px",b.style.height=e+"px"}},mc=function(){if(dc.length){for(var a,b=0;b=0,e=Math.min(c[0],_b()),g=Math.min(c[1],_b());for(b=1;(d?g:e)>=b;b++)f.lazyLoadItem(m+b);for(b=1;(d?e:g)>=b;b++)f.lazyLoadItem(m-b)}),Ba("initialLayout",function(){f.currItem.initialLayout=i.getThumbBoundsFn&&i.getThumbBoundsFn(m)}),Ba("mainScrollAnimComplete",mc),Ba("initialZoomInEnd",mc),Ba("destroy",function(){for(var a,b=0;b=0&&void 0!==Xb[a]?Xb[a]:!1},allowProgressiveImg:function(){return i.forceProgressiveLoading||!G||i.mouseUsed||screen.width>1200},setContent:function(a,b){i.loop&&(b=za(b));var c=f.getItemAt(a.index);c&&(c.container=null);var d,g=f.getItemAt(b);if(!g)return void(a.el.innerHTML="");Ca("gettingData",b,g),a.index=b,a.item=g;var h=g.container=e.createEl("pswp__zoom-wrap");if(!g.src&&g.html&&(g.html.tagName?h.appendChild(g.html):h.innerHTML=g.html),kc(g),hc(g,pa),!g.src||g.loadError||g.loaded)g.src&&!g.loadError&&(d=e.createEl("pswp__img","img"),d.style.opacity=1,d.src=g.src,lc(g,d),ic(b,g,h,d,!0));else{if(g.loadComplete=function(c){if(j){if(a&&a.index===b){if(kc(c,!0))return c.loadComplete=c.img=null,hc(c,pa),Ha(c),void(a.index===m&&f.updateCurrZoomItem());c.imageAppended?!Zb&&c.placeholder&&(c.placeholder.style.display="none",c.placeholder=null):N.transform&&(ea||Zb)?dc.push({item:c,baseDiv:h,img:c.img,index:b,holder:a,clearPlaceholder:!0}):ic(b,c,h,c.img,ea||Zb,!0)}c.loadComplete=null,c.img=null,Ca("imageLoadComplete",b,c)}},e.features.transform){var k="pswp__img pswp__img--placeholder";k+=g.msrc?"":" pswp__img--placeholder--blank";var l=e.createEl(k,g.msrc?"img":"");g.msrc&&(l.src=g.msrc),lc(g,l),h.appendChild(l),g.placeholder=l}g.loading||jc(g),f.allowProgressiveImg()&&(!Yb&&N.transform?dc.push({item:g,baseDiv:h,img:g.img,index:b,holder:a}):ic(b,g,h,g.img,!0,!0))}Yb||b!==m?Ha(g):(da=h.style,bc(g,d||g.img)),a.el.innerHTML="",a.el.appendChild(h)},cleanSlide:function(a){a.img&&(a.img.onload=a.img.onerror=null),a.loaded=a.loading=a.img=a.imageAppended=!1}}});var nc,oc={},pc=function(a,b,c){var d=document.createEvent("CustomEvent"),e={origEvent:a,target:a.target,releasePoint:b,pointerType:c||"touch"};d.initCustomEvent("pswpTap",!0,!0,e),a.target.dispatchEvent(d)};ya("Tap",{publicMethods:{initTap:function(){Ba("firstTouchStart",f.onTapStart),Ba("touchRelease",f.onTapRelease),Ba("destroy",function(){oc={},nc=null})},onTapStart:function(a){a.length>1&&(clearTimeout(nc),nc=null)},onTapRelease:function(a,b){if(b&&!X&&!V&&!$a){var c=b;if(nc&&(clearTimeout(nc),nc=null,wb(c,oc)))return void Ca("doubleTap",c);if("mouse"===b.type)return void pc(a,b,"mouse");var d=a.target.tagName.toUpperCase();if("BUTTON"===d||e.hasClass(a.target,"pswp__single-tap"))return void pc(a,b);La(oc,c),nc=setTimeout(function(){pc(a,b),nc=null},300)}}}});var qc;ya("DesktopZoom",{publicMethods:{initDesktopZoom:function(){L||(G?Ba("mouseUsed",function(){f.setupDesktopZoom()}):f.setupDesktopZoom(!0))},setupDesktopZoom:function(b){qc={};var c="wheel mousewheel DOMMouseScroll";Ba("bindEvents",function(){e.bind(a,c,f.handleMouseWheel)}),Ba("unbindEvents",function(){qc&&e.unbind(a,c,f.handleMouseWheel)}),f.mouseZoomedIn=!1;var d,g=function(){f.mouseZoomedIn&&(e.removeClass(a,"pswp--zoomed-in"),f.mouseZoomedIn=!1),1>s?e.addClass(a,"pswp--zoom-allowed"):e.removeClass(a,"pswp--zoom-allowed"),h()},h=function(){d&&(e.removeClass(a,"pswp--dragging"),d=!1)};Ba("resize",g),Ba("afterChange",g),Ba("pointerDown",function(){f.mouseZoomedIn&&(d=!0,e.addClass(a,"pswp--dragging"))}),Ba("pointerUp",h),b||g()},handleMouseWheel:function(a){if(s<=f.currItem.fitRatio)return i.modal&&(!i.closeOnScroll||$a||U?a.preventDefault():E&&Math.abs(a.deltaY)>2&&(l=!0,f.close())),!0;if(a.stopPropagation(),qc.x=0,"deltaX"in a)1===a.deltaMode?(qc.x=18*a.deltaX,qc.y=18*a.deltaY):(qc.x=a.deltaX,qc.y=a.deltaY);else if("wheelDelta"in a)a.wheelDeltaX&&(qc.x=-.16*a.wheelDeltaX),a.wheelDeltaY?qc.y=-.16*a.wheelDeltaY:qc.y=-.16*a.wheelDelta;else{if(!("detail"in a))return;qc.y=a.detail}Ra(s,!0);var b=oa.x-qc.x,c=oa.y-qc.y;(i.modal||b<=ca.min.x&&b>=ca.max.x&&c<=ca.min.y&&c>=ca.max.y)&&a.preventDefault(),f.panTo(b,c)},toggleDesktopZoom:function(b){b=b||{x:pa.x/2+ra.x,y:pa.y/2+ra.y};var c=i.getDoubleTapZoom(!0,f.currItem),d=s===c;f.mouseZoomedIn=!d,f.zoomTo(d?f.currItem.initialZoomLevel:c,b,333),e[(d?"remove":"add")+"Class"](a,"pswp--zoomed-in")}}});var rc,sc,tc,uc,vc,wc,xc,yc,zc,Ac,Bc,Cc,Dc={history:!0,galleryUID:1},Ec=function(){return Bc.hash.substring(1)},Fc=function(){rc&&clearTimeout(rc),tc&&clearTimeout(tc)},Gc=function(){var a=Ec(),b={};if(a.length<5)return b;var c,d=a.split("&");for(c=0;c-1&&(xc=xc.split("&gid=")[0],xc=xc.split("?gid=")[0]),Ba("afterChange",f.updateURL),Ba("unbindEvents",function(){e.unbind(window,"hashchange",f.onHashChange)});var a=function(){wc=!0,zc||(Ac?history.back():xc?Bc.hash=xc:Cc?history.pushState("",document.title,Bc.pathname+Bc.search):Bc.hash=""),Fc()};Ba("unbindEvents",function(){l&&a()}),Ba("destroy",function(){wc||a()}),Ba("firstUpdate",function(){m=Gc().pid});var b=xc.indexOf("pid=");b>-1&&(xc=xc.substring(0,b),"&"===xc.slice(-1)&&(xc=xc.slice(0,-1))),setTimeout(function(){j&&e.bind(window,"hashchange",f.onHashChange)},40)}},onHashChange:function(){return Ec()===xc?(zc=!0,void f.close()):void(uc||(vc=!0,f.goTo(Gc().pid),vc=!1))},updateURL:function(){Fc(),vc||(yc?rc=setTimeout(Hc,800):Hc())}}}),e.extend(f,db)};return a});
\ No newline at end of file
+/*! PhotoSwipe - v4.1.3 - 2019-01-08
+ * http://photoswipe.com
+ * Copyright (c) 2019 Dmitry Semenov;
+ * Heavily Modified (c) 2025 Florian Greistorfer */
+!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.PhotoSwipe=t()}(this,function(){"use strict";return function(m,_,t,n){function e(){return{x:0,y:0}}function N(e,t){g.extend(w,t.publicMethods),Be.push(e)}function U(e){var t=L();return t-1t.min[e]?n[e]=t.min[e]:n[e]=ut&&(x=0w.currItem.fitRatio&&Yt(Ie))}}var K,q,$,p,j,J,Q,ee,o,f,te,ne,oe,ie,ae,r,re,le,se,ce,ue,de,me,i,pe,fe,he,ye,xe,ge,l,we,ve,be,Ie,Ce,De,Se,s,Te,Me,Ae,Ee,Oe,c,u,ke,d,h,y,x,Re,Pe,Ze,Le,Fe,g={features:null,bind:function(e,t,n,o){var i=(o?"remove":"add")+"EventListener";t=t.split(" ");for(var a=0;aw.currItem.fitRatio?Ge||(ln(w.currItem,!1,!0),Ge=!0):Ge&&(ln(w.currItem),Ge=!1)),Ve(h,b.x,b.y,f))},Ke=function(e){e.container&&Ve(e.container.style,e.initialPosition.x,e.initialPosition.y,e.initialZoomLevel,e)},qe=function(e,t){t[de]=ne+e+"px, 0px"+oe},$e=function(e,t){var n;!v.loop&&t&&(t=p+(C.x*Ue-e)/C.x,n=Math.round(e-Z.x),t<0&&0=L()-1&&n<0)&&(e=Z.x+n*v.mainScrollEndFriction),Z.x=e,qe(e,j)},je=function(e,t){var n=vt[e]-Ye[e];return Ne[e]+_e[e]+n-t/te*n},E=function(e,t){e.x=t.x,e.y=t.y,t.id&&(e.id=t.id)},Je=function(e){e.x=Math.round(e.x),e.y=Math.round(e.y)},Qe=null,et=function(){Qe&&(g.unbind(document,"mousemove",et),g.addClass(m,"pswp--has_mouse"),v.mouseUsed=!0,S("mouseUsed")),Qe=setTimeout(function(){Qe=null},100)},tt=function(e,t){e=an(w.currItem,I,e);return t&&(d=e),e},nt=function(e){return(e=e||w.currItem).initialZoomLevel},ot=function(e){return 0<(e=e||w.currItem).w?v.maxSpreadZoom:1},O={},it=0,at=function(e){O[e]&&(O[e].raf&&fe(O[e].raf),it--,delete O[e])},rt=function(e){O[e]&&at(e),O[e]||(it++,O[e]={})},lt=function(){for(var e in O)O.hasOwnProperty(e)&&at(e)},st=function(e,t,n,o,i,a,r){function l(){O[e]&&(s=T()-c,o<=s?(at(e),a(n),r&&r()):(a((n-t)*i(s/o)+t),O[e].raf=pe(l)))}var s,c=T();rt(e);l()},n={shout:S,listen:a,viewportSize:I,options:v,isMainScrollAnimating:function(){return y},getZoomLevel:function(){return f},getCurrentIndex:function(){return p},isDragging:function(){return s},isZooming:function(){return u},setScrollOffset:function(e,t){Ye.x=e,ge=Ye.y=t,S("updateScrollOffset",Ye)},applyZoomPan:function(e,t,n,o){b.x=t,b.y=n,f=e,A(o)},init:function(){if(!K&&!q){w.framework=g,w.template=m,w.bg=g.getChildByClass(m,"pswp__bg"),he=m.className,K=!0,l=g.detectFeatures(),pe=l.raf,fe=l.caf,de=l.transform,xe=l.oldIE,w.scrollWrap=g.getChildByClass(m,"pswp__scroll-wrap"),w.container=g.getChildByClass(w.scrollWrap,"pswp__container"),j=w.container.style,w.itemHolders=r=[{el:w.container.children[0],wrap:0,index:-1},{el:w.container.children[1],wrap:0,index:-1},{el:w.container.children[2],wrap:0,index:-1}],r[0].el.style.display=r[2].el.style.display="none",de?(t=l.perspective&&!i,ne="translate"+(t?"3d(":"("),oe=l.perspective?", 0px)":")"):(de="left",g.addClass(m,"pswp--ie"),qe=function(e,t){t.left=e+"px"},Ke=function(e){var t=1=L())&&(p=0),w.currItem=Jt(p),(l.isOldIOSPhone||l.isOldAndroid)&&(We=!1),m.setAttribute("aria-hidden","false"),v.modal&&(We?m.style.position="fixed":(m.style.position="absolute",m.style.top=g.getScrollY()+"px")),void 0===ge&&(S("initialLayout"),ge=ye=g.getScrollY());var n="pswp--open ";for(v.mainClass&&(n+=v.mainClass+" "),v.showHideOpacity&&(n+="pswp--animate_opacity "),n=(n=(n+=i?"pswp--touch":"pswp--notouch")+(l.animationName?" pswp--css_animation":""))+(l.svg?" pswp--svg":""),g.addClass(m,n),w.updateSize(),J=-1,D=null,e=0;e<3;e++)qe((e+J)*C.x,r[e].el.style);xe||g.bind(w.scrollWrap,ee,w),a("initialZoomInEnd",function(){w.setContent(r[0],p-1),w.setContent(r[2],p+1),r[0].el.style.display=r[2].el.style.display="block",v.focus&&m.focus(),g.bind(document,"keydown",w),l.transform&&g.bind(w.scrollWrap,"click",w),v.mouseUsed||g.bind(document,"mousemove",et),g.bind(window,"resize scroll orientationchange",w),S("bindEvents")}),w.setContent(r[1],p),w.updateCurrItem(),S("afterInit"),We||(ie=setInterval(function(){it||s||u||f!==w.currItem.initialZoomLevel||w.updateSize()},1e3)),g.addClass(m,"pswp--visible")}},close:function(){K&&(q=!(K=!1),S("close"),g.unbind(window,"resize scroll orientationchange",w),g.unbind(window,"scroll",o.scroll),g.unbind(document,"keydown",w),g.unbind(document,"mousemove",et),l.transform&&g.unbind(w.scrollWrap,"click",w),s&&g.unbind(window,Q,w),clearTimeout(we),S("unbindEvents"),Qt(w.currItem,null,!0,w.destroy))},destroy:function(){S("destroy"),Kt&&clearTimeout(Kt),m.setAttribute("aria-hidden","true"),m.className=he,ie&&clearInterval(ie),g.unbind(w.scrollWrap,ee,w),g.unbind(window,"scroll",w),Dt(),lt(),Xe=null},panTo:function(e,t,n){n||(e>d.min.x?e=d.min.x:ed.min.y?t=d.min.y:td.min[e]||ad.min[e]&&(c=v.panEndFriction,d.min[e],n=d.min[e]-Ne[e]),(n<=0||s<0)&&1ft.x&&(i=ft.x)):d.min.x!==d.max.x&&(o=a)):(aw.currItem.fitRatio&&(b[e]+=t[e]*c)},Nt=function(){if(c){var e,t,n,o,i,a=c.length;if(0!==a)if(E(k,c[0]),R.x=k.x-P.x,R.y=k.y-P.y,u&&1w.currItem.initialZoomLevel+w.currItem.initialZoomLevel/15&&(Le=!0),t=1,n=nt(),o=ot(),e=ut&&(R.x-=c[0].x-mt.x),Math.abs(R.y)>=ut)&&(R.y-=c[0].y-mt.y),P.x=k.x,P.y=k.y,0!==R.x||0!==R.y)){if("v"===x&&v.closeOnVerticalDrag)if(!Tt())return _e.y+=R.y,b.y+=R.y,i=Pt(),De=!0,S("onVerticalDrag",i),M(i),void A();Rt(T(),k.x,k.y),Ae=!0,d=w.currItem.bounds,_t("x",R)||(_t("y",R),Je(b),A())}}},Ut=function(){var t,n,o={lastFlickOffset:{},lastFlickDist:{},lastFlickSpeed:{},slowDownRatio:{},slowDownRatioReverse:{},speedDecelerationRatio:{},speedDecelerationRatioAbs:{},distanceOffset:{},backAnimDestination:{},backAnimStarted:{},calculateSwipeSpeed:function(e){n=(1d.min[t]?o.backAnimDestination[t]=d.min[t]:b[t]=L()&&(p=v.loop?0:L()-1,i=!0),i&&!v.loop||(D+=o,Ue-=o,n=!0));var n,o,i,e=C.x*Ue,a=Math.abs(e-Z.x),r=n||e>Z.x==0The image could not be loaded.',forceProgressiveLoading:!1,preload:[1,1],getNumItemsFn:function(){return qt.length}},nn=function(){return{center:{x:0,y:0},max:{x:0,y:0},min:{x:0,y:0}}},on=function(e,t,n){var o=e.bounds;o.center.x=Math.round((F.x-t)/2),o.center.y=Math.round((F.y-n)/2)+e.vGap.top,o.max.x=t>F.x?Math.round(F.x-t):o.center.x,o.max.y=n>F.y?Math.round(F.y-n)+e.vGap.top:o.center.y,o.min.x=t>F.x?0:o.center.x,o.min.y=n>F.y?e.vGap.top:o.center.y},an=function(e,t,n){var o,i;return!e.src||e.loadError?(e.w=e.h=0,e.initialZoomLevel=e.fitRatio=1,e.bounds=nn(),e.initialPosition=e.bounds.center,e.bounds):((o=!n)&&(e.vGap||(e.vGap={top:0,bottom:0}),S("parseVerticalMargin",e)),F.x=t.x,F.y=t.y-e.vGap.top-e.vGap.bottom,o&&(t=F.x/e.w,i=F.y/e.h,e.fitRatio=t=d.max.x&&n<=d.min.y&&n>=d.max.y)&&e.preventDefault(),w.panTo(t,n)},toggleDesktopZoom:function(e){e=e||{x:I.x/2+Ye.x,y:I.y/2+Ye.y};var t=v.getDoubleTapZoom(!0,w.currItem),n=f===t;w.mouseZoomedIn=!n,w.zoomTo(n?w.currItem.initialZoomLevel:t,e,333),g[(n?"remove":"add")+"Class"](m,"pswp--zoomed-in")}}}),{history:!0,galleryUID:1}),Sn=function(){return new URLSearchParams(window.location.search)},Tn=function(){var e,t;fn&&clearTimeout(fn),it||s?fn=setTimeout(Tn,500):(hn?clearTimeout(pn):hn=!0,e=p+1,(t=Jt(p)).hasOwnProperty("pid")&&(e=t.pid),(t=new URL(window.location.href)).searchParams.delete("pid"),t.searchParams.delete("gid"),t.searchParams.append("gid",v.galleryUID),t.searchParams.append("pid",e),In?history[gn?"replaceState":"pushState"]("",document.title,t):gn?window.location.replace(t.href):window.location=t.href,gn=!0,pn=setTimeout(function(){hn=!1},60))};N("History",{publicMethods:{initHistory:function(){var e;g.extend(v,Dn,!0),v.history&&(gn=wn=vn=!1,In="pushState"in history,bn=new URL(window.location.href),a("afterChange",w.updateURL),a("unbindEvents",function(){g.unbind(window,"searchchange",w.onSearchChange)}),e=function(){xn=!0,wn||(vn?history.back():(bn.searchParams.delete("gid"),bn.searchParams.delete("pid"),In?history.pushState("",document.title,bn):window.location=bn.href)),cn()},a("unbindEvents",function(){$&&e()}),a("destroy",function(){xn||e()}),a("firstUpdate",function(){p=un().pid}),setTimeout(function(){K&&g.bind(window,"searchchange",w.onSearchChange)},40))},onSearchChange:function(){hn||(yn=!0,w.goTo(un().pid),yn=!1)},updateURL:function(){cn(),yn||(gn?mn=setTimeout(Tn,800):Tn())}}}),g.extend(w,n)}});
\ No newline at end of file
diff --git a/templates/functionality.js b/templates/functionality.js
index ae06970..37dccac 100644
--- a/templates/functionality.js
+++ b/templates/functionality.js
@@ -71,6 +71,11 @@ class PhotoGallery {
const content = document.documentElement.innerHTML;
const title = document.title;
const folders = document.querySelector(".folders");
+ let path = window.location.origin + window.location.pathname;
+ if (path.startsWith("null")) {
+ path = window.location.protocol + "//" + path.substring(4);
+ }
+
if (folders) folders.style.display = "";
document.getElementById("recursive").checked = false;
document
@@ -79,7 +84,7 @@ class PhotoGallery {
window.history.replaceState(
{ html: content, pageTitle: title },
"",
- window.location.origin + window.location.pathname
+ path
);
this.requestMetadata();
}
@@ -94,22 +99,15 @@ class PhotoGallery {
if (!isChecked) {
if (folders) folders.style.display = "";
loc.searchParams.delete("recursive");
- window.history.replaceState(
- { html: content, pageTitle: title },
- "",
- loc
- );
+ window.history.replaceState({ html: content, pageTitle: title }, "", loc);
this.requestMetadata();
return;
}
if (folders) folders.style.display = "none";
- loc.searchParams.append("recursive", true)
- window.history.replaceState(
- { html: content, pageTitle: title },
- "",
- loc
- );
+ loc.searchParams.delete("recursive");
+ loc.searchParams.append("recursive", true);
+ window.history.replaceState({ html: content, pageTitle: title }, "", loc);
const visited = new Set();
const existingItems = new Set();
@@ -184,19 +182,20 @@ class PhotoGallery {
} else {
this.filter();
}
- const pid = searchParams.get("pid");
- if (pid != null) {
- this.openSwipe(parseInt(pid));
- }
})
.catch(() => {});
}
filter() {
+ const searchParams = new URLSearchParams(window.location.search);
this.shown = [];
- const path = decodeURIComponent(
- window.location.origin + window.location.pathname.replace("index.html", "")
+ let path = decodeURIComponent(
+ window.location.origin +
+ window.location.pathname.replace("index.html", "")
);
+ if (path.startsWith("null")) {
+ path = window.location.protocol + "//" + path.substring(4);
+ }
const selectedTags = [];
document
@@ -237,6 +236,11 @@ class PhotoGallery {
}
this.updateImageList();
window.location.hash = urltags;
+
+ const pid = searchParams.get("pid") - 1;
+ if (pid != -1) {
+ this.openSwipe(pid);
+ }
}
updateImageList() {