summaryrefslogtreecommitdiff
path: root/gallery/pswp
diff options
context:
space:
mode:
authorSiri Reiter <siri@jones.dk>2019-05-28 19:27:28 +0200
committerSiri Reiter <siri@jones.dk>2019-05-28 19:27:28 +0200
commitfbfd4a18c2d5535f97167741b3661d6f88f3b0b1 (patch)
tree76eb9958f8deb4d742571fc3374f7d18d25b43e7 /gallery/pswp
Diffstat (limited to 'gallery/pswp')
-rw-r--r--gallery/pswp/default-skin/default-skin.css483
-rw-r--r--gallery/pswp/default-skin/default-skin.pngbin0 -> 547 bytes
-rw-r--r--gallery/pswp/default-skin/default-skin.svg1
-rw-r--r--gallery/pswp/default-skin/preloader.gifbin0 -> 866 bytes
-rw-r--r--gallery/pswp/photoswipe-ui-default.js861
-rw-r--r--gallery/pswp/photoswipe-ui-default.min.js4
-rw-r--r--gallery/pswp/photoswipe.css177
-rw-r--r--gallery/pswp/photoswipe.js3718
-rw-r--r--gallery/pswp/photoswipe.min.js4
9 files changed, 5248 insertions, 0 deletions
diff --git a/gallery/pswp/default-skin/default-skin.css b/gallery/pswp/default-skin/default-skin.css
new file mode 100644
index 0000000..f99db1b
--- /dev/null
+++ b/gallery/pswp/default-skin/default-skin.css
@@ -0,0 +1,483 @@
+/*! PhotoSwipe Default UI CSS by Dmitry Semenov | photoswipe.com | MIT license */
+/*
+
+ Contents:
+
+ 1. Buttons
+ 2. Share modal and links
+ 3. Index indicator ("1 of X" counter)
+ 4. Caption
+ 5. Loading indicator
+ 6. Additional styles (root element, top bar, idle state, hidden state, etc.)
+
+*/
+/*
+
+ 1. Buttons
+
+ */
+/* <button> css reset */
+.pswp__button {
+ width: 44px;
+ height: 44px;
+ position: relative;
+ background: none;
+ cursor: pointer;
+ overflow: visible;
+ -webkit-appearance: none;
+ display: block;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ float: right;
+ opacity: 0.75;
+ -webkit-transition: opacity 0.2s;
+ transition: opacity 0.2s;
+ -webkit-box-shadow: none;
+ box-shadow: none; }
+ .pswp__button:focus,
+ .pswp__button:hover {
+ opacity: 1; }
+ .pswp__button:active {
+ outline: none;
+ opacity: 0.9; }
+ .pswp__button::-moz-focus-inner {
+ padding: 0;
+ border: 0; }
+
+/* pswp__ui--over-close class it added when mouse is over element that should close gallery */
+.pswp__ui--over-close .pswp__button--close {
+ opacity: 1; }
+
+.pswp__button,
+.pswp__button--arrow--left:before,
+.pswp__button--arrow--right:before {
+ background: url(default-skin.png) 0 0 no-repeat;
+ background-size: 264px 88px;
+ width: 44px;
+ height: 44px; }
+
+@media (-webkit-min-device-pixel-ratio: 1.1), (-webkit-min-device-pixel-ratio: 1.09375), (min-resolution: 105dpi), (min-resolution: 1.1dppx) {
+ /* Serve SVG sprite if browser supports SVG and resolution is more than 105dpi */
+ .pswp--svg .pswp__button,
+ .pswp--svg .pswp__button--arrow--left:before,
+ .pswp--svg .pswp__button--arrow--right:before {
+ background-image: url(default-skin.svg); }
+ .pswp--svg .pswp__button--arrow--left,
+ .pswp--svg .pswp__button--arrow--right {
+ background: none; } }
+
+.pswp__button--close {
+ background-position: 0 -44px; }
+
+.pswp__button--share {
+ background-position: -44px -44px; }
+
+.pswp__button--fs {
+ display: none; }
+
+.pswp--supports-fs .pswp__button--fs {
+ display: block; }
+
+.pswp--fs .pswp__button--fs {
+ background-position: -44px 0; }
+
+.pswp__button--zoom {
+ display: none;
+ background-position: -88px 0; }
+
+.pswp--zoom-allowed .pswp__button--zoom {
+ display: block; }
+
+.pswp--zoomed-in .pswp__button--zoom {
+ background-position: -132px 0; }
+
+/* no arrows on touch screens */
+.pswp--touch .pswp__button--arrow--left,
+.pswp--touch .pswp__button--arrow--right {
+ visibility: hidden; }
+
+/*
+ Arrow buttons hit area
+ (icon is added to :before pseudo-element)
+*/
+.pswp__button--arrow--left,
+.pswp__button--arrow--right {
+ background: none;
+ top: 50%;
+ margin-top: -50px;
+ width: 70px;
+ height: 100px;
+ position: absolute; }
+
+.pswp__button--arrow--left {
+ left: 0; }
+
+.pswp__button--arrow--right {
+ right: 0; }
+
+.pswp__button--arrow--left:before,
+.pswp__button--arrow--right:before {
+ content: '';
+ top: 35px;
+ background-color: rgba(0, 0, 0, 0.3);
+ height: 30px;
+ width: 32px;
+ position: absolute; }
+
+.pswp__button--arrow--left:before {
+ left: 6px;
+ background-position: -138px -44px; }
+
+.pswp__button--arrow--right:before {
+ right: 6px;
+ background-position: -94px -44px; }
+
+/*
+
+ 2. Share modal/popup and links
+
+ */
+.pswp__counter,
+.pswp__share-modal {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none; }
+
+.pswp__share-modal {
+ display: block;
+ background: rgba(0, 0, 0, 0.5);
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ padding: 10px;
+ position: absolute;
+ z-index: 1600;
+ opacity: 0;
+ -webkit-transition: opacity 0.25s ease-out;
+ transition: opacity 0.25s ease-out;
+ -webkit-backface-visibility: hidden;
+ will-change: opacity; }
+
+.pswp__share-modal--hidden {
+ display: none; }
+
+.pswp__share-tooltip {
+ z-index: 1620;
+ position: absolute;
+ background: #FFF;
+ top: 56px;
+ border-radius: 2px;
+ display: block;
+ width: auto;
+ right: 44px;
+ -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
+ -webkit-transform: translateY(6px);
+ -ms-transform: translateY(6px);
+ transform: translateY(6px);
+ -webkit-transition: -webkit-transform 0.25s;
+ transition: transform 0.25s;
+ -webkit-backface-visibility: hidden;
+ will-change: transform; }
+ .pswp__share-tooltip a {
+ display: block;
+ padding: 8px 12px;
+ color: #000;
+ text-decoration: none;
+ font-size: 14px;
+ line-height: 18px; }
+ .pswp__share-tooltip a:hover {
+ text-decoration: none;
+ color: #000; }
+ .pswp__share-tooltip a:first-child {
+ /* round corners on the first/last list item */
+ border-radius: 2px 2px 0 0; }
+ .pswp__share-tooltip a:last-child {
+ border-radius: 0 0 2px 2px; }
+
+.pswp__share-modal--fade-in {
+ opacity: 1; }
+ .pswp__share-modal--fade-in .pswp__share-tooltip {
+ -webkit-transform: translateY(0);
+ -ms-transform: translateY(0);
+ transform: translateY(0); }
+
+/* increase size of share links on touch devices */
+.pswp--touch .pswp__share-tooltip a {
+ padding: 16px 12px; }
+
+a.pswp__share--facebook:before {
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ position: absolute;
+ top: -12px;
+ right: 15px;
+ border: 6px solid transparent;
+ border-bottom-color: #FFF;
+ -webkit-pointer-events: none;
+ -moz-pointer-events: none;
+ pointer-events: none; }
+
+a.pswp__share--facebook:hover {
+ background: #3E5C9A;
+ color: #FFF; }
+ a.pswp__share--facebook:hover:before {
+ border-bottom-color: #3E5C9A; }
+
+a.pswp__share--twitter:hover {
+ background: #55ACEE;
+ color: #FFF; }
+
+a.pswp__share--pinterest:hover {
+ background: #CCC;
+ color: #CE272D; }
+
+a.pswp__share--download:hover {
+ background: #DDD; }
+
+/*
+
+ 3. Index indicator ("1 of X" counter)
+
+ */
+.pswp__counter {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 44px;
+ font-size: 13px;
+ line-height: 44px;
+ color: #FFF;
+ opacity: 0.75;
+ padding: 0 10px; }
+
+/*
+
+ 4. Caption
+
+ */
+.pswp__caption {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ min-height: 44px; }
+ .pswp__caption small {
+ font-size: 11px;
+ color: #BBB; }
+
+.pswp__caption__center {
+ text-align: left;
+ max-width: 420px;
+ margin: 0 auto;
+ font-size: 13px;
+ padding: 10px;
+ line-height: 20px;
+ color: #CCC; }
+
+.pswp__caption--empty {
+ display: none; }
+
+/* Fake caption element, used to calculate height of next/prev image */
+.pswp__caption--fake {
+ visibility: hidden; }
+
+/*
+
+ 5. Loading indicator (preloader)
+
+ You can play with it here - http://codepen.io/dimsemenov/pen/yyBWoR
+
+ */
+.pswp__preloader {
+ width: 44px;
+ height: 44px;
+ position: absolute;
+ top: 0;
+ left: 50%;
+ margin-left: -22px;
+ opacity: 0;
+ -webkit-transition: opacity 0.25s ease-out;
+ transition: opacity 0.25s ease-out;
+ will-change: opacity;
+ direction: ltr; }
+
+.pswp__preloader__icn {
+ width: 20px;
+ height: 20px;
+ margin: 12px; }
+
+.pswp__preloader--active {
+ opacity: 1; }
+ .pswp__preloader--active .pswp__preloader__icn {
+ /* We use .gif in browsers that don't support CSS animation */
+ background: url(preloader.gif) 0 0 no-repeat; }
+
+.pswp--css_animation .pswp__preloader--active {
+ opacity: 1; }
+ .pswp--css_animation .pswp__preloader--active .pswp__preloader__icn {
+ -webkit-animation: clockwise 500ms linear infinite;
+ animation: clockwise 500ms linear infinite; }
+ .pswp--css_animation .pswp__preloader--active .pswp__preloader__donut {
+ -webkit-animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite;
+ animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite; }
+
+.pswp--css_animation .pswp__preloader__icn {
+ background: none;
+ opacity: 0.75;
+ width: 14px;
+ height: 14px;
+ position: absolute;
+ left: 15px;
+ top: 15px;
+ margin: 0; }
+
+.pswp--css_animation .pswp__preloader__cut {
+ /*
+ The idea of animating inner circle is based on Polymer ("material") loading indicator
+ by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html
+ */
+ position: relative;
+ width: 7px;
+ height: 14px;
+ overflow: hidden; }
+
+.pswp--css_animation .pswp__preloader__donut {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 14px;
+ height: 14px;
+ border: 2px solid #FFF;
+ border-radius: 50%;
+ border-left-color: transparent;
+ border-bottom-color: transparent;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background: none;
+ margin: 0; }
+
+@media screen and (max-width: 1024px) {
+ .pswp__preloader {
+ position: relative;
+ left: auto;
+ top: auto;
+ margin: 0;
+ float: right; } }
+
+@-webkit-keyframes clockwise {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg); }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg); } }
+
+@keyframes clockwise {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg); }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg); } }
+
+@-webkit-keyframes donut-rotate {
+ 0% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0); }
+ 50% {
+ -webkit-transform: rotate(-140deg);
+ transform: rotate(-140deg); }
+ 100% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0); } }
+
+@keyframes donut-rotate {
+ 0% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0); }
+ 50% {
+ -webkit-transform: rotate(-140deg);
+ transform: rotate(-140deg); }
+ 100% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0); } }
+
+/*
+
+ 6. Additional styles
+
+ */
+/* root element of UI */
+.pswp__ui {
+ -webkit-font-smoothing: auto;
+ visibility: visible;
+ opacity: 1;
+ z-index: 1550; }
+
+/* top black bar with buttons and "1 of X" indicator */
+.pswp__top-bar {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 44px;
+ width: 100%; }
+
+.pswp__caption,
+.pswp__top-bar,
+.pswp--has_mouse .pswp__button--arrow--left,
+.pswp--has_mouse .pswp__button--arrow--right {
+ -webkit-backface-visibility: hidden;
+ will-change: opacity;
+ -webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
+ transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
+
+/* pswp--has_mouse class is added only when two subsequent mousemove events occur */
+.pswp--has_mouse .pswp__button--arrow--left,
+.pswp--has_mouse .pswp__button--arrow--right {
+ visibility: visible; }
+
+.pswp__top-bar,
+.pswp__caption {
+ background-color: rgba(0, 0, 0, 0.5); }
+
+/* pswp__ui--fit class is added when main image "fits" between top bar and bottom bar (caption) */
+.pswp__ui--fit .pswp__top-bar,
+.pswp__ui--fit .pswp__caption {
+ background-color: rgba(0, 0, 0, 0.3); }
+
+/* pswp__ui--idle class is added when mouse isn't moving for several seconds (JS option timeToIdle) */
+.pswp__ui--idle .pswp__top-bar {
+ opacity: 0; }
+
+.pswp__ui--idle .pswp__button--arrow--left,
+.pswp__ui--idle .pswp__button--arrow--right {
+ opacity: 0; }
+
+/*
+ pswp__ui--hidden class is added when controls are hidden
+ e.g. when user taps to toggle visibility of controls
+*/
+.pswp__ui--hidden .pswp__top-bar,
+.pswp__ui--hidden .pswp__caption,
+.pswp__ui--hidden .pswp__button--arrow--left,
+.pswp__ui--hidden .pswp__button--arrow--right {
+ /* Force paint & create composition layer for controls. */
+ opacity: 0.001; }
+
+/* pswp__ui--one-slide class is added when there is just one item in gallery */
+.pswp__ui--one-slide .pswp__button--arrow--left,
+.pswp__ui--one-slide .pswp__button--arrow--right,
+.pswp__ui--one-slide .pswp__counter {
+ display: none; }
+
+.pswp__element--disabled {
+ display: none !important; }
+
+.pswp--minimal--dark .pswp__top-bar {
+ background: none; }
diff --git a/gallery/pswp/default-skin/default-skin.png b/gallery/pswp/default-skin/default-skin.png
new file mode 100644
index 0000000..441c502
--- /dev/null
+++ b/gallery/pswp/default-skin/default-skin.png
Binary files differ
diff --git a/gallery/pswp/default-skin/default-skin.svg b/gallery/pswp/default-skin/default-skin.svg
new file mode 100644
index 0000000..9d5f0c6
--- /dev/null
+++ b/gallery/pswp/default-skin/default-skin.svg
@@ -0,0 +1 @@
+<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg> \ No newline at end of file
diff --git a/gallery/pswp/default-skin/preloader.gif b/gallery/pswp/default-skin/preloader.gif
new file mode 100644
index 0000000..b8faa69
--- /dev/null
+++ b/gallery/pswp/default-skin/preloader.gif
Binary files differ
diff --git a/gallery/pswp/photoswipe-ui-default.js b/gallery/pswp/photoswipe-ui-default.js
new file mode 100644
index 0000000..7f3a71a
--- /dev/null
+++ b/gallery/pswp/photoswipe-ui-default.js
@@ -0,0 +1,861 @@
+/*! PhotoSwipe Default UI - 4.1.1 - 2015-12-24
+* http://photoswipe.com
+* Copyright (c) 2015 Dmitry Semenov; */
+/**
+*
+* UI on top of main sliding area (caption, arrows, close button, etc.).
+* Built just using public methods/properties of PhotoSwipe.
+*
+*/
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(factory);
+ } else if (typeof exports === 'object') {
+ module.exports = factory();
+ } else {
+ root.PhotoSwipeUI_Default = factory();
+ }
+})(this, function () {
+
+ 'use strict';
+
+
+
+var PhotoSwipeUI_Default =
+ function(pswp, framework) {
+
+ var ui = this;
+ var _overlayUIUpdated = false,
+ _controlsVisible = true,
+ _fullscrenAPI,
+ _controls,
+ _captionContainer,
+ _fakeCaptionContainer,
+ _indexIndicator,
+ _shareButton,
+ _shareModal,
+ _shareModalHidden = true,
+ _initalCloseOnScrollValue,
+ _isIdle,
+ _listen,
+
+ _loadingIndicator,
+ _loadingIndicatorHidden,
+ _loadingIndicatorTimeout,
+
+ _galleryHasOneSlide,
+
+ _options,
+ _defaultUIOptions = {
+ barsSize: {top:44, bottom:'auto'},
+ closeElClasses: ['item', 'caption', 'zoom-wrap', 'ui', 'top-bar'],
+ timeToIdle: 4000,
+ timeToIdleOutside: 1000,
+ loadingIndicatorDelay: 1000, // 2s
+
+ addCaptionHTMLFn: function(item, captionEl /*, isFake */) {
+ if(!item.title) {
+ captionEl.children[0].innerHTML = '';
+ return false;
+ }
+ captionEl.children[0].innerHTML = item.title;
+ return true;
+ },
+
+ closeEl:true,
+ captionEl: true,
+ fullscreenEl: true,
+ zoomEl: true,
+ shareEl: true,
+ counterEl: true,
+ arrowEl: true,
+ preloaderEl: true,
+
+ tapToClose: false,
+ tapToToggleControls: true,
+
+ clickToCloseNonZoomable: true,
+
+ 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:true}
+ ],
+ getImageURLForShare: function( /* shareButtonData */ ) {
+ return pswp.currItem.src || '';
+ },
+ getPageURLForShare: function( /* shareButtonData */ ) {
+ return window.location.href;
+ },
+ getTextForShare: function( /* shareButtonData */ ) {
+ return pswp.currItem.title || '';
+ },
+
+ indexIndicatorSep: ' / ',
+ fitControlsWidth: 1200
+
+ },
+ _blockControlsTap,
+ _blockControlsTapTimeout;
+
+
+
+ var _onControlsTap = function(e) {
+ if(_blockControlsTap) {
+ return true;
+ }
+
+
+ e = e || window.event;
+
+ if(_options.timeToIdle && _options.mouseUsed && !_isIdle) {
+ // reset idle timer
+ _onIdleMouseMove();
+ }
+
+
+ var target = e.target || e.srcElement,
+ uiElement,
+ clickedClass = target.getAttribute('class') || '',
+ found;
+
+ for(var i = 0; i < _uiElements.length; i++) {
+ uiElement = _uiElements[i];
+ if(uiElement.onTap && clickedClass.indexOf('pswp__' + uiElement.name ) > -1 ) {
+ uiElement.onTap();
+ found = true;
+
+ }
+ }
+
+ if(found) {
+ if(e.stopPropagation) {
+ e.stopPropagation();
+ }
+ _blockControlsTap = true;
+
+ // Some versions of Android don't prevent ghost click event
+ // when preventDefault() was called on touchstart and/or touchend.
+ //
+ // This happens on v4.3, 4.2, 4.1,
+ // older versions strangely work correctly,
+ // but just in case we add delay on all of them)
+ var tapDelay = framework.features.isOldAndroid ? 600 : 30;
+ _blockControlsTapTimeout = setTimeout(function() {
+ _blockControlsTap = false;
+ }, tapDelay);
+ }
+
+ },
+ _fitControlsInViewport = function() {
+ return !pswp.likelyTouchDevice || _options.mouseUsed || screen.width > _options.fitControlsWidth;
+ },
+ _togglePswpClass = function(el, cName, add) {
+ framework[ (add ? 'add' : 'remove') + 'Class' ](el, 'pswp__' + cName);
+ },
+
+ // add class when there is just one item in the gallery
+ // (by default it hides left/right arrows and 1ofX counter)
+ _countNumItems = function() {
+ var hasOneSlide = (_options.getNumItemsFn() === 1);
+
+ if(hasOneSlide !== _galleryHasOneSlide) {
+ _togglePswpClass(_controls, 'ui--one-slide', hasOneSlide);
+ _galleryHasOneSlide = hasOneSlide;
+ }
+ },
+ _toggleShareModalClass = function() {
+ _togglePswpClass(_shareModal, 'share-modal--hidden', _shareModalHidden);
+ },
+ _toggleShareModal = function() {
+
+ _shareModalHidden = !_shareModalHidden;
+
+
+ if(!_shareModalHidden) {
+ _toggleShareModalClass();
+ setTimeout(function() {
+ if(!_shareModalHidden) {
+ framework.addClass(_shareModal, 'pswp__share-modal--fade-in');
+ }
+ }, 30);
+ } else {
+ framework.removeClass(_shareModal, 'pswp__share-modal--fade-in');
+ setTimeout(function() {
+ if(_shareModalHidden) {
+ _toggleShareModalClass();
+ }
+ }, 300);
+ }
+
+ if(!_shareModalHidden) {
+ _updateShareURLs();
+ }
+ return false;
+ },
+
+ _openWindowPopup = function(e) {
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+
+ pswp.shout('shareLinkClick', e, target);
+
+ if(!target.href) {
+ return false;
+ }
+
+ if( target.hasAttribute('download') ) {
+ return true;
+ }
+
+ window.open(target.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) );
+
+ if(!_shareModalHidden) {
+ _toggleShareModal();
+ }
+
+ return false;
+ },
+ _updateShareURLs = function() {
+ var shareButtonOut = '',
+ shareButtonData,
+ shareURL,
+ image_url,
+ page_url,
+ share_text;
+
+ for(var i = 0; i < _options.shareButtons.length; i++) {
+ shareButtonData = _options.shareButtons[i];
+
+ image_url = _options.getImageURLForShare(shareButtonData);
+ page_url = _options.getPageURLForShare(shareButtonData);
+ share_text = _options.getTextForShare(shareButtonData);
+
+ shareURL = shareButtonData.url.replace('{{url}}', encodeURIComponent(page_url) )
+ .replace('{{image_url}}', encodeURIComponent(image_url) )
+ .replace('{{raw_image_url}}', image_url )
+ .replace('{{text}}', encodeURIComponent(share_text) );
+
+ shareButtonOut += '<a href="' + shareURL + '" target="_blank" '+
+ 'class="pswp__share--' + shareButtonData.id + '"' +
+ (shareButtonData.download ? 'download' : '') + '>' +
+ shareButtonData.label + '</a>';
+
+ if(_options.parseShareButtonOut) {
+ shareButtonOut = _options.parseShareButtonOut(shareButtonData, shareButtonOut);
+ }
+ }
+ _shareModal.children[0].innerHTML = shareButtonOut;
+ _shareModal.children[0].onclick = _openWindowPopup;
+
+ },
+ _hasCloseClass = function(target) {
+ for(var i = 0; i < _options.closeElClasses.length; i++) {
+ if( framework.hasClass(target, 'pswp__' + _options.closeElClasses[i]) ) {
+ return true;
+ }
+ }
+ },
+ _idleInterval,
+ _idleTimer,
+ _idleIncrement = 0,
+ _onIdleMouseMove = function() {
+ clearTimeout(_idleTimer);
+ _idleIncrement = 0;
+ if(_isIdle) {
+ ui.setIdle(false);
+ }
+ },
+ _onMouseLeaveWindow = function(e) {
+ e = e ? e : window.event;
+ var from = e.relatedTarget || e.toElement;
+ if (!from || from.nodeName === 'HTML') {
+ clearTimeout(_idleTimer);
+ _idleTimer = setTimeout(function() {
+ ui.setIdle(true);
+ }, _options.timeToIdleOutside);
+ }
+ },
+ _setupFullscreenAPI = function() {
+ if(_options.fullscreenEl && !framework.features.isOldAndroid) {
+ if(!_fullscrenAPI) {
+ _fullscrenAPI = ui.getFullscreenAPI();
+ }
+ if(_fullscrenAPI) {
+ framework.bind(document, _fullscrenAPI.eventK, ui.updateFullscreen);
+ ui.updateFullscreen();
+ framework.addClass(pswp.template, 'pswp--supports-fs');
+ } else {
+ framework.removeClass(pswp.template, 'pswp--supports-fs');
+ }
+ }
+ },
+ _setupLoadingIndicator = function() {
+ // Setup loading indicator
+ if(_options.preloaderEl) {
+
+ _toggleLoadingIndicator(true);
+
+ _listen('beforeChange', function() {
+
+ clearTimeout(_loadingIndicatorTimeout);
+
+ // display loading indicator with delay
+ _loadingIndicatorTimeout = setTimeout(function() {
+
+ if(pswp.currItem && pswp.currItem.loading) {
+
+ if( !pswp.allowProgressiveImg() || (pswp.currItem.img && !pswp.currItem.img.naturalWidth) ) {
+ // show preloader if progressive loading is not enabled,
+ // or image width is not defined yet (because of slow connection)
+ _toggleLoadingIndicator(false);
+ // items-controller.js function allowProgressiveImg
+ }
+
+ } else {
+ _toggleLoadingIndicator(true); // hide preloader
+ }
+
+ }, _options.loadingIndicatorDelay);
+
+ });
+ _listen('imageLoadComplete', function(index, item) {
+ if(pswp.currItem === item) {
+ _toggleLoadingIndicator(true);
+ }
+ });
+
+ }
+ },
+ _toggleLoadingIndicator = function(hide) {
+ if( _loadingIndicatorHidden !== hide ) {
+ _togglePswpClass(_loadingIndicator, 'preloader--active', !hide);
+ _loadingIndicatorHidden = hide;
+ }
+ },
+ _applyNavBarGaps = function(item) {
+ var gap = item.vGap;
+
+ if( _fitControlsInViewport() ) {
+
+ var bars = _options.barsSize;
+ if(_options.captionEl && bars.bottom === 'auto') {
+ if(!_fakeCaptionContainer) {
+ _fakeCaptionContainer = framework.createEl('pswp__caption pswp__caption--fake');
+ _fakeCaptionContainer.appendChild( framework.createEl('pswp__caption__center') );
+ _controls.insertBefore(_fakeCaptionContainer, _captionContainer);
+ framework.addClass(_controls, 'pswp__ui--fit');
+ }
+ if( _options.addCaptionHTMLFn(item, _fakeCaptionContainer, true) ) {
+
+ var captionSize = _fakeCaptionContainer.clientHeight;
+ gap.bottom = parseInt(captionSize,10) || 44;
+ } else {
+ gap.bottom = bars.top; // if no caption, set size of bottom gap to size of top
+ }
+ } else {
+ gap.bottom = bars.bottom === 'auto' ? 0 : bars.bottom;
+ }
+
+ // height of top bar is static, no need to calculate it
+ gap.top = bars.top;
+ } else {
+ gap.top = gap.bottom = 0;
+ }
+ },
+ _setupIdle = function() {
+ // Hide controls when mouse is used
+ if(_options.timeToIdle) {
+ _listen('mouseUsed', function() {
+
+ framework.bind(document, 'mousemove', _onIdleMouseMove);
+ framework.bind(document, 'mouseout', _onMouseLeaveWindow);
+
+ _idleInterval = setInterval(function() {
+ _idleIncrement++;
+ if(_idleIncrement === 2) {
+ ui.setIdle(true);
+ }
+ }, _options.timeToIdle / 2);
+ });
+ }
+ },
+ _setupHidingControlsDuringGestures = function() {
+
+ // Hide controls on vertical drag
+ _listen('onVerticalDrag', function(now) {
+ if(_controlsVisible && now < 0.95) {
+ ui.hideControls();
+ } else if(!_controlsVisible && now >= 0.95) {
+ ui.showControls();
+ }
+ });
+
+ // Hide controls when pinching to close
+ var pinchControlsHidden;
+ _listen('onPinchClose' , function(now) {
+ if(_controlsVisible && now < 0.9) {
+ ui.hideControls();
+ pinchControlsHidden = true;
+ } else if(pinchControlsHidden && !_controlsVisible && now > 0.9) {
+ ui.showControls();
+ }
+ });
+
+ _listen('zoomGestureEnded', function() {
+ pinchControlsHidden = false;
+ if(pinchControlsHidden && !_controlsVisible) {
+ ui.showControls();
+ }
+ });
+
+ };
+
+
+
+ var _uiElements = [
+ {
+ name: 'caption',
+ option: 'captionEl',
+ onInit: function(el) {
+ _captionContainer = el;
+ }
+ },
+ {
+ name: 'share-modal',
+ option: 'shareEl',
+ onInit: function(el) {
+ _shareModal = el;
+ },
+ onTap: function() {
+ _toggleShareModal();
+ }
+ },
+ {
+ name: 'button--share',
+ option: 'shareEl',
+ onInit: function(el) {
+ _shareButton = el;
+ },
+ onTap: function() {
+ _toggleShareModal();
+ }
+ },
+ {
+ name: 'button--zoom',
+ option: 'zoomEl',
+ onTap: pswp.toggleDesktopZoom
+ },
+ {
+ name: 'counter',
+ option: 'counterEl',
+ onInit: function(el) {
+ _indexIndicator = el;
+ }
+ },
+ {
+ name: 'button--close',
+ option: 'closeEl',
+ onTap: pswp.close
+ },
+ {
+ name: 'button--arrow--left',
+ option: 'arrowEl',
+ onTap: pswp.prev
+ },
+ {
+ name: 'button--arrow--right',
+ option: 'arrowEl',
+ onTap: pswp.next
+ },
+ {
+ name: 'button--fs',
+ option: 'fullscreenEl',
+ onTap: function() {
+ if(_fullscrenAPI.isFullscreen()) {
+ _fullscrenAPI.exit();
+ } else {
+ _fullscrenAPI.enter();
+ }
+ }
+ },
+ {
+ name: 'preloader',
+ option: 'preloaderEl',
+ onInit: function(el) {
+ _loadingIndicator = el;
+ }
+ }
+
+ ];
+
+ var _setupUIElements = function() {
+ var item,
+ classAttr,
+ uiElement;
+
+ var loopThroughChildElements = function(sChildren) {
+ if(!sChildren) {
+ return;
+ }
+
+ var l = sChildren.length;
+ for(var i = 0; i < l; i++) {
+ item = sChildren[i];
+ classAttr = item.className;
+
+ for(var a = 0; a < _uiElements.length; a++) {
+ uiElement = _uiElements[a];
+
+ if(classAttr.indexOf('pswp__' + uiElement.name) > -1 ) {
+
+ if( _options[uiElement.option] ) { // if element is not disabled from options
+
+ framework.removeClass(item, 'pswp__element--disabled');
+ if(uiElement.onInit) {
+ uiElement.onInit(item);
+ }
+
+ //item.style.display = 'block';
+ } else {
+ framework.addClass(item, 'pswp__element--disabled');
+ //item.style.display = 'none';
+ }
+ }
+ }
+ }
+ };
+ loopThroughChildElements(_controls.children);
+
+ var topBar = framework.getChildByClass(_controls, 'pswp__top-bar');
+ if(topBar) {
+ loopThroughChildElements( topBar.children );
+ }
+ };
+
+
+
+
+ ui.init = function() {
+
+ // extend options
+ framework.extend(pswp.options, _defaultUIOptions, true);
+
+ // create local link for fast access
+ _options = pswp.options;
+
+ // find pswp__ui element
+ _controls = framework.getChildByClass(pswp.scrollWrap, 'pswp__ui');
+
+ // create local link
+ _listen = pswp.listen;
+
+
+ _setupHidingControlsDuringGestures();
+
+ // update controls when slides change
+ _listen('beforeChange', ui.update);
+
+ // toggle zoom on double-tap
+ _listen('doubleTap', function(point) {
+ var initialZoomLevel = pswp.currItem.initialZoomLevel;
+ if(pswp.getZoomLevel() !== initialZoomLevel) {
+ pswp.zoomTo(initialZoomLevel, point, 333);
+ } else {
+ pswp.zoomTo(_options.getDoubleTapZoom(false, pswp.currItem), point, 333);
+ }
+ });
+
+ // Allow text selection in caption
+ _listen('preventDragEvent', function(e, isDown, preventObj) {
+ var t = e.target || e.srcElement;
+ if(
+ t &&
+ t.getAttribute('class') && e.type.indexOf('mouse') > -1 &&
+ ( t.getAttribute('class').indexOf('__caption') > 0 || (/(SMALL|STRONG|EM)/i).test(t.tagName) )
+ ) {
+ preventObj.prevent = false;
+ }
+ });
+
+ // bind events for UI
+ _listen('bindEvents', function() {
+ framework.bind(_controls, 'pswpTap click', _onControlsTap);
+ framework.bind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);
+
+ if(!pswp.likelyTouchDevice) {
+ framework.bind(pswp.scrollWrap, 'mouseover', ui.onMouseOver);
+ }
+ });
+
+ // unbind events for UI
+ _listen('unbindEvents', function() {
+ if(!_shareModalHidden) {
+ _toggleShareModal();
+ }
+
+ if(_idleInterval) {
+ clearInterval(_idleInterval);
+ }
+ framework.unbind(document, 'mouseout', _onMouseLeaveWindow);
+ framework.unbind(document, 'mousemove', _onIdleMouseMove);
+ framework.unbind(_controls, 'pswpTap click', _onControlsTap);
+ framework.unbind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);
+ framework.unbind(pswp.scrollWrap, 'mouseover', ui.onMouseOver);
+
+ if(_fullscrenAPI) {
+ framework.unbind(document, _fullscrenAPI.eventK, ui.updateFullscreen);
+ if(_fullscrenAPI.isFullscreen()) {
+ _options.hideAnimationDuration = 0;
+ _fullscrenAPI.exit();
+ }
+ _fullscrenAPI = null;
+ }
+ });
+
+
+ // clean up things when gallery is destroyed
+ _listen('destroy', function() {
+ if(_options.captionEl) {
+ if(_fakeCaptionContainer) {
+ _controls.removeChild(_fakeCaptionContainer);
+ }
+ framework.removeClass(_captionContainer, 'pswp__caption--empty');
+ }
+
+ if(_shareModal) {
+ _shareModal.children[0].onclick = null;
+ }
+ framework.removeClass(_controls, 'pswp__ui--over-close');
+ framework.addClass( _controls, 'pswp__ui--hidden');
+ ui.setIdle(false);
+ });
+
+
+ if(!_options.showAnimationDuration) {
+ framework.removeClass( _controls, 'pswp__ui--hidden');
+ }
+ _listen('initialZoomIn', function() {
+ if(_options.showAnimationDuration) {
+ framework.removeClass( _controls, 'pswp__ui--hidden');
+ }
+ });
+ _listen('initialZoomOut', function() {
+ framework.addClass( _controls, 'pswp__ui--hidden');
+ });
+
+ _listen('parseVerticalMargin', _applyNavBarGaps);
+
+ _setupUIElements();
+
+ if(_options.shareEl && _shareButton && _shareModal) {
+ _shareModalHidden = true;
+ }
+
+ _countNumItems();
+
+ _setupIdle();
+
+ _setupFullscreenAPI();
+
+ _setupLoadingIndicator();
+ };
+
+ ui.setIdle = function(isIdle) {
+ _isIdle = isIdle;
+ _togglePswpClass(_controls, 'ui--idle', isIdle);
+ };
+
+ ui.update = function() {
+ // Don't update UI if it's hidden
+ if(_controlsVisible && pswp.currItem) {
+
+ ui.updateIndexIndicator();
+
+ if(_options.captionEl) {
+ _options.addCaptionHTMLFn(pswp.currItem, _captionContainer);
+
+ _togglePswpClass(_captionContainer, 'caption--empty', !pswp.currItem.title);
+ }
+
+ _overlayUIUpdated = true;
+
+ } else {
+ _overlayUIUpdated = false;
+ }
+
+ if(!_shareModalHidden) {
+ _toggleShareModal();
+ }
+
+ _countNumItems();
+ };
+
+ ui.updateFullscreen = function(e) {
+
+ if(e) {
+ // some browsers change window scroll position during the fullscreen
+ // so PhotoSwipe updates it just in case
+ setTimeout(function() {
+ pswp.setScrollOffset( 0, framework.getScrollY() );
+ }, 50);
+ }
+
+ // toogle pswp--fs class on root element
+ framework[ (_fullscrenAPI.isFullscreen() ? 'add' : 'remove') + 'Class' ](pswp.template, 'pswp--fs');
+ };
+
+ ui.updateIndexIndicator = function() {
+ if(_options.counterEl) {
+ _indexIndicator.innerHTML = (pswp.getCurrentIndex()+1) +
+ _options.indexIndicatorSep +
+ _options.getNumItemsFn();
+ }
+ };
+
+ ui.onGlobalTap = function(e) {
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+
+ if(_blockControlsTap) {
+ return;
+ }
+
+ if(e.detail && e.detail.pointerType === 'mouse') {
+
+ // close gallery if clicked outside of the image
+ if(_hasCloseClass(target)) {
+ pswp.close();
+ return;
+ }
+
+ if(framework.hasClass(target, 'pswp__img')) {
+ if(pswp.getZoomLevel() === 1 && pswp.getZoomLevel() <= pswp.currItem.fitRatio) {
+ if(_options.clickToCloseNonZoomable) {
+ pswp.close();
+ }
+ } else {
+ pswp.toggleDesktopZoom(e.detail.releasePoint);
+ }
+ }
+
+ } else {
+
+ // tap anywhere (except buttons) to toggle visibility of controls
+ if(_options.tapToToggleControls) {
+ if(_controlsVisible) {
+ ui.hideControls();
+ } else {
+ ui.showControls();
+ }
+ }
+
+ // tap to close gallery
+ if(_options.tapToClose && (framework.hasClass(target, 'pswp__img') || _hasCloseClass(target)) ) {
+ pswp.close();
+ return;
+ }
+
+ }
+ };
+ ui.onMouseOver = function(e) {
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+
+ // add class when mouse is over an element that should close the gallery
+ _togglePswpClass(_controls, 'ui--over-close', _hasCloseClass(target));
+ };
+
+ ui.hideControls = function() {
+ framework.addClass(_controls,'pswp__ui--hidden');
+ _controlsVisible = false;
+ };
+
+ ui.showControls = function() {
+ _controlsVisible = true;
+ if(!_overlayUIUpdated) {
+ ui.update();
+ }
+ framework.removeClass(_controls,'pswp__ui--hidden');
+ };
+
+ ui.supportsFullscreen = function() {
+ var d = document;
+ return !!(d.exitFullscreen || d.mozCancelFullScreen || d.webkitExitFullscreen || d.msExitFullscreen);
+ };
+
+ ui.getFullscreenAPI = function() {
+ var dE = document.documentElement,
+ api,
+ tF = 'fullscreenchange';
+
+ if (dE.requestFullscreen) {
+ api = {
+ enterK: 'requestFullscreen',
+ exitK: 'exitFullscreen',
+ elementK: 'fullscreenElement',
+ eventK: tF
+ };
+
+ } else if(dE.mozRequestFullScreen ) {
+ api = {
+ enterK: 'mozRequestFullScreen',
+ exitK: 'mozCancelFullScreen',
+ elementK: 'mozFullScreenElement',
+ eventK: 'moz' + tF
+ };
+
+
+
+ } else if(dE.webkitRequestFullscreen) {
+ api = {
+ enterK: 'webkitRequestFullscreen',
+ exitK: 'webkitExitFullscreen',
+ elementK: 'webkitFullscreenElement',
+ eventK: 'webkit' + tF
+ };
+
+ } else if(dE.msRequestFullscreen) {
+ api = {
+ enterK: 'msRequestFullscreen',
+ exitK: 'msExitFullscreen',
+ elementK: 'msFullscreenElement',
+ eventK: 'MSFullscreenChange'
+ };
+ }
+
+ if(api) {
+ api.enter = function() {
+ // disable close-on-scroll in fullscreen
+ _initalCloseOnScrollValue = _options.closeOnScroll;
+ _options.closeOnScroll = false;
+
+ if(this.enterK === 'webkitRequestFullscreen') {
+ pswp.template[this.enterK]( Element.ALLOW_KEYBOARD_INPUT );
+ } else {
+ return pswp.template[this.enterK]();
+ }
+ };
+ api.exit = function() {
+ _options.closeOnScroll = _initalCloseOnScrollValue;
+
+ return document[this.exitK]();
+
+ };
+ api.isFullscreen = function() { return document[this.elementK]; };
+ }
+
+ return api;
+ };
+
+
+
+};
+return PhotoSwipeUI_Default;
+
+
+});
diff --git a/gallery/pswp/photoswipe-ui-default.min.js b/gallery/pswp/photoswipe-ui-default.min.js
new file mode 100644
index 0000000..67e3604
--- /dev/null
+++ b/gallery/pswp/photoswipe-ui-default.min.js
@@ -0,0 +1,4 @@
+/*! PhotoSwipe Default UI - 4.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.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<S.length;g++)c=S[g],c.onTap&&f.indexOf("pswp__"+c.name)>-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<q.shareButtons.length;g++)a=q.shareButtons[g],c=q.getImageURLForShare(a),d=q.getPageURLForShare(a),e=q.getTextForShare(a),b=a.url.replace("{{url}}",encodeURIComponent(d)).replace("{{image_url}}",encodeURIComponent(c)).replace("{{raw_image_url}}",c).replace("{{text}}",encodeURIComponent(e)),f+='<a href="'+b+'" target="_blank" class="pswp__share--'+a.id+'"'+(a.download?"download":"")+">"+a.label+"</a>",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<q.closeElClasses.length;c++)if(b.hasClass(a,"pswp__"+q.closeElClasses[c]))return!0},J=0,K=function(){clearTimeout(u),J=0,k&&v.setIdle(!1)},L=function(a){a=a?a:window.event;var b=a.relatedTarget||a.toElement;b&&"HTML"!==b.nodeName||(clearTimeout(u),u=setTimeout(function(){v.setIdle(!0)},q.timeToIdleOutside))},M=function(){q.fullscreenEl&&!b.features.isOldAndroid&&(c||(c=v.getFullscreenAPI()),c?(b.bind(document,c.eventK,v.updateFullscreen),v.updateFullscreen(),b.addClass(a.template,"pswp--supports-fs")):b.removeClass(a.template,"pswp--supports-fs"))},N=function(){q.preloaderEl&&(O(!0),l("beforeChange",function(){clearTimeout(o),o=setTimeout(function(){a.currItem&&a.currItem.loading?(!a.allowProgressiveImg()||a.currItem.img&&!a.currItem.img.naturalWidth)&&O(!1):O(!0)},q.loadingIndicatorDelay)}),l("imageLoadComplete",function(b,c){a.currItem===c&&O(!0)}))},O=function(a){n!==a&&(C(m,"preloader--active",!a),n=a)},P=function(a){var c=a.vGap;if(B()){var g=q.barsSize;if(q.captionEl&&"auto"===g.bottom)if(f||(f=b.createEl("pswp__caption pswp__caption--fake"),f.appendChild(b.createEl("pswp__caption__center")),d.insertBefore(f,e),b.addClass(d,"pswp__ui--fit")),q.addCaptionHTMLFn(a,f,!0)){var h=f.clientHeight;c.bottom=parseInt(h,10)||44}else c.bottom=g.top;else c.bottom="auto"===g.bottom?0:g.bottom;c.top=g.top}else c.top=c.bottom=0},Q=function(){q.timeToIdle&&l("mouseUsed",function(){b.bind(document,"mousemove",K),b.bind(document,"mouseout",L),t=setInterval(function(){J++,2===J&&v.setIdle(!0)},q.timeToIdle/2)})},R=function(){l("onVerticalDrag",function(a){x&&.95>a?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<S.length;h++)e=S[h],c.indexOf("pswp__"+e.name)>-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});
diff --git a/gallery/pswp/photoswipe.css b/gallery/pswp/photoswipe.css
new file mode 100644
index 0000000..3bf3b40
--- /dev/null
+++ b/gallery/pswp/photoswipe.css
@@ -0,0 +1,177 @@
+/*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */
+/*
+ Styles for basic PhotoSwipe functionality (sliding area, open/close transitions)
+*/
+/* pswp = photoswipe */
+.pswp {
+ display: none;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ overflow: hidden;
+ -ms-touch-action: none;
+ touch-action: none;
+ z-index: 1500;
+ -webkit-text-size-adjust: 100%;
+ /* create separate layer, to avoid paint on window.onscroll in webkit/blink */
+ -webkit-backface-visibility: hidden;
+ 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 {
+ /* 0.001, because opacity:0 doesn't trigger Paint action, which causes lag at start of transition */
+ opacity: 0.001;
+ 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); }
+
+.pswp--open {
+ display: block; }
+
+.pswp--zoom-allowed .pswp__img {
+ /* autoprefixer: off */
+ cursor: -webkit-zoom-in;
+ cursor: -moz-zoom-in;
+ cursor: zoom-in; }
+
+.pswp--zoomed-in .pswp__img {
+ /* autoprefixer: off */
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ cursor: grab; }
+
+.pswp--dragging .pswp__img {
+ /* autoprefixer: off */
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ cursor: grabbing; }
+
+/*
+ Background is added as a separate element.
+ As animating opacity is much faster than animating rgba() background-color.
+*/
+.pswp__bg {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background: #000;
+ opacity: 0;
+ -webkit-backface-visibility: hidden;
+ will-change: opacity; }
+
+.pswp__scroll-wrap {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden; }
+
+.pswp__container,
+.pswp__zoom-wrap {
+ -ms-touch-action: none;
+ touch-action: none;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0; }
+
+/* Prevent selection and tap highlights */
+.pswp__container,
+.pswp__img {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-touch-callout: none; }
+
+.pswp__zoom-wrap {
+ position: absolute;
+ width: 100%;
+ -webkit-transform-origin: left top;
+ -ms-transform-origin: left top;
+ 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); }
+
+.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); }
+
+.pswp--animated-in .pswp__bg,
+.pswp--animated-in .pswp__zoom-wrap {
+ -webkit-transition: none;
+ transition: none; }
+
+.pswp__container,
+.pswp__zoom-wrap {
+ -webkit-backface-visibility: hidden; }
+
+.pswp__item {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ overflow: hidden; }
+
+.pswp__img {
+ position: absolute;
+ width: auto;
+ height: auto;
+ top: 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; }
+
+/*
+ div element that matches size of large image
+ large image loads on top of it
+*/
+.pswp__img--placeholder--blank {
+ background: #222; }
+
+.pswp--ie .pswp__img {
+ width: 100% !important;
+ height: auto !important;
+ left: 0;
+ top: 0; }
+
+/*
+ Error message appears when image is not loaded
+ (JS option errorMsg controls markup)
+*/
+.pswp__error-msg {
+ position: absolute;
+ left: 0;
+ top: 50%;
+ width: 100%;
+ text-align: center;
+ font-size: 14px;
+ line-height: 16px;
+ margin-top: -8px;
+ color: #CCC; }
+
+.pswp__error-msg a {
+ color: #CCC;
+ text-decoration: underline; }
diff --git a/gallery/pswp/photoswipe.js b/gallery/pswp/photoswipe.js
new file mode 100644
index 0000000..0a58e22
--- /dev/null
+++ b/gallery/pswp/photoswipe.js
@@ -0,0 +1,3718 @@
+/*! PhotoSwipe - v4.1.1 - 2015-12-24
+* http://photoswipe.com
+* Copyright (c) 2015 Dmitry Semenov; */
+(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
+ * transform : 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 = navigator.pointerEnabled || 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,
+
+ // 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', self);
+
+ _shout('bindEvents');
+ },
+
+ _unbindEvents = function() {
+ framework.unbind(window, 'resize', 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);
+ }
+
+ _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,
+ 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 until 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 initial 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.pointerEnabled) {
+ releasePoint.type = e.pointerType || 'mouse';
+ } else {
+ 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';
+ }
+ }
+
+ }
+ }
+
+ 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.pointerEnabled) {
+ addEventNames('pointer', 'down', 'move', 'up', 'cancel');
+ } else {
+ // IE10 pointer events are case-sensitive
+ addEventNames('MSPointer', '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: '<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not be loaded.</div>',
+ 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,
+ _hashChangedByScript,
+ _hashChangedByHistory,
+ _hashReseted,
+ _initialHash,
+ _historyChanged,
+ _closedFromURL,
+ _urlChangedOnce,
+ _windowLoc,
+
+ _supportsPushState,
+
+ _getHash = function() {
+ return _windowLoc.hash.substring(1);
+ },
+ _cleanHistoryTimeouts = function() {
+
+ if(_historyUpdateTimeout) {
+ clearTimeout(_historyUpdateTimeout);
+ }
+
+ if(_hashAnimCheckTimeout) {
+ clearTimeout(_hashAnimCheckTimeout);
+ }
+ },
+
+ // pid - Picture index
+ // gid - Gallery index
+ _parseItemIndexFromURL = function() {
+ var hash = _getHash(),
+ params = {};
+
+ if(hash.length < 5) { // pid=1
+ return params;
+ }
+
+ var i, vars = hash.split('&');
+ for (i = 0; i < vars.length; i++) {
+ if(!vars[i]) {
+ continue;
+ }
+ var pair = vars[i].split('=');
+ if(pair.length < 2) {
+ continue;
+ }
+ params[pair[0]] = pair[1];
+ }
+ if(_options.galleryPIDs) {
+ // detect custom pid in hash and search for it among the items collection
+ var searchfor = params.pid;
+ params.pid = 0; // if custom pid cannot be found, fallback to the first item
+ for(i = 0; i < _items.length; i++) {
+ if(_items[i].pid === searchfor) {
+ params.pid = i;
+ break;
+ }
+ }
+ } else {
+ params.pid = parseInt(params.pid,10)-1;
+ }
+ if( params.pid < 0 ) {
+ params.pid = 0;
+ }
+ return params;
+ },
+ _updateHash = function() {
+
+ if(_hashAnimCheckTimeout) {
+ clearTimeout(_hashAnimCheckTimeout);
+ }
+
+
+ if(_numAnimations || _isDragging) {
+ // changing browser URL forces layout/paint in some browsers, which causes noticeable lag during animation
+ // that's why we update hash only when no animations running
+ _hashAnimCheckTimeout = setTimeout(_updateHash, 500);
+ return;
+ }
+
+ if(_hashChangedByScript) {
+ clearTimeout(_hashChangeTimeout);
+ } else {
+ _hashChangedByScript = 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 newHash = _initialHash + '&' + 'gid=' + _options.galleryUID + '&' + 'pid=' + pid;
+
+ if(!_historyChanged) {
+ if(_windowLoc.hash.indexOf(newHash) === -1) {
+ _urlChangedOnce = true;
+ }
+ // first time - add new history record, then just replace
+ }
+
+ var newURL = _windowLoc.href.split('#')[0] + '#' + newHash;
+
+ if( _supportsPushState ) {
+
+ if('#' + newHash !== window.location.hash) {
+ history[_historyChanged ? 'replaceState' : 'pushState']('', document.title, newURL);
+ }
+
+ } else {
+ if(_historyChanged) {
+ _windowLoc.replace( newURL );
+ } else {
+ _windowLoc.hash = newHash;
+ }
+ }
+
+
+
+ _historyChanged = true;
+ _hashChangeTimeout = setTimeout(function() {
+ _hashChangedByScript = false;
+ }, 60);
+ };
+
+
+
+
+
+_registerModule('History', {
+
+
+
+ publicMethods: {
+ initHistory: function() {
+
+ framework.extend(_options, _historyDefaultOptions, true);
+
+ if( !_options.history ) {
+ return;
+ }
+
+
+ _windowLoc = window.location;
+ _urlChangedOnce = false;
+ _closedFromURL = false;
+ _historyChanged = false;
+ _initialHash = _getHash();
+ _supportsPushState = ('pushState' in history);
+
+
+ if(_initialHash.indexOf('gid=') > -1) {
+ _initialHash = _initialHash.split('&gid=')[0];
+ _initialHash = _initialHash.split('?gid=')[0];
+ }
+
+
+ _listen('afterChange', self.updateURL);
+ _listen('unbindEvents', function() {
+ framework.unbind(window, 'hashchange', self.onHashChange);
+ });
+
+
+ var returnToOriginal = function() {
+ _hashReseted = true;
+ if(!_closedFromURL) {
+
+ if(_urlChangedOnce) {
+ history.back();
+ } else {
+
+ if(_initialHash) {
+ _windowLoc.hash = _initialHash;
+ } else {
+ if (_supportsPushState) {
+
+ // remove hash from url without refreshing it or scrolling to top
+ history.pushState('', document.title, _windowLoc.pathname + _windowLoc.search );
+ } else {
+ _windowLoc.hash = '';
+ }
+ }
+ }
+
+ }
+
+ _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;
+ });
+
+
+
+
+ var index = _initialHash.indexOf('pid=');
+ if(index > -1) {
+ _initialHash = _initialHash.substring(0, index);
+ if(_initialHash.slice(-1) === '&') {
+ _initialHash = _initialHash.slice(0, -1);
+ }
+ }
+
+
+ setTimeout(function() {
+ if(_isOpen) { // hasn't destroyed yet
+ framework.bind(window, 'hashchange', self.onHashChange);
+ }
+ }, 40);
+
+ },
+ onHashChange: function() {
+
+ if(_getHash() === _initialHash) {
+
+ _closedFromURL = true;
+ self.close();
+ return;
+ }
+ if(!_hashChangedByScript) {
+
+ _hashChangedByHistory = true;
+ self.goTo( _parseItemIndexFromURL().pid );
+ _hashChangedByHistory = 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(_hashChangedByHistory) {
+ return;
+ }
+
+ if(!_historyChanged) {
+ _updateHash(); // first time
+ } else {
+ _historyUpdateTimeout = setTimeout(_updateHash, 800);
+ }
+ }
+
+ }
+});
+
+
+/*>>history*/
+ framework.extend(self, publicMethods); };
+ return PhotoSwipe;
+}); \ No newline at end of file
diff --git a/gallery/pswp/photoswipe.min.js b/gallery/pswp/photoswipe.min.js
new file mode 100644
index 0000000..913e783
--- /dev/null
+++ b/gallery/pswp/photoswipe.min.js
@@ -0,0 +1,4 @@
+/*! 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;f<b.length;f++)b[f]&&a[e](b[f],c,!1)},isArray:function(a){return a instanceof Array},createEl:function(a,b){var c=document.createElement(b||"div");return a&&(c.className=a),c},getScrollY:function(){var a=window.pageYOffset;return void 0!==a?a:document.documentElement.scrollTop},unbind:function(a,b,c){e.bind(a,b,c,!0)},removeClass:function(a,b){var c=new RegExp("(\\s|^)"+b+"(\\s|$)");a.className=a.className.replace(c," ").replace(/^\s\s*/,"").replace(/\s\s*$/,"")},addClass:function(a,b){e.hasClass(a,b)||(a.className+=(a.className?" ":"")+b)},hasClass:function(a,b){return a.className&&new RegExp("(^|\\s)"+b+"(\\s|$)").test(a.className)},getChildByClass:function(a,b){for(var c=a.firstChild;c;){if(e.hasClass(c,b))return c;c=c.nextSibling}},arraySearch:function(a,b,c){for(var d=a.length;d--;)if(a[d][c]===b)return d;return-1},extend:function(a,b,c){for(var d in b)if(b.hasOwnProperty(d)){if(c&&a.hasOwnProperty(d))continue;a[d]=b[d]}},easing:{sine:{out:function(a){return Math.sin(a*(Math.PI/2))},inOut:function(a){return-(Math.cos(Math.PI*a)-1)/2}},cubic:{out:function(a){return--a*a*a+1}}},detectFeatures:function(){if(e.features)return e.features;var a=e.createEl(),b=a.style,c="",d={};if(d.oldIE=document.all&&!document.addEventListener,d.touch="ontouchstart"in window,window.requestAnimationFrame&&(d.raf=window.requestAnimationFrame,d.caf=window.cancelAnimationFrame),d.pointerEvent=navigator.pointerEnabled||navigator.msPointerEnabled,!d.pointerEvent){var f=navigator.userAgent;if(/iP(hone|od)/.test(navigator.platform)){var g=navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);g&&g.length>0&&(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;h<b.length;h++)if(e=b[h])if("object"==typeof c&&c.handleEvent){if(d){if(!c["oldIE"+e])return!1}else c["oldIE"+e]=g;a[f]("on"+e,c["oldIE"+e])}else a[f]("on"+e,c)});var f=this,g=25,h=3,i={allowPanToNext:!0,spacing:.12,bgOpacity:1,mouseUsed:!1,loop:!0,pinchToClose:!0,closeOnScroll:!0,closeOnVerticalDrag:!0,verticalDragRange:.75,hideAnimationDuration:333,showAnimationDuration:333,showHideOpacity:!1,focus:!0,escKey:!0,arrowKeys:!0,mainScrollEndFriction:.35,panEndFriction:.35,isClickableElement:function(a){return"A"===a.tagName},getDoubleTapZoom:function(a,b){return a?1:b.initialZoomLevel<.7?1:1.33},maxSpreadZoom:1.33,modal:!0,scaleMode:"fit"};e.extend(i,d);var j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,aa,ba,ca,da,ea,fa,ga,ha,ia,ja,ka,la=function(){return{x:0,y:0}},ma=la(),na=la(),oa=la(),pa={},qa=0,ra={},sa=la(),ta=0,ua=!0,va=[],wa={},xa=!1,ya=function(a,b){e.extend(f,b.publicMethods),va.push(a)},za=function(a){var b=_b();return a>b-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;d<b.length;d++)b[d].apply(f,c)}},Da=function(){return(new Date).getTime()},Ea=function(a){ia=a,f.bg.style.opacity=a*i.bgOpacity},Fa=function(a,b,c,d,e){(!xa||e&&e!==f.currItem)&&(d/=e?e.fitRatio:f.currItem.fitRatio),a[E]=u+b+"px, "+c+"px"+v+" scale("+d+")"},Ga=function(a){da&&(a&&(s>f.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]<b.max[a]?(c[a]=b.max[a],!0):!1)},Va=function(){if(E){var b=N.perspective&&!G;return u="translate"+(b?"3d(":"("),void(v=N.perspective?", 0px)":")")}E="left",e.addClass(a,"pswp--ie"),Ia=function(a,b){b.left=a+"px"},Ha=function(a){var b=a.fitRatio>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;c<va.length;c++)f["init"+va[c]]();if(b){var g=f.ui=new b(f,e);g.init()}Ca("firstUpdate"),m=m||i.index||0,(isNaN(m)||0>m||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:a<ca.max.x&&(a=ca.max.x),b>ca.min.y?b=ca.min.y:b<ca.max.y&&(b=ca.max.y)),oa.x=a,oa.y=b,Ga()},handleEvent:function(a){a=a||window.event,r[a.type]&&r[a.type](a)},goTo:function(a){a=za(a);var b=a-m;ta=b,m=a,f.currItem=$b(m),qa-=b,Ja(sa.x*qa),bb(),ea=!1,f.updateCurrItem()},next:function(){f.goTo(m+1)},prev:function(){f.goTo(m-1)},updateCurrZoomItem:function(a){if(a&&Ca("beforeChange",0),y[1].el.children.length){var b=y[1].el.children[0];da=e.hasClass(b,"pswp__zoom-wrap")?b.style:null}else da=null;ca=f.currItem.bounds,t=s=f.currItem.initialZoomLevel,oa.x=ca.center.x,oa.y=ca.center.y,a&&Ca("afterChange")},invalidateCurrItems:function(){x=!0;for(var a=0;h>a;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)<g&&Math.abs(a.y-b.y)<g},xb=function(a,b){return ob.x=Math.abs(a.x-b.x),ob.y=Math.abs(a.y-b.y),Math.sqrt(ob.x*ob.x+ob.y*ob.y)},yb=function(){Y&&(I(Y),Y=null)},zb=function(){U&&(Y=H(zb),Pb())},Ab=function(){return!("fit"===i.scaleMode&&s===f.currItem.initialZoomLevel)},Bb=function(a,b){return a&&a!==document?a.getAttribute("class")&&a.getAttribute("class").indexOf("pswp__scroll-wrap")>-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]||j<ca.max[a]?i.panEndFriction:1,j=oa[a]+b[a]*c,!i.allowPanToNext&&s!==f.currItem.initialZoomLevel||(da?"h"!==fa||"x"!==a||W||(k?(j>ca.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<ca.max[a]&&(c=i.panEndFriction,h=j-ca.max[a],d=na[a]-ca.max[a]),(0>=d||m>0)&&_b()>1?(g=l,m>0&&l<mb.x&&(g=mb.x)):ca.min.x!==ca.max.x&&(e=j))):g=l,"x"!==a)?void(ea||Z||s>f.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(l<i.verticalDragRange)f.close();else{var m=oa.y,n=ia;cb("verticalDrag",0,1,300,e.easing.cubic.out,function(a){oa.y=(f.currItem.initialPosition.y-m)*a+m,Ea((1-n)*a+n),Ga()}),Ca("onVerticalDrag",1)}}else{if((Z||ea)&&0===j){var o=Tb(g,Q);if(o)return;g="zoomPointerUp"}if(!ea)return"swipe"!==g?void Vb():void(!Z&&s>f.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]<ca.max[a]&&(c.backAnimDestination[a]=ca.max[a]),void 0!==c.backAnimDestination[a]&&(c.slowDownRatio[a]=.7,c.slowDownRatioReverse[a]=1-c.slowDownRatio[a],c.speedDecelerationRatioAbs[a]<.05&&(c.lastFlickSpeed[a]=0,c.backAnimStarted[a]=!0,cb("bounceZoomPan"+a,oa[a],c.backAnimDestination[a],b||300,e.easing.sine.out,function(b){oa[a]=b,Ga()}))))},calculateAnimOffset:function(a){c.backAnimStarted[a]||(c.speedDecelerationRatio[a]=c.speedDecelerationRatio[a]*(c.slowDownRatio[a]+c.slowDownRatioReverse[a]-c.slowDownRatioReverse[a]*c.timeDiff/10),c.speedDecelerationRatioAbs[a]=Math.abs(c.lastFlickSpeed[a]*c.speedDecelerationRatio[a]),c.distanceOffset[a]=c.lastFlickSpeed[a]*c.speedDecelerationRatio[a]*c.timeDiff,oa[a]+=c.distanceOffset[a])},panAnimLoop:function(){return Za.zoomPan&&(Za.zoomPan.raf=H(c.panAnimLoop),c.now=Da(),c.timeDiff=c.now-c.lastNow,c.lastNow=c.now,c.calculateAnimOffset("x"),c.calculateAnimOffset("y"),Ga(),c.calculateOverBoundsAnimOffset("x"),c.calculateOverBoundsAnimOffset("y"),c.speedDecelerationRatioAbs.x<.05&&c.speedDecelerationRatioAbs.y<.05)?(oa.x=Math.round(oa.x),oa.y=Math.round(oa.y),Ga(),void _a("zoomPan")):void 0}};return c},Sb=function(a){return a.calculateSwipeSpeed("y"),ca=f.currItem.bounds,a.backAnimDestination={},a.backAnimStarted={},Math.abs(a.lastFlickSpeed.x)<=.05&&Math.abs(a.lastFlickSpeed.y)<=.05?(a.speedDecelerationRatioAbs.x=a.speedDecelerationRatioAbs.y=0,a.calculateOverBoundsAnimOffset("x"),a.calculateOverBoundsAnimOffset("y"),!0):(ab("zoomPan"),a.lastNow=Da(),void a.panAnimLoop())},Tb=function(a,b){var c;ea||(pb=m);var d;if("swipe"===a){var g=jb.x-kb.x,h=b.lastFlickDist.x<10;g>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:'<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not be loaded.</div>',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<dc.length;b++)a=dc[b],a.holder.index===a.index&&ic(a.index,a.item,a.baseDiv,a.img,!1,a.clearPlaceholder);dc=[]}};ya("Controller",{publicMethods:{lazyLoadItem:function(a){a=za(a);var b=$b(a);b&&(!b.loaded&&!b.loading||x)&&(Ca("gettingData",a,b),b.src&&jc(b))},initController:function(){e.extend(i,ec,!0),f.items=Xb=c,$b=f.getItemAt,_b=i.getNumItemsFn,ac=i.loop,_b()<3&&(i.loop=!1),Ba("beforeChange",function(a){var b,c=i.preload,d=null===a?!0:a>=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<Xb.length;b++)a=Xb[b],a.container&&(a.container=null),a.placeholder&&(a.placeholder=null),a.img&&(a.img=null),a.preloader&&(a.preloader=null),a.loadError&&(a.loaded=a.loadError=!1);dc=null})},getItemAt:function(a){return a>=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<d.length;c++)if(d[c]){var e=d[c].split("=");e.length<2||(b[e[0]]=e[1])}if(i.galleryPIDs){var f=b.pid;for(b.pid=0,c=0;c<Xb.length;c++)if(Xb[c].pid===f){b.pid=c;break}}else b.pid=parseInt(b.pid,10)-1;return b.pid<0&&(b.pid=0),b},Hc=function(){if(tc&&clearTimeout(tc),$a||U)return void(tc=setTimeout(Hc,500));uc?clearTimeout(sc):uc=!0;var a=m+1,b=$b(m);b.hasOwnProperty("pid")&&(a=b.pid);var c=xc+"&gid="+i.galleryUID+"&pid="+a;yc||-1===Bc.hash.indexOf(c)&&(Ac=!0);var d=Bc.href.split("#")[0]+"#"+c;Cc?"#"+c!==window.location.hash&&history[yc?"replaceState":"pushState"]("",document.title,d):yc?Bc.replace(d):Bc.hash=c,yc=!0,sc=setTimeout(function(){uc=!1},60)};ya("History",{publicMethods:{initHistory:function(){if(e.extend(i,Dc,!0),i.history){Bc=window.location,Ac=!1,zc=!1,yc=!1,xc=Ec(),Cc="pushState"in history,xc.indexOf("gid=")>-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