assets-rails 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/README.md +13 -9
  4. data/assets-rails.gemspec +2 -0
  5. data/lib/assets-rails/engine.rb +3 -1
  6. data/lib/assets-rails/version.rb +1 -1
  7. data/vendor/assets/fonts/lightgallery/Slide-icons.dev.svg +33 -0
  8. data/vendor/assets/fonts/lightgallery/Slide-icons.eot +0 -0
  9. data/vendor/assets/fonts/lightgallery/Slide-icons.svg +33 -0
  10. data/vendor/assets/fonts/lightgallery/Slide-icons.ttf +0 -0
  11. data/vendor/assets/fonts/lightgallery/Slide-icons.woff +0 -0
  12. data/vendor/assets/fonts/photoswipe/v4.0.0/default-skin/default-skin.svg +38 -0
  13. data/vendor/assets/fonts/photoswipe/v4.0.2/default-skin/default-skin.svg +1 -0
  14. data/vendor/assets/images/jquery.share/social-icons-sprite.png +0 -0
  15. data/vendor/assets/images/jquery.share/social-icons.png +0 -0
  16. data/vendor/assets/images/lightgallery/loading.gif +0 -0
  17. data/vendor/assets/images/photoswipe/v4.0.0/default-skin/default-skin.png +0 -0
  18. data/vendor/assets/images/photoswipe/v4.0.0/default-skin/preloader.gif +0 -0
  19. data/vendor/assets/images/photoswipe/v4.0.2/default-skin/default-skin.png +0 -0
  20. data/vendor/assets/images/photoswipe/v4.0.2/default-skin/preloader.gif +0 -0
  21. data/vendor/assets/javascripts/imagesloaded/v3.1.8.js +1 -1
  22. data/vendor/assets/javascripts/imagesloaded/v3.1.8/imagesloaded.js +561 -3
  23. data/vendor/assets/javascripts/jquery.cookie/v1.4.1.js +2 -0
  24. data/vendor/assets/javascripts/jquery.cookie/v1.4.1/jquery.cookie.js +117 -0
  25. data/vendor/assets/javascripts/jquery.share.js +1 -0
  26. data/vendor/assets/javascripts/jquery.share/jquery.share.js +137 -0
  27. data/vendor/assets/javascripts/jquery.zoom/v1.7.13.js +1 -0
  28. data/vendor/assets/javascripts/jquery.zoom/v1.7.13/jquery.zoom.js +233 -0
  29. data/vendor/assets/javascripts/lightgallery/v1.1.4.js +1 -0
  30. data/vendor/assets/javascripts/lightgallery/v1.1.4/lightgallery.min.js +11 -0
  31. data/vendor/assets/javascripts/photoswipe/v4.0.0.js +2 -0
  32. data/vendor/assets/javascripts/photoswipe/v4.0.0/photoswipe-ui-default.js +824 -0
  33. data/vendor/assets/javascripts/photoswipe/v4.0.0/photoswipe.js +3647 -0
  34. data/vendor/assets/javascripts/photoswipe/v4.0.2.js +2 -0
  35. data/vendor/assets/javascripts/photoswipe/v4.0.2/photoswipe-ui-default.js +792 -0
  36. data/vendor/assets/javascripts/photoswipe/v4.0.2/photoswipe.js +3589 -0
  37. data/vendor/assets/stylesheets/font-awesome/v4.2.0/_variables.scss +1 -1
  38. data/vendor/assets/stylesheets/jquery.collageplus/v0.3.3/transitions.css +202 -0
  39. data/vendor/assets/stylesheets/jquery.share.css +3 -0
  40. data/vendor/assets/stylesheets/jquery.share/jquery.share.scss +215 -0
  41. data/vendor/assets/stylesheets/lightgallery/v1.1.4/lightgallery.scss +629 -0
  42. data/vendor/assets/stylesheets/photoswipe/v4.0.0.css +3 -0
  43. data/vendor/assets/stylesheets/photoswipe/v4.0.0/default-skin.scss +418 -0
  44. data/vendor/assets/stylesheets/photoswipe/v4.0.0/photoswipe.css +125 -0
  45. data/vendor/assets/stylesheets/photoswipe/v4.0.2.css +3 -0
  46. data/vendor/assets/stylesheets/photoswipe/v4.0.2/default-skin.scss +390 -0
  47. data/vendor/assets/stylesheets/photoswipe/v4.0.2/photoswipe.css +148 -0
  48. metadata +43 -5
@@ -0,0 +1,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
+ });