assets-rails 0.1.0 → 0.1.1

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