diff options
author | Siri Reiter <siri@jones.dk> | 2019-05-28 19:27:28 +0200 |
---|---|---|
committer | Siri Reiter <siri@jones.dk> | 2019-05-28 19:27:28 +0200 |
commit | fbfd4a18c2d5535f97167741b3661d6f88f3b0b1 (patch) | |
tree | 76eb9958f8deb4d742571fc3374f7d18d25b43e7 /gallery/pswp |
Create gallery.HEADsynced/mastermaster
Diffstat (limited to 'gallery/pswp')
-rw-r--r-- | gallery/pswp/default-skin/default-skin.css | 483 | ||||
-rw-r--r-- | gallery/pswp/default-skin/default-skin.png | bin | 0 -> 547 bytes | |||
-rw-r--r-- | gallery/pswp/default-skin/default-skin.svg | 1 | ||||
-rw-r--r-- | gallery/pswp/default-skin/preloader.gif | bin | 0 -> 866 bytes | |||
-rw-r--r-- | gallery/pswp/photoswipe-ui-default.js | 861 | ||||
-rw-r--r-- | gallery/pswp/photoswipe-ui-default.min.js | 4 | ||||
-rw-r--r-- | gallery/pswp/photoswipe.css | 177 | ||||
-rw-r--r-- | gallery/pswp/photoswipe.js | 3718 | ||||
-rw-r--r-- | gallery/pswp/photoswipe.min.js | 4 |
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 Binary files differnew file mode 100644 index 0000000..441c502 --- /dev/null +++ b/gallery/pswp/default-skin/default-skin.png 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 Binary files differnew file mode 100644 index 0000000..b8faa69 --- /dev/null +++ b/gallery/pswp/default-skin/preloader.gif 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 |