intranet-pictures 1.0.6 → 1.1.0

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