assets-rails 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/README.md +13 -9
  4. data/assets-rails.gemspec +2 -0
  5. data/lib/assets-rails/engine.rb +3 -1
  6. data/lib/assets-rails/version.rb +1 -1
  7. data/vendor/assets/fonts/lightgallery/Slide-icons.dev.svg +33 -0
  8. data/vendor/assets/fonts/lightgallery/Slide-icons.eot +0 -0
  9. data/vendor/assets/fonts/lightgallery/Slide-icons.svg +33 -0
  10. data/vendor/assets/fonts/lightgallery/Slide-icons.ttf +0 -0
  11. data/vendor/assets/fonts/lightgallery/Slide-icons.woff +0 -0
  12. data/vendor/assets/fonts/photoswipe/v4.0.0/default-skin/default-skin.svg +38 -0
  13. data/vendor/assets/fonts/photoswipe/v4.0.2/default-skin/default-skin.svg +1 -0
  14. data/vendor/assets/images/jquery.share/social-icons-sprite.png +0 -0
  15. data/vendor/assets/images/jquery.share/social-icons.png +0 -0
  16. data/vendor/assets/images/lightgallery/loading.gif +0 -0
  17. data/vendor/assets/images/photoswipe/v4.0.0/default-skin/default-skin.png +0 -0
  18. data/vendor/assets/images/photoswipe/v4.0.0/default-skin/preloader.gif +0 -0
  19. data/vendor/assets/images/photoswipe/v4.0.2/default-skin/default-skin.png +0 -0
  20. data/vendor/assets/images/photoswipe/v4.0.2/default-skin/preloader.gif +0 -0
  21. data/vendor/assets/javascripts/imagesloaded/v3.1.8.js +1 -1
  22. data/vendor/assets/javascripts/imagesloaded/v3.1.8/imagesloaded.js +561 -3
  23. data/vendor/assets/javascripts/jquery.cookie/v1.4.1.js +2 -0
  24. data/vendor/assets/javascripts/jquery.cookie/v1.4.1/jquery.cookie.js +117 -0
  25. data/vendor/assets/javascripts/jquery.share.js +1 -0
  26. data/vendor/assets/javascripts/jquery.share/jquery.share.js +137 -0
  27. data/vendor/assets/javascripts/jquery.zoom/v1.7.13.js +1 -0
  28. data/vendor/assets/javascripts/jquery.zoom/v1.7.13/jquery.zoom.js +233 -0
  29. data/vendor/assets/javascripts/lightgallery/v1.1.4.js +1 -0
  30. data/vendor/assets/javascripts/lightgallery/v1.1.4/lightgallery.min.js +11 -0
  31. data/vendor/assets/javascripts/photoswipe/v4.0.0.js +2 -0
  32. data/vendor/assets/javascripts/photoswipe/v4.0.0/photoswipe-ui-default.js +824 -0
  33. data/vendor/assets/javascripts/photoswipe/v4.0.0/photoswipe.js +3647 -0
  34. data/vendor/assets/javascripts/photoswipe/v4.0.2.js +2 -0
  35. data/vendor/assets/javascripts/photoswipe/v4.0.2/photoswipe-ui-default.js +792 -0
  36. data/vendor/assets/javascripts/photoswipe/v4.0.2/photoswipe.js +3589 -0
  37. data/vendor/assets/stylesheets/font-awesome/v4.2.0/_variables.scss +1 -1
  38. data/vendor/assets/stylesheets/jquery.collageplus/v0.3.3/transitions.css +202 -0
  39. data/vendor/assets/stylesheets/jquery.share.css +3 -0
  40. data/vendor/assets/stylesheets/jquery.share/jquery.share.scss +215 -0
  41. data/vendor/assets/stylesheets/lightgallery/v1.1.4/lightgallery.scss +629 -0
  42. data/vendor/assets/stylesheets/photoswipe/v4.0.0.css +3 -0
  43. data/vendor/assets/stylesheets/photoswipe/v4.0.0/default-skin.scss +418 -0
  44. data/vendor/assets/stylesheets/photoswipe/v4.0.0/photoswipe.css +125 -0
  45. data/vendor/assets/stylesheets/photoswipe/v4.0.2.css +3 -0
  46. data/vendor/assets/stylesheets/photoswipe/v4.0.2/default-skin.scss +390 -0
  47. data/vendor/assets/stylesheets/photoswipe/v4.0.2/photoswipe.css +148 -0
  48. metadata +43 -5
@@ -0,0 +1,3589 @@
1
+ /*! PhotoSwipe - v4.0.2 - 2014-12-28
2
+ * http://photoswipe.com
3
+ * Copyright (c) 2014 Dmitry Semenov; */
4
+ (function (root, factory) {
5
+ if (typeof define === 'function' && define.amd) {
6
+ define(factory);
7
+ } else if (typeof exports === 'object') {
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
+ */
25
+ var 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 = navigator.pointerEnabled || 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
+
257
+ framework.detectFeatures();
258
+
259
+ // Override addEventListener for old versions of IE
260
+ if(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
+
301
+ var self = this;
302
+
303
+ /**
304
+ * Static vars, don't change unless you know what you're doing.
305
+ */
306
+ var DOUBLE_TAP_RADIUS = 25,
307
+ NUM_HOLDERS = 3;
308
+
309
+ /**
310
+ * Options
311
+ */
312
+ var _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
+ hideAnimationDuration: 333,
322
+ showAnimationDuration: 333,
323
+ showHideOpacity: false,
324
+ focus: true,
325
+ escKey: true,
326
+ arrowKeys: true,
327
+ mainScrollEndFriction: 0.35,
328
+ panEndFriction: 0.35,
329
+ isClickableElement: function(el) {
330
+ return el.tagName === 'A';
331
+ },
332
+ getDoubleTapZoom: function(isMouseClick, item) {
333
+ if(isMouseClick) {
334
+ return 1;
335
+ } else {
336
+ return item.initialZoomLevel < 0.7 ? 1 : 1.5;
337
+ }
338
+ },
339
+
340
+ // not fully implemented yet
341
+ scaleMode: 'fit', // TODO
342
+ modal: true, // TODO
343
+ alwaysFadeIn: false // TODO
344
+ };
345
+ framework.extend(_options, options);
346
+
347
+
348
+ /**
349
+ * Private helper variables & functions
350
+ */
351
+
352
+ var _getEmptyPoint = function() {
353
+ return {x:0,y:0};
354
+ };
355
+
356
+ var _isOpen,
357
+ _isDestroying,
358
+ _closedByScroll,
359
+ _currentItemIndex,
360
+ _containerStyle,
361
+ _containerShiftIndex,
362
+ _currPanDist = _getEmptyPoint(),
363
+ _startPanOffset = _getEmptyPoint(),
364
+ _panOffset = _getEmptyPoint(),
365
+ _upMoveEvents, // drag move, drag end & drag cancel events array
366
+ _downEvents, // drag start events array
367
+ _globalEventHandlers,
368
+ _viewportSize = {},
369
+ _currZoomLevel,
370
+ _startZoomLevel,
371
+ _translatePrefix,
372
+ _translateSufix,
373
+ _updateSizeInterval,
374
+ _currPositionIndex = 0,
375
+ _offset,
376
+ _slideSize = _getEmptyPoint(), // size of slide area, including spacing
377
+ _scrollChanged,
378
+ _itemHolders,
379
+ _prevItemIndex,
380
+ _indexDiff = 0, // difference of indexes since last content update
381
+ _dragStartEvent,
382
+ _dragMoveEvent,
383
+ _dragEndEvent,
384
+ _dragCancelEvent,
385
+ _transformKey,
386
+ _pointerEventEnabled,
387
+ _isFixedPosition = true,
388
+ _likelyTouchDevice,
389
+ _modules = [],
390
+ _requestAF,
391
+ _cancelAF,
392
+ _initalClassName,
393
+ _initalWindowScrollY,
394
+ _oldIE,
395
+ _currentWindowScrollY,
396
+ _features,
397
+ _windowVisibleSize = {},
398
+
399
+ // Registers PhotoSWipe module (History, Controller ...)
400
+ _registerModule = function(name, module) {
401
+ framework.extend(self, module.publicMethods);
402
+ _modules.push(name);
403
+ },
404
+
405
+ _getLoopedId = function(index) {
406
+ var numSlides = _getNumItems();
407
+ if(index > numSlides - 1) {
408
+ return index - numSlides;
409
+ } else if(index < 0) {
410
+ return numSlides + index;
411
+ }
412
+ return index;
413
+ },
414
+
415
+ // Micro bind/trigger
416
+ _listeners = {},
417
+ _listen = function(name, fn) {
418
+ if(!_listeners[name]) {
419
+ _listeners[name] = [];
420
+ }
421
+ return _listeners[name].push(fn);
422
+ },
423
+ _shout = function(name) {
424
+ var listeners = _listeners[name];
425
+
426
+ if(listeners) {
427
+ var args = Array.prototype.slice.call(arguments);
428
+ args.shift();
429
+
430
+ for(var i = 0; i < listeners.length; i++) {
431
+ listeners[i].apply(self, args);
432
+ }
433
+ }
434
+ },
435
+
436
+ _getCurrentTime = function() {
437
+ return new Date().getTime();
438
+ },
439
+ _applyBgOpacity = function(opacity) {
440
+ _bgOpacity = opacity;
441
+ self.bg.style.opacity = opacity * _options.bgOpacity;
442
+ },
443
+
444
+ _applyZoomTransform = function(styleObj,x,y,zoom) {
445
+ styleObj[_transformKey] = _translatePrefix + x + 'px, ' + y + 'px' + _translateSufix + ' scale(' + zoom + ')';
446
+ },
447
+ _applyCurrentZoomPan = function() {
448
+ if(_currZoomElementStyle) {
449
+ _applyZoomTransform(_currZoomElementStyle, _panOffset.x, _panOffset.y, _currZoomLevel);
450
+ }
451
+ },
452
+ _applyZoomPanToItem = function(item) {
453
+ if(item.container) {
454
+ _applyZoomTransform(item.container.style,
455
+ item.initialPosition.x,
456
+ item.initialPosition.y,
457
+ item.initialZoomLevel);
458
+ }
459
+ },
460
+ _setTranslateX = function(x, elStyle) {
461
+ elStyle[_transformKey] = _translatePrefix + x + 'px, 0px' + _translateSufix;
462
+ },
463
+ _moveMainScroll = function(x, dragging) {
464
+
465
+ if(!_options.loop && dragging) {
466
+ // if of current item during scroll (float)
467
+ var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x)/_slideSize.x;
468
+ var delta = Math.round(x - _mainScrollPos.x);
469
+
470
+ if( (newSlideIndexOffset < 0 && delta > 0) ||
471
+ (newSlideIndexOffset >= _getNumItems()-1 && delta < 0) ) {
472
+ x = _mainScrollPos.x + delta * _options.mainScrollEndFriction;
473
+ }
474
+ }
475
+
476
+ _mainScrollPos.x = x;
477
+ _setTranslateX(x, _containerStyle);
478
+ },
479
+ _calculatePanOffset = function(axis, zoomLevel) {
480
+ var m = _midZoomPoint[axis] - _offset[axis];
481
+ return _startPanOffset[axis] + _currPanDist[axis] + m - m * ( zoomLevel / _startZoomLevel );
482
+ },
483
+
484
+ _equalizePoints = function(p1, p2) {
485
+ p1.x = p2.x;
486
+ p1.y = p2.y;
487
+ if(p2.id) {
488
+ p1.id = p2.id;
489
+ }
490
+ },
491
+
492
+ _mouseMoveTimeout = null,
493
+ _onFirstMouseMove = function() {
494
+ // Wait until mouse move event is fired at least twice during 100ms
495
+ // We do this, because some mobile browsers trigger it on touchstart
496
+ if(_mouseMoveTimeout ) {
497
+ framework.unbind(document, 'mousemove', _onFirstMouseMove);
498
+ framework.addClass(template, 'pswp--has_mouse');
499
+ _options.mouseUsed = true;
500
+ _shout('mouseUsed');
501
+ }
502
+ _mouseMoveTimeout = setTimeout(function() {
503
+ _mouseMoveTimeout = null;
504
+ }, 100);
505
+ },
506
+
507
+ _bindEvents = function() {
508
+ framework.bind(document, 'keydown', self);
509
+
510
+ if(_features.transform) {
511
+ // don't bind click event in browsers that don't support transform (mostly IE8)
512
+ framework.bind(self.scrollWrap, 'click', self);
513
+ }
514
+
515
+
516
+ if(!_options.mouseUsed) {
517
+ framework.bind(document, 'mousemove', _onFirstMouseMove);
518
+ }
519
+
520
+ framework.bind(window, 'resize scroll', self);
521
+
522
+ _shout('bindEvents');
523
+ },
524
+
525
+ _unbindEvents = function() {
526
+ framework.unbind(window, 'resize', self);
527
+ framework.unbind(window, 'scroll', _globalEventHandlers.scroll);
528
+ framework.unbind(document, 'keydown', self);
529
+ framework.unbind(document, 'mousemove', _onFirstMouseMove);
530
+
531
+ if(_features.transform) {
532
+ framework.unbind(self.scrollWrap, 'click', self);
533
+ }
534
+
535
+ if(_isDragging) {
536
+ framework.unbind(window, _upMoveEvents, self);
537
+ }
538
+
539
+ _shout('unbindEvents');
540
+ },
541
+
542
+ _calculatePanBounds = function(zoomLevel, update) {
543
+ var bounds = _calculateItemSize( self.currItem, _viewportSize, zoomLevel );
544
+ if(update) {
545
+ _currPanBounds = bounds;
546
+ }
547
+ return bounds;
548
+ },
549
+
550
+ // Return true if offset is out of the bounds
551
+ _modifyDestPanOffset = function(axis, destPanBounds, destPanOffset, destZoomLevel) {
552
+ if(destZoomLevel === self.currItem.initialZoomLevel) {
553
+ destPanOffset[axis] = self.currItem.initialPosition[axis];
554
+ return true;
555
+ } else {
556
+ destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel);
557
+
558
+ if(destPanOffset[axis] > destPanBounds.min[axis]) {
559
+ destPanOffset[axis] = destPanBounds.min[axis];
560
+ return true;
561
+ } else if(destPanOffset[axis] < destPanBounds.max[axis] ) {
562
+ destPanOffset[axis] = destPanBounds.max[axis];
563
+ return true;
564
+ }
565
+ }
566
+ return false;
567
+ },
568
+
569
+ _setupTransforms = function() {
570
+
571
+ if(_transformKey) {
572
+ // setup 3d transforms
573
+ var allow3dTransform = _features.perspective && !_likelyTouchDevice;
574
+ _translatePrefix = 'translate' + (allow3dTransform ? '3d(' : '(');
575
+ _translateSufix = _features.perspective ? ', 0px)' : ')';
576
+ return;
577
+ }
578
+
579
+ // Override zoom/pan/move functions in case old browser is used (most likely IE)
580
+ // (so they use left/top/width/height, instead of CSS transform)
581
+
582
+ _transformKey = 'left';
583
+ framework.addClass(template, 'pswp--ie');
584
+
585
+ _setTranslateX = function(x, elStyle) {
586
+ elStyle.left = x + 'px';
587
+ };
588
+ _applyZoomPanToItem = function(item) {
589
+
590
+ var s = item.container.style,
591
+ w = item.fitRatio * item.w,
592
+ h = item.fitRatio * item.h;
593
+
594
+ s.width = w + 'px';
595
+ s.height = h + 'px';
596
+ s.left = item.initialPosition.x + 'px';
597
+ s.top = item.initialPosition.y + 'px';
598
+
599
+ };
600
+ _applyCurrentZoomPan = function() {
601
+ if(_currZoomElementStyle) {
602
+ var s = _currZoomElementStyle;
603
+ var item = self.currItem;
604
+
605
+ var w = item.fitRatio * item.w;
606
+ var h = item.fitRatio * item.h;
607
+
608
+ s.width = w + 'px';
609
+ s.height = h + 'px';
610
+
611
+
612
+ s.left = _panOffset.x + 'px';
613
+ s.top = _panOffset.y + 'px';
614
+ }
615
+
616
+ };
617
+ },
618
+
619
+ _onKeyDown = function(e) {
620
+ var keydownAction = '';
621
+ if(_options.escKey && e.keyCode === 27) {
622
+ keydownAction = 'close';
623
+ } else if(_options.arrowKeys) {
624
+ if(e.keyCode === 37) {
625
+ keydownAction = 'prev';
626
+ } else if(e.keyCode === 39) {
627
+ keydownAction = 'next';
628
+ }
629
+ }
630
+
631
+ if(keydownAction) {
632
+ // don't do anything if special key pressed to prevent from overriding default browser actions
633
+ // e.g. in Chrome on Mac cmd+arrow-left returns to previous page
634
+ if( !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) {
635
+ if(e.preventDefault) {
636
+ e.preventDefault();
637
+ } else {
638
+ e.returnValue = false;
639
+ }
640
+ self[keydownAction]();
641
+ }
642
+ }
643
+ },
644
+
645
+ _onGlobalClick = function(e) {
646
+ if(!e) {
647
+ return;
648
+ }
649
+
650
+ // don't allow click event to pass through when triggering after drag or some other gesture
651
+ if(_moved || _zoomStarted || _mainScrollAnimating || _verticalDragInitiated) {
652
+ e.preventDefault();
653
+ e.stopPropagation();
654
+ }
655
+ },
656
+
657
+ _onPageScroll = function() {
658
+ _scrollChanged = true;
659
+ // "close" on scroll works only on desktop devices, or when mouse is used
660
+ if(_options.closeOnScroll && _isOpen && (!self.likelyTouchDevice || _options.mouseUsed) ) {
661
+ // if scrolled for more than 2px
662
+ if(Math.abs(framework.getScrollY() - _initalWindowScrollY) > 2) {
663
+ _closedByScroll = true;
664
+ self.close();
665
+ }
666
+ }
667
+ };
668
+
669
+
670
+
671
+
672
+
673
+
674
+
675
+ // Micro animation engine
676
+ var _animations = {},
677
+ _numAnimations = 0,
678
+ _stopAnimation = function(name) {
679
+ if(_animations[name]) {
680
+ if(_animations[name].raf) {
681
+ _cancelAF( _animations[name].raf );
682
+ }
683
+ _numAnimations--;
684
+ delete _animations[name];
685
+ }
686
+ },
687
+ _registerStartAnimation = function(name) {
688
+ if(_animations[name]) {
689
+ _stopAnimation(name);
690
+ }
691
+ if(!_animations[name]) {
692
+ _numAnimations++;
693
+ _animations[name] = {};
694
+ }
695
+ },
696
+ _stopAllAnimations = function() {
697
+ for (var prop in _animations) {
698
+
699
+ if( _animations.hasOwnProperty( prop ) ) {
700
+ _stopAnimation(prop);
701
+ }
702
+
703
+ }
704
+ },
705
+ _animateProp = function(name, b, endProp, d, easingFn, onUpdate, onComplete) {
706
+ var startAnimTime = _getCurrentTime(), t;
707
+ _registerStartAnimation(name);
708
+
709
+ var animloop = function(){
710
+ if ( _animations[name] ) {
711
+
712
+ t = _getCurrentTime() - startAnimTime; // time diff
713
+ //b - beginning (start prop)
714
+ //d - anim duration
715
+
716
+ if ( t >= d ) {
717
+ _stopAnimation(name);
718
+ onUpdate(endProp);
719
+ if(onComplete) {
720
+ onComplete();
721
+ }
722
+ return;
723
+ }
724
+ onUpdate( (endProp - b) * easingFn(t/d) + b );
725
+
726
+ _animations[name].raf = _requestAF(animloop);
727
+ }
728
+ };
729
+ animloop();
730
+ };
731
+
732
+
733
+
734
+ var publicMethods = {
735
+
736
+ // make a few local variables and functions public
737
+ shout: _shout,
738
+ listen: _listen,
739
+ viewportSize: _viewportSize,
740
+ options: _options,
741
+
742
+ isMainScrollAnimating: function() {
743
+ return _mainScrollAnimating;
744
+ },
745
+ getZoomLevel: function() {
746
+ return _currZoomLevel;
747
+ },
748
+ getCurrentIndex: function() {
749
+ return _currentItemIndex;
750
+ },
751
+ isDragging: function() {
752
+ return _isDragging;
753
+ },
754
+ isZooming: function() {
755
+ return _isZooming;
756
+ },
757
+ applyZoomPan: function(zoomLevel,panX,panY) {
758
+ _panOffset.x = panX;
759
+ _panOffset.y = panY;
760
+ _currZoomLevel = zoomLevel;
761
+ _applyCurrentZoomPan();
762
+ },
763
+
764
+ init: function() {
765
+
766
+ if(_isOpen || _isDestroying) {
767
+ return;
768
+ }
769
+
770
+ var i;
771
+
772
+ self.framework = framework; // basic function
773
+ self.template = template; // root DOM element of PhotoSwipe
774
+ self.bg = framework.getChildByClass(template, 'pswp__bg');
775
+
776
+ _initalClassName = template.className;
777
+ _isOpen = true;
778
+
779
+ _features = framework.detectFeatures();
780
+ _requestAF = _features.raf;
781
+ _cancelAF = _features.caf;
782
+ _transformKey = _features.transform;
783
+ _oldIE = _features.oldIE;
784
+
785
+ self.scrollWrap = framework.getChildByClass(template, 'pswp__scroll-wrap');
786
+ self.container = framework.getChildByClass(self.scrollWrap, 'pswp__container');
787
+
788
+ _containerStyle = self.container.style; // for fast access
789
+
790
+ // Objects that hold slides (there are only 3 in DOM)
791
+ self.itemHolders = _itemHolders = [
792
+ {el:self.container.children[0] , wrap:0, index: -1},
793
+ {el:self.container.children[1] , wrap:0, index: -1},
794
+ {el:self.container.children[2] , wrap:0, index: -1}
795
+ ];
796
+
797
+ // hide nearby item holders until initial zoom animation finishes (to avoid extra Paints)
798
+ _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'none';
799
+
800
+ _setupTransforms();
801
+
802
+ // Setup global events
803
+ _globalEventHandlers = {
804
+ resize: self.updateSize,
805
+ scroll: _onPageScroll,
806
+ keydown: _onKeyDown,
807
+ click: _onGlobalClick
808
+ };
809
+
810
+ // disable show/hide effects on old browsers that don't support CSS animations or transforms,
811
+ // old IOS, Android and Opera mobile. Blackberry seems to work fine, even older models.
812
+ var oldPhone = _features.isOldIOSPhone || _features.isOldAndroid || _features.isMobileOpera;
813
+ if(!_features.animationName || !_features.transform || oldPhone) {
814
+ _options.showAnimationDuration = _options.hideAnimationDuration = 0;
815
+ }
816
+
817
+ // init modules
818
+ for(i = 0; i < _modules.length; i++) {
819
+ self['init' + _modules[i]]();
820
+ }
821
+
822
+ // init
823
+ if(UiClass) {
824
+ var ui = self.ui = new UiClass(self, framework);
825
+ ui.init();
826
+ }
827
+
828
+ _shout('firstUpdate');
829
+ _currentItemIndex = _currentItemIndex || _options.index || 0;
830
+ // validate index
831
+ if( isNaN(_currentItemIndex) || _currentItemIndex < 0 || _currentItemIndex >= _getNumItems() ) {
832
+ _currentItemIndex = 0;
833
+ }
834
+ self.currItem = _getItemAt( _currentItemIndex );
835
+
836
+
837
+ if(_features.isOldIOSPhone || _features.isOldAndroid) {
838
+ _isFixedPosition = false;
839
+ }
840
+
841
+ if(_options.modal) {
842
+ template.setAttribute('aria-hidden', 'false');
843
+ if(!_isFixedPosition) {
844
+ template.style.position = 'absolute';
845
+ template.style.top = framework.getScrollY() + 'px';
846
+ } else {
847
+ template.style.position = 'fixed';
848
+ }
849
+ }
850
+
851
+ if(_currentWindowScrollY === undefined) {
852
+ _shout('initialLayout');
853
+ _currentWindowScrollY = _initalWindowScrollY = framework.getScrollY();
854
+ }
855
+
856
+ // add classes to root element of PhotoSwipe
857
+ var rootClasses = 'pswp--open ';
858
+ if(_options.mainClass) {
859
+ rootClasses += _options.mainClass + ' ';
860
+ }
861
+ if(_options.showHideOpacity) {
862
+ rootClasses += 'pswp--animate_opacity ';
863
+ }
864
+ rootClasses += _likelyTouchDevice ? 'pswp--touch' : 'pswp--notouch';
865
+ rootClasses += _features.animationName ? ' pswp--css_animation' : '';
866
+ rootClasses += _features.svg ? ' pswp--svg' : '';
867
+ framework.addClass(template, rootClasses);
868
+
869
+ self.updateSize();
870
+
871
+ // initial update
872
+ _containerShiftIndex = -1;
873
+ _indexDiff = null;
874
+ for(i = 0; i < NUM_HOLDERS; i++) {
875
+ _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, _itemHolders[i].el.style);
876
+ }
877
+
878
+ if(!_oldIE) {
879
+ framework.bind(self.scrollWrap, _downEvents, self); // no dragging for old IE
880
+ }
881
+
882
+ _listen('initialZoomInEnd', function() {
883
+ self.setContent(_itemHolders[0], _currentItemIndex-1);
884
+ self.setContent(_itemHolders[2], _currentItemIndex+1);
885
+
886
+ _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'block';
887
+
888
+ if(_options.focus) {
889
+ // focus causes layout,
890
+ // which causes lag during the animation,
891
+ // that's why we delay it untill the initial zoom transition ends
892
+ template.focus();
893
+ }
894
+
895
+
896
+ _bindEvents();
897
+ });
898
+
899
+ // set content for center slide (first time)
900
+ self.setContent(_itemHolders[1], _currentItemIndex);
901
+
902
+ self.updateCurrItem();
903
+
904
+ _shout('afterInit');
905
+
906
+ if(!_isFixedPosition) {
907
+
908
+ // On all versions of iOS lower than 8.0, we check size of viewport every second.
909
+ //
910
+ // This is done to detect when Safari top & bottom bars appear,
911
+ // as this action doesn't trigger any events (like resize).
912
+ //
913
+ // On iOS8 they fixed this.
914
+ //
915
+ // 10 Nov 2014: iOS 7 usage ~40%. iOS 8 usage 56%.
916
+
917
+ _updateSizeInterval = setInterval(function() {
918
+ if(!_numAnimations && !_isDragging && !_isZooming && (_currZoomLevel === self.currItem.initialZoomLevel) ) {
919
+ self.updateSize();
920
+ }
921
+ }, 1000);
922
+ }
923
+
924
+ framework.addClass(template, 'pswp--visible');
925
+ },
926
+
927
+ // Closes the gallery, then destroy it
928
+ close: function() {
929
+ if(!_isOpen) {
930
+ return;
931
+ }
932
+
933
+ _isOpen = false;
934
+ _isDestroying = true;
935
+ _shout('close');
936
+ _unbindEvents();
937
+
938
+ _showOrHide( self.currItem, null, true, self.destroy);
939
+ },
940
+
941
+ // destroys gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks)
942
+ destroy: function() {
943
+ _shout('destroy');
944
+
945
+ if(_showOrHideTimeout) {
946
+ clearTimeout(_showOrHideTimeout);
947
+ }
948
+
949
+ if(_options.modal) {
950
+ template.setAttribute('aria-hidden', 'true');
951
+ template.className = _initalClassName;
952
+ }
953
+
954
+ if(_updateSizeInterval) {
955
+ clearInterval(_updateSizeInterval);
956
+ }
957
+
958
+ framework.unbind(self.scrollWrap, _downEvents, self);
959
+
960
+ // we unbind lost event at the end, as closing animation may depend on it
961
+ framework.unbind(window, 'scroll', self);
962
+
963
+ _stopDragUpdateLoop();
964
+
965
+ _stopAllAnimations();
966
+
967
+ _listeners = null;
968
+ },
969
+
970
+ /**
971
+ * Pan image to position
972
+ * @param {Number} x
973
+ * @param {Number} y
974
+ * @param {Boolean} force Will ignore bounds if set to true.
975
+ */
976
+ panTo: function(x,y,force) {
977
+ if(!force) {
978
+ if(x > _currPanBounds.min.x) {
979
+ x = _currPanBounds.min.x;
980
+ } else if(x < _currPanBounds.max.x) {
981
+ x = _currPanBounds.max.x;
982
+ }
983
+
984
+ if(y > _currPanBounds.min.y) {
985
+ y = _currPanBounds.min.y;
986
+ } else if(y < _currPanBounds.max.y) {
987
+ y = _currPanBounds.max.y;
988
+ }
989
+ }
990
+
991
+ _panOffset.x = x;
992
+ _panOffset.y = y;
993
+ _applyCurrentZoomPan();
994
+ },
995
+
996
+ handleEvent: function (e) {
997
+ e = e || window.event;
998
+ if(_globalEventHandlers[e.type]) {
999
+ _globalEventHandlers[e.type](e);
1000
+ }
1001
+ },
1002
+
1003
+
1004
+ goTo: function(index) {
1005
+
1006
+ index = _getLoopedId(index);
1007
+
1008
+ var diff = index - _currentItemIndex;
1009
+ _indexDiff = diff;
1010
+
1011
+ _currentItemIndex = index;
1012
+ self.currItem = _getItemAt( _currentItemIndex );
1013
+ _currPositionIndex -= diff;
1014
+
1015
+ _moveMainScroll(_slideSize.x * _currPositionIndex);
1016
+
1017
+
1018
+ _stopAllAnimations();
1019
+ _mainScrollAnimating = false;
1020
+
1021
+ self.updateCurrItem();
1022
+ },
1023
+ next: function() {
1024
+ self.goTo( _currentItemIndex + 1);
1025
+ },
1026
+ prev: function() {
1027
+ self.goTo( _currentItemIndex - 1);
1028
+ },
1029
+
1030
+ // update current zoom/pan objects
1031
+ updateCurrZoomItem: function(emulateSetContent) {
1032
+ if(emulateSetContent) {
1033
+ _shout('beforeChange', 0);
1034
+ }
1035
+
1036
+ // itemHolder[1] is middle (current) item
1037
+ if(_itemHolders[1].el.children.length) {
1038
+ var zoomElement = _itemHolders[1].el.children[0];
1039
+ if( framework.hasClass(zoomElement, 'pswp__zoom-wrap') ) {
1040
+ _currZoomElementStyle = zoomElement.style;
1041
+ } else {
1042
+ _currZoomElementStyle = null;
1043
+ }
1044
+ } else {
1045
+ _currZoomElementStyle = null;
1046
+ }
1047
+
1048
+ _currPanBounds = self.currItem.bounds;
1049
+ _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
1050
+
1051
+ _panOffset.x = _currPanBounds.center.x;
1052
+ _panOffset.y = _currPanBounds.center.y;
1053
+
1054
+ if(emulateSetContent) {
1055
+ _shout('afterChange');
1056
+ }
1057
+ },
1058
+
1059
+ invalidateCurrItems: function() {
1060
+ for(var i = 0; i < NUM_HOLDERS; i++) {
1061
+ if( _itemHolders[i].item ) {
1062
+ _itemHolders[i].item.needsUpdate = true;
1063
+ }
1064
+ }
1065
+ },
1066
+
1067
+ updateCurrItem: function(beforeAnimation) {
1068
+
1069
+ if(_indexDiff === 0) {
1070
+ return;
1071
+ }
1072
+
1073
+ var diffAbs = Math.abs(_indexDiff),
1074
+ tempHolder;
1075
+
1076
+ if(beforeAnimation && diffAbs < 2) {
1077
+ return;
1078
+ }
1079
+
1080
+
1081
+ self.currItem = _getItemAt( _currentItemIndex );
1082
+
1083
+ _shout('beforeChange', _indexDiff);
1084
+
1085
+ if(diffAbs >= NUM_HOLDERS) {
1086
+ _containerShiftIndex += _indexDiff + (_indexDiff > 0 ? -NUM_HOLDERS : NUM_HOLDERS);
1087
+ diffAbs = NUM_HOLDERS;
1088
+ }
1089
+ for(var i = 0; i < diffAbs; i++) {
1090
+ if(_indexDiff > 0) {
1091
+ tempHolder = _itemHolders.shift();
1092
+ _itemHolders[NUM_HOLDERS-1] = tempHolder; // move first to last
1093
+
1094
+ _containerShiftIndex++;
1095
+ _setTranslateX( (_containerShiftIndex+2) * _slideSize.x, tempHolder.el.style);
1096
+ self.setContent(tempHolder, _currentItemIndex - diffAbs + i + 1 + 1);
1097
+ } else {
1098
+ tempHolder = _itemHolders.pop();
1099
+ _itemHolders.unshift( tempHolder ); // move last to first
1100
+
1101
+ _containerShiftIndex--;
1102
+ _setTranslateX( _containerShiftIndex * _slideSize.x, tempHolder.el.style);
1103
+ self.setContent(tempHolder, _currentItemIndex + diffAbs - i - 1 - 1);
1104
+ }
1105
+
1106
+ }
1107
+
1108
+ // reset zoom/pan on previous item
1109
+ if(_currZoomElementStyle && Math.abs(_indexDiff) === 1) {
1110
+
1111
+ var prevItem = _getItemAt(_prevItemIndex);
1112
+ if(prevItem.initialZoomLevel !== _currZoomLevel) {
1113
+ _calculateItemSize(prevItem , _viewportSize );
1114
+ _applyZoomPanToItem( prevItem );
1115
+ }
1116
+
1117
+ }
1118
+
1119
+ // reset diff after update
1120
+ _indexDiff = 0;
1121
+
1122
+ self.updateCurrZoomItem();
1123
+
1124
+ _prevItemIndex = _currentItemIndex;
1125
+
1126
+ _shout('afterChange');
1127
+
1128
+ },
1129
+
1130
+
1131
+
1132
+ updateSize: function(force) {
1133
+
1134
+ if(!_isFixedPosition) {
1135
+ var windowScrollY = framework.getScrollY();
1136
+ if(_currentWindowScrollY !== windowScrollY) {
1137
+ template.style.top = windowScrollY + 'px';
1138
+ _currentWindowScrollY = windowScrollY;
1139
+ }
1140
+ if(!force && _windowVisibleSize.x === window.innerWidth && _windowVisibleSize.y === window.innerHeight) {
1141
+ return;
1142
+ }
1143
+ _windowVisibleSize.x = window.innerWidth;
1144
+ _windowVisibleSize.y = window.innerHeight;
1145
+
1146
+ //template.style.width = _windowVisibleSize.x + 'px';
1147
+ template.style.height = _windowVisibleSize.y + 'px';
1148
+ }
1149
+
1150
+
1151
+
1152
+ _viewportSize.x = self.scrollWrap.clientWidth;
1153
+ _viewportSize.y = self.scrollWrap.clientHeight;
1154
+
1155
+
1156
+ _offset = {x:0,y:_currentWindowScrollY};//framework.getOffset(template);
1157
+
1158
+ _slideSize.x = _viewportSize.x + Math.round(_viewportSize.x * _options.spacing);
1159
+ _slideSize.y = _viewportSize.y;
1160
+
1161
+ _moveMainScroll(_slideSize.x * _currPositionIndex);
1162
+
1163
+ _shout('beforeResize'); // even may be used for example to switch image sources
1164
+
1165
+ // don't re-calculate size on inital size update
1166
+ if(_containerShiftIndex !== undefined) {
1167
+
1168
+ var holder,
1169
+ item;
1170
+
1171
+ for(var i = 0; i < NUM_HOLDERS; i++) {
1172
+ holder = _itemHolders[i];
1173
+ _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, holder.el.style);
1174
+
1175
+ // update zoom level on items and refresh source (if needsUpdate)
1176
+ item = _getItemAt( holder.index );
1177
+ if(item.needsUpdate) {
1178
+ self.cleanSlide( item );
1179
+
1180
+ self.setContent( holder, holder.index );
1181
+
1182
+ // if "center" slide
1183
+ if(i === 1) {
1184
+ self.updateCurrZoomItem(true);
1185
+ }
1186
+
1187
+ item.needsUpdate = false;
1188
+ }
1189
+ if(item && item.container) {
1190
+ _calculateItemSize(item, _viewportSize);
1191
+ _applyZoomPanToItem( item );
1192
+ }
1193
+
1194
+ }
1195
+ }
1196
+
1197
+ _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
1198
+ _currPanBounds = self.currItem.bounds;
1199
+
1200
+ if(_currPanBounds) {
1201
+ _panOffset.x = _currPanBounds.center.x;
1202
+ _panOffset.y = _currPanBounds.center.y;
1203
+ _applyCurrentZoomPan();
1204
+ }
1205
+
1206
+ _shout('resize');
1207
+ },
1208
+
1209
+ //Zoom current item to
1210
+ zoomTo: function(destZoomLevel, centerPoint, speed, easingFn, updateFn) {
1211
+
1212
+ /*
1213
+ if(destZoomLevel === 'fit') {
1214
+ destZoomLevel = self.currItem.fitRatio;
1215
+ } else if(destZoomLevel === 'fill') {
1216
+ destZoomLevel = self.currItem.fillRatio;
1217
+ }
1218
+ */
1219
+
1220
+ if(centerPoint) {
1221
+ _startZoomLevel = _currZoomLevel;
1222
+ _midZoomPoint.x = Math.abs(centerPoint.x) - _panOffset.x ;
1223
+ _midZoomPoint.y = Math.abs(centerPoint.y) - _panOffset.y ;
1224
+ _equalizePoints(_startPanOffset, _panOffset);
1225
+ }
1226
+
1227
+ var destPanBounds = _calculatePanBounds(destZoomLevel, false),
1228
+ destPanOffset = {};
1229
+
1230
+ _modifyDestPanOffset('x', destPanBounds, destPanOffset, destZoomLevel);
1231
+ _modifyDestPanOffset('y', destPanBounds, destPanOffset, destZoomLevel);
1232
+
1233
+ var initialZoomLevel = _currZoomLevel;
1234
+ var initialPanOffset = {
1235
+ x: _panOffset.x,
1236
+ y: _panOffset.y
1237
+ };
1238
+
1239
+ //_startZoomLevel = destZoomLevel;
1240
+ var onUpdate = function(now) {
1241
+ if(now === 1) {
1242
+ _currZoomLevel = destZoomLevel;
1243
+ _panOffset.x = destPanOffset.x;
1244
+ _panOffset.y = destPanOffset.y;
1245
+ } else {
1246
+ _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
1247
+ _panOffset.x = (destPanOffset.x - initialPanOffset.x) * now + initialPanOffset.x;
1248
+ _panOffset.y = (destPanOffset.y - initialPanOffset.y) * now + initialPanOffset.y;
1249
+ }
1250
+
1251
+ if(updateFn) {
1252
+ updateFn(now);
1253
+ }
1254
+
1255
+ _applyCurrentZoomPan();
1256
+ };
1257
+
1258
+ if(speed) {
1259
+ _animateProp('customZoomTo', 0, 1, speed, easingFn || framework.easing.sine.inOut, onUpdate);
1260
+ } else {
1261
+ onUpdate(1);
1262
+ }
1263
+ }
1264
+
1265
+
1266
+ };
1267
+
1268
+ /*>>core*/
1269
+
1270
+ /*>>gestures*/
1271
+ /**
1272
+ * Mouse/touch/pointer event handlers.
1273
+ *
1274
+ * separated from @core.js for readability
1275
+ */
1276
+
1277
+ var MIN_SWIPE_DISTANCE = 30,
1278
+ DIRECTION_CHECK_OFFSET = 10; // amount of pixels to drag to determine direction of swipe
1279
+
1280
+ var _gestureStartTime,
1281
+ _gestureCheckSpeedTime,
1282
+
1283
+ // pool of objects that are used during dragging of zooming
1284
+ p = {}, // first point
1285
+ p2 = {}, // second point (for zoom gesture)
1286
+ delta = {},
1287
+ _currPoint = {},
1288
+ _startPoint = {},
1289
+ _currPointers = [],
1290
+ _startMainScrollPos = {},
1291
+ _releaseAnimData,
1292
+ _posPoints = [], // array of points during dragging, used to determine type of gesture
1293
+ _tempPoint = {},
1294
+
1295
+ _isZoomingIn,
1296
+ _verticalDragInitiated,
1297
+ _oldAndroidTouchEndTimeout,
1298
+ _currZoomedItemIndex = 0,
1299
+ _centerPoint = _getEmptyPoint(),
1300
+ _lastReleaseTime = 0,
1301
+ _isDragging, // at least one pointer is down
1302
+ _isMultitouch, // at least two _pointers are down
1303
+ _zoomStarted, // zoom level changed during zoom gesture
1304
+ _moved,
1305
+ _dragAnimFrame,
1306
+ _mainScrollShifted,
1307
+ _currentPoints, // array of current touch points
1308
+ _isZooming,
1309
+ _currPointsDistance,
1310
+ _startPointsDistance,
1311
+ _currPanBounds,
1312
+ _mainScrollPos = _getEmptyPoint(),
1313
+ _currZoomElementStyle,
1314
+ _mainScrollAnimating, // true, if animation after swipe gesture is running
1315
+ _midZoomPoint = _getEmptyPoint(),
1316
+ _currCenterPoint = _getEmptyPoint(),
1317
+ _direction,
1318
+ _isFirstMove,
1319
+ _opacityChanged,
1320
+ _bgOpacity,
1321
+ _wasOverInitialZoom,
1322
+
1323
+ _isEqualPoints = function(p1, p2) {
1324
+ return p1.x === p2.x && p1.y === p2.y;
1325
+ },
1326
+ _isNearbyPoints = function(touch0, touch1) {
1327
+ return Math.abs(touch0.x - touch1.x) < DOUBLE_TAP_RADIUS && Math.abs(touch0.y - touch1.y) < DOUBLE_TAP_RADIUS;
1328
+ },
1329
+ _calculatePointsDistance = function(p1, p2) {
1330
+ _tempPoint.x = Math.abs( p1.x - p2.x );
1331
+ _tempPoint.y = Math.abs( p1.y - p2.y );
1332
+ return Math.sqrt(_tempPoint.x * _tempPoint.x + _tempPoint.y * _tempPoint.y);
1333
+ },
1334
+ _stopDragUpdateLoop = function() {
1335
+ if(_dragAnimFrame) {
1336
+ _cancelAF(_dragAnimFrame);
1337
+ _dragAnimFrame = null;
1338
+ }
1339
+ },
1340
+ _dragUpdateLoop = function() {
1341
+ if(_isDragging) {
1342
+ _dragAnimFrame = _requestAF(_dragUpdateLoop);
1343
+ _renderMovement();
1344
+ }
1345
+ },
1346
+ _canPan = function() {
1347
+ return !(_options.scaleMode === 'fit' && _currZoomLevel === self.currItem.initialZoomLevel);
1348
+ },
1349
+
1350
+ // find the closest parent DOM element
1351
+ _closestElement = function(el, fn) {
1352
+ if(!el) {
1353
+ return false;
1354
+ }
1355
+
1356
+ // don't search elements above pswp__scroll-wrap
1357
+ if(el.className && el.className.indexOf('pswp__scroll-wrap') > -1 ) {
1358
+ return false;
1359
+ }
1360
+
1361
+ if( fn(el) ) {
1362
+ return el;
1363
+ }
1364
+
1365
+ return _closestElement(el.parentNode, fn);
1366
+ },
1367
+
1368
+ _preventObj = {},
1369
+ _preventDefaultEventBehaviour = function(e, isDown) {
1370
+ _preventObj.prevent = !_closestElement(e.target, _options.isClickableElement);
1371
+
1372
+ _shout('preventDragEvent', e, isDown, _preventObj);
1373
+ return _preventObj.prevent;
1374
+
1375
+ },
1376
+ _convertTouchToPoint = function(touch, p) {
1377
+ p.x = touch.pageX;
1378
+ p.y = touch.pageY;
1379
+ p.id = touch.identifier;
1380
+ return p;
1381
+ },
1382
+ _findCenterOfPoints = function(p1, p2, pCenter) {
1383
+ pCenter.x = (p1.x + p2.x) * 0.5;
1384
+ pCenter.y = (p1.y + p2.y) * 0.5;
1385
+ },
1386
+ _pushPosPoint = function(time, x, y) {
1387
+ if(time - _gestureCheckSpeedTime > 50) {
1388
+ var o = _posPoints.length > 2 ? _posPoints.shift() : {};
1389
+ o.x = x;
1390
+ o.y = y;
1391
+ _posPoints.push(o);
1392
+ _gestureCheckSpeedTime = time;
1393
+ }
1394
+ },
1395
+
1396
+ _calculateVerticalDragOpacityRatio = function() {
1397
+ var yOffset = _panOffset.y - self.currItem.initialPosition.y; // difference between initial and current position
1398
+ return 1 - Math.abs( yOffset / (_viewportSize.y / 2) );
1399
+ },
1400
+
1401
+
1402
+ // points pool, reused during touch events
1403
+ _ePoint1 = {},
1404
+ _ePoint2 = {},
1405
+ _tempPointsArr = [],
1406
+ _tempCounter,
1407
+ _getTouchPoints = function(e) {
1408
+ // clean up previous points, without recreating array
1409
+ while(_tempPointsArr.length > 0) {
1410
+ _tempPointsArr.pop();
1411
+ }
1412
+
1413
+ if(!_pointerEventEnabled) {
1414
+ if(e.type.indexOf('touch') > -1) {
1415
+
1416
+ if(e.touches && e.touches.length > 0) {
1417
+ _tempPointsArr[0] = _convertTouchToPoint(e.touches[0], _ePoint1);
1418
+ if(e.touches.length > 1) {
1419
+ _tempPointsArr[1] = _convertTouchToPoint(e.touches[1], _ePoint2);
1420
+ }
1421
+ }
1422
+
1423
+ } else {
1424
+ _ePoint1.x = e.pageX;
1425
+ _ePoint1.y = e.pageY;
1426
+ _ePoint1.id = '';
1427
+ _tempPointsArr[0] = _ePoint1;//_ePoint1;
1428
+ }
1429
+ } else {
1430
+ _tempCounter = 0;
1431
+ // we can use forEach, as pointer events are supported only in modern browsers
1432
+ _currPointers.forEach(function(p) {
1433
+ if(_tempCounter === 0) {
1434
+ _tempPointsArr[0] = p;
1435
+ } else if(_tempCounter === 1) {
1436
+ _tempPointsArr[1] = p;
1437
+ }
1438
+ _tempCounter++;
1439
+
1440
+ });
1441
+ }
1442
+ return _tempPointsArr;
1443
+ },
1444
+
1445
+ _panOrMoveMainScroll = function(axis, delta) {
1446
+
1447
+ var panFriction,
1448
+ overDiff = 0,
1449
+ newOffset = _panOffset[axis] + delta[axis],
1450
+ startOverDiff,
1451
+ dir = delta[axis] > 0,
1452
+ newMainScrollPosition = _mainScrollPos.x + delta.x,
1453
+ mainScrollDiff = _mainScrollPos.x - _startMainScrollPos.x,
1454
+ newPanPos,
1455
+ newMainScrollPos;
1456
+
1457
+ // calculate fdistance over the bounds and friction
1458
+ if(newOffset > _currPanBounds.min[axis] || newOffset < _currPanBounds.max[axis]) {
1459
+ panFriction = _options.panEndFriction;
1460
+ // Linear increasing of friction, so at 1/4 of viewport it's at max value.
1461
+ // Looks not as nice as was expected. Left for history.
1462
+ // panFriction = (1 - (_panOffset[axis] + delta[axis] + panBounds.min[axis]) / (_viewportSize[axis] / 4) );
1463
+ } else {
1464
+ panFriction = 1;
1465
+ }
1466
+
1467
+ newOffset = _panOffset[axis] + delta[axis] * panFriction;
1468
+
1469
+ // move main scroll or start panning
1470
+ if(_options.allowPanToNext || _currZoomLevel === self.currItem.initialZoomLevel) {
1471
+
1472
+
1473
+ if(!_currZoomElementStyle) {
1474
+
1475
+ newMainScrollPos = newMainScrollPosition;
1476
+
1477
+ } else if(_direction === 'h' && axis === 'x' && !_zoomStarted ) {
1478
+
1479
+ if(dir) {
1480
+ if(newOffset > _currPanBounds.min[axis]) {
1481
+ panFriction = _options.panEndFriction;
1482
+ overDiff = _currPanBounds.min[axis] - newOffset;
1483
+ startOverDiff = _currPanBounds.min[axis] - _startPanOffset[axis];
1484
+ }
1485
+
1486
+ // drag right
1487
+ if( (startOverDiff <= 0 || mainScrollDiff < 0) && _getNumItems() > 1 ) {
1488
+ newMainScrollPos = newMainScrollPosition;
1489
+ if(mainScrollDiff < 0 && newMainScrollPosition > _startMainScrollPos.x) {
1490
+ newMainScrollPos = _startMainScrollPos.x;
1491
+ }
1492
+ } else {
1493
+ if(_currPanBounds.min.x !== _currPanBounds.max.x) {
1494
+ newPanPos = newOffset;
1495
+ }
1496
+
1497
+ }
1498
+
1499
+ } else {
1500
+
1501
+ if(newOffset < _currPanBounds.max[axis] ) {
1502
+ panFriction =_options.panEndFriction;
1503
+ overDiff = newOffset - _currPanBounds.max[axis];
1504
+ startOverDiff = _startPanOffset[axis] - _currPanBounds.max[axis];
1505
+ }
1506
+
1507
+ if( (startOverDiff <= 0 || mainScrollDiff > 0) && _getNumItems() > 1 ) {
1508
+ newMainScrollPos = newMainScrollPosition;
1509
+
1510
+ if(mainScrollDiff > 0 && newMainScrollPosition < _startMainScrollPos.x) {
1511
+ newMainScrollPos = _startMainScrollPos.x;
1512
+ }
1513
+
1514
+ } else {
1515
+ if(_currPanBounds.min.x !== _currPanBounds.max.x) {
1516
+ newPanPos = newOffset;
1517
+ }
1518
+ }
1519
+
1520
+ }
1521
+
1522
+
1523
+ //
1524
+ }
1525
+
1526
+ if(axis === 'x') {
1527
+
1528
+ if(newMainScrollPos !== undefined) {
1529
+ _moveMainScroll(newMainScrollPos, true);
1530
+ if(newMainScrollPos === _startMainScrollPos.x) {
1531
+ _mainScrollShifted = false;
1532
+ } else {
1533
+ _mainScrollShifted = true;
1534
+ }
1535
+ }
1536
+
1537
+ if(_currPanBounds.min.x !== _currPanBounds.max.x) {
1538
+ if(newPanPos !== undefined) {
1539
+ _panOffset.x = newPanPos;
1540
+ } else if(!_mainScrollShifted) {
1541
+ _panOffset.x += delta.x * panFriction;
1542
+ }
1543
+ }
1544
+
1545
+ return newMainScrollPos !== undefined;
1546
+ }
1547
+
1548
+ }
1549
+
1550
+ if(!_mainScrollAnimating) {
1551
+
1552
+ if(!_mainScrollShifted) {
1553
+ if(_currZoomLevel > self.currItem.fitRatio) {
1554
+ _panOffset[axis] += delta[axis] * panFriction;
1555
+
1556
+ }
1557
+ }
1558
+
1559
+
1560
+ }
1561
+
1562
+ },
1563
+
1564
+ // Pointerdown/touchstart/mousedown handler
1565
+ _onDragStart = function(e) {
1566
+
1567
+ // Allow dragging only via left mouse button.
1568
+ // As this handler is not added in IE8 - we ignore e.which
1569
+ //
1570
+ // http://www.quirksmode.org/js/events_properties.html
1571
+ // https://developer.mozilla.org/en-US/docs/Web/API/event.button
1572
+ if(e.type === 'mousedown' && e.button > 0 ) {
1573
+ return;
1574
+ }
1575
+
1576
+ if(_initialZoomRunning) {
1577
+ e.preventDefault();
1578
+ return;
1579
+ }
1580
+
1581
+ if(_oldAndroidTouchEndTimeout && e.type === 'mousedown') {
1582
+ return;
1583
+ }
1584
+
1585
+ if(_preventDefaultEventBehaviour(e, true)) {
1586
+ e.preventDefault();
1587
+ }
1588
+
1589
+
1590
+
1591
+ _shout('pointerDown');
1592
+
1593
+ if(_pointerEventEnabled) {
1594
+ var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
1595
+ if(pointerIndex < 0) {
1596
+ pointerIndex = _currPointers.length;
1597
+ }
1598
+ _currPointers[pointerIndex] = {x:e.pageX, y:e.pageY, id: e.pointerId};
1599
+ }
1600
+
1601
+
1602
+
1603
+ var startPointsList = _getTouchPoints(e),
1604
+ numPoints = startPointsList.length;
1605
+
1606
+ _currentPoints = null;
1607
+
1608
+ _stopAllAnimations();
1609
+
1610
+ // init drag
1611
+ if(!_isDragging || numPoints === 1) {
1612
+
1613
+
1614
+
1615
+ _isDragging = _isFirstMove = true;
1616
+ framework.bind(window, _upMoveEvents, self);
1617
+
1618
+ _isZoomingIn =
1619
+ _wasOverInitialZoom =
1620
+ _opacityChanged =
1621
+ _verticalDragInitiated =
1622
+ _mainScrollShifted =
1623
+ _moved =
1624
+ _isMultitouch =
1625
+ _zoomStarted = false;
1626
+
1627
+ _direction = null;
1628
+
1629
+ _shout('firstTouchStart', startPointsList);
1630
+
1631
+ _equalizePoints(_startPanOffset, _panOffset);
1632
+
1633
+ _currPanDist.x = _currPanDist.y = 0;
1634
+ _equalizePoints(_currPoint, startPointsList[0]);
1635
+ _equalizePoints(_startPoint, _currPoint);
1636
+
1637
+ //_equalizePoints(_startMainScrollPos, _mainScrollPos);
1638
+ _startMainScrollPos.x = _slideSize.x * _currPositionIndex;
1639
+
1640
+ _posPoints = [{
1641
+ x: _currPoint.x,
1642
+ y: _currPoint.y
1643
+ }];
1644
+
1645
+ _gestureCheckSpeedTime = _gestureStartTime = _getCurrentTime();
1646
+
1647
+ //_mainScrollAnimationEnd(true);
1648
+ _calculatePanBounds( _currZoomLevel, true );
1649
+
1650
+ // Start rendering
1651
+ _stopDragUpdateLoop();
1652
+ _dragUpdateLoop();
1653
+
1654
+ }
1655
+
1656
+ // init zoom
1657
+ if(!_isZooming && numPoints > 1 && !_mainScrollAnimating && !_mainScrollShifted) {
1658
+ _startZoomLevel = _currZoomLevel;
1659
+ _zoomStarted = false; // true if zoom changed at least once
1660
+
1661
+ _isZooming = _isMultitouch = true;
1662
+ _currPanDist.y = _currPanDist.x = 0;
1663
+
1664
+ _equalizePoints(_startPanOffset, _panOffset);
1665
+
1666
+ _equalizePoints(p, startPointsList[0]);
1667
+ _equalizePoints(p2, startPointsList[1]);
1668
+
1669
+ _findCenterOfPoints(p, p2, _currCenterPoint);
1670
+
1671
+ _midZoomPoint.x = Math.abs(_currCenterPoint.x) - _panOffset.x;
1672
+ _midZoomPoint.y = Math.abs(_currCenterPoint.y) - _panOffset.y;
1673
+ _currPointsDistance = _startPointsDistance = _calculatePointsDistance(p, p2);
1674
+ }
1675
+
1676
+
1677
+ },
1678
+
1679
+ // Pointermove/touchmove/mousemove handler
1680
+ _onDragMove = function(e) {
1681
+
1682
+ e.preventDefault();
1683
+
1684
+ if(_pointerEventEnabled) {
1685
+ var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
1686
+ if(pointerIndex > -1) {
1687
+ var p = _currPointers[pointerIndex];
1688
+ p.x = e.pageX;
1689
+ p.y = e.pageY;
1690
+ }
1691
+ }
1692
+
1693
+ if(_isDragging) {
1694
+ var touchesList = _getTouchPoints(e);
1695
+ if(!_direction && !_moved && !_isZooming) {
1696
+ var diff = Math.abs(touchesList[0].x - _currPoint.x) - Math.abs(touchesList[0].y - _currPoint.y);
1697
+ // check the direction of movement
1698
+ if(Math.abs(diff) >= DIRECTION_CHECK_OFFSET) {
1699
+ _direction = diff > 0 ? 'h' : 'v';
1700
+ _currentPoints = touchesList;
1701
+ }
1702
+ } else {
1703
+ _currentPoints = touchesList;
1704
+ }
1705
+ }
1706
+ },
1707
+ //
1708
+ _renderMovement = function() {
1709
+
1710
+ if(!_currentPoints) {
1711
+ return;
1712
+ }
1713
+
1714
+ var numPoints = _currentPoints.length;
1715
+
1716
+ if(numPoints === 0) {
1717
+ return;
1718
+ }
1719
+
1720
+ _equalizePoints(p, _currentPoints[0]);
1721
+
1722
+ delta.x = p.x - _currPoint.x;
1723
+ delta.y = p.y - _currPoint.y;
1724
+
1725
+ if(_isZooming && numPoints > 1) {
1726
+ // Handle behaviour for more than 1 point
1727
+
1728
+ _currPoint.x = p.x;
1729
+ _currPoint.y = p.y;
1730
+
1731
+ // check if one of two points changed
1732
+ if( !delta.x && !delta.y && _isEqualPoints(_currentPoints[1], p2) ) {
1733
+ return;
1734
+ }
1735
+
1736
+ _equalizePoints(p2, _currentPoints[1]);
1737
+
1738
+
1739
+ if(!_zoomStarted) {
1740
+ _zoomStarted = true;
1741
+ _shout('zoomGestureStarted');
1742
+ }
1743
+
1744
+ // Distance between two points
1745
+ var pointsDistance = _calculatePointsDistance(p,p2);
1746
+
1747
+ var zoomLevel = _calculateZoomLevel(pointsDistance);
1748
+
1749
+ // slightly over the of initial zoom level
1750
+ if(zoomLevel > self.currItem.initialZoomLevel + self.currItem.initialZoomLevel / 15) {
1751
+ _wasOverInitialZoom = true;
1752
+ }
1753
+
1754
+ // Apply the friction if zoom level is out of the bounds
1755
+ var zoomFriction = 1;
1756
+ if ( zoomLevel < self.currItem.minZoom ) {
1757
+
1758
+ if(_options.pinchToClose && !_wasOverInitialZoom && _startZoomLevel <= self.currItem.initialZoomLevel) {
1759
+ // fade out background if zooming out
1760
+ var minusDiff = self.currItem.minZoom - zoomLevel;
1761
+ var percent = 1 - minusDiff / (self.currItem.minZoom / 1.2);
1762
+
1763
+ _applyBgOpacity(percent);
1764
+ _shout('onPinchClose', percent);
1765
+ _opacityChanged = true;
1766
+ } else {
1767
+ zoomFriction = (self.currItem.minZoom - zoomLevel) / (self.currItem.minZoom);
1768
+ if(zoomFriction > 1) {
1769
+ zoomFriction = 1;
1770
+ }
1771
+ zoomLevel = self.currItem.minZoom - (zoomFriction) * (self.currItem.minZoom / 3);
1772
+ }
1773
+
1774
+ } else if ( zoomLevel > self.currItem.maxZoom ) {
1775
+ // 1.5 - extra zoom level above the max. E.g. if max is x6, real max 6 + 1.5 = 7.5
1776
+ zoomFriction = (zoomLevel - self.currItem.maxZoom) / ( self.currItem.minZoom * 6 );
1777
+ if(zoomFriction > 1) {
1778
+ zoomFriction = 1;
1779
+ }
1780
+ zoomLevel = self.currItem.maxZoom + (zoomFriction) * (self.currItem.minZoom);
1781
+ }
1782
+
1783
+ if(zoomFriction < 0) {
1784
+ zoomFriction = 0;
1785
+ }
1786
+
1787
+ // distance between touch points after friction is applied
1788
+ _currPointsDistance = pointsDistance;
1789
+
1790
+ // _centerPoint - The point in the middle of two pointers
1791
+ _findCenterOfPoints(p, p2, _centerPoint);
1792
+
1793
+ // paning with two pointers pressed
1794
+ _currPanDist.x += _centerPoint.x - _currCenterPoint.x;
1795
+ _currPanDist.y += _centerPoint.y - _currCenterPoint.y;
1796
+ _equalizePoints(_currCenterPoint, _centerPoint);
1797
+
1798
+ _panOffset.x = _calculatePanOffset('x', zoomLevel);
1799
+ _panOffset.y = _calculatePanOffset('y', zoomLevel);
1800
+
1801
+ _isZoomingIn = zoomLevel > _currZoomLevel;
1802
+ _currZoomLevel = zoomLevel;
1803
+ _applyCurrentZoomPan();
1804
+
1805
+ } else {
1806
+
1807
+ // handle behaviour for one point (dragging or panning)
1808
+
1809
+ if(!_direction) {
1810
+ return;
1811
+ }
1812
+
1813
+ if(_isFirstMove) {
1814
+ _isFirstMove = false;
1815
+
1816
+ // subtract drag distance that was used during the detection direction
1817
+
1818
+ if( Math.abs(delta.x) >= DIRECTION_CHECK_OFFSET) {
1819
+ delta.x -= _currentPoints[0].x - _startPoint.x;
1820
+ }
1821
+
1822
+ if( Math.abs(delta.y) >= DIRECTION_CHECK_OFFSET) {
1823
+ delta.y -= _currentPoints[0].y - _startPoint.y;
1824
+ }
1825
+ }
1826
+
1827
+ _currPoint.x = p.x;
1828
+ _currPoint.y = p.y;
1829
+
1830
+ // do nothing if pointers position hasn't changed
1831
+ if(delta.x === 0 && delta.y === 0) {
1832
+ return;
1833
+ }
1834
+
1835
+ if(_direction === 'v' && _options.closeOnVerticalDrag) {
1836
+ if(!_canPan()) {
1837
+
1838
+ _currPanDist.y += delta.y;
1839
+ _panOffset.y += delta.y;
1840
+
1841
+ var opacityRatio = _calculateVerticalDragOpacityRatio();
1842
+
1843
+ _verticalDragInitiated = true;
1844
+ _shout('onVerticalDrag', opacityRatio);
1845
+
1846
+ _applyBgOpacity(opacityRatio);
1847
+ _applyCurrentZoomPan();
1848
+ return ;
1849
+ }
1850
+ }
1851
+
1852
+ _pushPosPoint(_getCurrentTime(), p.x, p.y);
1853
+
1854
+ _moved = true;
1855
+ _currPanBounds = self.currItem.bounds;
1856
+
1857
+ var mainScrollChanged = _panOrMoveMainScroll('x', delta);
1858
+ if(!mainScrollChanged) {
1859
+ _panOrMoveMainScroll('y', delta);
1860
+ }
1861
+
1862
+ _applyCurrentZoomPan();
1863
+ }
1864
+
1865
+ },
1866
+
1867
+ // Pointerup/pointercancel/touchend/touchcancel/mouseup event handler
1868
+ _onDragRelease = function(e) {
1869
+
1870
+ if(_features.isOldAndroid ) {
1871
+
1872
+ if(_oldAndroidTouchEndTimeout && e.type === 'mouseup') {
1873
+ return;
1874
+ }
1875
+
1876
+ // on Android (v4.1, 4.2, 4.3 & possibly older)
1877
+ // ghost mousedown/up event isn't preventable via e.preventDefault,
1878
+ // which causes fake mousedown event
1879
+ // so we block mousedown/up for 600ms
1880
+ if( e.type.indexOf('touch') > -1 ) {
1881
+ clearTimeout(_oldAndroidTouchEndTimeout);
1882
+ _oldAndroidTouchEndTimeout = setTimeout(function() {
1883
+ _oldAndroidTouchEndTimeout = 0;
1884
+ }, 600);
1885
+ }
1886
+
1887
+ }
1888
+
1889
+ _shout('pointerUp');
1890
+
1891
+ if(_preventDefaultEventBehaviour(e, false)) {
1892
+ e.preventDefault();
1893
+ }
1894
+
1895
+ var releasePoint;
1896
+
1897
+ if(_pointerEventEnabled) {
1898
+ var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
1899
+
1900
+ if(pointerIndex > -1) {
1901
+ releasePoint = _currPointers.splice(pointerIndex, 1)[0];
1902
+
1903
+ if(navigator.pointerEnabled) {
1904
+ releasePoint.type = e.pointerType || 'mouse';
1905
+ } else {
1906
+ var MSPOINTER_TYPES = {
1907
+ 4: 'mouse', // event.MSPOINTER_TYPE_MOUSE
1908
+ 2: 'touch', // event.MSPOINTER_TYPE_TOUCH
1909
+ 3: 'pen' // event.MSPOINTER_TYPE_PEN
1910
+ };
1911
+ releasePoint.type = MSPOINTER_TYPES[e.pointerType];
1912
+
1913
+ if(!releasePoint.type) {
1914
+ releasePoint.type = e.pointerType || 'mouse';
1915
+ }
1916
+ }
1917
+
1918
+ }
1919
+ }
1920
+
1921
+ var touchList = _getTouchPoints(e),
1922
+ gestureType,
1923
+ numPoints = touchList.length;
1924
+
1925
+ if(e.type === 'mouseup') {
1926
+ numPoints = 0;
1927
+ }
1928
+
1929
+ // Do nothing if there were 3 touch points or more
1930
+ if(numPoints === 2) {
1931
+ _currentPoints = null;
1932
+ return true;
1933
+ }
1934
+
1935
+ // if second pointer released
1936
+ if(numPoints === 1) {
1937
+ _equalizePoints(_startPoint, touchList[0]);
1938
+ }
1939
+
1940
+
1941
+ // pointer hasn't moved, send "tap release" point
1942
+ if(numPoints === 0 && !_direction && !_mainScrollAnimating) {
1943
+ if(!releasePoint) {
1944
+ if(e.type === 'mouseup') {
1945
+ releasePoint = {x: e.pageX, y: e.pageY, type:'mouse'};
1946
+ } else if(e.changedTouches && e.changedTouches[0]) {
1947
+ releasePoint = {x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY, type:'touch'};
1948
+ }
1949
+ }
1950
+
1951
+ _shout('touchRelease', e, releasePoint);
1952
+ }
1953
+
1954
+ // Difference in time between releasing of two last touch points (zoom gesture)
1955
+ var releaseTimeDiff = -1;
1956
+
1957
+ // Gesture completed, no pointers left
1958
+ if(numPoints === 0) {
1959
+ _isDragging = false;
1960
+ framework.unbind(window, _upMoveEvents, self);
1961
+
1962
+ _stopDragUpdateLoop();
1963
+
1964
+ if(_isZooming) {
1965
+ // Two points released at the same time
1966
+ releaseTimeDiff = 0;
1967
+ } else if(_lastReleaseTime !== -1) {
1968
+ releaseTimeDiff = _getCurrentTime() - _lastReleaseTime;
1969
+ }
1970
+ }
1971
+ _lastReleaseTime = numPoints === 1 ? _getCurrentTime() : -1;
1972
+
1973
+ if(releaseTimeDiff !== -1 && releaseTimeDiff < 150) {
1974
+ gestureType = 'zoom';
1975
+ } else {
1976
+ gestureType = 'swipe';
1977
+ }
1978
+
1979
+ if(_isZooming && numPoints < 2) {
1980
+ _isZooming = false;
1981
+
1982
+ // Only second point released
1983
+ if(numPoints === 1) {
1984
+ gestureType = 'zoomPointerUp';
1985
+ }
1986
+ _shout('zoomGestureEnded');
1987
+ }
1988
+
1989
+ _currentPoints = null;
1990
+ if(!_moved && !_zoomStarted && !_mainScrollAnimating && !_verticalDragInitiated) {
1991
+ // nothing to animate
1992
+ return;
1993
+ }
1994
+
1995
+ _stopAllAnimations();
1996
+
1997
+
1998
+ if(!_releaseAnimData) {
1999
+ _releaseAnimData = _initDragReleaseAnimationData();
2000
+ }
2001
+
2002
+ _releaseAnimData.calculateSwipeSpeed('x');
2003
+
2004
+
2005
+ if(_verticalDragInitiated) {
2006
+
2007
+ var opacityRatio = _calculateVerticalDragOpacityRatio();
2008
+
2009
+ if(opacityRatio < 0.6) {
2010
+ self.close();
2011
+ } else {
2012
+ var initalPanY = _panOffset.y,
2013
+ initialBgOpacity = _bgOpacity;
2014
+
2015
+ _animateProp('verticalDrag', 0, 1, 300, framework.easing.cubic.out, function(now) {
2016
+
2017
+ _panOffset.y = (self.currItem.initialPosition.y - initalPanY) * now + initalPanY;
2018
+
2019
+ _applyBgOpacity( (1 - initialBgOpacity) * now + initialBgOpacity );
2020
+ _applyCurrentZoomPan();
2021
+ });
2022
+
2023
+ _shout('onVerticalDrag', 1);
2024
+ }
2025
+
2026
+ return;
2027
+ }
2028
+
2029
+
2030
+ // main scroll
2031
+ if( (_mainScrollShifted || _mainScrollAnimating) && numPoints === 0) {
2032
+ var itemChanged = _finishSwipeMainScrollGesture(gestureType, _releaseAnimData);
2033
+ if(itemChanged) {
2034
+ return;
2035
+ }
2036
+ gestureType = 'zoomPointerUp';
2037
+ }
2038
+
2039
+ // prevent zoom/pan animation when main scroll animation runs
2040
+ if(_mainScrollAnimating) {
2041
+ return;
2042
+ }
2043
+
2044
+ // Complete simple zoom gesture (reset zoom level if it's out of the bounds)
2045
+ if(gestureType !== 'swipe') {
2046
+ _completeZoomGesture();
2047
+ return;
2048
+ }
2049
+
2050
+ // Complete pan gesture if main scroll is not shifted, and it's possible to pan current image
2051
+ if(!_mainScrollShifted && _currZoomLevel > self.currItem.fitRatio) {
2052
+ _completePanGesture(_releaseAnimData);
2053
+ }
2054
+ },
2055
+
2056
+
2057
+ // Returns object with data about gesture
2058
+ // It's created only once and then reused
2059
+ _initDragReleaseAnimationData = function() {
2060
+ // temp local vars
2061
+ var lastFlickDuration,
2062
+ tempReleasePos;
2063
+
2064
+ // s = this
2065
+ var s = {
2066
+ lastFlickOffset: {},
2067
+ lastFlickDist: {},
2068
+ lastFlickSpeed: {},
2069
+ slowDownRatio: {},
2070
+ slowDownRatioReverse: {},
2071
+ speedDecelerationRatio: {},
2072
+ speedDecelerationRatioAbs: {},
2073
+ distanceOffset: {},
2074
+ backAnimDestination: {},
2075
+ backAnimStarted: {},
2076
+ calculateSwipeSpeed: function(axis) {
2077
+
2078
+
2079
+ if( _posPoints.length > 1) {
2080
+ lastFlickDuration = _getCurrentTime() - _gestureCheckSpeedTime + 50;
2081
+ tempReleasePos = _posPoints[_posPoints.length-2][axis];
2082
+ } else {
2083
+ lastFlickDuration = _getCurrentTime() - _gestureStartTime; // total gesture duration
2084
+ tempReleasePos = _startPoint[axis];
2085
+ }
2086
+ s.lastFlickOffset[axis] = _currPoint[axis] - tempReleasePos;
2087
+ s.lastFlickDist[axis] = Math.abs(s.lastFlickOffset[axis]);
2088
+ if(s.lastFlickDist[axis] > 20) {
2089
+ s.lastFlickSpeed[axis] = s.lastFlickOffset[axis] / lastFlickDuration;
2090
+ } else {
2091
+ s.lastFlickSpeed[axis] = 0;
2092
+ }
2093
+ if( Math.abs(s.lastFlickSpeed[axis]) < 0.1 ) {
2094
+ s.lastFlickSpeed[axis] = 0;
2095
+ }
2096
+
2097
+ s.slowDownRatio[axis] = 0.95;
2098
+ s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
2099
+ s.speedDecelerationRatio[axis] = 1;
2100
+ },
2101
+
2102
+ calculateOverBoundsAnimOffset: function(axis, speed) {
2103
+ if(!s.backAnimStarted[axis]) {
2104
+
2105
+ if(_panOffset[axis] > _currPanBounds.min[axis]) {
2106
+ s.backAnimDestination[axis] = _currPanBounds.min[axis];
2107
+
2108
+ } else if(_panOffset[axis] < _currPanBounds.max[axis]) {
2109
+ s.backAnimDestination[axis] = _currPanBounds.max[axis];
2110
+ }
2111
+
2112
+ if(s.backAnimDestination[axis] !== undefined) {
2113
+ s.slowDownRatio[axis] = 0.7;
2114
+ s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
2115
+ if(s.speedDecelerationRatioAbs[axis] < 0.05) {
2116
+
2117
+ s.lastFlickSpeed[axis] = 0;
2118
+ s.backAnimStarted[axis] = true;
2119
+
2120
+ _animateProp('bounceZoomPan'+axis,_panOffset[axis],
2121
+ s.backAnimDestination[axis],
2122
+ speed || 300,
2123
+ framework.easing.sine.out,
2124
+ function(pos) {
2125
+ _panOffset[axis] = pos;
2126
+ _applyCurrentZoomPan();
2127
+ }
2128
+ );
2129
+
2130
+ }
2131
+ }
2132
+ }
2133
+ },
2134
+
2135
+ // Reduces the speed by slowDownRatio (per 10ms)
2136
+ calculateAnimOffset: function(axis) {
2137
+ if(!s.backAnimStarted[axis]) {
2138
+ s.speedDecelerationRatio[axis] = s.speedDecelerationRatio[axis] * (s.slowDownRatio[axis] +
2139
+ s.slowDownRatioReverse[axis] -
2140
+ s.slowDownRatioReverse[axis] * s.timeDiff / 10);
2141
+
2142
+ s.speedDecelerationRatioAbs[axis] = Math.abs(s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis]);
2143
+ s.distanceOffset[axis] = s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis] * s.timeDiff;
2144
+ _panOffset[axis] += s.distanceOffset[axis];
2145
+ }
2146
+ },
2147
+
2148
+ panAnimLoop: function() {
2149
+ if ( _animations.zoomPan ) {
2150
+ _animations.zoomPan.raf = _requestAF(s.panAnimLoop);
2151
+
2152
+ s.now = _getCurrentTime();
2153
+ s.timeDiff = s.now - s.lastNow;
2154
+ s.lastNow = s.now;
2155
+
2156
+ s.calculateAnimOffset('x');
2157
+ s.calculateAnimOffset('y');
2158
+
2159
+ _applyCurrentZoomPan();
2160
+
2161
+ s.calculateOverBoundsAnimOffset('x');
2162
+ s.calculateOverBoundsAnimOffset('y');
2163
+
2164
+
2165
+ if (s.speedDecelerationRatioAbs.x < 0.05 && s.speedDecelerationRatioAbs.y < 0.05) {
2166
+ _stopAnimation('zoomPan');
2167
+ return;
2168
+ }
2169
+ }
2170
+
2171
+ }
2172
+ };
2173
+ return s;
2174
+ },
2175
+
2176
+ _completePanGesture = function(animData) {
2177
+ // calculate swipe speed for Y axis (paanning)
2178
+ animData.calculateSwipeSpeed('y');
2179
+
2180
+ _currPanBounds = self.currItem.bounds;
2181
+
2182
+ animData.backAnimDestination = {};
2183
+ animData.backAnimStarted = {};
2184
+
2185
+ // Avoid acceleration animation if speed is too low
2186
+ if(Math.abs(animData.lastFlickSpeed.x) <= 0.05 && Math.abs(animData.lastFlickSpeed.y) <= 0.05 ) {
2187
+ animData.speedDecelerationRatioAbs.x = animData.speedDecelerationRatioAbs.y = 0;
2188
+
2189
+ // Run pan drag release animation. E.g. if you drag image and release finger without momentum.
2190
+ animData.calculateOverBoundsAnimOffset('x');
2191
+ animData.calculateOverBoundsAnimOffset('y');
2192
+ return true;
2193
+ }
2194
+
2195
+ // Animation loop that controls the acceleration after pan gesture ends
2196
+ _registerStartAnimation('zoomPan');
2197
+ animData.lastNow = _getCurrentTime();
2198
+ animData.panAnimLoop();
2199
+ },
2200
+
2201
+
2202
+ _finishSwipeMainScrollGesture = function(gestureType, _releaseAnimData) {
2203
+ var itemChanged;
2204
+ if(!_mainScrollAnimating) {
2205
+ _currZoomedItemIndex = _currentItemIndex;
2206
+ }
2207
+
2208
+
2209
+
2210
+ var itemsDiff;
2211
+
2212
+ if(gestureType === 'swipe') {
2213
+ var totalShiftDist = _currPoint.x - _startPoint.x,
2214
+ isFastLastFlick = _releaseAnimData.lastFlickDist.x < 10;
2215
+
2216
+ // if container is shifted for more than MIN_SWIPE_DISTANCE,
2217
+ // and last flick gesture was in right direction
2218
+ if(totalShiftDist > MIN_SWIPE_DISTANCE &&
2219
+ (isFastLastFlick || _releaseAnimData.lastFlickOffset.x > 20) ) {
2220
+ // go to prev item
2221
+ itemsDiff = -1;
2222
+ } else if(totalShiftDist < -MIN_SWIPE_DISTANCE &&
2223
+ (isFastLastFlick || _releaseAnimData.lastFlickOffset.x < -20) ) {
2224
+ // go to next item
2225
+ itemsDiff = 1;
2226
+ }
2227
+ }
2228
+
2229
+ var nextCircle;
2230
+
2231
+ if(itemsDiff) {
2232
+
2233
+ _currentItemIndex += itemsDiff;
2234
+
2235
+ if(_currentItemIndex < 0) {
2236
+ _currentItemIndex = _options.loop ? _getNumItems()-1 : 0;
2237
+ nextCircle = true;
2238
+ } else if(_currentItemIndex >= _getNumItems()) {
2239
+ _currentItemIndex = _options.loop ? 0 : _getNumItems()-1;
2240
+ nextCircle = true;
2241
+ }
2242
+
2243
+ if(!nextCircle || _options.loop) {
2244
+ _indexDiff += itemsDiff;
2245
+ _currPositionIndex -= itemsDiff;
2246
+ itemChanged = true;
2247
+ }
2248
+
2249
+
2250
+
2251
+ }
2252
+
2253
+ var animateToX = _slideSize.x * _currPositionIndex;
2254
+ var animateToDist = Math.abs( animateToX - _mainScrollPos.x );
2255
+ var finishAnimDuration;
2256
+
2257
+
2258
+ if(!itemChanged && animateToX > _mainScrollPos.x !== _releaseAnimData.lastFlickSpeed.x > 0) {
2259
+ // "return to current" duration, e.g. when dragging from slide 0 to -1
2260
+ finishAnimDuration = 333;
2261
+ } else {
2262
+ finishAnimDuration = Math.abs(_releaseAnimData.lastFlickSpeed.x) > 0 ?
2263
+ animateToDist / Math.abs(_releaseAnimData.lastFlickSpeed.x) :
2264
+ 333;
2265
+
2266
+ finishAnimDuration = Math.min(finishAnimDuration, 400);
2267
+ finishAnimDuration = Math.max(finishAnimDuration, 250);
2268
+ }
2269
+
2270
+ if(_currZoomedItemIndex === _currentItemIndex) {
2271
+ itemChanged = false;
2272
+ }
2273
+
2274
+ _mainScrollAnimating = true;
2275
+
2276
+ _animateProp('mainScroll', _mainScrollPos.x, animateToX, finishAnimDuration, framework.easing.cubic.out,
2277
+ _moveMainScroll,
2278
+ function() {
2279
+ _stopAllAnimations();
2280
+ _mainScrollAnimating = false;
2281
+ _currZoomedItemIndex = -1;
2282
+
2283
+ if(itemChanged || _currZoomedItemIndex !== _currentItemIndex) {
2284
+ self.updateCurrItem();
2285
+ }
2286
+
2287
+ _shout('mainScrollAnimComplete');
2288
+ }
2289
+ );
2290
+
2291
+ if(itemChanged) {
2292
+ self.updateCurrItem(true);
2293
+ }
2294
+
2295
+ return itemChanged;
2296
+ },
2297
+
2298
+ _calculateZoomLevel = function(touchesDistance) {
2299
+ return 1 / _startPointsDistance * touchesDistance * _startZoomLevel;
2300
+ },
2301
+
2302
+ // Resets zoom if it's out of bounds
2303
+ _completeZoomGesture = function() {
2304
+ var destZoomLevel = _currZoomLevel;
2305
+
2306
+ if ( _currZoomLevel < self.currItem.minZoom ) {
2307
+ destZoomLevel = self.currItem.minZoom;
2308
+ } else if ( _currZoomLevel > self.currItem.maxZoom ) {
2309
+ destZoomLevel = self.currItem.maxZoom;
2310
+ }
2311
+
2312
+ var destOpacity = 1,
2313
+ onUpdate,
2314
+ initialOpacity = _bgOpacity;
2315
+
2316
+ if(_opacityChanged && !_isZoomingIn && !_wasOverInitialZoom && _currZoomLevel < self.currItem.minZoom) {
2317
+ //_closedByScroll = true;
2318
+ self.close();
2319
+ return true;
2320
+ }
2321
+
2322
+ if(_opacityChanged) {
2323
+ onUpdate = function(now) {
2324
+
2325
+ _applyBgOpacity( (destOpacity - initialOpacity) * now + initialOpacity );
2326
+
2327
+ };
2328
+ }
2329
+
2330
+ self.zoomTo(destZoomLevel, 0, 300, framework.easing.cubic.out, onUpdate);
2331
+ return true;
2332
+ };
2333
+
2334
+
2335
+ _registerModule('Gestures', {
2336
+ publicMethods: {
2337
+
2338
+ initGestures: function() {
2339
+
2340
+ // helper function that builds touch/pointer/mouse events
2341
+ var addEventNames = function(pref, down, move, up, cancel) {
2342
+ _dragStartEvent = pref + down;
2343
+ _dragMoveEvent = pref + move;
2344
+ _dragEndEvent = pref + up;
2345
+ if(cancel) {
2346
+ _dragCancelEvent = pref + cancel;
2347
+ } else {
2348
+ _dragCancelEvent = '';
2349
+ }
2350
+ };
2351
+
2352
+ _pointerEventEnabled = _features.pointerEvent;
2353
+ if(_pointerEventEnabled && _features.touch) {
2354
+ // we don't need touch events, if browser supports pointer events
2355
+ _features.touch = false;
2356
+ }
2357
+
2358
+ if(_pointerEventEnabled) {
2359
+ if(navigator.pointerEnabled) {
2360
+ addEventNames('pointer', 'down', 'move', 'up', 'cancel');
2361
+ } else {
2362
+ // IE10 pointer events are case-sensitive
2363
+ addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel');
2364
+ }
2365
+ } else if(_features.touch) {
2366
+ addEventNames('touch', 'start', 'move', 'end', 'cancel');
2367
+ _likelyTouchDevice = true;
2368
+ } else {
2369
+ addEventNames('mouse', 'down', 'move', 'up');
2370
+ }
2371
+
2372
+ _upMoveEvents = _dragMoveEvent + ' ' + _dragEndEvent + ' ' + _dragCancelEvent;
2373
+ _downEvents = _dragStartEvent;
2374
+
2375
+ if(_pointerEventEnabled && !_likelyTouchDevice) {
2376
+ _likelyTouchDevice = (navigator.maxTouchPoints > 1) || (navigator.msMaxTouchPoints > 1);
2377
+ }
2378
+ // make variable public
2379
+ self.likelyTouchDevice = _likelyTouchDevice;
2380
+
2381
+ _globalEventHandlers[_dragStartEvent] = _onDragStart;
2382
+ _globalEventHandlers[_dragMoveEvent] = _onDragMove;
2383
+ _globalEventHandlers[_dragEndEvent] = _onDragRelease; // the Kraken
2384
+
2385
+ if(_dragCancelEvent) {
2386
+ _globalEventHandlers[_dragCancelEvent] = _globalEventHandlers[_dragEndEvent];
2387
+ }
2388
+
2389
+ // Bind mouse events on device with detected hardware touch support, in case it supports multiple types of input.
2390
+ if(_features.touch) {
2391
+ _downEvents += ' mousedown';
2392
+ _upMoveEvents += ' mousemove mouseup';
2393
+ _globalEventHandlers.mousedown = _globalEventHandlers[_dragStartEvent];
2394
+ _globalEventHandlers.mousemove = _globalEventHandlers[_dragMoveEvent];
2395
+ _globalEventHandlers.mouseup = _globalEventHandlers[_dragEndEvent];
2396
+ }
2397
+
2398
+ if(!_likelyTouchDevice) {
2399
+ // don't allow pan to next slide from zoomed state on Desktop
2400
+ _options.allowPanToNext = false;
2401
+ }
2402
+ }
2403
+
2404
+ }
2405
+ });
2406
+
2407
+ /*>>gestures*/
2408
+
2409
+ /*>>show-hide-transition*/
2410
+ /**
2411
+ * show-hide-transition.js:
2412
+ *
2413
+ * Manages initial opening or closing transition.
2414
+ *
2415
+ * If you're not planning to use transition for gallery at all,
2416
+ * you may set options hideAnimationDuration and showAnimationDuration to 0,
2417
+ * and just delete startAnimation function.
2418
+ *
2419
+ */
2420
+
2421
+
2422
+ var _showOrHideTimeout,
2423
+ _showOrHide = function(item, img, out, completeFn) {
2424
+
2425
+ if(_showOrHideTimeout) {
2426
+ clearTimeout(_showOrHideTimeout);
2427
+ }
2428
+
2429
+ _initialZoomRunning = true;
2430
+ _initialContentSet = true;
2431
+
2432
+ // dimensions of small thumbnail {x:,y:,w:}.
2433
+ // Height is optional, as calculated based on large image.
2434
+ var thumbBounds;
2435
+ if(item.initialLayout) {
2436
+ thumbBounds = item.initialLayout;
2437
+ item.initialLayout = null;
2438
+ } else {
2439
+ thumbBounds = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex);
2440
+ }
2441
+
2442
+ var duration = out ? _options.hideAnimationDuration : _options.showAnimationDuration;
2443
+
2444
+ var onComplete = function() {
2445
+ _stopAnimation('initialZoom');
2446
+ if(!out) {
2447
+ _applyBgOpacity(1);
2448
+ if(img) {
2449
+ img.style.display = 'block';
2450
+ }
2451
+ framework.addClass(template, 'pswp--animated-in');
2452
+ _shout('initialZoom' + (out ? 'OutEnd' : 'InEnd'));
2453
+ } else {
2454
+ self.template.removeAttribute('style');
2455
+ self.bg.removeAttribute('style');
2456
+ }
2457
+
2458
+ if(completeFn) {
2459
+ completeFn();
2460
+ }
2461
+ _initialZoomRunning = false;
2462
+ };
2463
+
2464
+ // if bounds aren't provided, just open gallery without animation
2465
+ if(!duration || !thumbBounds || thumbBounds.x === undefined) {
2466
+
2467
+ var finishWithoutAnimation = function() {
2468
+ _shout('initialZoom' + (out ? 'Out' : 'In') );
2469
+
2470
+ _currZoomLevel = item.initialZoomLevel;
2471
+ _equalizePoints(_panOffset, item.initialPosition );
2472
+ _applyCurrentZoomPan();
2473
+
2474
+ // no transition
2475
+ template.style.opacity = out ? 0 : 1;
2476
+ _applyBgOpacity(1);
2477
+
2478
+ onComplete();
2479
+ };
2480
+ finishWithoutAnimation();
2481
+
2482
+ return;
2483
+ }
2484
+
2485
+ var startAnimation = function() {
2486
+ var closeWithRaf = _closedByScroll,
2487
+ fadeEverything = !self.currItem.src || self.currItem.loadError || _options.showHideOpacity;
2488
+
2489
+ // apply hw-acceleration to image
2490
+ if(item.miniImg) {
2491
+ item.miniImg.style.webkitBackfaceVisibility = 'hidden';
2492
+ }
2493
+
2494
+ if(!out) {
2495
+ _currZoomLevel = thumbBounds.w / item.w;
2496
+ _panOffset.x = thumbBounds.x;
2497
+ _panOffset.y = thumbBounds.y - _initalWindowScrollY;
2498
+
2499
+ self[fadeEverything ? 'template' : 'bg'].style.opacity = 0.001;
2500
+ _applyCurrentZoomPan();
2501
+ }
2502
+
2503
+ _registerStartAnimation('initialZoom');
2504
+
2505
+ if(out && !closeWithRaf) {
2506
+ framework.removeClass(template, 'pswp--animated-in');
2507
+ }
2508
+
2509
+ if(fadeEverything) {
2510
+ if(out) {
2511
+ framework[ (closeWithRaf ? 'remove' : 'add') + 'Class' ](template, 'pswp--animate_opacity');
2512
+ } else {
2513
+ setTimeout(function() {
2514
+ framework.addClass(template, 'pswp--animate_opacity');
2515
+ }, 30);
2516
+ }
2517
+ }
2518
+
2519
+ _showOrHideTimeout = setTimeout(function() {
2520
+
2521
+ _shout('initialZoom' + (out ? 'Out' : 'In') );
2522
+
2523
+
2524
+ if(!out) {
2525
+
2526
+ // "in" animation always uses CSS transitions (instead of rAF).
2527
+ // CSS transition work faster here,
2528
+ // as developer may also want to animate other things,
2529
+ // like ui on top of sliding area, which can be animated just via CSS
2530
+
2531
+ _currZoomLevel = item.initialZoomLevel;
2532
+ _equalizePoints(_panOffset, item.initialPosition );
2533
+ _applyCurrentZoomPan();
2534
+ _applyBgOpacity(1);
2535
+
2536
+ if(fadeEverything) {
2537
+ template.style.opacity = 1;
2538
+ } else {
2539
+ _applyBgOpacity(1);
2540
+ }
2541
+
2542
+ _showOrHideTimeout = setTimeout(onComplete, duration + 20);
2543
+ } else {
2544
+
2545
+ // "out" animation uses rAF only when PhotoSwipe is closed by browser scroll, to recalculate position
2546
+ var destZoomLevel = thumbBounds.w / item.w,
2547
+ initialPanOffset = {
2548
+ x: _panOffset.x,
2549
+ y: _panOffset.y
2550
+ },
2551
+ initialZoomLevel = _currZoomLevel,
2552
+ scrollY = _initalWindowScrollY,
2553
+ initalBgOpacity = _bgOpacity,
2554
+ onUpdate = function(now) {
2555
+ if(_scrollChanged) {
2556
+ scrollY = framework.getScrollY();
2557
+ _scrollChanged = false;
2558
+ }
2559
+
2560
+ if(now === 1) {
2561
+ _currZoomLevel = destZoomLevel;
2562
+ _panOffset.x = thumbBounds.x;
2563
+ _panOffset.y = thumbBounds.y - scrollY;
2564
+ } else {
2565
+ _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
2566
+ _panOffset.x = (thumbBounds.x - initialPanOffset.x) * now + initialPanOffset.x;
2567
+ _panOffset.y = (thumbBounds.y - scrollY - initialPanOffset.y) * now + initialPanOffset.y;
2568
+ }
2569
+
2570
+ _applyCurrentZoomPan();
2571
+ if(fadeEverything) {
2572
+ template.style.opacity = 1 - now;
2573
+ } else {
2574
+ _applyBgOpacity( initalBgOpacity - now * initalBgOpacity );
2575
+ }
2576
+ };
2577
+
2578
+ if(closeWithRaf) {
2579
+ _animateProp('initialZoom', 0, 1, duration, framework.easing.cubic.out, onUpdate, onComplete);
2580
+ } else {
2581
+ onUpdate(1);
2582
+ _showOrHideTimeout = setTimeout(onComplete, duration + 20);
2583
+ }
2584
+ }
2585
+
2586
+ }, out ? 25 : 90); // Main purpose of this delay is to give browser time to paint and
2587
+ // create composite layers of PhotoSwipe UI parts (background, controls, caption, arrows).
2588
+ // Which avoids lag at the beginning of scale transition.
2589
+ };
2590
+ startAnimation();
2591
+
2592
+
2593
+ };
2594
+
2595
+ /*>>show-hide-transition*/
2596
+
2597
+ /*>>items-controller*/
2598
+ /**
2599
+ *
2600
+ * Controller manages gallery items, their dimensions, and their content.
2601
+ *
2602
+ */
2603
+
2604
+ var _items,
2605
+ _tempPanAreaSize = {},
2606
+ _imagesToAppendPool = [],
2607
+ _initialContentSet,
2608
+ _initialZoomRunning,
2609
+ _controllerDefaultOptions = {
2610
+ index: 0,
2611
+ errorMsg: '<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not be loaded.</div>',
2612
+ forceProgressiveLoading: false, // TODO
2613
+ preload: [1,1],
2614
+ getNumItemsFn: function() {
2615
+ return _items.length;
2616
+ }
2617
+ };
2618
+
2619
+
2620
+ var _getItemAt,
2621
+ _getNumItems,
2622
+ _initialIsLoop,
2623
+ _getZeroBounds = function() {
2624
+ return {
2625
+ center:{x:0,y:0},
2626
+ max:{x:0,y:0},
2627
+ min:{x:0,y:0}
2628
+ };
2629
+ },
2630
+ _calculateSingleItemPanBounds = function(item, realPanElementW, realPanElementH ) {
2631
+ var bounds = item.bounds;
2632
+
2633
+ // position of element when it's centered
2634
+ bounds.center.x = Math.round((_tempPanAreaSize.x - realPanElementW) / 2);
2635
+ bounds.center.y = Math.round((_tempPanAreaSize.y - realPanElementH) / 2) + item.vGap.top;
2636
+
2637
+ // maximum pan position
2638
+ bounds.max.x = (realPanElementW > _tempPanAreaSize.x) ?
2639
+ Math.round(_tempPanAreaSize.x - realPanElementW) :
2640
+ bounds.center.x;
2641
+
2642
+ bounds.max.y = (realPanElementH > _tempPanAreaSize.y) ?
2643
+ Math.round(_tempPanAreaSize.y - realPanElementH) + item.vGap.top :
2644
+ bounds.center.y;
2645
+
2646
+ // minimum pan position
2647
+ bounds.min.x = (realPanElementW > _tempPanAreaSize.x) ? 0 : bounds.center.x;
2648
+ bounds.min.y = (realPanElementH > _tempPanAreaSize.y) ? item.vGap.top : bounds.center.y;
2649
+ },
2650
+ _calculateItemSize = function(item, viewportSize, zoomLevel) {
2651
+
2652
+ if (item.src && !item.loadError) {
2653
+ var isInitial = !zoomLevel;
2654
+
2655
+ if(isInitial) {
2656
+ if(!item.vGap) {
2657
+ item.vGap = {top:0,bottom:0};
2658
+ }
2659
+ // allows overriding vertical margin for individual items
2660
+ _shout('parseVerticalMargin', item);
2661
+ }
2662
+
2663
+
2664
+ _tempPanAreaSize.x = viewportSize.x;
2665
+ _tempPanAreaSize.y = viewportSize.y - item.vGap.top - item.vGap.bottom;
2666
+
2667
+ if (isInitial) {
2668
+ var hRatio = _tempPanAreaSize.x / item.w;
2669
+ var vRatio = _tempPanAreaSize.y / item.h;
2670
+
2671
+ item.fitRatio = hRatio < vRatio ? hRatio : vRatio;
2672
+ item.fillRatio = hRatio > vRatio ? hRatio : vRatio;
2673
+
2674
+ var scaleMode = _options.scaleMode;
2675
+
2676
+ if (scaleMode === 'orig') {
2677
+ zoomLevel = 1;
2678
+ } else if (scaleMode === 'fit') {
2679
+ zoomLevel = item.fitRatio;
2680
+ } else if (scaleMode === 'fill') {
2681
+ zoomLevel = item.fillRatio;
2682
+ }
2683
+
2684
+ if (zoomLevel > 1) {
2685
+ zoomLevel = 1;
2686
+ }
2687
+ item.initialZoomLevel = zoomLevel;
2688
+ item.maxZoom = 2;
2689
+ item.minZoom = zoomLevel;
2690
+
2691
+ if(!item.bounds) {
2692
+ item.bounds = _getZeroBounds(); // reuse bounds object
2693
+ }
2694
+ }
2695
+
2696
+ if(!zoomLevel) {
2697
+ return;
2698
+ }
2699
+
2700
+ _calculateSingleItemPanBounds(item, item.w * zoomLevel, item.h * zoomLevel);
2701
+
2702
+ if (isInitial && zoomLevel === item.initialZoomLevel) {
2703
+ item.initialPosition = item.bounds.center;
2704
+ }
2705
+
2706
+ return item.bounds;
2707
+ } else {
2708
+ item.w = item.h = 0;
2709
+ item.initialZoomLevel = item.maxZoom = item.minZoom = item.fitRatio = item.fillRatio = 1;
2710
+ item.bounds = _getZeroBounds();
2711
+ item.initialPosition = item.bounds.center;
2712
+
2713
+ // if it's not image, we return zero bounds (content is not zoomable)
2714
+ return item.bounds;
2715
+ }
2716
+ return false;
2717
+ },
2718
+
2719
+
2720
+
2721
+
2722
+ _appendImage = function(index, item, baseDiv, img, preventAnimation, keepPlaceholder) {
2723
+
2724
+
2725
+ if(item.loadError) {
2726
+ return;
2727
+ }
2728
+
2729
+ var animate,
2730
+ isSwiping = self.isDragging() && !self.isZooming(),
2731
+ slideMightBeVisible = index === _currentItemIndex || self.isMainScrollAnimating() || isSwiping;
2732
+
2733
+ // fade in loaded image only when current holder is active, or might be visible
2734
+ if(!preventAnimation && (_likelyTouchDevice || _options.alwaysFadeIn) && slideMightBeVisible) {
2735
+ animate = true;
2736
+ }
2737
+
2738
+ if(img) {
2739
+ if(animate) {
2740
+ img.style.opacity = 0;
2741
+ }
2742
+
2743
+ item.imageAppended = true;
2744
+
2745
+ baseDiv.appendChild(img);
2746
+
2747
+ if(animate) {
2748
+ setTimeout(function() {
2749
+ img.style.opacity = 1;
2750
+ if(keepPlaceholder) {
2751
+ setTimeout(function() {
2752
+ // hide image placeholder "behind"
2753
+ if(item && item.loaded && item.placeholder) {
2754
+ item.placeholder.style.display = 'none';
2755
+ item.placeholder = null;
2756
+ }
2757
+ }, 500);
2758
+ }
2759
+ }, 50);
2760
+ }
2761
+ }
2762
+ },
2763
+
2764
+
2765
+
2766
+ _preloadImage = function(item) {
2767
+ item.loading = true;
2768
+ item.loaded = false;
2769
+ var img = item.img = framework.createEl('pswp__img', 'img');
2770
+ var onComplete = function() {
2771
+ item.loading = false;
2772
+ item.loaded = true;
2773
+
2774
+ if(item.loadComplete) {
2775
+ item.loadComplete(item);
2776
+ } else {
2777
+ item.img = null; // no need to store image object
2778
+ }
2779
+ img.onload = img.onerror = null;
2780
+ img = null;
2781
+ };
2782
+ img.onload = onComplete;
2783
+ img.onerror = function() {
2784
+ item.loadError = true;
2785
+ onComplete();
2786
+ };
2787
+
2788
+ img.src = item.src;// + '?a=' + Math.random();
2789
+
2790
+ return img;
2791
+ },
2792
+ _checkForError = function(item, cleanUp) {
2793
+ if(item.src && item.loadError && item.container) {
2794
+
2795
+ if(cleanUp) {
2796
+ item.container.innerHTML = '';
2797
+ }
2798
+
2799
+ item.container.innerHTML = _options.errorMsg.replace('%url%', item.src );
2800
+ return true;
2801
+
2802
+ }
2803
+ },
2804
+ _appendImagesPool = function() {
2805
+
2806
+ if(_imagesToAppendPool.length) {
2807
+ var poolItem;
2808
+
2809
+ for(var i = 0; i < _imagesToAppendPool.length; i++) {
2810
+ poolItem = _imagesToAppendPool[i];
2811
+ if( poolItem.holder.index === poolItem.index ) {
2812
+ _appendImage(poolItem.index, poolItem.item, poolItem.baseDiv, poolItem.img);
2813
+ }
2814
+ }
2815
+ _imagesToAppendPool = [];
2816
+ }
2817
+ };
2818
+
2819
+
2820
+
2821
+ _registerModule('Controller', {
2822
+
2823
+ publicMethods: {
2824
+
2825
+ lazyLoadItem: function(index) {
2826
+ index = _getLoopedId(index);
2827
+ var item = _getItemAt(index);
2828
+
2829
+ if(!item || !item.src || item.loaded || item.loading) {
2830
+ return;
2831
+ }
2832
+
2833
+ _shout('gettingData', index, item);
2834
+ _preloadImage(item);
2835
+ },
2836
+ initController: function() {
2837
+ framework.extend(_options, _controllerDefaultOptions, true);
2838
+ self.items = _items = items;
2839
+ _getItemAt = self.getItemAt;
2840
+ _getNumItems = _options.getNumItemsFn; //self.getNumItems;
2841
+
2842
+
2843
+
2844
+ _initialIsLoop = _options.loop;
2845
+ if(_getNumItems() < 3) {
2846
+ _options.loop = false; // disable loop if less then 3 items
2847
+ }
2848
+
2849
+ _listen('beforeChange', function(diff) {
2850
+
2851
+ var p = _options.preload,
2852
+ isNext = diff === null ? true : (diff > 0),
2853
+ preloadBefore = Math.min(p[0], _getNumItems() ),
2854
+ preloadAfter = Math.min(p[1], _getNumItems() ),
2855
+ i;
2856
+
2857
+
2858
+ for(i = 1; i <= (isNext ? preloadAfter : preloadBefore); i++) {
2859
+ self.lazyLoadItem(_currentItemIndex+i);
2860
+ }
2861
+ for(i = 1; i <= (isNext ? preloadBefore : preloadAfter); i++) {
2862
+ self.lazyLoadItem(_currentItemIndex-i);
2863
+ }
2864
+ });
2865
+
2866
+ _listen('initialLayout', function() {
2867
+ self.currItem.initialLayout = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex);
2868
+ });
2869
+
2870
+ _listen('mainScrollAnimComplete', _appendImagesPool);
2871
+ _listen('initialZoomInEnd', _appendImagesPool);
2872
+
2873
+
2874
+
2875
+ _listen('destroy', function() {
2876
+ var item;
2877
+ for(var i = 0; i < _items.length; i++) {
2878
+ item = _items[i];
2879
+ // remove reference to DOM elements, for GC
2880
+ if(item.container) {
2881
+ item.container = null;
2882
+ }
2883
+ if(item.placeholder) {
2884
+ item.placeholder = null;
2885
+ }
2886
+ if(item.img) {
2887
+ item.img = null;
2888
+ }
2889
+ if(item.preloader) {
2890
+ item.preloader = null;
2891
+ }
2892
+ if(item.loadError) {
2893
+ item.loaded = item.loadError = false;
2894
+ }
2895
+ }
2896
+ _imagesToAppendPool = null;
2897
+ });
2898
+ },
2899
+
2900
+
2901
+ getItemAt: function(index) {
2902
+ if (index >= 0) {
2903
+ return _items[index] !== undefined ? _items[index] : false;
2904
+ }
2905
+ return false;
2906
+ },
2907
+
2908
+ allowProgressiveImg: function() {
2909
+ // 1. Progressive image loading isn't working on webkit/blink
2910
+ // when hw-acceleration (e.g. translateZ) is applied to IMG element.
2911
+ // That's why in PhotoSwipe parent element gets zoom transform, not image itself.
2912
+ //
2913
+ // 2. Progressive image loading sometimes blinks in webkit/blink when applying animation to parent element.
2914
+ // That's why it's disabled on touch devices (mainly because of swipe transition)
2915
+ //
2916
+ // 3. Progressive image loading sometimes doesn't work in IE (up to 11).
2917
+
2918
+ // Don't allow progressive loading on non-large touch devices
2919
+ return _options.forceProgressiveLoading || !_likelyTouchDevice || _options.mouseUsed || screen.width > 1200;
2920
+ // 1200 - to eliminate touch devices with large screen (like Chromebook Pixel)
2921
+ },
2922
+
2923
+ setContent: function(holder, index) {
2924
+
2925
+ if(_options.loop) {
2926
+ index = _getLoopedId(index);
2927
+ }
2928
+
2929
+ var prevItem = self.getItemAt(holder.index);
2930
+ if(prevItem) {
2931
+ prevItem.container = null;
2932
+ }
2933
+
2934
+ var item = self.getItemAt(index),
2935
+ img;
2936
+
2937
+ if(!item) {
2938
+ holder.el.innerHTML = '';
2939
+ return;
2940
+ }
2941
+
2942
+ // allow to override data
2943
+ _shout('gettingData', index, item);
2944
+
2945
+ holder.index = index;
2946
+ holder.item = item;
2947
+
2948
+ // base container DIV is created only once for each of 3 holders
2949
+ var baseDiv = item.container = framework.createEl('pswp__zoom-wrap');
2950
+
2951
+
2952
+
2953
+ if(!item.src && item.html) {
2954
+ if(item.html.tagName) {
2955
+ baseDiv.appendChild(item.html);
2956
+ } else {
2957
+ baseDiv.innerHTML = item.html;
2958
+ }
2959
+ }
2960
+
2961
+ _checkForError(item);
2962
+
2963
+ if(item.src && !item.loadError && !item.loaded) {
2964
+
2965
+ item.loadComplete = function(item) {
2966
+
2967
+ // gallery closed before image finished loading
2968
+ if(!_isOpen) {
2969
+ return;
2970
+ }
2971
+
2972
+ // Apply hw-acceleration only after image is loaded.
2973
+ // This is webkit progressive image loading bugfix.
2974
+ // https://bugs.webkit.org/show_bug.cgi?id=108630
2975
+ // https://code.google.com/p/chromium/issues/detail?id=404547
2976
+ item.img.style.webkitBackfaceVisibility = 'hidden';
2977
+
2978
+ // check if holder hasn't changed while image was loading
2979
+ if(holder && holder.index === index ) {
2980
+ if( _checkForError(item, true) ) {
2981
+ item.loadComplete = item.img = null;
2982
+ _calculateItemSize(item, _viewportSize);
2983
+ _applyZoomPanToItem(item);
2984
+
2985
+ if(holder.index === _currentItemIndex) {
2986
+ // recalculate dimensions
2987
+ self.updateCurrZoomItem();
2988
+ }
2989
+ return;
2990
+ }
2991
+ if( !item.imageAppended ) {
2992
+ if(_features.transform && (_mainScrollAnimating || _initialZoomRunning) ) {
2993
+ _imagesToAppendPool.push({
2994
+ item:item,
2995
+ baseDiv:baseDiv,
2996
+ img:item.img,
2997
+ index:index,
2998
+ holder:holder
2999
+ });
3000
+ } else {
3001
+ _appendImage(index, item, baseDiv, item.img, _mainScrollAnimating || _initialZoomRunning);
3002
+ }
3003
+ } else {
3004
+ // remove preloader & mini-img
3005
+ if(!_initialZoomRunning && item.placeholder) {
3006
+ item.placeholder.style.display = 'none';
3007
+ item.placeholder = null;
3008
+ }
3009
+ }
3010
+ }
3011
+
3012
+ item.loadComplete = null;
3013
+ item.img = null; // no need to store image element after it's added
3014
+
3015
+ _shout('imageLoadComplete', index, item);
3016
+ };
3017
+
3018
+ if(framework.features.transform) {
3019
+
3020
+ var placeholderClassName = 'pswp__img pswp__img--placeholder';
3021
+ placeholderClassName += (item.msrc ? '' : ' pswp__img--placeholder--blank');
3022
+
3023
+ var placeholder = framework.createEl(placeholderClassName, item.msrc ? 'img' : '');
3024
+ if(item.msrc) {
3025
+ placeholder.src = item.msrc;
3026
+ }
3027
+
3028
+ placeholder.style.width = item.w + 'px';
3029
+ placeholder.style.height = item.h + 'px';
3030
+
3031
+ baseDiv.appendChild(placeholder);
3032
+ item.placeholder = placeholder;
3033
+
3034
+ }
3035
+
3036
+
3037
+
3038
+
3039
+ if(!item.loading) {
3040
+ _preloadImage(item);
3041
+ }
3042
+
3043
+
3044
+ if( self.allowProgressiveImg() ) {
3045
+ // just append image
3046
+ if(!_initialContentSet && _features.transform) {
3047
+ _imagesToAppendPool.push({
3048
+ item:item,
3049
+ baseDiv:baseDiv,
3050
+ img:item.img,
3051
+ index:index,
3052
+ holder:holder
3053
+ });
3054
+ } else {
3055
+ _appendImage(index, item, baseDiv, item.img, true, true);
3056
+ }
3057
+ }
3058
+
3059
+ } else if(item.src && !item.loadError) {
3060
+ // image object is created every time, due to bugs of image loading & delay when switching images
3061
+ img = framework.createEl('pswp__img', 'img');
3062
+ img.style.webkitBackfaceVisibility = 'hidden';
3063
+ img.style.opacity = 1;
3064
+ img.src = item.src;
3065
+ _appendImage(index, item, baseDiv, img, true);
3066
+ }
3067
+
3068
+ _calculateItemSize(item, _viewportSize);
3069
+
3070
+ if(!_initialContentSet && index === _currentItemIndex) {
3071
+ _currZoomElementStyle = baseDiv.style;
3072
+ _showOrHide(item, (img ||item.img) );
3073
+ } else {
3074
+ _applyZoomPanToItem(item);
3075
+ }
3076
+
3077
+ holder.el.innerHTML = '';
3078
+ holder.el.appendChild(baseDiv);
3079
+ },
3080
+
3081
+ cleanSlide: function( item ) {
3082
+ if(item.img ) {
3083
+ item.img.onload = item.img.onerror = null;
3084
+ }
3085
+ item.loaded = item.loading = item.img = item.imageAppended = false;
3086
+ }
3087
+
3088
+ }
3089
+ });
3090
+
3091
+ /*>>items-controller*/
3092
+
3093
+ /*>>tap*/
3094
+ /**
3095
+ * tap.js:
3096
+ *
3097
+ * Displatches tap and double-tap events.
3098
+ *
3099
+ */
3100
+
3101
+ var tapTimer,
3102
+ tapReleasePoint = {},
3103
+ _dispatchTapEvent = function(origEvent, releasePoint, pointerType) {
3104
+ var e = document.createEvent( 'CustomEvent' ),
3105
+ eDetail = {
3106
+ origEvent:origEvent,
3107
+ target:origEvent.target,
3108
+ releasePoint: releasePoint,
3109
+ pointerType:pointerType || 'touch'
3110
+ };
3111
+
3112
+ e.initCustomEvent( 'pswpTap', true, true, eDetail );
3113
+ origEvent.target.dispatchEvent(e);
3114
+ };
3115
+
3116
+ _registerModule('Tap', {
3117
+ publicMethods: {
3118
+ initTap: function() {
3119
+ _listen('firstTouchStart', self.onTapStart);
3120
+ _listen('touchRelease', self.onTapRelease);
3121
+ _listen('destroy', function() {
3122
+ tapReleasePoint = {};
3123
+ tapTimer = null;
3124
+ });
3125
+ },
3126
+ onTapStart: function(touchList) {
3127
+ if(touchList.length > 1) {
3128
+ clearTimeout(tapTimer);
3129
+ tapTimer = null;
3130
+ }
3131
+ },
3132
+ onTapRelease: function(e, releasePoint) {
3133
+ if(!releasePoint) {
3134
+ return;
3135
+ }
3136
+
3137
+ if(!_moved && !_isMultitouch && !_numAnimations) {
3138
+ var p0 = releasePoint;
3139
+ if(tapTimer) {
3140
+ clearTimeout(tapTimer);
3141
+ tapTimer = null;
3142
+
3143
+ // Check if taped on the same place
3144
+ if ( _isNearbyPoints(p0, tapReleasePoint) ) {
3145
+ _shout('doubleTap', p0);
3146
+ return;
3147
+ }
3148
+ }
3149
+
3150
+ if(releasePoint.type === 'mouse') {
3151
+ _dispatchTapEvent(e, releasePoint, 'mouse');
3152
+ return;
3153
+ }
3154
+
3155
+ var clickedTagName = e.target.tagName.toUpperCase();
3156
+ // avoid double tap delay on buttons and elements that have class pswp__single-tap
3157
+ if(clickedTagName === 'BUTTON' || framework.hasClass(e.target, 'pswp__single-tap') ) {
3158
+ _dispatchTapEvent(e, releasePoint);
3159
+ return;
3160
+ }
3161
+
3162
+ _equalizePoints(tapReleasePoint, p0);
3163
+
3164
+ tapTimer = setTimeout(function() {
3165
+ _dispatchTapEvent(e, releasePoint);
3166
+ tapTimer = null;
3167
+ }, 300);
3168
+ }
3169
+ }
3170
+ }
3171
+ });
3172
+
3173
+ /*>>tap*/
3174
+
3175
+ /*>>desktop-zoom*/
3176
+ /**
3177
+ *
3178
+ * desktop-zoom.js:
3179
+ *
3180
+ * - Binds mousewheel event for paning zoomed image.
3181
+ * - Manages "dragging", "zoomed-in", "zoom-out" classes.
3182
+ * (which are used for cursors and zoom icon)
3183
+ * - Adds toggleDesktopZoom function.
3184
+ *
3185
+ */
3186
+
3187
+ var _wheelDelta;
3188
+
3189
+ _registerModule('DesktopZoom', {
3190
+
3191
+ publicMethods: {
3192
+
3193
+ initDesktopZoom: function() {
3194
+
3195
+ if(_oldIE) {
3196
+ // no zoom for old IE (<=8)
3197
+ return;
3198
+ }
3199
+
3200
+ if(_likelyTouchDevice) {
3201
+ // if detected hardware touch support, we wait until mouse is used,
3202
+ // and only then apply desktop-zoom features
3203
+ _listen('mouseUsed', function() {
3204
+ self.setupDesktopZoom();
3205
+ });
3206
+ } else {
3207
+ self.setupDesktopZoom(true);
3208
+ }
3209
+
3210
+ },
3211
+
3212
+ setupDesktopZoom: function(onInit) {
3213
+
3214
+ _wheelDelta = {};
3215
+
3216
+ var events = 'wheel mousewheel DOMMouseScroll';
3217
+
3218
+ _listen('bindEvents', function() {
3219
+ framework.bind(template, events, self.handleMouseWheel);
3220
+ });
3221
+
3222
+ _listen('unbindEvents', function() {
3223
+ if(_wheelDelta) {
3224
+ framework.unbind(template, events, self.handleMouseWheel);
3225
+ }
3226
+ });
3227
+
3228
+ self.mouseZoomedIn = false;
3229
+
3230
+ var hasDraggingClass,
3231
+ updateZoomable = function() {
3232
+ if(self.mouseZoomedIn) {
3233
+ framework.removeClass(template, 'pswp--zoomed-in');
3234
+ self.mouseZoomedIn = false;
3235
+ }
3236
+ if(_currZoomLevel < 1) {
3237
+ framework.addClass(template, 'pswp--zoom-allowed');
3238
+ } else {
3239
+ framework.removeClass(template, 'pswp--zoom-allowed');
3240
+ }
3241
+ removeDraggingClass();
3242
+ },
3243
+ removeDraggingClass = function() {
3244
+ if(hasDraggingClass) {
3245
+ framework.removeClass(template, 'pswp--dragging');
3246
+ hasDraggingClass = false;
3247
+ }
3248
+ };
3249
+
3250
+ _listen('resize' , updateZoomable);
3251
+ _listen('afterChange' , updateZoomable);
3252
+ _listen('pointerDown', function() {
3253
+ if(self.mouseZoomedIn) {
3254
+ hasDraggingClass = true;
3255
+ framework.addClass(template, 'pswp--dragging');
3256
+ }
3257
+ });
3258
+ _listen('pointerUp', removeDraggingClass);
3259
+
3260
+ if(!onInit) {
3261
+ updateZoomable();
3262
+ }
3263
+
3264
+ },
3265
+
3266
+ handleMouseWheel: function(e) {
3267
+
3268
+ if(_currZoomLevel <= self.currItem.fitRatio) {
3269
+ return true;
3270
+ }
3271
+
3272
+ e.preventDefault();
3273
+ // allow just one event to fire
3274
+ e.stopPropagation();
3275
+
3276
+ // https://developer.mozilla.org/en-US/docs/Web/Events/wheel
3277
+ _wheelDelta.x = 0;
3278
+
3279
+ if('deltaX' in e) {
3280
+ _wheelDelta.x = e.deltaX;
3281
+ _wheelDelta.y = e.deltaY;
3282
+ } else if('wheelDelta' in e) {
3283
+ if(e.wheelDeltaX) {
3284
+ _wheelDelta.x = -0.16 * e.wheelDeltaX;
3285
+ }
3286
+ if(e.wheelDeltaY) {
3287
+ _wheelDelta.y = -0.16 * e.wheelDeltaY;
3288
+ } else {
3289
+ _wheelDelta.y = -0.16 * e.wheelDelta;
3290
+ }
3291
+ } else if('detail' in e) {
3292
+ _wheelDelta.y = e.detail;
3293
+ } else {
3294
+ return;
3295
+ }
3296
+
3297
+ // TODO: use rAF instead of mousewheel?
3298
+ _calculatePanBounds(_currZoomLevel, true);
3299
+ self.panTo(_panOffset.x - _wheelDelta.x, _panOffset.y - _wheelDelta.y);
3300
+ },
3301
+
3302
+ toggleDesktopZoom: function(centerPoint) {
3303
+ centerPoint = centerPoint || {x:_viewportSize.x/2, y:_viewportSize.y/2 + _initalWindowScrollY };
3304
+
3305
+ var doubleTapZoomLevel = _options.getDoubleTapZoom(true, self.currItem);
3306
+ var zoomOut = _currZoomLevel === doubleTapZoomLevel;
3307
+
3308
+ self.mouseZoomedIn = !zoomOut;
3309
+
3310
+ self.zoomTo(zoomOut ? self.currItem.initialZoomLevel : doubleTapZoomLevel, centerPoint, 333);
3311
+ framework[ (!zoomOut ? 'add' : 'remove') + 'Class'](template, 'pswp--zoomed-in');
3312
+ }
3313
+
3314
+ }
3315
+ });
3316
+
3317
+ /*>>desktop-zoom*/
3318
+
3319
+ /*>>history*/
3320
+ /**
3321
+ *
3322
+ * history.js:
3323
+ *
3324
+ * - Back button to close gallery.
3325
+ *
3326
+ * - Unique URL for each slide: example.com/&pid=1&gid=3
3327
+ * (where PID is picture index, and GID and gallery index)
3328
+ *
3329
+ * - Switch URL when slides change.
3330
+ *
3331
+ */
3332
+
3333
+
3334
+ var _historyDefaultOptions = {
3335
+ history: true,
3336
+ galleryUID: 1
3337
+ };
3338
+
3339
+ var _historyUpdateTimeout,
3340
+ _hashChangeTimeout,
3341
+ _hashAnimCheckTimeout,
3342
+ _hashChangedByScript,
3343
+ _hashChangedByHistory,
3344
+ _hashReseted,
3345
+ _initialHash,
3346
+ _historyChanged,
3347
+ _closedFromURL,
3348
+ _urlChangedOnce,
3349
+ _windowLoc,
3350
+
3351
+ _supportsPushState,
3352
+
3353
+ _getHash = function() {
3354
+ return _windowLoc.hash.substring(1);
3355
+ },
3356
+ _cleanHistoryTimeouts = function() {
3357
+
3358
+ if(_historyUpdateTimeout) {
3359
+ clearTimeout(_historyUpdateTimeout);
3360
+ }
3361
+
3362
+ if(_hashAnimCheckTimeout) {
3363
+ clearTimeout(_hashAnimCheckTimeout);
3364
+ }
3365
+ },
3366
+
3367
+ // pid - Picture index
3368
+ // gid - Gallery index
3369
+ _parseItemIndexFromURL = function() {
3370
+ var hash = _getHash(),
3371
+ params = {};
3372
+
3373
+ if(hash.length < 5) { // pid=1
3374
+ return params;
3375
+ }
3376
+
3377
+ var vars = hash.split('&');
3378
+ for (var i = 0; i < vars.length; i++) {
3379
+ if(!vars[i]) {
3380
+ continue;
3381
+ }
3382
+ var pair = vars[i].split('=');
3383
+ if(pair.length < 2) {
3384
+ continue;
3385
+ }
3386
+ params[pair[0]] = pair[1];
3387
+ }
3388
+ params.pid = parseInt(params.pid,10)-1;
3389
+ if( params.pid < 0 ) {
3390
+ params.pid = 0;
3391
+ }
3392
+ return params;
3393
+ },
3394
+ _updateHash = function() {
3395
+
3396
+ if(_hashAnimCheckTimeout) {
3397
+ clearTimeout(_hashAnimCheckTimeout);
3398
+ }
3399
+
3400
+
3401
+ if(_numAnimations || _isDragging) {
3402
+ // changing browser URL forces layout/paint in some browsers, which causes noticable lag during animation
3403
+ // that's why we update hash only when no animations running
3404
+ _hashAnimCheckTimeout = setTimeout(_updateHash, 500);
3405
+ return;
3406
+ }
3407
+
3408
+ if(_hashChangedByScript) {
3409
+ clearTimeout(_hashChangeTimeout);
3410
+ } else {
3411
+ _hashChangedByScript = true;
3412
+ }
3413
+
3414
+
3415
+ var newHash = _initialHash + '&' + 'gid=' + _options.galleryUID + '&' + 'pid=' + (_currentItemIndex + 1);
3416
+
3417
+ if(!_historyChanged) {
3418
+ if(_windowLoc.hash.indexOf(newHash) === -1) {
3419
+ _urlChangedOnce = true;
3420
+ }
3421
+ // first time - add new hisory record, then just replace
3422
+ }
3423
+
3424
+ var newURL = _windowLoc.href.split('#')[0] + '#' + newHash;
3425
+
3426
+ if( _supportsPushState ) {
3427
+
3428
+ if('#' + newHash !== window.location.hash) {
3429
+ history[_historyChanged ? 'replaceState' : 'pushState']('', document.title, newURL);
3430
+ }
3431
+
3432
+ } else {
3433
+ if(_historyChanged) {
3434
+ _windowLoc.replace( newURL );
3435
+ } else {
3436
+ _windowLoc.hash = newHash;
3437
+ }
3438
+ }
3439
+
3440
+
3441
+
3442
+ _historyChanged = true;
3443
+ _hashChangeTimeout = setTimeout(function() {
3444
+ _hashChangedByScript = false;
3445
+ }, 60);
3446
+ };
3447
+
3448
+
3449
+
3450
+
3451
+
3452
+ _registerModule('History', {
3453
+
3454
+
3455
+
3456
+ publicMethods: {
3457
+ initHistory: function() {
3458
+
3459
+ framework.extend(_options, _historyDefaultOptions, true);
3460
+
3461
+ if( !_options.history ) {
3462
+ return;
3463
+ }
3464
+
3465
+
3466
+ _windowLoc = window.location;
3467
+ _urlChangedOnce = false;
3468
+ _closedFromURL = false;
3469
+ _historyChanged = false;
3470
+ _initialHash = _getHash();
3471
+ _supportsPushState = ('pushState' in history);
3472
+
3473
+
3474
+ if(_initialHash.indexOf('gid=') > -1) {
3475
+ _initialHash = _initialHash.split('&gid=')[0];
3476
+ _initialHash = _initialHash.split('?gid=')[0];
3477
+ }
3478
+
3479
+
3480
+ _listen('afterChange', self.updateURL);
3481
+ _listen('unbindEvents', function() {
3482
+ framework.unbind(window, 'hashchange', self.onHashChange);
3483
+ });
3484
+
3485
+
3486
+ var returnToOriginal = function() {
3487
+ _hashReseted = true;
3488
+ if(!_closedFromURL) {
3489
+
3490
+ if(_urlChangedOnce) {
3491
+ history.back();
3492
+ } else {
3493
+
3494
+ if(_initialHash) {
3495
+ _windowLoc.hash = _initialHash;
3496
+ } else {
3497
+ if (_supportsPushState) {
3498
+
3499
+ // remove hash from url without refreshing it or scrolling to top
3500
+ history.pushState('', document.title, _windowLoc.pathname + _windowLoc.search );
3501
+ } else {
3502
+ _windowLoc.hash = '';
3503
+ }
3504
+ }
3505
+ }
3506
+
3507
+ }
3508
+
3509
+ _cleanHistoryTimeouts();
3510
+ };
3511
+
3512
+
3513
+ _listen('unbindEvents', function() {
3514
+ if(_closedByScroll) {
3515
+ // if PhotoSwipe is closed by scroll, we go "back" before the closing animation starts
3516
+ // this is done to keep the scroll position
3517
+ returnToOriginal();
3518
+ }
3519
+ });
3520
+ _listen('destroy', function() {
3521
+ if(!_hashReseted) {
3522
+ returnToOriginal();
3523
+ }
3524
+ });
3525
+ _listen('firstUpdate', function() {
3526
+ _currentItemIndex = _parseItemIndexFromURL().pid;
3527
+ });
3528
+
3529
+
3530
+
3531
+
3532
+ var index = _initialHash.indexOf('pid=');
3533
+ if(index > -1) {
3534
+ _initialHash = _initialHash.substring(0, index);
3535
+ if(_initialHash.slice(-1) === '&') {
3536
+ _initialHash = _initialHash.slice(0, -1);
3537
+ }
3538
+ }
3539
+
3540
+
3541
+ setTimeout(function() {
3542
+ if(_isOpen) { // hasn't destroyed yet
3543
+ framework.bind(window, 'hashchange', self.onHashChange);
3544
+ }
3545
+ }, 40);
3546
+
3547
+ },
3548
+ onHashChange: function() {
3549
+
3550
+ if(_getHash() === _initialHash) {
3551
+
3552
+ _closedFromURL = true;
3553
+ self.close();
3554
+ return;
3555
+ }
3556
+ if(!_hashChangedByScript) {
3557
+
3558
+ _hashChangedByHistory = true;
3559
+ self.goTo( _parseItemIndexFromURL().pid );
3560
+ _hashChangedByHistory = false;
3561
+ }
3562
+
3563
+ },
3564
+ updateURL: function() {
3565
+
3566
+ // Delay the update of URL, to avoid lag during transition,
3567
+ // and to not to trigger actions like "refresh page sound" or "blinking favicon" to often
3568
+
3569
+ _cleanHistoryTimeouts();
3570
+
3571
+
3572
+ if(_hashChangedByHistory) {
3573
+ return;
3574
+ }
3575
+
3576
+ if(!_historyChanged) {
3577
+ _updateHash(); // first time
3578
+ } else {
3579
+ _historyUpdateTimeout = setTimeout(_updateHash, 800);
3580
+ }
3581
+ }
3582
+
3583
+ }
3584
+ });
3585
+
3586
+ /*>>history*/
3587
+ framework.extend(self, publicMethods); };
3588
+ return PhotoSwipe;
3589
+ });