1/*! PhotoSwipe - v4.1.3 - 2019-01-08 2* http://photoswipe.com 3* Copyright (c) 2019 Dmitry Semenov; */ 4(function (root, factory) { 5 if (typeof define === 'function' && define.amd) { 6 define(factory); 7 } else if (typeof module !== 'undefined' && typeof exports === 'object') { // JIG addition of extra clause 8 module.exports = factory(); 9 } else { 10 root.PhotoSwipe = factory(); 11 } 12})(this, function () { 13 14 'use strict'; 15 var PhotoSwipe = function(template, UiClass, items, options){ 16 17/*>>framework-bridge*/ 18/** 19 * 20 * Set of generic functions used by gallery. 21 * 22 * You're free to modify anything here as long as functionality is kept. 23 * 24 */ 25var framework = { 26 features: null, 27 bind: function(target, type, listener, unbind) { 28 var methodName = (unbind ? 'remove' : 'add') + 'EventListener'; 29 type = type.split(' '); 30 for(var i = 0; i < type.length; i++) { 31 if(type[i]) { 32 target[methodName]( type[i], listener, false); 33 } 34 } 35 }, 36 isArray: function(obj) { 37 return (obj instanceof Array); 38 }, 39 createEl: function(classes, tag) { 40 var el = document.createElement(tag || 'div'); 41 if(classes) { 42 el.className = classes; 43 } 44 return el; 45 }, 46 getScrollY: function() { 47 var yOffset = window.pageYOffset; 48 return yOffset !== undefined ? yOffset : document.documentElement.scrollTop; 49 }, 50 unbind: function(target, type, listener) { 51 framework.bind(target,type,listener,true); 52 }, 53 removeClass: function(el, className) { 54 var reg = new RegExp('(\\s|^)' + className + '(\\s|$)'); 55 el.className = el.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 56 }, 57 addClass: function(el, className) { 58 if( !framework.hasClass(el,className) ) { 59 el.className += (el.className ? ' ' : '') + className; 60 } 61 }, 62 hasClass: function(el, className) { 63 return el.className && new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className); 64 }, 65 getChildByClass: function(parentEl, childClassName) { 66 var node = parentEl.firstChild; 67 while(node) { 68 if( framework.hasClass(node, childClassName) ) { 69 return node; 70 } 71 node = node.nextSibling; 72 } 73 }, 74 arraySearch: function(array, value, key) { 75 var i = array.length; 76 while(i--) { 77 if(array[i][key] === value) { 78 return i; 79 } 80 } 81 return -1; 82 }, 83 extend: function(o1, o2, preventOverwrite) { 84 for (var prop in o2) { 85 if (o2.hasOwnProperty(prop)) { 86 if(preventOverwrite && o1.hasOwnProperty(prop)) { 87 continue; 88 } 89 o1[prop] = o2[prop]; 90 } 91 } 92 }, 93 easing: { 94 sine: { 95 out: function(k) { 96 return Math.sin(k * (Math.PI / 2)); 97 }, 98 inOut: function(k) { 99 return - (Math.cos(Math.PI * k) - 1) / 2; 100 } 101 }, 102 cubic: { 103 out: function(k) { 104 return --k * k * k + 1; 105 } 106 } 107 /* 108 elastic: { 109 out: function ( k ) { 110 111 var s, a = 0.1, p = 0.4; 112 if ( k === 0 ) return 0; 113 if ( k === 1 ) return 1; 114 if ( !a || a < 1 ) { a = 1; s = p / 4; } 115 else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); 116 return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 ); 117 118 }, 119 }, 120 back: { 121 out: function ( k ) { 122 var s = 1.70158; 123 return --k * k * ( ( s + 1 ) * k + s ) + 1; 124 } 125 } 126 */ 127 }, 128 129 /** 130 * 131 * @return {object} 132 * 133 * { 134 * raf : request animation frame function 135 * caf : cancel animation frame function 136 * transfrom : transform property key (with vendor), or null if not supported 137 * oldIE : IE8 or below 138 * } 139 * 140 */ 141 detectFeatures: function() { 142 if(framework.features) { 143 return framework.features; 144 } 145 var helperEl = framework.createEl(), 146 helperStyle = helperEl.style, 147 vendor = '', 148 features = {}; 149 150 // IE8 and below 151 features.oldIE = document.all && !document.addEventListener; 152 153 features.touch = 'ontouchstart' in window; 154 155 if(window.requestAnimationFrame) { 156 features.raf = window.requestAnimationFrame; 157 features.caf = window.cancelAnimationFrame; 158 } 159 160 features.pointerEvent = !!(window.PointerEvent) || navigator.msPointerEnabled; 161
162 // fix false-positive detection of old Android in new IE 163 // (IE11 ua string contains "Android 4.0") 164 165 if(!features.pointerEvent) { 166 167 var ua = navigator.userAgent; 168 169 // Detect if device is iPhone or iPod and if it's older than iOS 8 170 // http://stackoverflow.com/a/14223920 171 // 172 // This detection is made because of buggy top/bottom toolbars 173 // that don't trigger window.resize event. 174 // For more info refer to _isFixedPosition variable in core.js 175 176 if (/iP(hone|od)/.test(navigator.platform)) { 177 var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/); 178 if(v && v.length > 0) { 179 v = parseInt(v[1], 10); 180 if(v >= 1 && v < 8 ) { 181 features.isOldIOSPhone = true; 182 } 183 } 184 } 185 186 // Detect old Android (before KitKat) 187 // due to bugs related to position:fixed 188 // http://stackoverflow.com/questions/7184573/pick-up-the-android-version-in-the-browser-by-javascript 189 190 var match = ua.match(/Android\s([0-9\.]*)/); 191 var androidversion = match ? match[1] : 0; 192 androidversion = parseFloat(androidversion); 193 if(androidversion >= 1 ) { 194 if(androidversion < 4.4) { 195 features.isOldAndroid = true; // for fixed position bug & performance 196 } 197 features.androidVersion = androidversion; // for touchend bug 198 }
199 features.isMobileOpera = /opera mini|opera mobi/i.test(ua); 200 201 // p.s. yes, yes, UA sniffing is bad, propose your solution for above bugs. 202 } 203 204 var styleChecks = ['transform', 'perspective', 'animationName'], 205 vendors = ['', 'webkit','Moz','ms','O'], 206 styleCheckItem, 207 styleName; 208 209 for(var i = 0; i < 4; i++) { 210 vendor = vendors[i]; 211 212 for(var a = 0; a < 3; a++) { 213 styleCheckItem = styleChecks[a]; 214 215 // uppercase first letter of property name, if vendor is present 216 styleName = vendor + (vendor ? 217 styleCheckItem.charAt(0).toUpperCase() + styleCheckItem.slice(1) : 218 styleCheckItem); 219 220 if(!features[styleCheckItem] && styleName in helperStyle ) { 221 features[styleCheckItem] = styleName; 222 } 223 } 224 225 if(vendor && !features.raf) { 226 vendor = vendor.toLowerCase(); 227 features.raf = window[vendor+'RequestAnimationFrame']; 228 if(features.raf) { 229 features.caf = window[vendor+'CancelAnimationFrame'] || 230 window[vendor+'CancelRequestAnimationFrame']; 231 } 232 } 233 } 234 235 if(!features.raf) { 236 var lastTime = 0; 237 features.raf = function(fn) { 238 var currTime = new Date().getTime(); 239 var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 240 var id = window.setTimeout(function() { fn(currTime + timeToCall); }, timeToCall); 241 lastTime = currTime + timeToCall; 242 return id; 243 }; 244 features.caf = function(id) { clearTimeout(id); }; 245 } 246 247 // Detect SVG support 248 features.svg = !!document.createElementNS && 249 !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect; 250 251 framework.features = features; 252 253 return features; 254 } 255}; 256 257framework.detectFeatures(); 258 259// Override addEventListener for old versions of IE 260if(framework.features.oldIE) { 261 262 framework.bind = function(target, type, listener, unbind) { 263 264 type = type.split(' '); 265 266 var methodName = (unbind ? 'detach' : 'attach') + 'Event', 267 evName, 268 _handleEv = function() { 269 listener.handleEvent.call(listener); 270 }; 271 272 for(var i = 0; i < type.length; i++) { 273 evName = type[i]; 274 if(evName) { 275 276 if(typeof listener === 'object' && listener.handleEvent) { 277 if(!unbind) { 278 listener['oldIE' + evName] = _handleEv; 279 } else { 280 if(!listener['oldIE' + evName]) { 281 return false; 282 } 283 } 284 285 target[methodName]( 'on' + evName, listener['oldIE' + evName]); 286 } else { 287 target[methodName]( 'on' + evName, listener); 288 } 289 290 } 291 } 292 }; 293 294} 295 296/*>>framework-bridge*/ 297 298/*>>core*/ 299//function(template, UiClass, items, options) 300 301var self = this; 302 303/** 304 * Static vars, don't change unless you know what you're doing. 305 */ 306var DOUBLE_TAP_RADIUS = 25, 307 NUM_HOLDERS = 3; 308 309/** 310 * Options 311 */ 312var _options = { 313 allowPanToNext:true, 314 spacing: 0.12, 315 bgOpacity: 1, 316 mouseUsed: false, 317 loop: true, 318 pinchToClose: true, 319 closeOnScroll: true, 320 closeOnVerticalDrag: true, 321 verticalDragRange: 0.75, 322 hideAnimationDuration: 333, 323 showAnimationDuration: 333, 324 showHideOpacity: false, 325 focus: true, 326 escKey: true, 327 arrowKeys: true, 328 mainScrollEndFriction: 0.35, 329 panEndFriction: 0.35, 330 isClickableElement: function(el) { 331 return el.tagName === 'A'; 332 }, 333 getDoubleTapZoom: function(isMouseClick, item) { 334 if(isMouseClick) { 335 return 1; 336 } else { 337 return item.initialZoomLevel < 0.7 ? 1 : 1.33; 338 } 339 }, 340 maxSpreadZoom: 1.33, 341 modal: true, 342 343 // not fully implemented yet 344 scaleMode: 'fit' // TODO 345}; 346framework.extend(_options, options); 347 348 349/** 350 * Private helper variables & functions 351 */ 352 353var _getEmptyPoint = function() { 354 return {x:0,y:0}; 355 }; 356 357var _isOpen, 358 _isDestroying, 359 _closedByScroll, 360 _currentItemIndex, 361 _containerStyle, 362 _containerShiftIndex, 363 _currPanDist = _getEmptyPoint(), 364 _startPanOffset = _getEmptyPoint(), 365 _panOffset = _getEmptyPoint(), 366 _upMoveEvents, // drag move, drag end & drag cancel events array 367 _downEvents, // drag start events array 368 _globalEventHandlers, 369 _viewportSize = {}, 370 _currZoomLevel, 371 _startZoomLevel, 372 _translatePrefix, 373 _translateSufix, 374 _updateSizeInterval, 375 _itemsNeedUpdate,
376 _currPositionIndex = 0, 377 _offset = {}, 378 _slideSize = _getEmptyPoint(), // size of slide area, including spacing 379 _itemHolders, 380 _prevItemIndex, 381 _indexDiff = 0, // difference of indexes since last content update 382 _dragStartEvent, 383 _dragMoveEvent, 384 _dragEndEvent, 385 _dragCancelEvent, 386 _transformKey, 387 _pointerEventEnabled, 388 _isFixedPosition = true, 389 _likelyTouchDevice, 390 _modules = [], 391 _requestAF, 392 _cancelAF, 393 _initalClassName, 394 _initalWindowScrollY, 395 _oldIE, 396 _currentWindowScrollY, 397 _features, 398 _windowVisibleSize = {}, 399 _renderMaxResolution = false, 400 _orientationChangeTimeout, 401 _orientationChangeTimeout2, 402 403 // Registers PhotoSWipe module (History, Controller ...) 404 _registerModule = function(name, module) { 405 framework.extend(self, module.publicMethods); 406 _modules.push(name); 407 }, 408 409 _getLoopedId = function(index) { 410 var numSlides = _getNumItems(); 411 if(index > numSlides - 1) { 412 return index - numSlides; 413 } else if(index < 0) { 414 return numSlides + index; 415 } 416 return index; 417 }, 418 419 // Micro bind/trigger 420 _listeners = {}, 421 _listen = function(name, fn) { 422 if(!_listeners[name]) { 423 _listeners[name] = []; 424 } 425 return _listeners[name].push(fn); 426 }, 427 _shout = function(name) { 428 var listeners = _listeners[name]; 429 430 if(listeners) { 431 var args = Array.prototype.slice.call(arguments); 432 args.shift(); 433 434 for(var i = 0; i < listeners.length; i++) { 435 listeners[i].apply(self, args); 436 } 437 } 438 }, 439 440 _getCurrentTime = function() { 441 return new Date().getTime(); 442 }, 443 _applyBgOpacity = function(opacity) { 444 _bgOpacity = opacity; 445 self.bg.style.opacity = opacity * _options.bgOpacity; 446 }, 447 448 _applyZoomTransform = function(styleObj,x,y,zoom,item) { 449 if(!_renderMaxResolution || (item && item !== self.currItem) ) { 450 zoom = zoom / (item ? item.fitRatio : self.currItem.fitRatio); 451 } 452 453 styleObj[_transformKey] = _translatePrefix + x + 'px, ' + y + 'px' + _translateSufix + ' scale(' + zoom + ')'; 454 }, 455 _applyCurrentZoomPan = function( allowRenderResolution ) { 456 if(_currZoomElementStyle) { 457 458 if(allowRenderResolution) { 459 if(_currZoomLevel > self.currItem.fitRatio) { 460 if(!_renderMaxResolution) { 461 _setImageSize(self.currItem, false, true); 462 _renderMaxResolution = true; 463 } 464 } else { 465 if(_renderMaxResolution) { 466 _setImageSize(self.currItem); 467 _renderMaxResolution = false; 468 } 469 } 470 } 471 472 473 _applyZoomTransform(_currZoomElementStyle, _panOffset.x, _panOffset.y, _currZoomLevel); 474 } 475 }, 476 _applyZoomPanToItem = function(item) { 477 if(item.container) { 478 479 _applyZoomTransform(item.container.style, 480 item.initialPosition.x, 481 item.initialPosition.y, 482 item.initialZoomLevel, 483 item); 484 } 485 }, 486 _setTranslateX = function(x, elStyle) { 487 elStyle[_transformKey] = _translatePrefix + x + 'px, 0px' + _translateSufix; 488 }, 489 _moveMainScroll = function(x, dragging) { 490 491 if(!_options.loop && dragging) { 492 var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x) / _slideSize.x, 493 delta = Math.round(x - _mainScrollPos.x); 494 495 if( (newSlideIndexOffset < 0 && delta > 0) || 496 (newSlideIndexOffset >= _getNumItems() - 1 && delta < 0) ) { 497 x = _mainScrollPos.x + delta * _options.mainScrollEndFriction; 498 } 499 } 500 501 _mainScrollPos.x = x; 502 _setTranslateX(x, _containerStyle); 503 }, 504 _calculatePanOffset = function(axis, zoomLevel) { 505 var m = _midZoomPoint[axis] - _offset[axis]; 506 return _startPanOffset[axis] + _currPanDist[axis] + m - m * ( zoomLevel / _startZoomLevel ); 507 }, 508 509 _equalizePoints = function(p1, p2) { 510 p1.x = p2.x; 511 p1.y = p2.y; 512 if(p2.id) { 513 p1.id = p2.id; 514 } 515 }, 516 _roundPoint = function(p) { 517 p.x = Math.round(p.x); 518 p.y = Math.round(p.y); 519 }, 520 521 _mouseMoveTimeout = null, 522 _onFirstMouseMove = function() { 523 // Wait until mouse move event is fired at least twice during 100ms 524 // We do this, because some mobile browsers trigger it on touchstart 525 if(_mouseMoveTimeout ) { 526 framework.unbind(document, 'mousemove', _onFirstMouseMove);
527 framework.addClass(template, 'pswp--has_mouse'); 528 _options.mouseUsed = true; 529 _shout('mouseUsed'); 530 } 531 _mouseMoveTimeout = setTimeout(function() { 532 _mouseMoveTimeout = null; 533 }, 100); 534 }, 535 536 _bindEvents = function() { 537 framework.bind(document, 'keydown', self); 538 539 if(_features.transform) { 540 // don't bind click event in browsers that don't support transform (mostly IE8) 541 framework.bind(self.scrollWrap, 'click', self); 542 } 543 544 545 if(!_options.mouseUsed) { 546 framework.bind(document, 'mousemove', _onFirstMouseMove); 547 } 548 549 framework.bind(window, 'resize scroll orientationchange', self); 550 551 _shout('bindEvents'); 552 }, 553 554 _unbindEvents = function() { 555 framework.unbind(window, 'resize scroll orientationchange', self); 556 framework.unbind(window, 'scroll', _globalEventHandlers.scroll); 557 framework.unbind(document, 'keydown', self); 558 framework.unbind(document, 'mousemove', _onFirstMouseMove); 559 560 if(_features.transform) { 561 framework.unbind(self.scrollWrap, 'click', self); 562 } 563 564 if(_isDragging) { 565 framework.unbind(window, _upMoveEvents, self); 566 } 567 568 clearTimeout(_orientationChangeTimeout); 569 clearTimeout(_orientationChangeTimeout2); 570 571 _shout('unbindEvents'); 572 }, 573 574 _calculatePanBounds = function(zoomLevel, update) { 575 var bounds = _calculateItemSize( self.currItem, _viewportSize, zoomLevel ); 576 if(update) { 577 _currPanBounds = bounds; 578 } 579 return bounds; 580 }, 581 582 _getMinZoomLevel = function(item) { 583 if(!item) { 584 item = self.currItem; 585 } 586 return item.initialZoomLevel; 587 }, 588 _getMaxZoomLevel = function(item) { 589 if(!item) { 590 item = self.currItem; 591 } 592 return item.w > 0 ? _options.maxSpreadZoom : 1; 593 }, 594 595 // Return true if offset is out of the bounds 596 _modifyDestPanOffset = function(axis, destPanBounds, destPanOffset, destZoomLevel) { 597 if(destZoomLevel === self.currItem.initialZoomLevel) { 598 destPanOffset[axis] = self.currItem.initialPosition[axis]; 599 return true; 600 } else { 601 destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel); 602 603 if(destPanOffset[axis] > destPanBounds.min[axis]) { 604 destPanOffset[axis] = destPanBounds.min[axis]; 605 return true; 606 } else if(destPanOffset[axis] < destPanBounds.max[axis] ) { 607 destPanOffset[axis] = destPanBounds.max[axis]; 608 return true; 609 } 610 } 611 return false; 612 }, 613 614 _setupTransforms = function() { 615 616 if(_transformKey) { 617 // setup 3d transforms 618 var allow3dTransform = _features.perspective && !_likelyTouchDevice; 619 _translatePrefix = 'translate' + (allow3dTransform ? '3d(' : '('); 620 _translateSufix = _features.perspective ? ', 0px)' : ')'; 621 return; 622 } 623 624 // Override zoom/pan/move functions in case old browser is used (most likely IE) 625 // (so they use left/top/width/height, instead of CSS transform) 626 627 _transformKey = 'left'; 628 framework.addClass(template, 'pswp--ie'); 629 630 _setTranslateX = function(x, elStyle) { 631 elStyle.left = x + 'px'; 632 }; 633 _applyZoomPanToItem = function(item) { 634 635 var zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio, 636 s = item.container.style, 637 w = zoomRatio * item.w, 638 h = zoomRatio * item.h; 639 640 s.width = w + 'px'; 641 s.height = h + 'px'; 642 s.left = item.initialPosition.x + 'px'; 643 s.top = item.initialPosition.y + 'px'; 644 645 }; 646 _applyCurrentZoomPan = function() { 647 if(_currZoomElementStyle) { 648 649 var s = _currZoomElementStyle, 650 item = self.currItem, 651 zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio, 652 w = zoomRatio * item.w, 653 h = zoomRatio * item.h; 654 655 s.width = w + 'px'; 656 s.height = h + 'px'; 657 658 659 s.left = _panOffset.x + 'px'; 660 s.top = _panOffset.y + 'px'; 661 } 662 663 }; 664 }, 665 666 _onKeyDown = function(e) { 667 var keydownAction = ''; 668 if(_options.escKey && e.keyCode === 27) { 669 keydownAction = 'close'; 670 } else if(_options.arrowKeys) { 671 if(e.keyCode === 37) { 672 keydownAction = 'prev'; 673 } else if(e.keyCode === 39) { 674 keydownAction = 'next'; 675 } 676 } 677 678 if(keydownAction) { 679 // don't do anything if special key pressed to prevent from overriding default browser actions 680 // e.g. in Chrome on Mac cmd+arrow-left returns to previous page 681 if( !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) { 682 if(e.preventDefault) { 683 e.preventDefault(); 684 } else { 685 e.returnValue = false; 686 } 687 self[keydownAction](); 688 } 689 } 690 }, 691 692 _onGlobalClick = function(e) { 693 if(!e) { 694 return; 695 } 696 697 // don't allow click event to pass through when triggering after drag or some other gesture 698 if(_moved || _zoomStarted || _mainScrollAnimating || _verticalDragInitiated) { 699 e.preventDefault(); 700 e.stopPropagation(); 701 } 702 }, 703 704 _updatePageScrollOffset = function() { 705 self.setScrollOffset(0, framework.getScrollY()); 706 }; 707 708 709 710 711 712 713 714// Micro animation engine 715var _animations = {}, 716 _numAnimations = 0, 717 _stopAnimation = function(name) { 718 if(_animations[name]) { 719 if(_animations[name].raf) { 720 _cancelAF( _animations[name].raf ); 721 } 722 _numAnimations--; 723 delete _animations[name]; 724 } 725 }, 726 _registerStartAnimation = function(name) { 727 if(_animations[name]) { 728 _stopAnimation(name); 729 } 730 if(!_animations[name]) { 731 _numAnimations++; 732 _animations[name] = {}; 733 } 734 }, 735 _stopAllAnimations = function() { 736 for (var prop in _animations) { 737 738 if( _animations.hasOwnProperty( prop ) ) { 739 _stopAnimation(prop); 740 } 741 742 } 743 },
744 _animateProp = function(name, b, endProp, d, easingFn, onUpdate, onComplete) { 745 var startAnimTime = _getCurrentTime(), t; 746 _registerStartAnimation(name); 747 748 var animloop = function(){ 749 if ( _animations[name] ) { 750 751 t = _getCurrentTime() - startAnimTime; // time diff 752 //b - beginning (start prop) 753 //d - anim duration 754 755 if ( t >= d ) { 756 _stopAnimation(name); 757 onUpdate(endProp); 758 if(onComplete) { 759 onComplete(); 760 } 761 return; 762 } 763 onUpdate( (endProp - b) * easingFn(t/d) + b ); 764 765 _animations[name].raf = _requestAF(animloop); 766 } 767 }; 768 animloop(); 769 }; 770 771 772 773var publicMethods = { 774 775 // make a few local variables and functions public 776 shout: _shout, 777 listen: _listen, 778 viewportSize: _viewportSize, 779 options: _options, 780 781 isMainScrollAnimating: function() { 782 return _mainScrollAnimating; 783 }, 784 getZoomLevel: function() { 785 return _currZoomLevel; 786 }, 787 getCurrentIndex: function() { 788 return _currentItemIndex; 789 }, 790 isDragging: function() { 791 return _isDragging; 792 }, 793 isZooming: function() { 794 return _isZooming; 795 }, 796 setScrollOffset: function(x,y) { 797 _offset.x = x; 798 _currentWindowScrollY = _offset.y = y; 799 _shout('updateScrollOffset', _offset); 800 }, 801 applyZoomPan: function(zoomLevel,panX,panY,allowRenderResolution) { 802 _panOffset.x = panX; 803 _panOffset.y = panY; 804 _currZoomLevel = zoomLevel; 805 _applyCurrentZoomPan( allowRenderResolution ); 806 }, 807 808 init: function() { 809 810 if(_isOpen || _isDestroying) { 811 return; 812 } 813 814 var i; 815 816 self.framework = framework; // basic functionality 817 self.template = template; // root DOM element of PhotoSwipe 818 self.bg = framework.getChildByClass(template, 'pswp__bg'); 819 820 _initalClassName = template.className; 821 _isOpen = true; 822 823 _features = framework.detectFeatures(); 824 _requestAF = _features.raf; 825 _cancelAF = _features.caf; 826 _transformKey = _features.transform; 827 _oldIE = _features.oldIE; 828 829 self.scrollWrap = framework.getChildByClass(template, 'pswp__scroll-wrap'); 830 self.container = framework.getChildByClass(self.scrollWrap, 'pswp__container'); 831 832 _containerStyle = self.container.style; // for fast access 833 834 // Objects that hold slides (there are only 3 in DOM) 835 self.itemHolders = _itemHolders = [ 836 {el:self.container.children[0] , wrap:0, index: -1}, 837 {el:self.container.children[1] , wrap:0, index: -1}, 838 {el:self.container.children[2] , wrap:0, index: -1} 839 ]; 840 841 // hide nearby item holders until initial zoom animation finishes (to avoid extra Paints) 842 _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'none'; 843 844 _setupTransforms(); 845 846 // Setup global events 847 _globalEventHandlers = { 848 resize: self.updateSize, 849 850 // Fixes: iOS 10.3 resize event 851 // does not update scrollWrap.clientWidth instantly after resize 852 // https://github.com/dimsemenov/PhotoSwipe/issues/1315 853 orientationchange: function() { 854 clearTimeout(_orientationChangeTimeout); 855 _orientationChangeTimeout = setTimeout(function() { 856 if(_viewportSize.x !== self.scrollWrap.clientWidth) { 857 self.updateSize(); 858 } 859 }, 500); 860 // JIG adds a secondary backup thing here, just in case -.- 861 clearTimeout(_orientationChangeTimeout2); 862 _orientationChangeTimeout2 = setTimeout(function() { 863 if(_viewportSize.x !== self.scrollWrap.clientWidth) { 864 self.updateSize(); 865 } 866 }, 1000); 867 }, 868 scroll: _updatePageScrollOffset, 869 keydown: _onKeyDown, 870 click: _onGlobalClick 871 }; 872 873 // disable show/hide effects on old browsers that don't support CSS animations or transforms, 874 // old IOS, Android and Opera mobile. Blackberry seems to work fine, even older models. 875 var oldPhone = _features.isOldIOSPhone || _features.isOldAndroid || _features.isMobileOpera; 876 if(!_features.animationName || !_features.transform || oldPhone) { 877 _options.showAnimationDuration = _options.hideAnimationDuration = 0; 878 } 879 880 // init modules 881 for(i = 0; i < _modules.length; i++) { 882 self['init' + _modules[i]](); 883 } 884 885 // init 886 if(UiClass) { 887 var ui = self.ui = new UiClass(self, framework); 888 ui.init(); 889 } 890 891 _shout('firstUpdate'); 892 _currentItemIndex = _currentItemIndex || _options.index || 0; 893 // validate index 894 if( isNaN(_currentItemIndex) || _currentItemIndex < 0 || _currentItemIndex >= _getNumItems() ) { 895 _currentItemIndex = 0; 896 } 897 self.currItem = _getItemAt( _currentItemIndex ); 898 899 900 if(_features.isOldIOSPhone || _features.isOldAndroid) { 901 _isFixedPosition = false; 902 } 903 904 template.setAttribute('aria-hidden', 'false'); 905 if(_options.modal) { 906 if(!_isFixedPosition) { 907 template.style.position = 'absolute'; 908 template.style.top = framework.getScrollY() + 'px'; 909 } else { 910 template.style.position = 'fixed'; 911 } 912 } 913 914 if(_currentWindowScrollY === undefined) { 915 _shout('initialLayout'); 916 _currentWindowScrollY = _initalWindowScrollY = framework.getScrollY(); 917 } 918 919 // add classes to root element of PhotoSwipe 920 var rootClasses = 'pswp--open '; 921 if(_options.mainClass) { 922 rootClasses += _options.mainClass + ' '; 923 } 924 if(_options.showHideOpacity) { 925 rootClasses += 'pswp--animate_opacity '; 926 } 927 rootClasses += _likelyTouchDevice ? 'pswp--touch' : 'pswp--notouch'; 928 rootClasses += _features.animationName ? ' pswp--css_animation' : ''; 929 rootClasses += _features.svg ? ' pswp--svg' : ''; 930 // JIG addition to disable magnifier icon when zoom is disabled 931 if(_options.maxSpreadZoom === 1 && _options.pinchToClose === false && _options.zoomEl === false){ 932 rootClasses += ' pswp--zoom-prevented'; 933 } 934 // End of JIG addition 935 framework.addClass(template, rootClasses); 936 937 self.updateSize(); 938 939 // initial update 940 _containerShiftIndex = -1; 941 _indexDiff = null; 942 for(i = 0; i < NUM_HOLDERS; i++) { 943 _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, _itemHolders[i].el.style); 944 } 945 946 if(!_oldIE) { 947 framework.bind(self.scrollWrap, _downEvents, self); // no dragging for old IE 948 } 949 950 _listen('initialZoomInEnd', function() { 951 self.setContent(_itemHolders[0], _currentItemIndex-1); 952 self.setContent(_itemHolders[2], _currentItemIndex+1); 953 954 _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'block'; 955 956 if(_options.focus) { 957 // focus causes layout, 958 // which causes lag during the animation, 959 // that's why we delay it untill the initial zoom transition ends 960 template.focus(); 961 } 962 963 964 _bindEvents(); 965 }); 966 967 // set content for center slide (first time) 968 self.setContent(_itemHolders[1], _currentItemIndex); 969 970 self.updateCurrItem(); 971 972 _shout('afterInit'); 973 974 if(!_isFixedPosition) { 975 976 // On all versions of iOS lower than 8.0, we check size of viewport every second. 977 // 978 // This is done to detect when Safari top & bottom bars appear, 979 // as this action doesn't trigger any events (like resize). 980 // 981 // On iOS8 they fixed this. 982 // 983 // 10 Nov 2014: iOS 7 usage ~40%. iOS 8 usage 56%. 984 985 _updateSizeInterval = setInterval(function() { 986 if(!_numAnimations && !_isDragging && !_isZooming && (_currZoomLevel === self.currItem.initialZoomLevel) ) { 987 self.updateSize(); 988 } 989 }, 1000); 990 } 991
992 framework.addClass(template, 'pswp--visible'); 993 }, 994 995 // Close the gallery, then destroy it 996 close: function() { 997 if(!_isOpen) { 998 return; 999 } 1000 1001 _isOpen = false; 1002 _isDestroying = true; 1003 _shout('close'); 1004 _unbindEvents(); 1005 1006 //Johann 1007 if(document.getElementById("ifrm")) { 1008 document.getElementById("ifrm").remove();} 1009 1010 _showOrHide(self.currItem, null, true, self.destroy); 1011 }, 1012 1013 // destroys the gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks) 1014 destroy: function() { 1015 _shout('destroy'); 1016 1017 if(_showOrHideTimeout) { 1018 clearTimeout(_showOrHideTimeout); 1019 } 1020 1021 template.setAttribute('aria-hidden', 'true'); 1022 template.className = _initalClassName; 1023 1024 if(_updateSizeInterval) { 1025 clearInterval(_updateSizeInterval); 1026 } 1027 1028 framework.unbind(self.scrollWrap, _downEvents, self); 1029 1030 // we unbind scroll event at the end, as closing animation may depend on it 1031 framework.unbind(window, 'scroll', self); 1032 1033 _stopDragUpdateLoop(); 1034 1035 _stopAllAnimations(); 1036 1037 _listeners = null; 1038 }, 1039 1040 /** 1041 * Pan image to position 1042 * @param {Number} x 1043 * @param {Number} y 1044 * @param {Boolean} force Will ignore bounds if set to true. 1045 */ 1046 panTo: function(x,y,force) { 1047 if(!force) { 1048 if(x > _currPanBounds.min.x) { 1049 x = _currPanBounds.min.x; 1050 } else if(x < _currPanBounds.max.x) { 1051 x = _currPanBounds.max.x; 1052 } 1053 1054 if(y > _currPanBounds.min.y) { 1055 y = _currPanBounds.min.y; 1056 } else if(y < _currPanBounds.max.y) { 1057 y = _currPanBounds.max.y; 1058 } 1059 } 1060 1061 _panOffset.x = x; 1062 _panOffset.y = y; 1063 _applyCurrentZoomPan(); 1064 }, 1065 1066 handleEvent: function (e) { 1067 e = e || window.event; 1068 if(_globalEventHandlers[e.type]) { 1069 _globalEventHandlers[e.type](e); 1070 } 1071 }, 1072 1073 1074 goTo: function(index) { 1075 1076 index = _getLoopedId(index); 1077 1078 var diff = index - _currentItemIndex; 1079 _indexDiff = diff; 1080 1081 _currentItemIndex = index; 1082 self.currItem = _getItemAt( _currentItemIndex ); 1083 _currPositionIndex -= diff; 1084 1085 _moveMainScroll(_slideSize.x * _currPositionIndex); 1086 1087 1088 _stopAllAnimations(); 1089 _mainScrollAnimating = false; 1090 1091 self.updateCurrItem(); 1092 }, 1093 next: function() { 1094 self.goTo( _currentItemIndex + 1); 1095 }, 1096 prev: function() { 1097 self.goTo( _currentItemIndex - 1); 1098 }, 1099 1100 // update current zoom/pan objects 1101 updateCurrZoomItem: function(emulateSetContent) { 1102 if(emulateSetContent) { 1103 _shout('beforeChange', 0); 1104 } 1105 1106 // itemHolder[1] is middle (current) item 1107 if(_itemHolders[1].el.children.length) { 1108 var zoomElement = _itemHolders[1].el.children[0]; 1109 if( framework.hasClass(zoomElement, 'pswp__zoom-wrap') ) { 1110 _currZoomElementStyle = zoomElement.style; 1111 } else { 1112 _currZoomElementStyle = null; 1113 } 1114 } else { 1115 _currZoomElementStyle = null; 1116 } 1117 1118 _currPanBounds = self.currItem.bounds; 1119 _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel; 1120 1121 _panOffset.x = _currPanBounds.center.x; 1122 _panOffset.y = _currPanBounds.center.y; 1123 1124 if(emulateSetContent) { 1125 _shout('afterChange'); 1126 } 1127 }, 1128 1129 1130 invalidateCurrItems: function() { 1131 _itemsNeedUpdate = true; 1132 for(var i = 0; i < NUM_HOLDERS; i++) { 1133 if( _itemHolders[i].item ) { 1134 _itemHolders[i].item.needsUpdate = true; 1135 } 1136 } 1137 }, 1138 1139 updateCurrItem: function(beforeAnimation) { 1140 1141 if(_indexDiff === 0) { 1142 return; 1143 } 1144 1145 var diffAbs = Math.abs(_indexDiff), 1146 tempHolder; 1147 1148 if(beforeAnimation && diffAbs < 2) { 1149 return; 1150 } 1151 1152 1153 self.currItem = _getItemAt( _currentItemIndex ); 1154 _renderMaxResolution = false; 1155 1156 _shout('beforeChange', _indexDiff); 1157 1158 if(diffAbs >= NUM_HOLDERS) { 1159 _containerShiftIndex += _indexDiff + (_indexDiff > 0 ? -NUM_HOLDERS : NUM_HOLDERS); 1160 diffAbs = NUM_HOLDERS; 1161 } 1162 for(var i = 0; i < diffAbs; i++) { 1163 if(_indexDiff > 0) { 1164 tempHolder = _itemHolders.shift(); 1165 _itemHolders[NUM_HOLDERS-1] = tempHolder; // move first to last 1166 1167 _containerShiftIndex++; 1168 _setTranslateX( (_containerShiftIndex+2) * _slideSize.x, tempHolder.el.style); 1169 self.setContent(tempHolder, _currentItemIndex - diffAbs + i + 1 + 1); 1170 } else { 1171 tempHolder = _itemHolders.pop(); 1172 _itemHolders.unshift( tempHolder ); // move last to first 1173 1174 _containerShiftIndex--; 1175 _setTranslateX( _containerShiftIndex * _slideSize.x, tempHolder.el.style); 1176 self.setContent(tempHolder, _currentItemIndex + diffAbs - i - 1 - 1); 1177 } 1178 1179 } 1180 1181 // reset zoom/pan on previous item 1182 if(_currZoomElementStyle && Math.abs(_indexDiff) === 1) { 1183 1184 var prevItem = _getItemAt(_prevItemIndex); 1185 if(prevItem.initialZoomLevel !== _currZoomLevel) { 1186 _calculateItemSize(prevItem , _viewportSize ); 1187 _setImageSize(prevItem); 1188 _applyZoomPanToItem( prevItem ); 1189 } 1190 1191 } 1192 1193 // reset diff after update 1194 _indexDiff = 0; 1195 1196 self.updateCurrZoomItem(); 1197 1198 _prevItemIndex = _currentItemIndex; 1199 1200 _shout('afterChange'); 1201 1202 }, 1203 1204 1205 1206 updateSize: function(force) { 1207 1208 if(!_isFixedPosition && _options.modal) { 1209 var windowScrollY = framework.getScrollY(); 1210 if(_currentWindowScrollY !== windowScrollY) { 1211 template.style.top = windowScrollY + 'px'; 1212 _currentWindowScrollY = windowScrollY; 1213 } 1214 if(!force && _windowVisibleSize.x === window.innerWidth && _windowVisibleSize.y === window.innerHeight) { 1215 return; 1216 } 1217 _windowVisibleSize.x = window.innerWidth; 1218 _windowVisibleSize.y = window.innerHeight; 1219 1220 //template.style.width = _windowVisibleSize.x + 'px'; 1221 template.style.height = _windowVisibleSize.y + 'px'; 1222 } 1223 1224 1225 1226 _viewportSize.x = self.scrollWrap.clientWidth; 1227 _viewportSize.y = self.scrollWrap.clientHeight; 1228 1229 _updatePageScrollOffset(); 1230 1231 _slideSize.x = _viewportSize.x + Math.round(_viewportSize.x * _options.spacing); 1232 _slideSize.y = _viewportSize.y; 1233 1234 _moveMainScroll(_slideSize.x * _currPositionIndex); 1235 1236 _shout('beforeResize'); // even may be used for example to switch image sources 1237 1238 1239 // don't re-calculate size on inital size update 1240 if(_containerShiftIndex !== undefined) { 1241 1242 var holder, 1243 item, 1244 hIndex; 1245 1246 for(var i = 0; i < NUM_HOLDERS; i++) { 1247 holder = _itemHolders[i];
1248 _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, holder.el.style); 1249 1250 hIndex = _currentItemIndex+i-1; 1251 1252 if(_options.loop && _getNumItems() > 2) { 1253 hIndex = _getLoopedId(hIndex); 1254 } 1255 1256 // update zoom level on items and refresh source (if needsUpdate) 1257 item = _getItemAt( hIndex ); 1258 1259 // re-render gallery item if `needsUpdate`, 1260 // or doesn't have `bounds` (entirely new slide object) 1261 if( item && (_itemsNeedUpdate || item.needsUpdate || !item.bounds) ) { 1262 1263 self.cleanSlide( item ); 1264 1265 self.setContent( holder, hIndex ); 1266 1267 // if "center" slide 1268 if(i === 1) { 1269 self.currItem = item; 1270 self.updateCurrZoomItem(true); 1271 } 1272 1273 item.needsUpdate = false; 1274 1275 } else if(holder.index === -1 && hIndex >= 0) { 1276 // add content first time 1277 self.setContent( holder, hIndex ); 1278 } 1279 if(item && item.container) { 1280 _calculateItemSize(item, _viewportSize); 1281 _setImageSize(item); 1282 _applyZoomPanToItem( item ); 1283 } 1284 1285 } 1286 _itemsNeedUpdate = false; 1287 } 1288 1289 _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel; 1290 _currPanBounds = self.currItem.bounds; 1291 1292 if(_currPanBounds) { 1293 _panOffset.x = _currPanBounds.center.x; 1294 _panOffset.y = _currPanBounds.center.y; 1295 _applyCurrentZoomPan( true ); 1296 } 1297 1298 _shout('resize'); 1299 }, 1300 1301 // Zoom current item to 1302 zoomTo: function(destZoomLevel, centerPoint, speed, easingFn, updateFn) { 1303 /* 1304 if(destZoomLevel === 'fit') { 1305 destZoomLevel = self.currItem.fitRatio; 1306 } else if(destZoomLevel === 'fill') { 1307 destZoomLevel = self.currItem.fillRatio; 1308 } 1309 */ 1310 1311 if(centerPoint) { 1312 _startZoomLevel = _currZoomLevel; 1313 _midZoomPoint.x = Math.abs(centerPoint.x) - _panOffset.x ; 1314 _midZoomPoint.y = Math.abs(centerPoint.y) - _panOffset.y ; 1315 _equalizePoints(_startPanOffset, _panOffset); 1316 } 1317 1318 var destPanBounds = _calculatePanBounds(destZoomLevel, false), 1319 destPanOffset = {}; 1320 1321 _modifyDestPanOffset('x', destPanBounds, destPanOffset, destZoomLevel); 1322 _modifyDestPanOffset('y', destPanBounds, destPanOffset, destZoomLevel); 1323 1324 var initialZoomLevel = _currZoomLevel; 1325 var initialPanOffset = { 1326 x: _panOffset.x, 1327 y: _panOffset.y 1328 }; 1329 1330 _roundPoint(destPanOffset); 1331 1332 var onUpdate = function(now) { 1333 if(now === 1) { 1334 _currZoomLevel = destZoomLevel; 1335 _panOffset.x = destPanOffset.x; 1336 _panOffset.y = destPanOffset.y; 1337 } else { 1338 _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel; 1339 _panOffset.x = (destPanOffset.x - initialPanOffset.x) * now + initialPanOffset.x;
1340 _panOffset.y = (destPanOffset.y - initialPanOffset.y) * now + initialPanOffset.y; 1341 } 1342 1343 if(updateFn) { 1344 updateFn(now); 1345 } 1346 1347 _applyCurrentZoomPan( now === 1 ); 1348 }; 1349 1350 if(speed) { 1351 _animateProp('customZoomTo', 0, 1, speed, easingFn || framework.easing.sine.inOut, onUpdate); 1352 } else { 1353 onUpdate(1); 1354 } 1355 } 1356 1357 1358}; 1359 1360 1361/*>>core*/ 1362 1363/*>>gestures*/ 1364/** 1365 * Mouse/touch/pointer event handlers. 1366 * 1367 * separated from @core.js for readability 1368 */ 1369 1370var MIN_SWIPE_DISTANCE = 30, 1371 DIRECTION_CHECK_OFFSET = 10; // amount of pixels to drag to determine direction of swipe 1372 1373var _gestureStartTime, 1374 _gestureCheckSpeedTime, 1375 1376 // pool of objects that are used during dragging of zooming 1377 p = {}, // first point 1378 p2 = {}, // second point (for zoom gesture) 1379 delta = {}, 1380 _currPoint = {}, 1381 _startPoint = {}, 1382 _currPointers = [], 1383 _startMainScrollPos = {}, 1384 _releaseAnimData, 1385 _posPoints = [], // array of points during dragging, used to determine type of gesture 1386 _tempPoint = {}, 1387 1388 _isZoomingIn, 1389 _verticalDragInitiated, 1390 _oldAndroidTouchEndTimeout, 1391 _currZoomedItemIndex = 0, 1392 _centerPoint = _getEmptyPoint(), 1393 _lastReleaseTime = 0, 1394 _isDragging, // at least one pointer is down 1395 _isMultitouch, // at least two _pointers are down 1396 _zoomStarted, // zoom level changed during zoom gesture 1397 _moved, 1398 _dragAnimFrame, 1399 _mainScrollShifted, 1400 _currentPoints, // array of current touch points 1401 1402 _isZooming, 1403 _currPointsDistance, 1404 _startPointsDistance, 1405 _currPanBounds, 1406 _mainScrollPos = _getEmptyPoint(), 1407 _currZoomElementStyle, 1408 _mainScrollAnimating, // true, if animation after swipe gesture is running 1409 _midZoomPoint = _getEmptyPoint(), 1410 _currCenterPoint = _getEmptyPoint(), 1411 _direction, 1412 _isFirstMove, 1413 _opacityChanged, 1414 _bgOpacity, 1415 _wasOverInitialZoom, 1416 1417 _isEqualPoints = function(p1, p2) { 1418 return p1.x === p2.x && p1.y === p2.y; 1419 }, 1420 _isNearbyPoints = function(touch0, touch1) { 1421 return Math.abs(touch0.x - touch1.x) < DOUBLE_TAP_RADIUS && Math.abs(touch0.y - touch1.y) < DOUBLE_TAP_RADIUS; 1422 }, 1423 _calculatePointsDistance = function(p1, p2) { 1424 _tempPoint.x = Math.abs( p1.x - p2.x ); 1425 _tempPoint.y = Math.abs( p1.y - p2.y ); 1426 return Math.sqrt(_tempPoint.x * _tempPoint.x + _tempPoint.y * _tempPoint.y); 1427 }, 1428 _stopDragUpdateLoop = function() { 1429 if(_dragAnimFrame) { 1430 _cancelAF(_dragAnimFrame); 1431 _dragAnimFrame = null; 1432 } 1433 }, 1434 _dragUpdateLoop = function() { 1435 if(_isDragging) { 1436 _dragAnimFrame = _requestAF(_dragUpdateLoop); 1437 _renderMovement(); 1438 } 1439 }, 1440 _canPan = function() { 1441 return !(_options.scaleMode === 'fit' && _currZoomLevel === self.currItem.initialZoomLevel); 1442 }, 1443 1444 // find the closest parent DOM element 1445 _closestElement = function(el, fn) { 1446 if(!el || el === document) { 1447 return false; 1448 } 1449 1450 // don't search elements above pswp__scroll-wrap 1451 if(el.getAttribute('class') && el.getAttribute('class').indexOf('pswp__scroll-wrap') > -1 ) { 1452 return false; 1453 } 1454 1455 if( fn(el) ) { 1456 return el; 1457 } 1458 1459 return _closestElement(el.parentNode, fn); 1460 }, 1461 1462 _preventObj = {}, 1463 _preventDefaultEventBehaviour = function(e, isDown) { 1464 _preventObj.prevent = !_closestElement(e.target, _options.isClickableElement); 1465 1466 _shout('preventDragEvent', e, isDown, _preventObj); 1467 return _preventObj.prevent; 1468 1469 }, 1470 _convertTouchToPoint = function(touch, p) { 1471 p.x = touch.pageX; 1472 p.y = touch.pageY; 1473 p.id = touch.identifier; 1474 return p; 1475 }, 1476 _findCenterOfPoints = function(p1, p2, pCenter) { 1477 pCenter.x = (p1.x + p2.x) * 0.5; 1478 pCenter.y = (p1.y + p2.y) * 0.5; 1479 }, 1480 _pushPosPoint = function(time, x, y) { 1481 if(time - _gestureCheckSpeedTime > 50) { 1482 var o = _posPoints.length > 2 ? _posPoints.shift() : {}; 1483 o.x = x; 1484 o.y = y; 1485 _posPoints.push(o); 1486 _gestureCheckSpeedTime = time; 1487 } 1488 }, 1489 1490 _calculateVerticalDragOpacityRatio = function() { 1491 var yOffset = _panOffset.y - self.currItem.initialPosition.y; // difference between initial and current position 1492 return 1 - Math.abs( yOffset / (_viewportSize.y / 2) ); 1493 }, 1494 1495 1496 // points pool, reused during touch events 1497 _ePoint1 = {}, 1498 _ePoint2 = {}, 1499 _tempPointsArr = [], 1500 _tempCounter, 1501 _getTouchPoints = function(e) { 1502 // clean up previous points, without recreating array 1503 while(_tempPointsArr.length > 0) { 1504 _tempPointsArr.pop();
1505 } 1506 1507 if(!_pointerEventEnabled) { 1508 if(e.type.indexOf('touch') > -1) { 1509 1510 if(e.touches && e.touches.length > 0) { 1511 _tempPointsArr[0] = _convertTouchToPoint(e.touches[0], _ePoint1); 1512 if(e.touches.length > 1) { 1513 _tempPointsArr[1] = _convertTouchToPoint(e.touches[1], _ePoint2); 1514 } 1515 } 1516 1517 } else { 1518 _ePoint1.x = e.pageX; 1519 _ePoint1.y = e.pageY; 1520 _ePoint1.id = ''; 1521 _tempPointsArr[0] = _ePoint1;//_ePoint1; 1522 } 1523 } else { 1524 _tempCounter = 0; 1525 // we can use forEach, as pointer events are supported only in modern browsers 1526 _currPointers.forEach(function(p) { 1527 if(_tempCounter === 0) { 1528 _tempPointsArr[0] = p; 1529 } else if(_tempCounter === 1) { 1530 _tempPointsArr[1] = p; 1531 } 1532 _tempCounter++; 1533 1534 }); 1535 } 1536 return _tempPointsArr; 1537 }, 1538 1539 _panOrMoveMainScroll = function(axis, delta) { 1540 1541 var panFriction, 1542 overDiff = 0, 1543 newOffset = _panOffset[axis] + delta[axis], 1544 startOverDiff, 1545 dir = delta[axis] > 0, 1546 newMainScrollPosition = _mainScrollPos.x + delta.x, 1547 mainScrollDiff = _mainScrollPos.x - _startMainScrollPos.x, 1548 newPanPos, 1549 newMainScrollPos; 1550 1551 // calculate fdistance over the bounds and friction 1552 if(newOffset > _currPanBounds.min[axis] || newOffset < _currPanBounds.max[axis]) { 1553 panFriction = _options.panEndFriction; 1554 // Linear increasing of friction, so at 1/4 of viewport it's at max value. 1555 // Looks not as nice as was expected. Left for history. 1556 // panFriction = (1 - (_panOffset[axis] + delta[axis] + panBounds.min[axis]) / (_viewportSize[axis] / 4) ); 1557 } else { 1558 panFriction = 1; 1559 } 1560 1561 newOffset = _panOffset[axis] + delta[axis] * panFriction; 1562 1563 // move main scroll or start panning 1564 if(_options.allowPanToNext || _currZoomLevel === self.currItem.initialZoomLevel) { 1565 1566 1567 if(!_currZoomElementStyle) { 1568 1569 newMainScrollPos = newMainScrollPosition; 1570 1571 } else if(_direction === 'h' && axis === 'x' && !_zoomStarted ) { 1572 1573 if(dir) { 1574 if(newOffset > _currPanBounds.min[axis]) { 1575 panFriction = _options.panEndFriction; 1576 overDiff = _currPanBounds.min[axis] - newOffset; 1577 startOverDiff = _currPanBounds.min[axis] - _startPanOffset[axis]; 1578 } 1579 1580 // drag right 1581 if( (startOverDiff <= 0 || mainScrollDiff < 0) && _getNumItems() > 1 ) { 1582 newMainScrollPos = newMainScrollPosition; 1583 if(mainScrollDiff < 0 && newMainScrollPosition > _startMainScrollPos.x) { 1584 newMainScrollPos = _startMainScrollPos.x; 1585 } 1586 } else { 1587 if(_currPanBounds.min.x !== _currPanBounds.max.x) { 1588 newPanPos = newOffset; 1589 } 1590 1591 } 1592 1593 } else { 1594 1595 if(newOffset < _currPanBounds.max[axis] ) { 1596 panFriction =_options.panEndFriction; 1597 overDiff = newOffset - _currPanBounds.max[axis]; 1598 startOverDiff = _startPanOffset[axis] - _currPanBounds.max[axis]; 1599 } 1600 1601 if( (startOverDiff <= 0 || mainScrollDiff > 0) && _getNumItems() > 1 ) { 1602 newMainScrollPos = newMainScrollPosition; 1603 1604 if(mainScrollDiff > 0 && newMainScrollPosition < _startMainScrollPos.x) { 1605 newMainScrollPos = _startMainScrollPos.x; 1606 } 1607 1608 } else { 1609 if(_currPanBounds.min.x !== _currPanBounds.max.x) { 1610 newPanPos = newOffset; 1611 } 1612 } 1613 1614 } 1615 1616 1617 // 1618 } 1619 1620 if(axis === 'x') { 1621 1622 if(newMainScrollPos !== undefined) { 1623 _moveMainScroll(newMainScrollPos, true); 1624 if(newMainScrollPos === _startMainScrollPos.x) { 1625 _mainScrollShifted = false; 1626 } else { 1627 _mainScrollShifted = true; 1628 } 1629 } 1630 1631 if(_currPanBounds.min.x !== _currPanBounds.max.x) { 1632 if(newPanPos !== undefined) { 1633 _panOffset.x = newPanPos; 1634 } else if(!_mainScrollShifted) { 1635 _panOffset.x += delta.x * panFriction; 1636 } 1637 } 1638 1639 return newMainScrollPos !== undefined; 1640 } 1641 1642 } 1643 1644 if(!_mainScrollAnimating) { 1645 1646 if(!_mainScrollShifted) { 1647 if(_currZoomLevel > self.currItem.fitRatio) { 1648 _panOffset[axis] += delta[axis] * panFriction; 1649 1650 } 1651 } 1652 1653 1654 } 1655 1656 }, 1657 1658 // Pointerdown/touchstart/mousedown handler 1659 _onDragStart = function(e) { 1660 1661 // Allow dragging only via left mouse button. 1662 // As this handler is not added in IE8 - we ignore e.which 1663 // 1664 // http://www.quirksmode.org/js/events_properties.html 1665 // https://developer.mozilla.org/en-US/docs/Web/API/event.button 1666 if(e.type === 'mousedown' && e.button > 0 ) { 1667 return; 1668 } 1669 1670 if(_initialZoomRunning) { 1671 e.preventDefault(); 1672 return; 1673 } 1674 1675 if(_oldAndroidTouchEndTimeout && e.type === 'mousedown') { 1676 return; 1677 } 1678 1679 if(_preventDefaultEventBehaviour(e, true)) { 1680 e.preventDefault(); 1681 } 1682 1683 1684 1685 _shout('pointerDown'); 1686 1687 if(_pointerEventEnabled) { 1688 var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id'); 1689 if(pointerIndex < 0) { 1690 pointerIndex = _currPointers.length; 1691 } 1692 _currPointers[pointerIndex] = {x:e.pageX, y:e.pageY, id: e.pointerId}; 1693 } 1694 1695 1696 1697 var startPointsList = _getTouchPoints(e), 1698 numPoints = startPointsList.length; 1699 1700 _currentPoints = null; 1701 1702 _stopAllAnimations(); 1703 1704 // init drag 1705 if(!_isDragging || numPoints === 1) { 1706 1707 1708 1709 _isDragging = _isFirstMove = true; 1710 framework.bind(window, _upMoveEvents, self); 1711 1712 _isZoomingIn = 1713 _wasOverInitialZoom = 1714 _opacityChanged = 1715 _verticalDragInitiated = 1716 _mainScrollShifted = 1717 _moved = 1718 _isMultitouch = 1719 _zoomStarted = false; 1720 1721 _direction = null; 1722 1723 _shout('firstTouchStart', startPointsList); 1724 1725 _equalizePoints(_startPanOffset, _panOffset); 1726 1727 _currPanDist.x = _currPanDist.y = 0; 1728 _equalizePoints(_currPoint, startPointsList[0]); 1729 _equalizePoints(_startPoint, _currPoint); 1730
1731 //_equalizePoints(_startMainScrollPos, _mainScrollPos); 1732 _startMainScrollPos.x = _slideSize.x * _currPositionIndex; 1733 1734 _posPoints = [{ 1735 x: _currPoint.x, 1736 y: _currPoint.y 1737 }]; 1738 1739 _gestureCheckSpeedTime = _gestureStartTime = _getCurrentTime(); 1740 1741 //_mainScrollAnimationEnd(true); 1742 _calculatePanBounds( _currZoomLevel, true ); 1743 1744 // Start rendering 1745 _stopDragUpdateLoop(); 1746 _dragUpdateLoop(); 1747 1748 } 1749 1750 // init zoom 1751 if(!_isZooming && numPoints > 1 && !_mainScrollAnimating && !_mainScrollShifted) { 1752 _startZoomLevel = _currZoomLevel; 1753 _zoomStarted = false; // true if zoom changed at least once 1754 1755 _isZooming = _isMultitouch = true; 1756 _currPanDist.y = _currPanDist.x = 0; 1757 1758 _equalizePoints(_startPanOffset, _panOffset); 1759 1760 _equalizePoints(p, startPointsList[0]); 1761 _equalizePoints(p2, startPointsList[1]); 1762 1763 _findCenterOfPoints(p, p2, _currCenterPoint); 1764 1765 _midZoomPoint.x = Math.abs(_currCenterPoint.x) - _panOffset.x; 1766 _midZoomPoint.y = Math.abs(_currCenterPoint.y) - _panOffset.y; 1767 _currPointsDistance = _startPointsDistance = _calculatePointsDistance(p, p2); 1768 } 1769 1770 1771 }, 1772 1773 // Pointermove/touchmove/mousemove handler 1774 _onDragMove = function(e) { 1775 1776 e.preventDefault(); 1777 1778 if(_pointerEventEnabled) { 1779 var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id'); 1780 if(pointerIndex > -1) { 1781 var p = _currPointers[pointerIndex]; 1782 p.x = e.pageX; 1783 p.y = e.pageY; 1784 } 1785 } 1786 1787 if(_isDragging) { 1788 var touchesList = _getTouchPoints(e); 1789 if(!_direction && !_moved && !_isZooming) { 1790 1791 if(_mainScrollPos.x !== _slideSize.x * _currPositionIndex) { 1792 // if main scroll position is shifted – direction is always horizontal 1793 _direction = 'h'; 1794 } else { 1795 var diff = Math.abs(touchesList[0].x - _currPoint.x) - Math.abs(touchesList[0].y - _currPoint.y); 1796 // check the direction of movement 1797 if(Math.abs(diff) >= DIRECTION_CHECK_OFFSET) { 1798 _direction = diff > 0 ? 'h' : 'v'; 1799 _currentPoints = touchesList; 1800 } 1801 } 1802 1803 } else { 1804 _currentPoints = touchesList; 1805 } 1806 } 1807 }, 1808 // 1809 _renderMovement = function() { 1810 1811 if(!_currentPoints) { 1812 return; 1813 } 1814 1815 var numPoints = _currentPoints.length; 1816 1817 if(numPoints === 0) { 1818 return; 1819 } 1820 1821 _equalizePoints(p, _currentPoints[0]); 1822 1823 delta.x = p.x - _currPoint.x; 1824 delta.y = p.y - _currPoint.y; 1825 1826 if(_isZooming && numPoints > 1) { 1827 // Handle behaviour for more than 1 point 1828 1829 _currPoint.x = p.x; 1830 _currPoint.y = p.y; 1831 1832 // check if one of two points changed 1833 if( !delta.x && !delta.y && _isEqualPoints(_currentPoints[1], p2) ) { 1834 return; 1835 } 1836 1837 _equalizePoints(p2, _currentPoints[1]); 1838 1839 1840 if(!_zoomStarted) { 1841 _zoomStarted = true; 1842 _shout('zoomGestureStarted'); 1843 } 1844 1845 // Distance between two points 1846 var pointsDistance = _calculatePointsDistance(p,p2); 1847 1848 var zoomLevel = _calculateZoomLevel(pointsDistance); 1849 1850 // slightly over the of initial zoom level 1851 if(zoomLevel > self.currItem.initialZoomLevel + self.currItem.initialZoomLevel / 15) { 1852 _wasOverInitialZoom = true; 1853 } 1854 1855 // Apply the friction if zoom level is out of the bounds 1856 var zoomFriction = 1, 1857 minZoomLevel = _getMinZoomLevel(), 1858 maxZoomLevel = _getMaxZoomLevel(); 1859 1860 if ( zoomLevel < minZoomLevel ) { 1861 1862 if(_options.pinchToClose && !_wasOverInitialZoom && _startZoomLevel <= self.currItem.initialZoomLevel) { 1863 // fade out background if zooming out 1864 var minusDiff = minZoomLevel - zoomLevel; 1865 var percent = 1 - minusDiff / (minZoomLevel / 1.2); 1866 1867 _applyBgOpacity(percent); 1868 _shout('onPinchClose', percent); 1869 _opacityChanged = true; 1870 } else { 1871 zoomFriction = (minZoomLevel - zoomLevel) / minZoomLevel; 1872 if(zoomFriction > 1) { 1873 zoomFriction = 1; 1874 } 1875 zoomLevel = minZoomLevel - zoomFriction * (minZoomLevel / 3); 1876 } 1877 1878 } else if ( zoomLevel > maxZoomLevel ) { 1879 // 1.5 - extra zoom level above the max. E.g. if max is x6, real max 6 + 1.5 = 7.5 1880 zoomFriction = (zoomLevel - maxZoomLevel) / ( minZoomLevel * 6 ); 1881 if(zoomFriction > 1) { 1882 zoomFriction = 1; 1883 } 1884 zoomLevel = maxZoomLevel + zoomFriction * minZoomLevel; 1885 } 1886 1887 if(zoomFriction < 0) { 1888 zoomFriction = 0; 1889 } 1890 1891 // distance between touch points after friction is applied 1892 _currPointsDistance = pointsDistance; 1893 1894 // _centerPoint - The point in the middle of two pointers 1895 _findCenterOfPoints(p, p2, _centerPoint); 1896 1897 // paning with two pointers pressed 1898 _currPanDist.x += _centerPoint.x - _currCenterPoint.x; 1899 _currPanDist.y += _centerPoint.y - _currCenterPoint.y; 1900 _equalizePoints(_currCenterPoint, _centerPoint); 1901 1902 _panOffset.x = _calculatePanOffset('x', zoomLevel);
1903 _panOffset.y = _calculatePanOffset('y', zoomLevel); 1904 1905 _isZoomingIn = zoomLevel > _currZoomLevel; 1906 _currZoomLevel = zoomLevel; 1907 _applyCurrentZoomPan(); 1908 1909 } else { 1910 1911 // handle behaviour for one point (dragging or panning) 1912 1913 if(!_direction) { 1914 return; 1915 } 1916 1917 if(_isFirstMove) { 1918 _isFirstMove = false; 1919 1920 // subtract drag distance that was used during the detection direction 1921 1922 if( Math.abs(delta.x) >= DIRECTION_CHECK_OFFSET) { 1923 delta.x -= _currentPoints[0].x - _startPoint.x; 1924 } 1925 1926 if( Math.abs(delta.y) >= DIRECTION_CHECK_OFFSET) { 1927 delta.y -= _currentPoints[0].y - _startPoint.y; 1928 } 1929 } 1930 1931 _currPoint.x = p.x; 1932 _currPoint.y = p.y; 1933 1934 // do nothing if pointers position hasn't changed 1935 if(delta.x === 0 && delta.y === 0) { 1936 return; 1937 } 1938 1939 if(_direction === 'v' && _options.closeOnVerticalDrag) { 1940 if(!_canPan()) { 1941 _currPanDist.y += delta.y; 1942 _panOffset.y += delta.y; 1943 1944 var opacityRatio = _calculateVerticalDragOpacityRatio(); 1945 1946 _verticalDragInitiated = true; 1947 _shout('onVerticalDrag', opacityRatio); 1948 1949 _applyBgOpacity(opacityRatio); 1950 _applyCurrentZoomPan(); 1951 return ; 1952 } 1953 } 1954 1955 _pushPosPoint(_getCurrentTime(), p.x, p.y); 1956 1957 _moved = true; 1958 _currPanBounds = self.currItem.bounds; 1959 1960 var mainScrollChanged = _panOrMoveMainScroll('x', delta); 1961 if(!mainScrollChanged) { 1962 _panOrMoveMainScroll('y', delta); 1963 1964 _roundPoint(_panOffset); 1965 _applyCurrentZoomPan(); 1966 } 1967 1968 } 1969 1970 }, 1971 1972 // Pointerup/pointercancel/touchend/touchcancel/mouseup event handler 1973 _onDragRelease = function(e) { 1974 1975 if(_features.isOldAndroid ) { 1976 1977 if(_oldAndroidTouchEndTimeout && e.type === 'mouseup') { 1978 return; 1979 } 1980 1981 // on Android (v4.1, 4.2, 4.3 & possibly older) 1982 // ghost mousedown/up event isn't preventable via e.preventDefault, 1983 // which causes fake mousedown event 1984 // so we block mousedown/up for 600ms 1985 if( e.type.indexOf('touch') > -1 ) { 1986 clearTimeout(_oldAndroidTouchEndTimeout); 1987 _oldAndroidTouchEndTimeout = setTimeout(function() { 1988 _oldAndroidTouchEndTimeout = 0; 1989 }, 600); 1990 } 1991 1992 } 1993 1994 _shout('pointerUp'); 1995 1996 if(_preventDefaultEventBehaviour(e, false)) { 1997 e.preventDefault(); 1998 } 1999 2000 var releasePoint; 2001 2002 if(_pointerEventEnabled) { 2003 var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id'); 2004 2005 if(pointerIndex > -1) { 2006 releasePoint = _currPointers.splice(pointerIndex, 1)[0]; 2007 2008 if(navigator.msPointerEnabled) { 2009 var MSPOINTER_TYPES = { 2010 4: 'mouse', // event.MSPOINTER_TYPE_MOUSE 2011 2: 'touch', // event.MSPOINTER_TYPE_TOUCH 2012 3: 'pen' // event.MSPOINTER_TYPE_PEN 2013 }; 2014 releasePoint.type = MSPOINTER_TYPES[e.pointerType]; 2015 2016 if(!releasePoint.type) { 2017 releasePoint.type = e.pointerType || 'mouse'; 2018 } 2019 } else { 2020 releasePoint.type = e.pointerType || 'mouse'; 2021 } 2022 2023 } 2024 } 2025 2026 2027 var touchList = _getTouchPoints(e), 2028 gestureType, 2029 numPoints = touchList.length; 2030 2031 if(e.type === 'mouseup') { 2032 numPoints = 0; 2033 } 2034 2035 // Do nothing if there were 3 touch points or more 2036 if(numPoints === 2) { 2037 _currentPoints = null; 2038 return true; 2039 } 2040 2041 // if second pointer released 2042 if(numPoints === 1) { 2043 _equalizePoints(_startPoint, touchList[0]); 2044 } 2045 2046 2047 // pointer hasn't moved, send "tap release" point 2048 if(numPoints === 0 && !_direction && !_mainScrollAnimating) { 2049 if(!releasePoint) { 2050 if(e.type === 'mouseup') { 2051 releasePoint = {x: e.pageX, y: e.pageY, type:'mouse'}; 2052 } else if(e.changedTouches && e.changedTouches[0]) { 2053 releasePoint = {x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY, type:'touch'}; 2054 } 2055 } 2056 2057 _shout('touchRelease', e, releasePoint); 2058 } 2059 2060 // Difference in time between releasing of two last touch points (zoom gesture) 2061 var releaseTimeDiff = -1; 2062 2063 // Gesture completed, no pointers left 2064 if(numPoints === 0) { 2065 _isDragging = false; 2066 framework.unbind(window, _upMoveEvents, self); 2067 2068 _stopDragUpdateLoop(); 2069 2070 if(_isZooming) { 2071 // Two points released at the same time 2072 releaseTimeDiff = 0; 2073 } else if(_lastReleaseTime !== -1) { 2074 releaseTimeDiff = _getCurrentTime() - _lastReleaseTime; 2075 } 2076 } 2077 _lastReleaseTime = numPoints === 1 ? _getCurrentTime() : -1; 2078 2079 if(releaseTimeDiff !== -1 && releaseTimeDiff < 150) { 2080 gestureType = 'zoom'; 2081 } else { 2082 gestureType = 'swipe'; 2083 } 2084 2085 if(_isZooming && numPoints < 2) { 2086 _isZooming = false; 2087 2088 // Only second point released 2089 if(numPoints === 1) { 2090 gestureType = 'zoomPointerUp'; 2091 } 2092 _shout('zoomGestureEnded'); 2093 } 2094 2095 _currentPoints = null; 2096 if(!_moved && !_zoomStarted && !_mainScrollAnimating && !_verticalDragInitiated) { 2097 // nothing to animate 2098 return; 2099 } 2100 2101 _stopAllAnimations(); 2102 2103 2104 if(!_releaseAnimData) { 2105 _releaseAnimData = _initDragReleaseAnimationData(); 2106 } 2107 2108 _releaseAnimData.calculateSwipeSpeed('x'); 2109 2110 2111 if(_verticalDragInitiated) { 2112
2113 var opacityRatio = _calculateVerticalDragOpacityRatio(); 2114 2115 if(opacityRatio < _options.verticalDragRange) { 2116 self.close(); 2117 } else { 2118 var initalPanY = _panOffset.y, 2119 initialBgOpacity = _bgOpacity; 2120 2121 _animateProp('verticalDrag', 0, 1, 300, framework.easing.cubic.out, function(now) { 2122 2123 _panOffset.y = (self.currItem.initialPosition.y - initalPanY) * now + initalPanY; 2124 2125 _applyBgOpacity( (1 - initialBgOpacity) * now + initialBgOpacity ); 2126 _applyCurrentZoomPan(); 2127 }); 2128 2129 _shout('onVerticalDrag', 1); 2130 } 2131 2132 return; 2133 } 2134 2135 2136 // main scroll 2137 if( (_mainScrollShifted || _mainScrollAnimating) && numPoints === 0) { 2138 var itemChanged = _finishSwipeMainScrollGesture(gestureType, _releaseAnimData); 2139 if(itemChanged) { 2140 return; 2141 } 2142 gestureType = 'zoomPointerUp'; 2143 } 2144 2145 // prevent zoom/pan animation when main scroll animation runs 2146 if(_mainScrollAnimating) { 2147 return; 2148 } 2149 2150 // Complete simple zoom gesture (reset zoom level if it's out of the bounds) 2151 if(gestureType !== 'swipe') { 2152 _completeZoomGesture(); 2153 return; 2154 } 2155 2156 // Complete pan gesture if main scroll is not shifted, and it's possible to pan current image 2157 if(!_mainScrollShifted && _currZoomLevel > self.currItem.fitRatio) { 2158 _completePanGesture(_releaseAnimData); 2159 } 2160 }, 2161 2162 2163 // Returns object with data about gesture 2164 // It's created only once and then reused 2165 _initDragReleaseAnimationData = function() { 2166 // temp local vars 2167 var lastFlickDuration, 2168 tempReleasePos; 2169 2170 // s = this 2171 var s = { 2172 lastFlickOffset: {}, 2173 lastFlickDist: {}, 2174 lastFlickSpeed: {}, 2175 slowDownRatio: {}, 2176 slowDownRatioReverse: {}, 2177 speedDecelerationRatio: {}, 2178 speedDecelerationRatioAbs: {}, 2179 distanceOffset: {}, 2180 backAnimDestination: {}, 2181 backAnimStarted: {}, 2182 calculateSwipeSpeed: function(axis) { 2183 2184 2185 if( _posPoints.length > 1) { 2186 lastFlickDuration = _getCurrentTime() - _gestureCheckSpeedTime + 50; 2187 tempReleasePos = _posPoints[_posPoints.length-2][axis]; 2188 } else { 2189 lastFlickDuration = _getCurrentTime() - _gestureStartTime; // total gesture duration 2190 tempReleasePos = _startPoint[axis]; 2191 } 2192 s.lastFlickOffset[axis] = _currPoint[axis] - tempReleasePos; 2193 s.lastFlickDist[axis] = Math.abs(s.lastFlickOffset[axis]); 2194 if(s.lastFlickDist[axis] > 20) { 2195 s.lastFlickSpeed[axis] = s.lastFlickOffset[axis] / lastFlickDuration; 2196 } else { 2197 s.lastFlickSpeed[axis] = 0; 2198 } 2199 if( Math.abs(s.lastFlickSpeed[axis]) < 0.1 ) { 2200 s.lastFlickSpeed[axis] = 0; 2201 } 2202 2203 s.slowDownRatio[axis] = 0.95; 2204 s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis]; 2205 s.speedDecelerationRatio[axis] = 1; 2206 }, 2207 2208 calculateOverBoundsAnimOffset: function(axis, speed) { 2209 if(!s.backAnimStarted[axis]) { 2210 2211 if(_panOffset[axis] > _currPanBounds.min[axis]) { 2212 s.backAnimDestination[axis] = _currPanBounds.min[axis]; 2213 2214 } else if(_panOffset[axis] < _currPanBounds.max[axis]) { 2215 s.backAnimDestination[axis] = _currPanBounds.max[axis]; 2216 } 2217 2218 if(s.backAnimDestination[axis] !== undefined) { 2219 s.slowDownRatio[axis] = 0.7; 2220 s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis]; 2221 if(s.speedDecelerationRatioAbs[axis] < 0.05) { 2222 2223 s.lastFlickSpeed[axis] = 0; 2224 s.backAnimStarted[axis] = true; 2225 2226 _animateProp('bounceZoomPan'+axis,_panOffset[axis], 2227 s.backAnimDestination[axis], 2228 speed || 300, 2229 framework.easing.sine.out, 2230 function(pos) { 2231 _panOffset[axis] = pos; 2232 _applyCurrentZoomPan(); 2233 } 2234 ); 2235 2236 } 2237 } 2238 } 2239 }, 2240 2241 // Reduces the speed by slowDownRatio (per 10ms) 2242 calculateAnimOffset: function(axis) { 2243 if(!s.backAnimStarted[axis]) { 2244 s.speedDecelerationRatio[axis] = s.speedDecelerationRatio[axis] * (s.slowDownRatio[axis] + 2245 s.slowDownRatioReverse[axis] - 2246 s.slowDownRatioReverse[axis] * s.timeDiff / 10); 2247 2248 s.speedDecelerationRatioAbs[axis] = Math.abs(s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis]); 2249 s.distanceOffset[axis] = s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis] * s.timeDiff; 2250 _panOffset[axis] += s.distanceOffset[axis]; 2251 2252 } 2253 }, 2254 2255 panAnimLoop: function() { 2256 if ( _animations.zoomPan ) { 2257 _animations.zoomPan.raf = _requestAF(s.panAnimLoop); 2258 2259 s.now = _getCurrentTime(); 2260 s.timeDiff = s.now - s.lastNow; 2261 s.lastNow = s.now; 2262 2263 s.calculateAnimOffset('x'); 2264 s.calculateAnimOffset('y'); 2265 2266 _applyCurrentZoomPan(); 2267 2268 s.calculateOverBoundsAnimOffset('x'); 2269 s.calculateOverBoundsAnimOffset('y'); 2270 2271 2272 if (s.speedDecelerationRatioAbs.x < 0.05 && s.speedDecelerationRatioAbs.y < 0.05) { 2273 2274 // round pan position 2275 _panOffset.x = Math.round(_panOffset.x);
2276 _panOffset.y = Math.round(_panOffset.y); 2277 _applyCurrentZoomPan(); 2278 2279 _stopAnimation('zoomPan'); 2280 return; 2281 } 2282 } 2283 2284 } 2285 }; 2286 return s; 2287 }, 2288 2289 _completePanGesture = function(animData) { 2290 // calculate swipe speed for Y axis (paanning) 2291 animData.calculateSwipeSpeed('y'); 2292 2293 _currPanBounds = self.currItem.bounds; 2294 2295 animData.backAnimDestination = {}; 2296 animData.backAnimStarted = {}; 2297 2298 // Avoid acceleration animation if speed is too low 2299 if(Math.abs(animData.lastFlickSpeed.x) <= 0.05 && Math.abs(animData.lastFlickSpeed.y) <= 0.05 ) { 2300 animData.speedDecelerationRatioAbs.x = animData.speedDecelerationRatioAbs.y = 0; 2301 2302 // Run pan drag release animation. E.g. if you drag image and release finger without momentum. 2303 animData.calculateOverBoundsAnimOffset('x'); 2304 animData.calculateOverBoundsAnimOffset('y'); 2305 return true; 2306 } 2307 2308 // Animation loop that controls the acceleration after pan gesture ends 2309 _registerStartAnimation('zoomPan'); 2310 animData.lastNow = _getCurrentTime(); 2311 animData.panAnimLoop(); 2312 }, 2313 2314 2315 _finishSwipeMainScrollGesture = function(gestureType, _releaseAnimData) { 2316 var itemChanged; 2317 if(!_mainScrollAnimating) { 2318 _currZoomedItemIndex = _currentItemIndex; 2319 } 2320 2321 2322 2323 var itemsDiff; 2324 2325 if(gestureType === 'swipe') { 2326 var totalShiftDist = _currPoint.x - _startPoint.x, 2327 isFastLastFlick = _releaseAnimData.lastFlickDist.x < 10; 2328 2329 // if container is shifted for more than MIN_SWIPE_DISTANCE, 2330 // and last flick gesture was in right direction 2331 if(totalShiftDist > MIN_SWIPE_DISTANCE && 2332 (isFastLastFlick || _releaseAnimData.lastFlickOffset.x > 20) ) { 2333 // go to prev item 2334 itemsDiff = -1; 2335 } else if(totalShiftDist < -MIN_SWIPE_DISTANCE && 2336 (isFastLastFlick || _releaseAnimData.lastFlickOffset.x < -20) ) { 2337 // go to next item 2338 itemsDiff = 1; 2339 } 2340 } 2341 2342 var nextCircle; 2343 2344 if(itemsDiff) { 2345 2346 _currentItemIndex += itemsDiff; 2347 2348 if(_currentItemIndex < 0) { 2349 _currentItemIndex = _options.loop ? _getNumItems()-1 : 0; 2350 nextCircle = true; 2351 } else if(_currentItemIndex >= _getNumItems()) { 2352 _currentItemIndex = _options.loop ? 0 : _getNumItems()-1; 2353 nextCircle = true; 2354 } 2355 2356 if(!nextCircle || _options.loop) { 2357 _indexDiff += itemsDiff; 2358 _currPositionIndex -= itemsDiff; 2359 itemChanged = true; 2360 } 2361 2362 2363 2364 } 2365 2366 var animateToX = _slideSize.x * _currPositionIndex; 2367 var animateToDist = Math.abs( animateToX - _mainScrollPos.x ); 2368 var finishAnimDuration; 2369 2370 2371 if(!itemChanged && animateToX > _mainScrollPos.x !== _releaseAnimData.lastFlickSpeed.x > 0) { 2372 // "return to current" duration, e.g. when dragging from slide 0 to -1 2373 finishAnimDuration = 333; 2374 } else { 2375 finishAnimDuration = Math.abs(_releaseAnimData.lastFlickSpeed.x) > 0 ? 2376 animateToDist / Math.abs(_releaseAnimData.lastFlickSpeed.x) : 2377 333; 2378 2379 finishAnimDuration = Math.min(finishAnimDuration, 400); 2380 finishAnimDuration = Math.max(finishAnimDuration, 250); 2381 } 2382 2383 if(_currZoomedItemIndex === _currentItemIndex) { 2384 itemChanged = false; 2385 } 2386 2387 _mainScrollAnimating = true; 2388 2389 _shout('mainScrollAnimStart'); 2390 2391 _animateProp('mainScroll', _mainScrollPos.x, animateToX, finishAnimDuration, framework.easing.cubic.out, 2392 _moveMainScroll, 2393 function() { 2394 _stopAllAnimations(); 2395 _mainScrollAnimating = false; 2396 _currZoomedItemIndex = -1; 2397 2398 if(itemChanged || _currZoomedItemIndex !== _currentItemIndex) { 2399 self.updateCurrItem(); 2400 } 2401 2402 _shout('mainScrollAnimComplete'); 2403 } 2404 ); 2405 2406 if(itemChanged) { 2407 self.updateCurrItem(true); 2408 } 2409 2410 return itemChanged; 2411 }, 2412 2413 _calculateZoomLevel = function(touchesDistance) { 2414 return 1 / _startPointsDistance * touchesDistance * _startZoomLevel; 2415 }, 2416 2417 // Resets zoom if it's out of bounds 2418 _completeZoomGesture = function() { 2419 var destZoomLevel = _currZoomLevel, 2420 minZoomLevel = _getMinZoomLevel(), 2421 maxZoomLevel = _getMaxZoomLevel(); 2422 2423 if ( _currZoomLevel < minZoomLevel ) { 2424 destZoomLevel = minZoomLevel; 2425 } else if ( _currZoomLevel > maxZoomLevel ) { 2426 destZoomLevel = maxZoomLevel; 2427 } 2428 2429 var destOpacity = 1,
2430 onUpdate, 2431 initialOpacity = _bgOpacity; 2432 2433 if(_opacityChanged && !_isZoomingIn && !_wasOverInitialZoom && _currZoomLevel < minZoomLevel) { 2434 //_closedByScroll = true; 2435 self.close(); 2436 return true; 2437 } 2438 2439 if(_opacityChanged) { 2440 onUpdate = function(now) { 2441 _applyBgOpacity( (destOpacity - initialOpacity) * now + initialOpacity ); 2442 }; 2443 } 2444 2445 self.zoomTo(destZoomLevel, 0, 200, framework.easing.cubic.out, onUpdate); 2446 return true; 2447 }; 2448 2449 2450_registerModule('Gestures', { 2451 publicMethods: { 2452 2453 initGestures: function() { 2454 2455 // helper function that builds touch/pointer/mouse events 2456 var addEventNames = function(pref, down, move, up, cancel) { 2457 _dragStartEvent = pref + down; 2458 _dragMoveEvent = pref + move; 2459 _dragEndEvent = pref + up; 2460 if(cancel) { 2461 _dragCancelEvent = pref + cancel; 2462 } else { 2463 _dragCancelEvent = ''; 2464 } 2465 }; 2466 2467 _pointerEventEnabled = _features.pointerEvent; 2468 if(_pointerEventEnabled && _features.touch) { 2469 // we don't need touch events, if browser supports pointer events 2470 _features.touch = false; 2471 } 2472 2473 if(_pointerEventEnabled) { 2474 if(navigator.msPointerEnabled) { 2475 // IE10 pointer events are case-sensitive 2476 addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel'); 2477 } else { 2478 addEventNames('pointer', 'down', 'move', 'up', 'cancel'); 2479 } 2480 } else if(_features.touch) { 2481 addEventNames('touch', 'start', 'move', 'end', 'cancel'); 2482 _likelyTouchDevice = true; 2483 } else { 2484 addEventNames('mouse', 'down', 'move', 'up'); 2485 } 2486 2487 2488 _upMoveEvents = _dragMoveEvent + ' ' + _dragEndEvent + ' ' + _dragCancelEvent; 2489 _downEvents = _dragStartEvent; 2490 2491 if(_pointerEventEnabled && !_likelyTouchDevice) { 2492 _likelyTouchDevice = (navigator.maxTouchPoints > 1) || (navigator.msMaxTouchPoints > 1); 2493 } 2494 // make variable public 2495 self.likelyTouchDevice = _likelyTouchDevice; 2496 2497 _globalEventHandlers[_dragStartEvent] = _onDragStart; 2498 _globalEventHandlers[_dragMoveEvent] = _onDragMove; 2499 _globalEventHandlers[_dragEndEvent] = _onDragRelease; // the Kraken 2500 2501 if(_dragCancelEvent) { 2502 _globalEventHandlers[_dragCancelEvent] = _globalEventHandlers[_dragEndEvent]; 2503 } 2504 2505 // Bind mouse events on device with detected hardware touch support, in case it supports multiple types of input. 2506 if(_features.touch) { 2507 _downEvents += ' mousedown'; 2508 _upMoveEvents += ' mousemove mouseup'; 2509 _globalEventHandlers.mousedown = _globalEventHandlers[_dragStartEvent]; 2510 _globalEventHandlers.mousemove = _globalEventHandlers[_dragMoveEvent]; 2511 _globalEventHandlers.mouseup = _globalEventHandlers[_dragEndEvent]; 2512 } 2513 2514 if(!_likelyTouchDevice) { 2515 // don't allow pan to next slide from zoomed state on Desktop 2516 _options.allowPanToNext = false; 2517 } 2518 } 2519 2520 } 2521}); 2522 2523 2524/*>>gestures*/ 2525 2526/*>>show-hide-transition*/ 2527/** 2528 * show-hide-transition.js: 2529 * 2530 * Manages initial opening or closing transition. 2531 * 2532 * If you're not planning to use transition for gallery at all, 2533 * you may set options hideAnimationDuration and showAnimationDuration to 0, 2534 * and just delete startAnimation function. 2535 * 2536 */ 2537 2538 2539var _showOrHideTimeout, 2540 _showOrHide = function(item, img, out, completeFn) { 2541 2542 if(_showOrHideTimeout) { 2543 clearTimeout(_showOrHideTimeout); 2544 } 2545 2546 _initialZoomRunning = true; 2547 _initialContentSet = true; 2548 2549 // dimensions of small thumbnail {x:,y:,w:}. 2550 // Height is optional, as calculated based on large image. 2551 var thumbBounds; 2552 if(item.initialLayout) { 2553 thumbBounds = item.initialLayout; 2554 item.initialLayout = null; 2555 } else { 2556 thumbBounds = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex); 2557 } 2558 2559 var duration = out ? _options.hideAnimationDuration : _options.showAnimationDuration; 2560 2561 var onComplete = function() { 2562 _stopAnimation('initialZoom'); 2563 if(!out) { 2564 _applyBgOpacity(1); 2565 if(img) { 2566 img.style.display = 'block'; 2567 } 2568 framework.addClass(template, 'pswp--animated-in'); 2569 _shout('initialZoom' + (out ? 'OutEnd' : 'InEnd')); 2570 } else { 2571 self.template.removeAttribute('style'); 2572 self.bg.removeAttribute('style'); 2573 } 2574 2575 if(completeFn) { 2576 completeFn(); 2577 } 2578 _initialZoomRunning = false; 2579 }; 2580 2581 // if bounds aren't provided, just open gallery without animation 2582 if(!duration || !thumbBounds || thumbBounds.x === undefined) { 2583 2584 _shout('initialZoom' + (out ? 'Out' : 'In') ); 2585 2586 _currZoomLevel = item.initialZoomLevel; 2587 _equalizePoints(_panOffset, item.initialPosition ); 2588 _applyCurrentZoomPan(); 2589 2590 template.style.opacity = out ? 0 : 1; 2591 _applyBgOpacity(1); 2592 2593 if(duration) { 2594 setTimeout(function() { 2595 onComplete(); 2596 }, duration); 2597 } else { 2598 onComplete(); 2599 } 2600 2601 return; 2602 } 2603 2604 var startAnimation = function() { 2605 var closeWithRaf = _closedByScroll, 2606 fadeEverything = !self.currItem.src || self.currItem.loadError || _options.showHideOpacity; 2607 2608 // apply hw-acceleration to image 2609 if(item.miniImg) { 2610 item.miniImg.style.webkitBackfaceVisibility = 'hidden'; 2611 } 2612 2613 if(!out) { 2614 _currZoomLevel = thumbBounds.w / item.w; 2615 _panOffset.x = thumbBounds.x;
2616 _panOffset.y = thumbBounds.y - _initalWindowScrollY; 2617 2618 self[fadeEverything ? 'template' : 'bg'].style.opacity = 0.001; 2619 _applyCurrentZoomPan(); 2620 } 2621 2622 _registerStartAnimation('initialZoom'); 2623 2624 if(out && !closeWithRaf) { 2625 framework.removeClass(template, 'pswp--animated-in'); 2626 } 2627 2628 if(fadeEverything) { 2629 if(out) { 2630 framework[ (closeWithRaf ? 'remove' : 'add') + 'Class' ](template, 'pswp--animate_opacity'); 2631 } else { 2632 setTimeout(function() { 2633 framework.addClass(template, 'pswp--animate_opacity'); 2634 }, 30); 2635 } 2636 } 2637 2638 _showOrHideTimeout = setTimeout(function() { 2639 2640 _shout('initialZoom' + (out ? 'Out' : 'In') ); 2641 2642 2643 if(!out) { 2644 2645 // "in" animation always uses CSS transitions (instead of rAF). 2646 // CSS transition work faster here, 2647 // as developer may also want to animate other things, 2648 // like ui on top of sliding area, which can be animated just via CSS 2649 2650 _currZoomLevel = item.initialZoomLevel; 2651 _equalizePoints(_panOffset, item.initialPosition ); 2652 _applyCurrentZoomPan(); 2653 _applyBgOpacity(1); 2654 2655 if(fadeEverything) { 2656 template.style.opacity = 1; 2657 } else { 2658 _applyBgOpacity(1); 2659 } 2660 2661 _showOrHideTimeout = setTimeout(onComplete, duration + 20); 2662 } else { 2663 2664 // "out" animation uses rAF only when PhotoSwipe is closed by browser scroll, to recalculate position 2665 var destZoomLevel = thumbBounds.w / item.w, 2666 initialPanOffset = { 2667 x: _panOffset.x, 2668 y: _panOffset.y 2669 }, 2670 initialZoomLevel = _currZoomLevel, 2671 initalBgOpacity = _bgOpacity, 2672 onUpdate = function(now) { 2673 2674 if(now === 1) { 2675 _currZoomLevel = destZoomLevel; 2676 _panOffset.x = thumbBounds.x; 2677 _panOffset.y = thumbBounds.y - _currentWindowScrollY; 2678 } else { 2679 _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel; 2680 _panOffset.x = (thumbBounds.x - initialPanOffset.x) * now + initialPanOffset.x; 2681 _panOffset.y = (thumbBounds.y - _currentWindowScrollY - initialPanOffset.
2681y) * now + initialPanOffset.y; 2682 } 2683 2684 _applyCurrentZoomPan(); 2685 if(fadeEverything) { 2686 template.style.opacity = 1 - now; 2687 } else { 2688 _applyBgOpacity( initalBgOpacity - now * initalBgOpacity ); 2689 } 2690 }; 2691 2692 if(closeWithRaf) { 2693 _animateProp('initialZoom', 0, 1, duration, framework.easing.cubic.out, onUpdate, onComplete); 2694 } else { 2695 onUpdate(1); 2696 _showOrHideTimeout = setTimeout(onComplete, duration + 20); 2697 } 2698 } 2699 2700 }, out ? 25 : 90); // Main purpose of this delay is to give browser time to paint and 2701 // create composite layers of PhotoSwipe UI parts (background, controls, caption, arrows). 2702 // Which avoids lag at the beginning of scale transition. 2703 }; 2704 startAnimation(); 2705 2706 2707 }; 2708 2709/*>>show-hide-transition*/ 2710 2711/*>>items-controller*/ 2712/** 2713* 2714* Controller manages gallery items, their dimensions, and their content. 2715* 2716*/ 2717 2718var _items, 2719 _tempPanAreaSize = {}, 2720 _imagesToAppendPool = [], 2721 _initialContentSet, 2722 _initialZoomRunning, 2723 _controllerDefaultOptions = { 2724 index: 0, 2725 //errorMsg: '<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not just be loaded.</div>', 2726 //Johann 2727 errorMsg: '<iframe ID="ifrm" style="width: 100%;height: 70%;margin-top: 5%;border: 1px solid black;" src="%url%"></iframe>', 2728 forceProgressiveLoading: false, // TODO 2729 preload: [1,1], 2730 getNumItemsFn: function() { 2731 return _items.length; 2732 } 2733 }; 2734 2735 2736var _getItemAt, 2737 _getNumItems, 2738 _initialIsLoop, 2739 _getZeroBounds = function() { 2740 return { 2741 center:{x:0,y:0}, 2742 max:{x:0,y:0}, 2743 min:{x:0,y:0} 2744 }; 2745 }, 2746 _calculateSingleItemPanBounds = function(item, realPanElementW, realPanElementH ) { 2747 var bounds = item.bounds; 2748 2749 // position of element when it's centered 2750 bounds.center.x = Math.round((_tempPanAreaSize.x - realPanElementW) / 2); 2751 bounds.center.y = Math.round((_tempPanAreaSize.y - realPanElementH) / 2) + item.vGap.top; 2752 2753 // maximum pan position 2754 bounds.max.x = (realPanElementW > _tempPanAreaSize.x) ? 2755 Math.round(_tempPanAreaSize.x - realPanElementW) : 2756 bounds.center.x; 2757 2758 bounds.max.y = (realPanElementH > _tempPanAreaSize.y) ? 2759 Math.round(_tempPanAreaSize.y - realPanElementH) + item.vGap.top : 2760 bounds.center.y; 2761 2762 // minimum pan position 2763 bounds.min.x = (realPanElementW > _tempPanAreaSize.x) ? 0 : bounds.center.x; 2764 bounds.min.y = (realPanElementH > _tempPanAreaSize.y) ? item.vGap.top : bounds.center.y; 2765 }, 2766 _calculateItemSize = function(item, viewportSize, zoomLevel) { 2767 2768 if (item.src && !item.loadError) { 2769 var isInitial = !zoomLevel; 2770 2771 if(isInitial) { 2772 if(!item.vGap) { 2773 item.vGap = {top:0,bottom:0}; 2774 } 2775 // allows overriding vertical margin for individual items 2776 _shout('parseVerticalMargin', item); 2777 } 2778 2779 2780 _tempPanAreaSize.x = viewportSize.x; 2781 _tempPanAreaSize.y = viewportSize.y - item.vGap.top - item.vGap.bottom; 2782 2783 if (isInitial) { 2784 var hRatio = _tempPanAreaSize.x / item.w; 2785 var vRatio = _tempPanAreaSize.y / item.h; 2786 2787 item.fitRatio = hRatio < vRatio ? hRatio : vRatio; 2788 //item.fillRatio = hRatio > vRatio ? hRatio : vRatio; 2789 2790 var scaleMode = _options.scaleMode; 2791 2792 if (scaleMode === 'orig') { 2793 zoomLevel = 1; 2794 } else if (scaleMode === 'fit') { 2795 zoomLevel = item.fitRatio; 2796 } 2797 2798 if (zoomLevel > 1) { 2799 zoomLevel = 1; 2800 } 2801 2802 item.initialZoomLevel = zoomLevel; 2803 2804 if(!item.bounds) { 2805 // reuse bounds object 2806 item.bounds = _getZeroBounds(); 2807 } 2808 } 2809 2810 if(!zoomLevel) { 2811 return; 2812 } 2813 2814 _calculateSingleItemPanBounds(item, item.w * zoomLevel, item.h * zoomLevel); 2815 2816 if (isInitial && zoomLevel === item.initialZoomLevel) { 2817 item.initialPosition = item.bounds.center; 2818 } 2819 2820 return item.bounds; 2821 } else { 2822 item.w = item.h = 0; 2823 item.initialZoomLevel = item.fitRatio = 1; 2824 item.bounds = _getZeroBounds(); 2825 item.initialPosition = item.bounds.center; 2826 2827 // if it's not image, we return zero bounds (content is not zoomable) 2828 return item.bounds; 2829 } 2830 2831 }, 2832 2833 2834 2835 2836 _appendImage = function(index, item, baseDiv, img, preventAnimation, keepPlaceholder) { 2837 2838 if(item.loadError) { 2839 return; 2840 } 2841 2842 if(img) { 2843 2844 item.imageAppended = true; 2845 _setImageSize(item, img, (item === self.currItem && _renderMaxResolution) ); 2846 2847 baseDiv.appendChild(img); 2848 2849 if(keepPlaceholder) { 2850 setTimeout(function() { 2851 if(item && item.loaded && item.placeholder) { 2852 item.placeholder.style.display = 'none'; 2853 item.placeholder = null; 2854 } 2855 }, 500); 2856 } 2857 } 2858 }, 2859 2860 2861 2862 _preloadImage = function(item) { 2863 item.loading = true; 2864 item.loaded = false; 2865 var img = item.img = framework.createEl('pswp__img', 'img'); 2866 var onComplete = function() { 2867 item.loading = false; 2868 item.loaded = true; 2869 2870 if(item.loadComplete) { 2871 item.loadComplete(item); 2872 } else { 2873 item.img = null; // no need to store image object 2874 } 2875 img.onload = img.onerror = null; 2876 img = null; 2877 }; 2878 img.onload = onComplete; 2879 img.onerror = function() { 2880 item.loadError = true; 2881 onComplete(); 2882 }; 2883 2884 img.src = item.src;// + '?a=' + Math.random(); 2885 2886 return img; 2887 }, 2888 _checkForError = function(item, cleanUp) { 2889 if(item.src && item.loadError && item.container) { 2890 2891 if(cleanUp) { 2892 item.container.innerHTML = ''; 2893 } 2894 // jOHANN 2895 item.container.innerHTML = _options.errorMsg.replace('%url%', item.src.replace("www.kentridge.studio", "player.vimeo.com") ); 2896// item.container.innerHTML = _options.errorMsg; 2897 return true; 2898 2899 } 2900 }, 2901 _setImageSize = function(item, img, maxRes) { 2902 if(!item.src) { 2903 return; 2904 } 2905 2906 if(!img) { 2907 img = item.container.lastChild; 2908 } 2909 2910 var w = maxRes ? item.w : Math.round(item.w * item.fitRatio), 2911 h = maxRes ? item.h : Math.round(item.h * item.fitRatio); 2912 2913 if(item.placeholder && !item.loaded) { 2914 item.placeholder.style.width = w + 'px'; 2915 item.placeholder.style.height = h + 'px'; 2916 } 2917 2918 img.style.width = w + 'px'; 2919 img.style.height = h + 'px'; 2920 }, 2921 _appendImagesPool = function() { 2922 2923 if(_imagesToAppendPool.length) { 2924 var poolItem; 2925 2926 for(var i = 0; i < _imagesToAppendPool.length; i++) { 2927 poolItem = _imagesToAppendPool[i]; 2928 if( poolItem.holder.index === poolItem.index ) { 2929 _appendImage(poolItem.index, poolItem.item, poolItem.baseDiv, poolItem.img, false, poolItem.clearPlaceholder); 2930 } 2931 } 2932 _imagesToAppendPool = []; 2933 } 2934 }; 2935 2936 2937 2938_registerModule('Controller', { 2939 2940 publicMethods: { 2941 2942 lazyLoadItem: function(index) { 2943 index = _getLoopedId(index); 2944 var item = _getItemAt(index); 2945 2946 if(!item || ((item.loaded || item.loading) && !_itemsNeedUpdate)) { 2947 return; 2948 } 2949 2950 _shout('gettingData', index, item); 2951 2952 if (!item.src) { 2953 return; 2954 } 2955 2956 _preloadImage(item); 2957 }, 2958 initController: function() { 2959 framework.extend(_options, _controllerDefaultOptions, true); 2960 self.items = _items = items; 2961 _getItemAt = self.getItemAt; 2962 _getNumItems = _options.getNumItemsFn; //self.getNumItems; 2963 2964 2965 2966 _initialIsLoop = _options.loop; 2967 if(_getNumItems() < 3) { 2968 _options.loop = false; // disable loop if less then 3 items 2969 } 2970 2971 _listen('beforeChange', function(diff) { 2972 2973 var p = _options.preload, 2974 isNext = diff === null ? true : (diff >= 0), 2975 preloadBefore = Math.min(p[0], _getNumItems() ),
2976 preloadAfter = Math.min(p[1], _getNumItems() ), 2977 i; 2978 2979 2980 for(i = 1; i <= (isNext ? preloadAfter : preloadBefore); i++) { 2981 self.lazyLoadItem(_currentItemIndex+i); 2982 } 2983 for(i = 1; i <= (isNext ? preloadBefore : preloadAfter); i++) { 2984 self.lazyLoadItem(_currentItemIndex-i); 2985 } 2986 }); 2987 2988 _listen('initialLayout', function() { 2989 self.currItem.initialLayout = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex); 2990 }); 2991 2992 _listen('mainScrollAnimComplete', _appendImagesPool); 2993 _listen('initialZoomInEnd', _appendImagesPool); 2994 2995 2996 2997 _listen('destroy', function() { 2998 var item; 2999 for(var i = 0; i < _items.length; i++) { 3000 item = _items[i]; 3001 // remove reference to DOM elements, for GC 3002 if(item.container) { 3003 item.container = null; 3004 } 3005 if(item.placeholder) { 3006 item.placeholder = null; 3007 } 3008 if(item.img) { 3009 item.img = null; 3010 } 3011 if(item.preloader) { 3012 item.preloader = null; 3013 } 3014 if(item.loadError) { 3015 item.loaded = item.loadError = false; 3016 } 3017 } 3018 _imagesToAppendPool = null; 3019 }); 3020 }, 3021 3022 3023 getItemAt: function(index) { 3024 if (index >= 0) { 3025 return _items[index] !== undefined ? _items[index] : false; 3026 } 3027 return false; 3028 }, 3029 3030 allowProgressiveImg: function() { 3031 // 1. Progressive image loading isn't working on webkit/blink 3032 // when hw-acceleration (e.g. translateZ) is applied to IMG element. 3033 // That's why in PhotoSwipe parent element gets zoom transform, not image itself. 3034 // 3035 // 2. Progressive image loading sometimes blinks in webkit/blink when applying animation to parent element. 3036 // That's why it's disabled on touch devices (mainly because of swipe transition) 3037 // 3038 // 3. Progressive image loading sometimes doesn't work in IE (up to 11). 3039 3040 // Don't allow progressive loading on non-large touch devices 3041 return _options.forceProgressiveLoading || !_likelyTouchDevice || _options.mouseUsed || screen.width > 1200; 3042 // 1200 - to eliminate touch devices with large screen (like Chromebook Pixel) 3043 }, 3044 3045 setContent: function(holder, index) { 3046 3047 if(_options.loop) { 3048 index = _getLoopedId(index); 3049 } 3050 3051 var prevItem = self.getItemAt(holder.index); 3052 if(prevItem) { 3053 prevItem.container = null; 3054 } 3055 3056 var item = self.getItemAt(index), 3057 img; 3058 3059 if(!item) { 3060 holder.el.innerHTML = ''; 3061 return; 3062 } 3063 3064 // allow to override data 3065 _shout('gettingData', index, item); 3066 3067 holder.index = index; 3068 holder.item = item; 3069 3070 // base container DIV is created only once for each of 3 holders 3071 var baseDiv = item.container = framework.createEl('pswp__zoom-wrap'); 3072 3073 3074 3075 if(!item.src && item.html) { 3076 if(item.html.tagName) { 3077 baseDiv.appendChild(item.html); 3078 } else { 3079 baseDiv.innerHTML = item.html; 3080 } 3081 } 3082 3083 _checkForError(item); 3084 3085 _calculateItemSize(item, _viewportSize); 3086 3087 if(item.src && !item.loadError && !item.loaded) { 3088 3089 item.loadComplete = function(item) { 3090 3091 // gallery closed before image finished loading 3092 if(!_isOpen) { 3093 return; 3094 } 3095 3096 // check if holder hasn't changed while image was loading 3097 if(holder && holder.index === index ) { 3098 if( _checkForError(item, true) ) { 3099 item.loadComplete = item.img = null; 3100 _calculateItemSize(item, _viewportSize); 3101 _applyZoomPanToItem(item); 3102 3103 if(holder.index === _currentItemIndex) { 3104 // recalculate dimensions 3105 self.updateCurrZoomItem(); 3106 } 3107 return; 3108 } 3109 if( !item.imageAppended ) { 3110 if(_features.transform && (_mainScrollAnimating || _initialZoomRunning) ) { 3111 _imagesToAppendPool.push({ 3112 item:item, 3113 baseDiv:baseDiv, 3114 img:item.img, 3115 index:index, 3116 holder:holder, 3117 clearPlaceholder:true 3118 });
3119 } else { 3120 _appendImage(index, item, baseDiv, item.img, _mainScrollAnimating || _initialZoomRunning, true); 3121 } 3122 } else { 3123 // remove preloader & mini-img 3124 if(!_initialZoomRunning && item.placeholder) { 3125 item.placeholder.style.display = 'none'; 3126 item.placeholder = null; 3127 } 3128 } 3129 } 3130 3131 item.loadComplete = null; 3132 item.img = null; // no need to store image element after it's added 3133 3134 _shout('imageLoadComplete', index, item); 3135 }; 3136 3137 if(framework.features.transform) { 3138 3139 var placeholderClassName = 'pswp__img pswp__img--placeholder'; 3140 placeholderClassName += (item.msrc ? '' : ' pswp__img--placeholder--blank'); 3141 3142 var placeholder = framework.createEl(placeholderClassName, item.msrc ? 'img' : ''); 3143 if(item.msrc) { 3144 placeholder.src = item.msrc; 3145 } 3146 3147 _setImageSize(item, placeholder); 3148 3149 baseDiv.appendChild(placeholder); 3150 item.placeholder = placeholder; 3151 3152 } 3153 3154 3155 3156 3157 if(!item.loading) { 3158 _preloadImage(item); 3159 } 3160 3161 3162 if( self.allowProgressiveImg() ) { 3163 // just append image 3164 if(!_initialContentSet && _features.transform) { 3165 _imagesToAppendPool.push({ 3166 item:item, 3167 baseDiv:baseDiv, 3168 img:item.img, 3169 index:index, 3170 holder:holder 3171 }); 3172 } else { 3173 _appendImage(index, item, baseDiv, item.img, true, true); 3174 } 3175 } 3176 3177 } else if(item.src && !item.loadError) { 3178 // image object is created every time, due to bugs of image loading & delay when switching images 3179 img = framework.createEl('pswp__img', 'img'); 3180 img.style.opacity = 1; 3181 img.src = item.src; 3182 _setImageSize(item, img); 3183 _appendImage(index, item, baseDiv, img, true); 3184 } 3185 3186 3187 if(!_initialContentSet && index === _currentItemIndex) { 3188 _currZoomElementStyle = baseDiv.style; 3189 _showOrHide(item, (img ||item.img) ); 3190 } else { 3191 _applyZoomPanToItem(item); 3192 } 3193 3194 holder.el.innerHTML = ''; 3195 holder.el.appendChild(baseDiv); 3196 }, 3197 3198 cleanSlide: function( item ) { 3199 if(item.img ) { 3200 item.img.onload = item.img.onerror = null; 3201 } 3202 item.loaded = item.loading = item.img = item.imageAppended = false; 3203 } 3204 3205 } 3206}); 3207 3208/*>>items-controller*/ 3209 3210/*>>tap*/ 3211/** 3212 * tap.js: 3213 * 3214 * Displatches tap and double-tap events. 3215 * 3216 */ 3217 3218var tapTimer, 3219 tapReleasePoint = {}, 3220 _dispatchTapEvent = function(origEvent, releasePoint, pointerType) { 3221 var e = document.createEvent( 'CustomEvent' ), 3222 eDetail = { 3223 origEvent:origEvent, 3224 target:origEvent.target, 3225 releasePoint: releasePoint, 3226 pointerType:pointerType || 'touch' 3227 }; 3228 3229 e.initCustomEvent( 'pswpTap', true, true, eDetail ); 3230 origEvent.target.dispatchEvent(e); 3231 }; 3232 3233_registerModule('Tap', { 3234 publicMethods: { 3235 initTap: function() { 3236 _listen('firstTouchStart', self.onTapStart); 3237 _listen('touchRelease', self.onTapRelease); 3238 _listen('destroy', function() { 3239 tapReleasePoint = {}; 3240 tapTimer = null; 3241 }); 3242 }, 3243 onTapStart: function(touchList) { 3244 if(touchList.length > 1) { 3245 clearTimeout(tapTimer); 3246 tapTimer = null; 3247 } 3248 }, 3249 onTapRelease: function(e, releasePoint) { 3250 if(!releasePoint) { 3251 return; 3252 } 3253 3254 if(!_moved && !_isMultitouch && !_numAnimations) { 3255 var p0 = releasePoint; 3256 if(tapTimer) { 3257 clearTimeout(tapTimer); 3258 tapTimer = null; 3259 3260 // Check if taped on the same place 3261 if ( _isNearbyPoints(p0, tapReleasePoint) ) { 3262 _shout('doubleTap', p0); 3263 return; 3264 } 3265 } 3266 3267 if(releasePoint.type === 'mouse') { 3268 _dispatchTapEvent(e, releasePoint, 'mouse'); 3269 return; 3270 } 3271 3272 var clickedTagName = e.target.tagName.toUpperCase(); 3273 // avoid double tap delay on buttons and elements that have class pswp__single-tap 3274 if(clickedTagName === 'BUTTON' || framework.hasClass(e.target, 'pswp__single-tap') ) { 3275 _dispatchTapEvent(e, releasePoint); 3276 return; 3277 } 3278 3279 _equalizePoints(tapReleasePoint, p0); 3280 3281 tapTimer = setTimeout(function() { 3282 _dispatchTapEvent(e, releasePoint); 3283 tapTimer = null; 3284 }, 300); 3285 } 3286 } 3287 } 3288}); 3289 3290/*>>tap*/ 3291 3292/*>>desktop-zoom*/ 3293/** 3294 * 3295 * desktop-zoom.js: 3296 * 3297 * - Binds mousewheel event for paning zoomed image. 3298 * - Manages "dragging", "zoomed-in", "zoom-out" classes. 3299 * (which are used for cursors and zoom icon) 3300 * - Adds toggleDesktopZoom function. 3301 * 3302 */ 3303 3304var _wheelDelta; 3305 3306_registerModule('DesktopZoom', { 3307 3308 publicMethods: { 3309 3310 initDesktopZoom: function() { 3311 3312 if(_oldIE) { 3313 // no zoom for old IE (<=8) 3314 return; 3315 } 3316 3317 if(_likelyTouchDevice) { 3318 // if detected hardware touch support, we wait until mouse is used, 3319 // and only then apply desktop-zoom features 3320 _listen('mouseUsed', function() { 3321 self.setupDesktopZoom(); 3322 }); 3323 } else { 3324 self.setupDesktopZoom(true); 3325 } 3326 3327 }, 3328 3329 setupDesktopZoom: function(onInit) { 3330 3331 _wheelDelta = {}; 3332 3333 var events = 'wheel mousewheel DOMMouseScroll'; 3334 3335 _listen('bindEvents', function() { 3336 framework.bind(template, events, self.handleMouseWheel); 3337 }); 3338 3339 _listen('unbindEvents', function() { 3340 if(_wheelDelta) { 3341 framework.unbind(template, events, self.handleMouseWheel); 3342 } 3343 }); 3344 3345 self.mouseZoomedIn = false; 3346 3347 var hasDraggingClass, 3348 updateZoomable = function() { 3349 if(self.mouseZoomedIn) {
3350 framework.removeClass(template, 'pswp--zoomed-in'); 3351 self.mouseZoomedIn = false; 3352 } 3353 if(_currZoomLevel < 1) { 3354 framework.addClass(template, 'pswp--zoom-allowed'); 3355 } else { 3356 framework.removeClass(template, 'pswp--zoom-allowed'); 3357 } 3358 removeDraggingClass(); 3359 }, 3360 removeDraggingClass = function() { 3361 if(hasDraggingClass) { 3362 framework.removeClass(template, 'pswp--dragging'); 3363 hasDraggingClass = false; 3364 } 3365 }; 3366 3367 _listen('resize' , updateZoomable); 3368 _listen('afterChange' , updateZoomable); 3369 _listen('pointerDown', function() { 3370 if(self.mouseZoomedIn) { 3371 hasDraggingClass = true; 3372 framework.addClass(template, 'pswp--dragging'); 3373 } 3374 }); 3375 _listen('pointerUp', removeDraggingClass); 3376 3377 if(!onInit) { 3378 updateZoomable(); 3379 } 3380 3381 }, 3382 3383 handleMouseWheel: function(e) { 3384 3385 if(_currZoomLevel <= self.currItem.fitRatio) { 3386 if( _options.modal ) { 3387 3388 if (!_options.closeOnScroll || _numAnimations || _isDragging) { 3389 e.preventDefault(); 3390 } else if(_transformKey && Math.abs(e.deltaY) > 2) { 3391 // close PhotoSwipe 3392 // if browser supports transforms & scroll changed enough 3393 _closedByScroll = true; 3394 self.close(); 3395 } 3396 3397 } 3398 return true; 3399 } 3400 3401 // allow just one event to fire 3402 e.stopPropagation(); 3403 3404 // https://developer.mozilla.org/en-US/docs/Web/Events/wheel 3405 _wheelDelta.x = 0; 3406 3407 if('deltaX' in e) { 3408 if(e.deltaMode === 1 /* DOM_DELTA_LINE */) { 3409 // 18 - average line height 3410 _wheelDelta.x = e.deltaX * 18; 3411 _wheelDelta.y = e.deltaY * 18; 3412 } else { 3413 _wheelDelta.x = e.deltaX; 3414 _wheelDelta.y = e.deltaY; 3415 } 3416 } else if('wheelDelta' in e) { 3417 if(e.wheelDeltaX) { 3418 _wheelDelta.x = -0.16 * e.wheelDeltaX; 3419 } 3420 if(e.wheelDeltaY) { 3421 _wheelDelta.y = -0.16 * e.wheelDeltaY; 3422 } else { 3423 _wheelDelta.y = -0.16 * e.wheelDelta; 3424 } 3425 } else if('detail' in e) { 3426 _wheelDelta.y = e.detail; 3427 } else { 3428 return; 3429 } 3430 3431 _calculatePanBounds(_currZoomLevel, true); 3432 3433 var newPanX = _panOffset.x - _wheelDelta.x, 3434 newPanY = _panOffset.y - _wheelDelta.y; 3435 3436 // only prevent scrolling in nonmodal mode when not at edges 3437 if (_options.modal || 3438 ( 3439 newPanX <= _currPanBounds.min.x && newPanX >= _currPanBounds.max.x && 3440 newPanY <= _currPanBounds.min.y && newPanY >= _currPanBounds.max.y 3441 ) ) { 3442 e.preventDefault(); 3443 } 3444 3445 // TODO: use rAF instead of mousewheel? 3446 self.panTo(newPanX, newPanY); 3447 }, 3448 3449 toggleDesktopZoom: function(centerPoint) { 3450 centerPoint = centerPoint || {x:_viewportSize.x/2 + _offset.x, y:_viewportSize.y/2 + _offset.y }; 3451 3452 var doubleTapZoomLevel = _options.getDoubleTapZoom(true, self.currItem); 3453 var zoomOut = _currZoomLevel === doubleTapZoomLevel; 3454 3455 self.mouseZoomedIn = !zoomOut; 3456 3457 self.zoomTo(zoomOut ? self.currItem.initialZoomLevel : doubleTapZoomLevel, centerPoint, 333); 3458 framework[ (!zoomOut ? 'add' : 'remove') + 'Class'](template, 'pswp--zoomed-in'); 3459 } 3460 3461 } 3462}); 3463 3464 3465/*>>desktop-zoom*/ 3466 3467/*>>history*/ 3468/** 3469 * 3470 * history.js: 3471 * 3472 * - Back button to close gallery. 3473 * 3474 * - Unique URL for each slide: example.com/&pid=1&gid=3 3475 * (where PID is picture index, and GID and gallery index) 3476 * 3477 * - Switch URL when slides change. 3478 * 3479 */ 3480 3481 3482var _historyDefaultOptions = { 3483 history: true, 3484 galleryUID: 1 3485}; 3486 3487var _historyUpdateTimeout, 3488 _hashChangeTimeout, 3489 _hashAnimCheckTimeout, 3490 _hashChangedByScript, 3491 _hashChangedByHistory, 3492 _hashReseted, 3493 _initialHash, 3494 _historyChanged, 3495 _closedFromURL, 3496 _urlChangedOnce, 3497 _windowLoc, 3498 3499 _supportsPushState, 3500 3501 _getHash = function() { 3502 return _windowLoc.hash.substring(1); 3503 }, 3504 _cleanHistoryTimeouts = function() { 3505 3506 if(_historyUpdateTimeout) { 3507 clearTimeout(_historyUpdateTimeout); 3508 } 3509 3510 if(_hashAnimCheckTimeout) { 3511 clearTimeout(_hashAnimCheckTimeout); 3512 } 3513 }, 3514 3515 // pid - Picture index 3516 // [MODIFIED FOR JIG] 3517 _parseItemIndexFromURL = function() { 3518 3519 var hashTag = '', 3520 patt = /^!([\w\-[\]]+?\/(?:(\d+)(?=\/))?(https?:\/\/[^&]*)?([A-Z]{2}
3520\/[_\w]*)?)/im, 3521 hashMatches, 3522 hash = _getHash(), 3523 params = {}; 3524 hash = hash.replace('%5B','['); 3525 hash = hash.replace('%5D',']'); 3526 hash = hash.replace(/([^|])(poster=|videoplayer)/mg, "$1|$2"); 3527 hashMatches = hash.match(patt); 3528 if(hashMatches !== null){ 3529 hashTag = hashMatches[1]; 3530 hashTag = hashTag.substring(hashTag.indexOf('/')+1,hashTag.length); 3531 if((/^([A-Z]{2}\/[_\w]*)$/im.test(hashTag))){ 3532 hashTag = hashTag.replace("/","-"); 3533 } 3534 3535 jQuery.each(items, function(index,item) { 3536 if (typeof item.contentID !== 'undefined' && item.contentID == 'jig-contentID-'+hashTag) { 3537 params.pid = index; 3538 return false; 3539 }else if(item.src == hashTag){ 3540 params.pid = index; 3541 return false; 3542 } 3543 }); 3544 params.pid = parseInt(params.pid,10); 3545 }else{ 3546 params.pid = 0; 3547 } 3548 return params; 3549 3550 }, 3551 // [MODIFIED FOR JIG] 3552 _updateHash = function() { 3553 3554 if(_hashAnimCheckTimeout) { 3555 clearTimeout(_hashAnimCheckTimeout); 3556 } 3557 3558 3559 if(_numAnimations || _isDragging) { 3560 // changing browser URL forces layout/paint in some browsers, which causes noticable lag during animation 3561 // that's why we update hash only when no animations running 3562 _hashAnimCheckTimeout = setTimeout(_updateHash, 500); 3563 return; 3564 } 3565 3566 if(_hashChangedByScript) { 3567 clearTimeout(_hashChangeTimeout); 3568 } else { 3569 _hashChangedByScript = true; 3570 } 3571 /* JIG mod */ 3572 var newHash = '!'+_options.galleryUID+'/'; 3573 if(typeof items[_currentItemIndex].contentID !== 'undefined'){ 3574 var contentIDregex = /(?:jig-contentID)-(\w{2})-([^\s]*)/im, 3575 contentIDmatches = contentIDregex.exec(items[_currentItemIndex].contentID); 3576 if (contentIDmatches !== null) { 3577 newHash += contentIDmatches[1]+"/"+contentIDmatches[2]; 3578 } 3579 3580 }else{ 3581 newHash += items[_currentItemIndex].src; 3582 } 3583 /* /JIG mod */ 3584 3585 if(!_historyChanged) { 3586 if(_windowLoc.hash.indexOf(newHash) === -1) { 3587 3588 // This protects against a major bug 3589 if(_windowLoc.hash.indexOf('%5B') === -1 && _windowLoc.hash.indexOf('%5D') === -1){ 3590 _urlChangedOnce = true; 3591 } 3592 } 3593 _windowLoc.hash = newHash; 3594 // first time - add new hisory record, then just replace 3595 } else { 3596 var newURL = _windowLoc.href.split('#')[0] + '#' + newHash; 3597 3598 _windowLoc.replace( newURL ); 3599 } 3600 3601 3602 _historyChanged = true; 3603 _hashChangeTimeout = setTimeout(function() { 3604 _hashChangedByScript = false; 3605 }, 60); 3606 }; 3607 3608 3609 3610 3611 3612_registerModule('History', { 3613 3614 3615 3616 publicMethods: { 3617 // [MODIFIED FOR JIG] 3618 initHistory: function() { 3619 3620 framework.extend(_options, _historyDefaultOptions, true); 3621 3622 if( !_options.history ) { 3623 return; 3624 } 3625 3626 3627 _windowLoc = window.location; 3628 _urlChangedOnce = false; 3629 _closedFromURL = false; 3630 _historyChanged = false; 3631 _initialHash = ''; 3632 3633 3634 3635 _listen('afterChange', self.updateURL); 3636 _listen('unbindEvents', function() { 3637 framework.unbind(window, 'hashchange', self.onHashChange); 3638 }); 3639 3640 3641 var returnToOriginal = function() { 3642 _hashReseted = true; 3643 if(!_closedFromURL) { 3644 if(_urlChangedOnce) { 3645 history.back(); 3646 } else { 3647 if ('pushState' in history) { 3648 // remove hash from url without refreshing it or scrolling to top 3649 history.pushState("", document.title, _windowLoc.pathname + _windowLoc.search); 3650 } else { 3651 _windowLoc.hash = ""; 3652 } 3653 } 3654 } 3655 3656 _cleanHistoryTimeouts(); 3657 }; 3658 3659 _listen('unbindEvents', function() { 3660 if(_closedByScroll) { 3661 // if PhotoSwipe is closed by scroll, we go "back" before the closing animation starts 3662 // this is done to keep the scroll position 3663 returnToOriginal(); 3664 } 3665 }); 3666 _listen('destroy', function() { 3667 if(!_hashReseted) { 3668 returnToOriginal(); 3669 } 3670 }); 3671 _listen('firstUpdate', function() { 3672 _currentItemIndex = _parseItemIndexFromURL().pid; 3673 }); 3674 3675 3676 3677 3678 3679 setTimeout(function() { 3680 if(_isOpen) { // hasn't destroyed yet 3681 framework.bind(window, 'hashchange', self.onHashChange); 3682 } 3683 }, 40); 3684 3685 }, 3686 // [MODIFIED FOR JIG] 3687 onHashChange: function() { 3688 3689 if(_getHash() === '') { 3690 _closedFromURL = true; 3691 self.close(); 3692 return; 3693 } 3694 if(!_hashChangedByScript) { 3695 _hashChangedByHistory = true; 3696 self.goTo( _parseItemIndexFromURL().pid ); 3697 _hashChangedByHistory = false; 3698 } 3699 3700 }, 3701 updateURL: function() { 3702 3703 // Delay the update of URL, to avoid lag during transition, 3704 // and to not to trigger actions like "refresh page sound" or "blinking favicon" to often 3705 3706 _cleanHistoryTimeouts(); 3707 3708 3709 if(_hashChangedByHistory) { 3710 return; 3711 } 3712 3713 if(!_historyChanged) { 3714 _updateHash(); // first time 3715 } else { 3716 _historyUpdateTimeout = setTimeout(_updateHash, 800); 3717 } 3718 } 3719 3720 } 3721}); 3722 3723 3724/*>>history*/ 3725 framework.extend(self, publicMethods); }; 3726 return PhotoSwipe; 3727}); 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08 3817* http://photoswipe.com 3818* Copyright (c) 2019 Dmitry Semenov; */ 3819/** 3820* 3821* UI on top of main sliding area (caption, arrows, close button, etc.). 3822* Built just using public methods/properties of PhotoSwipe. 3823* 3824*/ 3825(function (root, factory) { 3826 if (typeof define === 'function' && define.amd) { 3827 define(factory); 3828 } else if (typeof module !== 'undefined' && typeof exports === 'object') { 3829 module.exports = factory(); 3830 } else { 3831 root.PhotoSwipeUI_Default = factory(); 3832 } 3833})(this, function () { 3834 3835 'use strict'; 3836 3837 3838 3839var PhotoSwipeUI_Default = 3840 function(pswp, framework) { 3841 3842 var ui = this; 3843 var _overlayUIUpdated = false, 3844 _controlsVisible = true, 3845 _fullscrenAPI, 3846 _controls, 3847 _captionContainer, 3848 _fakeCaptionContainer, 3849 _indexIndicator, 3850 _shareButton, 3851 _shareModal, 3852 _shareModalHidden = true, 3853 _initalCloseOnScrollValue, 3854 _isIdle, 3855 _listen, 3856 3857 _loadingIndicator, 3858 _loadingIndicatorHidden, 3859 _loadingIndicatorTimeout, 3860 3861 _galleryHasOneSlide, 3862 3863 _options, 3864 _defaultUIOptions = { 3865 barsSize: {top:44, bottom:'auto'}, 3866 closeElClasses: ['item', 'caption', 'zoom-wrap', 'ui', 'top-bar'], 3867 timeToIdle: 4000, 3868 timeToIdleOutside: 1000, 3869 loadingIndicatorDelay: 1000, // 2s 3870 3871 addCaptionHTMLFn: function(item, captionEl /*, isFake */) { 3872 if(!item.title) { 3873 captionEl.children[0].innerHTML = ''; 3874 return false; 3875 } 3876 captionEl.children[0].innerHTML = item.title; 3877 return true; 3878 }, 3879 3880 closeEl:true, 3881 captionEl: true, 3882 fullscreenEl: true, 3883 zoomEl: true, 3884 shareEl: true,
3885 counterEl: true, 3886 arrowEl: true, 3887 preloaderEl: true, 3888 3889 tapToClose: false, 3890 tapToToggleControls: true, 3891 3892 clickToCloseNonZoomable: true, 3893 3894 shareButtons: [ 3895 {id:'facebook', label:'Share on Facebook', url:'https://www.facebook.com/sharer/share-page/?u=https://www.kentridge.studio/share.php?image={{raw_image_url}}&text= &title={{title}}...'}, 3896 {id:'twitter', label:'Tweet', url:'https://twitter.com/intent/tweet?text= &url=https://www.kentridge.studio/share.php?image={{raw_image_url}}&text= &title={{title}}...'}, 3897 {id:'pinterest', label:'Pin it', url:'http://www.pinterest.com/pin/create/button/'+ 3898 '?url={{url}}&media={{image_url}}&description={{description}}'}, 3899 {id:'download', label:'Download image', url:'{{raw_image_url}}', download:true} 3900 ], 3901 getImageURLForShare: function( /* shareButtonData */ ) { 3902 return pswp.currItem.src || ''; 3903 }, 3904 getPageURLForShare: function( /* shareButtonData */ ) { 3905 return window.location.href; 3906 }, 3907 getTextForShare: function( /* shareButtonData */ ) { 3908 return pswp.currItem.title || ''; 3909 }, 3910 3911 indexIndicatorSep: ' / ', 3912 fitControlsWidth: 1200 3913 3914 }, 3915 _blockControlsTap, 3916 _blockControlsTapTimeout; 3917 3918 3919 3920 var _onControlsTap = function(e) { 3921 if(_blockControlsTap) { 3922 return true; 3923 } 3924 3925 3926 e = e || window.event; 3927 3928 if(_options.timeToIdle && _options.mouseUsed && !_isIdle) { 3929 // reset idle timer 3930 _onIdleMouseMove(); 3931 } 3932 3933 3934 var target = e.target || e.srcElement, 3935 uiElement, 3936 clickedClass = target.getAttribute('class') || '', 3937 found; 3938 3939 for(var i = 0; i < _uiElements.length; i++) { 3940 uiElement = _uiElements[i]; 3941 if(uiElement.onTap && clickedClass.indexOf('pswp__' + uiElement.name ) > -1 ) { 3942 uiElement.onTap(); 3943 found = true; 3944 3945 } 3946 } 3947 3948 if(found) { 3949 if(e.stopPropagation) { 3950 e.stopPropagation(); 3951 } 3952 _blockControlsTap = true; 3953 3954 // Some versions of Android don't prevent ghost click event 3955 // when preventDefault() was called on touchstart and/or touchend. 3956 // 3957 // This happens on v4.3, 4.2, 4.1, 3958 // older versions strangely work correctly, 3959 // but just in case we add delay on all of them) 3960 var tapDelay = framework.features.isOldAndroid ? 600 : 30; 3961 _blockControlsTapTimeout = setTimeout(function() { 3962 _blockControlsTap = false; 3963 }, tapDelay); 3964 } 3965 3966 }, 3967 _fitControlsInViewport = function() { 3968 return !pswp.likelyTouchDevice || _options.mouseUsed || screen.width > _options.fitControlsWidth; 3969 }, 3970 _togglePswpClass = function(el, cName, add) { 3971 framework[ (add ? 'add' : 'remove') + 'Class' ](el, 'pswp__' + cName); 3972 }, 3973 3974 // add class when there is just one item in the gallery 3975 // (by default it hides left/right arrows and 1ofX counter) 3976 _countNumItems = function() { 3977 var hasOneSlide = (_options.getNumItemsFn() === 1); 3978 3979 if(hasOneSlide !== _galleryHasOneSlide) { 3980 _togglePswpClass(_controls, 'ui--one-slide', hasOneSlide); 3981 _galleryHasOneSlide = hasOneSlide; 3982 } 3983 }, 3984 _toggleShareModalClass = function() { 3985 _togglePswpClass(_shareModal, 'share-modal--hidden', _shareModalHidden); 3986 }, 3987 _toggleShareModal = function() { 3988 3989 _shareModalHidden = !_shareModalHidden; 3990 3991 3992 if(!_shareModalHidden) { 3993 _toggleShareModalClass(); 3994 setTimeout(function() { 3995 if(!_shareModalHidden) { 3996 framework.addClass(_shareModal, 'pswp__share-modal--fade-in'); 3997 } 3998 }, 30); 3999 } else { 4000 framework.removeClass(_shareModal, 'pswp__share-modal--fade-in'); 4001 setTimeout(function() { 4002 if(_shareModalHidden) { 4003 _toggleShareModalClass(); 4004 } 4005 }, 300); 4006 } 4007 4008 if(!_shareModalHidden) { 4009 _updateShareURLs(); 4010 } 4011 return false; 4012 }, 4013 4014 _openWindowPopup = function(e) { 4015 e = e || window.event; 4016 var target = e.target || e.srcElement; 4017 4018 pswp.shout('shareLinkClick', e, target); 4019 4020 if(!target.href) { 4021 return false; 4022 } 4023 4024 if( target.hasAttribute('download') ) { 4025 return true; 4026 } 4027 4028 window.open(target.href, 'pswp_share', 'scrollbars=yes,resizable=yes,toolbar=no,'+ 4029 'location=yes,width=550,height=420,top=100,left=' + 4030 (window.screen ? Math.round(screen.width / 2 - 275) : 100) ); 4031 4032 if(!_shareModalHidden) { 4033 _toggleShareModal(); 4034 } 4035 4036 return false; 4037 }, 4038 // [MODIFIED FOR JIG] 4039 _updateShareURLs = function() { 4040 var shareButtonOut = '', 4041 shareButtons = pswp.options.shareButtons, 4042 shareButtonData, 4043 shareURL, 4044 URI, 4045 description; 4046 4047 if(location.hash){ 4048 URI = decodeURI(document.location.href);
4049 var URIarray = URI.split('#!'); 4050 URI = URIarray[0]+'#!'+URIarray[1]; 4051 }else{ 4052 URI = location.href; 4053 } 4054 4055 if(typeof pswp.options.socialButtons !== 'undefined'){ 4056 var socialButtons = pswp.options.socialButtons; 4057 shareButtons = []; 4058 4059 socialButtons = socialButtons.toLowerCase(); 4060 socialButtons = socialButtons.split(""); 4061 4062 var facebookIndex = jQuery.inArray('f',socialButtons), 4063 twitterIndex = jQuery.inArray('t',socialButtons), 4064 pinterestIndex = jQuery.inArray('p',socialButtons), 4065 googleIndex = jQuery.inArray('g',socialButtons); 4066 4067 if (~facebookIndex) shareButtons[facebookIndex] = pswp.options.shareButtons[0]; 4068 if (~twitterIndex) shareButtons[twitterIndex] = pswp.options.shareButtons[1]; 4069 if (~pinterestIndex) shareButtons[pinterestIndex] = pswp.options.shareButtons[2]; 4070 if (~googleIndex) shareButtons[googleIndex] = pswp.options.shareButtons[3]; 4071 if(typeof pswp.options.shareButtons[4] !== "undefined"){ 4072 shareButtons.push(pswp.options.shareButtons[4]); 4073 } 4074 } 4075 4076 4077 jQuery.each(shareButtons, function(i,shareButtonData){ 4078 if(typeof shareButtonData !== 'undefined'){ 4079 description = ((pswp.currItem.title || '')+(pswp.currItem.description ? (pswp.currItem.title ? ' ' : '')+pswp.currItem.description : '')) || document.title; 4080 console.log( '<div>'+'{{title}}'+'</div>' ); 4081 var newtitle = '<div>'+pswp.currItem.title+'</div>' ; 4082 4083 //.replace('{{title}}', encodeURIComponent(jQuery(pswp.currItem.title.replace(/<\/div>/g,'####').replace(/<a(?:.+?)href=['"](.+?)['"](?:.+?>)(.+?)(?:<\/a>)/mg, "$2: $1")).text().replace(/####/g,'. ').substring(0, 80) )) 4084 // 4085 // 4086 // 4087 shareURL = shareButtonData.url.replace('{{url}}', encodeURIComponent(URI) ) 4088 .replace("{{facebook_url}}",encodeURIComponent(URI.replace("#!", (URI.indexOf('?') === -1 ? "?" : "&")+'_escaped_fragment_='))) 4089 .replace('{{image_url}}', encodeURIComponent(pswp.currItem.src) ) 4090 .replace('{{raw_image_url}}', pswp.currItem.src.replace(/ /g,'***') ) 4091 .replace('{{title}}', encodeURIComponent(jQuery(newtitle.replace(/<\/div>/g,'####').replace(/<a(?:.+?)href=['"](.+?)['"](?:.+?>)(.+?)(?:<\/a>)/mg, "$2: $1")).text().replace(/####/g,'. ').substring(0, 80) )) 4092 .replace('{{description}}', encodeURIComponent(jQuery('<p>'+description.replace(/<a(?:.+?)href=['"](.+?)['"](?:.+?>)(.+?)(?:<\/a>)/mg, "$2: $1")+'</p>').text())); 4093 4094 shareButtonOut += '<a href="' + shareURL + '" target="_blank" class="pswp__share--' + shareButtonData.id + '"' + (shareButtonData.download ? 'download' : '') + '>' + shareButtonData.label + '</a>'; 4095 } 4096 }); 4097 _shareModal.children[0].innerHTML = shareButtonOut; 4098 _shareModal.children[0].onclick = _openWindowPopup; 4099 4100 }, 4101 _hasCloseClass = function(target) { 4102 for(var i = 0; i < _options.closeElClasses.length; i++) { 4103 if( framework.hasClass(target, 'pswp__' + _options.closeElClasses[i]) ) { 4104 return true; 4105 } 4106 } 4107 }, 4108 _idleInterval, 4109 _idleTimer, 4110 _idleIncrement = 0, 4111 _onIdleMouseMove = function() { 4112 clearTimeout(_idleTimer); 4113 _idleIncrement = 0; 4114 if(_isIdle) { 4115 ui.setIdle(false); 4116 } 4117 }, 4118 _onMouseLeaveWindow = function(e) { 4119 e = e ? e : window.event; 4120 var from = e.relatedTarget || e.toElement; 4121 if (!from || from.nodeName === 'HTML') { 4122 clearTimeout(_idleTimer); 4123 _idleTimer = setTimeout(function() { 4124 ui.setIdle(true); 4125 }, _options.timeToIdleOutside); 4126 } 4127 }, 4128 _setupFullscreenAPI = function() { 4129 if(_options.fullscreenEl && !framework.features.isOldAndroid) { 4130 if(!_fullscrenAPI) { 4131 _fullscrenAPI = ui.getFullscreenAPI(); 4132 } 4133 if(_fullscrenAPI) { 4134 framework.bind(document, _fullscrenAPI.eventK, ui.updateFullscreen); 4135 ui.updateFullscreen(); 4136 framework.addClass(pswp.template, 'pswp--supports-fs'); 4137 } else { 4138 framework.removeClass(pswp.template, 'pswp--supports-fs'); 4139 } 4140 } 4141 }, 4142 _setupLoadingIndicator = function() { 4143 // Setup loading indicator 4144 if(_options.preloaderEl) { 4145 4146 _toggleLoadingIndicator(true); 4147 4148 _listen('beforeChange', function() { 4149 4150 clearTimeout(_loadingIndicatorTimeout); 4151 4152 // display loading indicator with delay 4153 _loadingIndicatorTimeout = setTimeout(function() { 4154 4155 if(pswp.currItem && pswp.currItem.loading) { 4156 4157 if( !pswp.allowProgressiveImg() || (pswp.currItem.img && !pswp.currItem.img.naturalWi
4157dth) ) { 4158 // show preloader if progressive loading is not enabled, 4159 // or image width is not defined yet (because of slow connection) 4160 _toggleLoadingIndicator(false); 4161 // items-controller.js function allowProgressiveImg 4162 } 4163 4164 } else { 4165 _toggleLoadingIndicator(true); // hide preloader 4166 } 4167 4168 }, _options.loadingIndicatorDelay); 4169 4170 }); 4171 _listen('imageLoadComplete', function(index, item) { 4172 if(pswp.currItem === item) { 4173 _toggleLoadingIndicator(true); 4174 } 4175 }); 4176 4177 } 4178 }, 4179 _toggleLoadingIndicator = function(hide) { 4180 if( _loadingIndicatorHidden !== hide ) { 4181 _togglePswpClass(_loadingIndicator, 'preloader--active', !hide); 4182 _loadingIndicatorHidden = hide; 4183 } 4184 }, 4185 _applyNavBarGaps = function(item) { 4186 var gap = item.vGap; 4187 4188 if( _fitControlsInViewport() ) { 4189 4190 var bars = _options.barsSize; 4191 if(_options.captionEl && bars.bottom === 'auto') { 4192 if(!_fakeCaptionContainer) { 4193 _fakeCaptionContainer = framework.createEl('pswp__caption pswp__caption--fake'); 4194 _fakeCaptionContainer.appendChild( framework.createEl('pswp__caption__center') ); 4195 _controls.insertBefore(_fakeCaptionContainer, _captionContainer); 4196 framework.addClass(_controls, 'pswp__ui--fit'); 4197 } 4198 if( _options.addCaptionHTMLFn(item, _fakeCaptionContainer, true) ) { 4199 4200 var captionSize = _fakeCaptionContainer.clientHeight; 4201 gap.bottom = parseInt(captionSize,10) || 44; 4202 } else { 4203 gap.bottom = bars.top; // if no caption, set size of bottom gap to size of top 4204 } 4205 } else { 4206 gap.bottom = bars.bottom === 'auto' ? 0 : bars.bottom; 4207 } 4208 4209 // height of top bar is static, no need to calculate it 4210 gap.top = bars.top; 4211 } else { 4212 gap.top = gap.bottom = 0; 4213 } 4214 }, 4215 _setupIdle = function() { 4216 // Hide controls when mouse is used 4217 if(_options.timeToIdle) { 4218 _listen('mouseUsed', function() { 4219 4220 framework.bind(document, 'mousemove', _onIdleMouseMove); 4221 framework.bind(document, 'mouseout', _onMouseLeaveWindow); 4222 4223 _idleInterval = setInterval(function() { 4224 _idleIncrement++; 4225 if(_idleIncrement === 2) { 4226 ui.setIdle(true); 4227 } 4228 }, _options.timeToIdle / 2); 4229 }); 4230 } 4231 }, 4232 _setupHidingControlsDuringGestures = function() { 4233 4234 // Hide controls on vertical drag 4235 _listen('onVerticalDrag', function(now) { 4236 if(_controlsVisible && now < 0.95) { 4237 ui.hideControls(); 4238 } else if(!_controlsVisible && now >= 0.95) { 4239 ui.showControls(); 4240 } 4241 }); 4242 4243 // Hide controls when pinching to close 4244 var pinchControlsHidden; 4245 _listen('onPinchClose' , function(now) { 4246 if(_controlsVisible && now < 0.9) { 4247 ui.hideControls(); 4248 pinchControlsHidden = true; 4249 } else if(pinchControlsHidden && !_controlsVisible && now > 0.9) { 4250 ui.showControls(); 4251 } 4252 }); 4253 4254 _listen('zoomGestureEnded', function() { 4255 pinchControlsHidden = false; 4256 if(pinchControlsHidden && !_controlsVisible) { 4257 ui.showControls(); 4258 } 4259 }); 4260 4261 }; 4262 4263 4264 4265 var _uiElements = [ 4266 { 4267 name: 'caption', 4268 option: 'captionEl', 4269 onInit: function(el) { 4270 _captionContainer = el; 4271 } 4272 }, 4273 { 4274 name: 'share-modal', 4275 option: 'shareEl', 4276 onInit: function(el) { 4277 _shareModal = el; 4278 }, 4279 onTap: function() { 4280 _toggleShareModal(); 4281 } 4282 }, 4283 { 4284 name: 'button--share', 4285 option: 'shareEl', 4286 onInit: function(el) { 4287 _shareButton = el; 4288 }, 4289 onTap: function() { 4290 _toggleShareModal(); 4291 } 4292 }, 4293 { 4294 name: 'button--zoom', 4295 option: 'zoomEl', 4296 onTap: pswp.toggleDesktopZoom 4297 }, 4298 { 4299 name: 'counter', 4300 option: 'counterEl', 4301 onInit: function(el) { 4302 _indexIndicator = el; 4303 } 4304 }, 4305 { 4306 name: 'button--close', 4307 option: 'closeEl', 4308 onTap: pswp.close 4309 }, 4310 { 4311 name: 'button--arrow--left', 4312 option: 'arrowEl', 4313 onTap: pswp.prev 4314 }, 4315 { 4316 name: 'button--arrow--right', 4317 option: 'arrowEl', 4318 onTap: pswp.next 4319 }, 4320 { 4321 name: 'button--fs', 4322 option: 'fullscreenEl', 4323 onTap: function() { 4324 if(_fullscrenAPI.isFullscreen()) { 4325 _fullscrenAPI.exit(); 4326 } else { 4327 _fullscrenAPI.enter(); 4328 } 4329 } 4330 }, 4331 { 4332 name: 'preloader', 4333 option: 'preloaderEl', 4334 onInit: function(el) { 4335 _loadingIndicator = el; 4336 } 4337 } 4338 4339 ]; 4340 4341 var _setupUIElements = function() { 4342 var item, 4343 classAttr, 4344 uiElement; 4345 4346 var loopThroughChildElements = function(sChildren) { 4347 if(!sChildren) { 4348 return; 4349 } 4350 4351 var l = sChildren.length; 4352 for(var i = 0; i < l; i++) { 4353 item = sChildren[i]; 4354 classAttr = item.className; 4355 4356 for(var a = 0; a < _uiElements.length; a++) { 4357 uiElement = _uiElements[a]; 4358 4359 if(classAttr.indexOf('pswp__' + uiElement.name) > -1 ) { 4360 4361 if( _options[uiElement.option] ) { // if element is not disabled from options 4362
4363 framework.removeClass(item, 'pswp__element--disabled'); 4364 if(uiElement.onInit) { 4365 uiElement.onInit(item); 4366 } 4367 4368 //item.style.display = 'block'; 4369 } else { 4370 framework.addClass(item, 'pswp__element--disabled'); 4371 //item.style.display = 'none'; 4372 } 4373 } 4374 } 4375 } 4376 }; 4377 loopThroughChildElements(_controls.children); 4378 4379 var topBar = framework.getChildByClass(_controls, 'pswp__top-bar'); 4380 if(topBar) { 4381 loopThroughChildElements( topBar.children ); 4382 } 4383 }; 4384 4385 4386 4387 4388 ui.init = function() { 4389 4390 // extend options 4391 framework.extend(pswp.options, _defaultUIOptions, true); 4392 4393 // create local link for fast access 4394 _options = pswp.options; 4395 4396 // find pswp__ui element 4397 _controls = framework.getChildByClass(pswp.scrollWrap, 'pswp__ui'); 4398 4399 // create local link 4400 _listen = pswp.listen; 4401 4402 4403 _setupHidingControlsDuringGestures(); 4404 4405 // update controls when slides change 4406 _listen('beforeChange', ui.update); 4407 4408 // toggle zoom on double-tap 4409 _listen('doubleTap', function(point) { 4410 var initialZoomLevel = pswp.currItem.initialZoomLevel; 4411 if(pswp.getZoomLevel() !== initialZoomLevel) { 4412 pswp.zoomTo(initialZoomLevel, point, 333); 4413 } else { 4414 pswp.zoomTo(_options.getDoubleTapZoom(false, pswp.currItem), point, 333); 4415 } 4416 }); 4417 4418 // Allow text selection in caption 4419 _listen('preventDragEvent', function(e, isDown, preventObj) { 4420 var t = e.target || e.srcElement; 4421 if( 4422 t && 4423 t.getAttribute('class') && e.type.indexOf('mouse') > -1 && 4424 ( t.getAttribute('class').indexOf('__caption') > 0 || (/(SMALL|STRONG|EM)/i).test(t.tagName) ) 4425 ) { 4426 preventObj.prevent = false; 4427 } 4428 }); 4429 4430 // bind events for UI 4431 _listen('bindEvents', function() { 4432 framework.bind(_controls, 'pswpTap click', _onControlsTap); 4433 framework.bind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap); 4434 4435 if(!pswp.likelyTouchDevice) { 4436 framework.bind(pswp.scrollWrap, 'mouseover', ui.onMouseOver); 4437 } 4438 }); 4439 4440 // unbind events for UI 4441 _listen('unbindEvents', function() { 4442 if(!_shareModalHidden) { 4443 _toggleShareModal(); 4444 } 4445 4446 if(_idleInterval) { 4447 clearInterval(_idleInterval); 4448 } 4449 framework.unbind(document, 'mouseout', _onMouseLeaveWindow); 4450 framework.unbind(document, 'mousemove', _onIdleMouseMove); 4451 framework.unbind(_controls, 'pswpTap click', _onControlsTap); 4452 framework.unbind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap); 4453 framework.unbind(pswp.scrollWrap, 'mouseover', ui.onMouseOver); 4454 4455 if(_fullscrenAPI) { 4456 framework.unbind(document, _fullscrenAPI.eventK, ui.updateFullscreen); 4457 if(_fullscrenAPI.isFullscreen()) { 4458 _options.hideAnimationDuration = 0; 4459 _fullscrenAPI.exit(); 4460 } 4461 _fullscrenAPI = null; 4462 } 4463 }); 4464 4465 4466 // clean up things when gallery is destroyed 4467 _listen('destroy', function() { 4468 if(_options.captionEl) { 4469 if(_fakeCaptionContainer) { 4470 _controls.removeChild(_fakeCaptionContainer); 4471 } 4472 framework.removeClass(_captionContainer, 'pswp__caption--empty'); 4473 } 4474 4475 if(_shareModal) { 4476 _shareModal.children[0].onclick = null; 4477 } 4478 framework.removeClass(_controls, 'pswp__ui--over-close'); 4479 framework.addClass( _controls, 'pswp__ui--hidden'); 4480 ui.setIdle(false); 4481 }); 4482 4483 4484 if(!_options.showAnimationDuration) { 4485 framework.removeClass( _controls, 'pswp__ui--hidden'); 4486 } 4487 _listen('initialZoomIn', function() { 4488 if(_options.showAnimationDuration) { 4489 framework.removeClass( _controls, 'pswp__ui--hidden'); 4490 } 4491 }); 4492 _listen('initialZoomOut', function() { 4493 framework.addClass( _controls, 'pswp__ui--hidden'); 4494 }); 4495 4496 _listen('parseVerticalMargin', _applyNavBarGaps); 4497 4498 _setupUIElements(); 4499 4500 if(_options.shareEl && _shareButton && _shareModal) { 4501 _shareModalHidden = true; 4502 } 4503 4504 _countNumItems(); 4505 4506 _setupIdle(); 4507 4508 _setupFullscreenAPI(); 4509 4510 _setupLoadingIndicator(); 4511 }; 4512 4513 ui.setIdle = function(isIdle) { 4514 _isIdle = isIdle; 4515 _togglePswpClass(_controls, 'ui--idle', isIdle); 4516 }; 4517 // [MODIFIED FOR JIG] fix for checking both title and description 4518 ui.update = function() { 4519 // Don't update UI if it's hidden 4520 if(_controlsVisible && pswp.currItem) { 4521 4522 ui.updateIndexIndicator(); 4523 4524 if(_options.captionEl) { 4525 _options.addCaptionHTMLFn(pswp.currItem, _captionContainer); 4526 _togglePswpClass(_captionContainer, 'caption--empty', !pswp.currItem.title && !pswp.currItem.description); 4527 } 4528 4529 _overlayUIUpdated = true; 4530 4531 } else { 4532 _overlayUIUpdated = false; 4533 } 4534 4535 if(!_shareModalHidden) { 4536 _toggleShareModal(); 4537 } 4538 4539 _countNumItems();
4540 }; 4541 4542 ui.updateFullscreen = function(e) { 4543 4544 if(e) { 4545 // some browsers change window scroll position during the fullscreen 4546 // so PhotoSwipe updates it just in case 4547 setTimeout(function() { 4548 pswp.setScrollOffset( 0, framework.getScrollY() ); 4549 }, 50); 4550 } 4551 4552 // toogle pswp--fs class on root element 4553 framework[ (_fullscrenAPI.isFullscreen() ? 'add' : 'remove') + 'Class' ](pswp.template, 'pswp--fs'); 4554 }; 4555 4556 ui.updateIndexIndicator = function() { 4557 if(_options.counterEl) { 4558 _indexIndicator.innerHTML = (pswp.getCurrentIndex()+1) + 4559 _options.indexIndicatorSep + 4560 _options.getNumItemsFn(); 4561 } 4562 }; 4563 4564 ui.onGlobalTap = function(e) { 4565 e = e || window.event; 4566 var target = e.target || e.srcElement; 4567 4568 if(_blockControlsTap) { 4569 return; 4570 } 4571 4572 if(e.detail && e.detail.pointerType === 'mouse') { 4573 4574 // close gallery if clicked outside of the image 4575 if(_hasCloseClass(target)) { 4576 pswp.close(); 4577 return; 4578 } 4579 4580 if(framework.hasClass(target, 'pswp__img')) { 4581 if(pswp.getZoomLevel() === 1 && pswp.getZoomLevel() <= pswp.currItem.fitRatio) { 4582 if(_options.clickToCloseNonZoomable) { 4583 pswp.close(); 4584 } 4585 } else { 4586 pswp.toggleDesktopZoom(e.detail.releasePoint); 4587 } 4588 } 4589 4590 } else { 4591 4592 // tap anywhere (except buttons) to toggle visibility of controls 4593 if(_options.tapToToggleControls) { 4594 if(_controlsVisible) { 4595 ui.hideControls(); 4596 } else { 4597 ui.showControls(); 4598 } 4599 } 4600 4601 // tap to close gallery 4602 if(_options.tapToClose && (framework.hasClass(target, 'pswp__img') || _hasCloseClass(target)) ) { 4603 pswp.close(); 4604 return; 4605 } 4606 4607 } 4608 }; 4609 ui.onMouseOver = function(e) { 4610 e = e || window.event; 4611 var target = e.target || e.srcElement; 4612 4613 // add class when mouse is over an element that should close the gallery 4614 _togglePswpClass(_controls, 'ui--over-close', _hasCloseClass(target)); 4615 }; 4616 4617 ui.hideControls = function() { 4618 framework.addClass(_controls,'pswp__ui--hidden'); 4619 _controlsVisible = false; 4620 }; 4621 4622 ui.showControls = function() { 4623 _controlsVisible = true; 4624 if(!_overlayUIUpdated) { 4625 ui.update(); 4626 } 4627 framework.removeClass(_controls,'pswp__ui--hidden'); 4628 }; 4629 4630 ui.supportsFullscreen = function() { 4631 var d = document; 4632 return !!(d.exitFullscreen || d.mozCancelFullScreen || d.webkitExitFullscreen || d.msExitFullscreen); 4633 }; 4634 4635 ui.getFullscreenAPI = function() { 4636 var dE = document.documentElement, 4637 api, 4638 tF = 'fullscreenchange'; 4639 4640 if (dE.requestFullscreen) { 4641 api = { 4642 enterK: 'requestFullscreen', 4643 exitK: 'exitFullscreen', 4644 elementK: 'fullscreenElement', 4645 eventK: tF 4646 }; 4647 4648 } else if(dE.mozRequestFullScreen ) { 4649 api = { 4650 enterK: 'mozRequestFullScreen', 4651 exitK: 'mozCancelFullScreen', 4652 elementK: 'mozFullScreenElement', 4653 eventK: 'moz' + tF 4654 }; 4655 4656 4657 4658 } else if(dE.webkitRequestFullscreen) { 4659 api = { 4660 enterK: 'webkitRequestFullscreen', 4661 exitK: 'webkitExitFullscreen', 4662 elementK: 'webkitFullscreenElement', 4663 eventK: 'webkit' + tF 4664 }; 4665 4666 } else if(dE.msRequestFullscreen) { 4667 api = { 4668 enterK: 'msRequestFullscreen', 4669 exitK: 'msExitFullscreen', 4670 elementK: 'msFullscreenElement', 4671 eventK: 'MSFullscreenChange' 4672 }; 4673 } 4674 4675 if(api) { 4676 api.enter = function() { 4677 // disable close-on-scroll in fullscreen 4678 _initalCloseOnScrollValue = _options.closeOnScroll; 4679 _options.closeOnScroll = false; 4680 4681 if(this.enterK === 'webkitRequestFullscreen') { 4682 pswp.template[this.enterK]( Element.ALLOW_KEYBOARD_INPUT ); 4683 } else { 4684 return pswp.template[this.enterK](); 4685 } 4686 }; 4687 api.exit = function() { 4688 _options.closeOnScroll = _initalCloseOnScrollValue; 4689 4690 return document[this.exitK](); 4691 4692 }; 4693 api.isFullscreen = function() { return document[this.elementK]; }; 4694 } 4695 4696 return api; 4697 }; 4698 4699 4700 4701}; 4702return PhotoSwipeUI_Default; 4703 4704 4705}); 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746/*! JIGphotoSwipe(); jQuery wrapper made exclusively for JIG by Firsh 4747 Additional changes to original PhotoSwipe: 4748 photoswipe-ui-default.js 4749 _updateShareURLs 4750 ui.update 4751 photoswipe.js - affected functions: 4752 publicMethods init (just an if at "add classes to root element of PhotoSwipe") 4753 init > _globalEventHandlers > _orientationChangeTimeout2 4754 History 4755 _parseItemIndexFromURL 4756 _updateHash 4757 publicMethods: 4758 initHistory 4759 onHashChange 4760*/ 4761 4762(function ($){ 4763 $.fn.JIGphotoSwipe = function(o){ 4764 // set up default options 4765 var elements = this,
4766 parent = $(elements[0]).closest(".justified-image-grid"), 4767 origOptions = $.extend(true, {}, o); 4768 4769 // base setup of settings, mouse interaction, images load event handler [JIG REWRITE DONE - CHECK:NY] 4770 var init = function(){ 4771 4772 if($('.pswp').length === 0){ 4773 var captionClass = typeof o.captionAlign !== 'undefined' ? ' pswp__ca_'+o.captionAlign : ' pswp__ca_center'; 4774 $('body').append('<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true"><div class="pswp__bg"></div><div class="pswp__scroll-wrap"><div class="pswp__container"><div class="pswp__item"></div><div class="pswp__item"></div><div class="pswp__item"></div></div><div class="pswp__ui pswp__ui--hidden'+captionClass+'"><div class="pswp__top-bar"><div class="pswp__counter"></div><span class="pswp__button pswp__button--close" title="Close (Esc)"></span><span class="pswp__button pswp__button--share" title="Share"></span><span class="pswp__button pswp__button--fs" title="Toggle fullscreen"></span><span class="pswp__button pswp__button--zoom" title="Zoom in/out"></span><div class="pswp__preloader"><div class="pswp__preloader__icn"><div class="pswp__preloader__cut"><div class="pswp__preloader__donut"></div></div></div></div></div><div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap"><div class="pswp__share-tooltip"></div></div><span class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)"></span><span class="pswp__button pswp__button--arrow--right" title="Next (arrow right)"></span><div class="pswp__caption"><div class="pswp__caption__center"></div></div></div></div></div>'); 4775 } 4776 4777 // (re)bind click event 4778 parent.off('click.photoswipe').on('click.photoswipe','a.jig-link',onThumbnailsClick); 4779 4780 // Parse URL and open gallery if it contains smart deeeplinking 4781 var hashData = photoswipeParseHash(); 4782 if(hashData.theRel && hashData.contentID) { 4783 openPhotoSwipe(hashData.contentID, hashData.theRel, true ); 4784 } 4785 }; // end of init 4786 4787 4788 // parse slide data (url, title, size ...) from DOM elements [JIG REWRITE DONE - CHECK:NY] 4789 var parseThumbnailElements = function(rel) { 4790 var items = []; 4791 elements.filter('[rel~="'+rel+'"]').each(function(index){ 4792 var el = $(this), 4793 img = el.find('img'), 4794 imgAlt = el.find('img').attr('alt'), 4795 linkTitle = el.attr('title'), 4796 wh = el.attr('data-wh').split('x'), 4797 contentIDbearerClass = el.parent().attr('class') == 'jig-overflow' ? el.closest('.jig-imageContainer').attr('class') : el.attr('class'), 4798 contentIDregex = /(?:jig-contentID)-(\w{2})-([^\s]*)/im, 4799 contentID = contentIDregex.exec(contentIDbearerClass), 4800 item = { 4801 src: el.attr('href'), 4802 w: parseInt(wh[0], 10), 4803 h: parseInt(wh[1], 10), 4804 msrc: img.attr('src') 4805 }; 4806 4807 if (contentID != null) { 4808 item.contentID = contentID[0]; 4809 } 4810 4811 if(imgAlt){ 4812 item.title = imgAlt; 4813 } 4814 if(linkTitle){ 4815 item.description = linkTitle; 4816 } 4817 4818 item.el = el[0]; // save link to element for getThumbBoundsFn 4819 items.push(item); 4820 }); 4821 4822 return items; 4823 }; 4824 4825 4826 // triggers when user clicks on thumbnail [JIG REWRITE DONE - CHECK:NY] 4827 var onThumbnailsClick = function(e) { 4828 e.preventDefault(); 4829 // find contentID of clicked item 4830 var containerClass = $(e.target).closest('.jig-imageContainer').attr('class'), 4831 contentID, 4832 contentIDregex = /(?:jig-contentID)-(\w{2})-([^\s]*)/im, 4833 contentIDmatch = contentIDregex.exec(containerClass), 4834 theLink = $(e.target).closest('a.jig-link'), 4835 rel = theLink.attr('rel').split(" ").shift(); 4836 4837 if (contentIDmatch != null) { 4838 contentID = contentIDmatch[0]; 4839 }else{ 4840 // In case there is no JIG ID (like RSS), just use the image URL (could be a hash though) 4841 contentID = theLink.attr('href'); 4842 } 4843 4844 openPhotoSwipe(contentID, rel); 4845 4846 return false; 4847 }; 4848 4849 // parse contentID (or URL) and REL from hash 4850 var photoswipeParseHash = function() { 4851 4852 var hashTag = '', 4853 patt = /^#!([\w\-[\]]+?\/(?:(\d+)(?=\/))?(https?:\/\/[^&]*)?([A-Z]{2}
4853\/[_\w]*)?)/im, 4854 hashMatches, 4855 hash = location.hash, 4856 params = {}; 4857 hash = hash.replace('%5B','['); 4858 hash = hash.replace('%5D',']'); 4859 hash = hash.replace(/([^|])(poster=|videoplayer)/mg, "$1|$2"); 4860 hashMatches = hash.match(patt); 4861 if(hashMatches !== null){ 4862 hashTag = hashMatches[1]; 4863 hashTag = hashTag.substring(hashTag.indexOf('/')+1,hashTag.length); 4864 if((/^([A-Z]{2}\/[_\w]*)$/im.test(hashTag))){ 4865 hashTag = hashTag.replace("/","-"); 4866 } 4867 } 4868 4869 params.contentID = 'jig-contentID-'+hashTag; 4870 params.theRel = hash.substring(2,hash.indexOf('/')); 4871 4872 return params; 4873 4874 }; 4875 4876 // opens PS (by deeplink or click) [JIG REWRITE DONE - CHECK:NY] 4877 var openPhotoSwipe = function(contentID, rel, disableAnimation) { 4878 4879 var items = parseThumbnailElements(rel), 4880 putBackScroll, 4881 cropped = false; 4882 4883 if(items.length == 0){ 4884 return false; 4885 } 4886 4887 $.each(items, function(index,item) { 4888 if (typeof item.contentID !== 'undefined' && item.contentID == contentID) { 4889 o.index = index; 4890 return false; 4891 }else if(item.src == contentID){ 4892 o.index = index; 4893 return false; 4894 } 4895 }); 4896 4897 o.galleryUID = rel; 4898 o.getThumbBoundsFn = function(index) { 4899 if($(items[index].el).parent().attr('class') == 'jig-overflow' ){ 4900 4901 // See o -> getThumbBoundsFn section of docs for more info 4902 var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail 4903 pageYScroll = $(window).scrollTop(), 4904 rect = thumbnail.getBoundingClientRect(); 4905 4906 return {x:rect.left, y:rect.top + pageYScroll, w:rect.width}; 4907 }else{ 4908 // zoom-close hidden thumbs to the center of the viewport 4909 return {x:$(window).width()/2, y:$(window).height()/2+$(window).scrollTop(), w:0}; 4910 } 4911 4912 }; 4913 4914 o.addCaptionHTMLFn = function(item, captionEl, isFake) { 4915 var fragments = []; 4916 if(typeof item.title !== 'undefined'){ 4917 fragments.push(item.title); 4918 } 4919 if(typeof item.description !== 'undefined'){ 4920 fragments.push('<small>'+item.description+'</small>'); 4921 } 4922 if(fragments.length == 0){ 4923 captionEl.children[0].innerText = ''; 4924 return false; 4925 }else{ 4926 captionEl.children[0].innerHTML = fragments.join('<br/>'); 4927 return true; 4928 } 4929 } 4930 4931 o.shareButtons = [ 4932 {id:'facebook', label:'<i class="eicon-facebook"></i> Share on Facebook', url:'https://www.facebook.com/sharer/sharer.php?u=https://www.kentridge.studio/share.php?image={{raw_image_url}}&text= &title={{title}}...'}, 4933 {id:'twitter', label:'<i class="eicon-twitter"></i> Tweet', url:'https://twitter.com/intent/tweet?text= &url=https://www.kentridge.studio/share.php?image={{raw_image_url}}&text= &title={{title}}...'}, 4934 {id:'pinterest', label:'<i class="eicon-pinterest"></i> Pin it', url:'http://www.pinterest.com/pin/create/button/?url={{url}}&media={{image_url}}&description={{description}}'}, 4935 ]; 4936 if(typeof o.allowDownload !== 'undefined' && o.allowDownload === true){ 4937 o.shareButtons.push({id:'download', label:'Download image', url:'{{raw_image_url}}', download:true}); 4938 } 4939 4940 4941 if(disableAnimation) { 4942 o.showAnimationDuration = 0; 4943 if(typeof origOptions.closeOnScroll == 'undefined' || origOptions.closeOnScroll == true){ 4944 // Delay reacting to close on scroll, in case of the browser scrolls initially 4945 // It's in disableAnimation check as it's the same scenario (opening by URL hash) 4946 o.closeOnScroll = false; 4947 putBackScroll = true; 4948 } 4949 }else{ 4950 o.showAnimationDuration = typeof origOptions.showAnimationDuration == 'undefined' ? 333 : origOptions.showAnimationDuration; // Put it back 4951 } 4952 4953 4954 4955 // detect JIG cropping 4956 var element = $(items[o.index].el); 4957 if(element.parent().attr('class') == 'jig-overflow' ){ 4958 if(element.find('img').css('margin-left') !== '0px' || element.parent().parent().hasClass('differentAspectRatio')){ 4959 cropped = true; 4960 } 4961 } 4962 4963 if(cropped || (typeof o.aspectRatioMismatch !== 'undefined' && o.aspectRatioMismatch === true)){ 4964 o.showAnimationDuration = 0; 4965 o.showHideOpacity = typeof origOptions.showHideOpacity == 'undefined' ? true : origOptions.showHideOpacity; 4966 o.mainClass = "hidePsPlaceholder"; 4967 4968 }else{ 4969 o.showAnimationDuration = typeof origOptions.showAnimationDuration == 'undefined' ? 333 : origOptions.showAnimationDuration; 4970 o.showHideOpacity = typeof origOptions.showHideOpacity == 'undefined' ? false : origOptions.showHideOpacity; 4971 o.mainClass = typeof origOptions.mainClass == 'undefined' ? '' : origOptions.mainClass; 4972 } 4973 4974 // Pass data to PhotoSwipe and initialize it 4975 // this way disallows orhpan instances in the memory 4976 $('.pswp').removeAttr('style'); // Needed to clear element styles that remain after a differently animated PS 4977 4978 $(".pswp").removeClass(function(index, css){ 4979 return (css.match (/pswp--\S+theme/g) || []).join(' '); 4980 }); 4981 if(o.customTheme !== 'undefined'){ 4982 $('.pswp').addClass('pswp--'+o.customTheme+'-theme'); 4983 } 4984 4985 $.fn.JIGphotoSwipe.gallery = new PhotoSwipe($('.pswp')[0], PhotoSwipeUI_Default, items, o); 4986 $.fn.JIGphotoSwipe.gallery.init(); 4987 if(putBackScroll == true){ 4988 // This puts back close on scroll feature 4989 setTimeout(function(){$.fn.JIGphotoSwipe.gallery.options.closeOnScroll = true;},2000); 4990 } 4991 4992 }; 4993 4994 init(); 4995 4996 }; // end of 'class' 4997 4998})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.