PageSourceSearch

https://sacra.it/wp-content/themes/lf_tds_custom/js/pinch-zoom.umd.js?ver=7.0.6

js sacra.it collected 2026-10-02 13:01:06 UTC 33,307 bytes, 873 lines download raw bytes

vendor: 32,783 bytes, lines 1-850
1(function (global, factory) {
2    if (typeof define === "function" && define.amd) {
3        define(['exports'], factory);
4    } else if (typeof exports !== "undefined") {
5        factory(exports);
6    } else {
7        var mod = {
8            exports: {}
9        };
10        factory(mod.exports);
11        global.PinchZoom = mod.exports;
12    }
13})(this, function (exports) {
14    'use strict';
15
16    Object.defineProperty(exports, "__esModule", {
17        value: true
18    });
19    /*
20    
21        PinchZoom.js
22        Copyright (c) Manuel Stofer 2013 - today
23    
24        Author: Manuel Stofer ([email protected])
25        Version: 2.2.0
26    
27        Permission is hereby granted, free of charge, to any person obtaining a copy
28        of this software and associated documentation files (the "Software"), to deal
29        in the Software without restriction, including without limitation the rights
30        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31        copies of the Software, and to permit persons to whom the Software is
32        furnished to do so, subject to the following conditions:
33    
34        The above copyright notice and this permission notice shall be included in
35        all copies or substantial portions of the Software.
36    
37        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43        THE SOFTWARE.
44    
45    */
46
47    // polyfills
48    if (typeof Object.assign != 'function') {
49        // Must be writable: true, enumerable: false, configurable: true
50        Object.defineProperty(Object, "assign", {
51            value: function assign(target, varArgs) {
52                // .length of function is 2
53                if (target == null) {
54                    // TypeError if undefined or null
55                    throw new TypeError('Cannot convert undefined or null to object');
56                }
57
58                var to = Object(target);
59
60                for (var index = 1; index < arguments.length; index++) {
61                    var nextSource = arguments[index];
62
63                    if (nextSource != null) {
64                        // Skip over if undefined or null
65                        for (var nextKey in nextSource) {
66                            // Avoid bugs when hasOwnProperty is shadowed
67                            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
68                                to[nextKey] = nextSource[nextKey];
69                            }
70                        }
71                    }
72                }
73                return to;
74            },
75            writable: true,
76            configurable: true
77        });
78    }
79
80    if (typeof Array.from != 'function') {
81        Array.from = function (object) {
82            return [].slice.call(object);
83        };
84    }
85
86    // utils
87    var buildElement = function buildElement(str) {
88        // empty string as title argument required by IE and Edge
89        var tmp = document.implementation.createHTMLDocument('');
90        tmp.body.innerHTML = str;
91        return Array.from(tmp.body.children)[0];
92    };
93
94    var triggerEvent = function triggerEvent(el, name) {
95        var event = document.createEvent('HTMLEvents');
96        event.initEvent(name, true, false);
97        el.dispatchEvent(event);
98    };
99
100    var definePinchZoom = function definePinchZoom() {
101
102        /**
103         * Pinch zoom
104         * @param el
105         * @param options
106         * @constructor
107         */
108        var PinchZoom = function PinchZoom(el, options) {
109            this.el = el;
110            this.zoomFactor = 1;
111            this.lastScale = 1;
112            this.offset = {
113                x: 0,
114                y: 0
115            };
116            this.initialOffset = {
117                x: 0,
118                y: 0
119            };
120            this.options = Object.assign({}, this.defaults, options);
121            this.setupMarkup();
122            this.bindEvents();
123            this.update();
124
125            // The image may already be loaded when PinchZoom is initialized,
126            // and then the load event (which trigger update) will never fire.
127            if (this.isImageLoaded(this.el)) {
128                this.updateAspectRatio();
129                this.setupInitialOffset();
130            }
131
132            this.enable();
133        },
134            sum = function sum(a, b) {
135            return a + b;
136        },
137            isCloseTo = function isCloseTo(value, expected) {
138            return value > expected - 0.01 && value < expected + 0.01;
139        };
140
141        PinchZoom.prototype = {
142
143            defaults: {
144                tapZoomFactor: 2,
145                zoomOutFactor: 1.3,
146                animationDuration: 300,
147                maxZoom: 4,
148                minZoom: 0.5,
149                draggableUnzoomed: true,
150                lockDragAxis: false,
151                use2d: true,
152                zoomStartEventName: 'pz_zoomstart',
153                zoomUpdateEventName: 'pz_zoomupdate',
154                zoomEndEventName: 'pz_zoomend',
155                dragStartEventName: 'pz_dragstart',
156                dragUpdateEventName: 'pz_dragupdate',
157                dragEndEventName: 'pz_dragend',
158                doubleTapEventName: 'pz_doubletap',
159                verticalPadding: 0,
160                horizontalPadding: 0
161            },
162
163            /**
164             * Event handler for 'dragstart'
165             * @param event
166             */
167            handleDragStart: function handleDragStart(event) {
168                triggerEvent(this.el, this.options.dragStartEventName);
169                this.stopAnimation();
170                this.lastDragPosition = false;
171                this.hasInteraction = true;
172                this.handleDrag(event);
173            },
174
175            /**
176             * Event handler for 'drag'
177             * @param event
178             */
179            handleDrag: function handleDrag(event) {
180                var touch = this.getTouches(event)[0];
181                this.drag(touch, this.lastDragPosition);
182                this.offset = this.sanitizeOffset(this.offset);
183                this.lastDragPosition = touch;
184            },
185
186            handleDragEnd: function handleDragEnd() {
187                triggerEvent(this.el, this.options.dragEndEventName);
188                this.end();
189            },
190
191            /**
192             * Event handler for 'zoomstart'
193             * @param event
194             */
195            handleZoomStart: function handleZoomStart(event) {
196                triggerEvent(this.el, this.options.zoomStartEventName);
197                this.stopAnimation();
198                this.lastScale = 1;
199                this.nthZoom = 0;
200                this.lastZoomCenter = false;
201                this.hasInteraction = true;
202            },
203
204            /**
205             * Event handler for 'zoom'
206             * @param event
207             */
208            handleZoom: function handleZoom(event, newScale) {
209                // a relative scale factor is used
210                var touchCenter = this.getTouchCenter(this.getTouches(event)),
211                    scale = newScale / this.lastScale;
212                this.lastScale = newScale;
213
214                // the first touch events are thrown away since they are not precise
215                this.nthZoom += 1;
216                if (this.nthZoom > 3) {
217
218                    this.scale(scale, touchCenter);
219                    this.drag(touchCenter, this.lastZoomCenter);
220                }
221                this.lastZoomCenter = touchCenter;
222            },
223
224            handleZoomEnd: function handleZoomEnd() {
225                triggerEvent(this.el, this.options.zoomEndEventName);
226                this.end();
227            },
228
229            /**
230             * Event handler for 'doubletap'
231             * @param event
232             */
233            handleDoubleTap: function handleDoubleTap(event) {
234                var center = this.getTouches(event)[0],
235                    zoomFactor = this.zoomFactor > 1 ? 1 : this.options.tapZoomFactor,
236                    startZoomFactor = this.zoomFactor,
237                    updateProgress = function (progress) {
238                    this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center);
239                }.bind(this);
240
241                if (this.hasInteraction) {
242                    return;
243                }
244
245                this.isDoubleTap = true;
246
247                if (startZoomFactor > zoomFactor) {
248                    center = this.getCurrentZoomCenter();
249                }
250
251                this.animate(this.options.animationDuration, updateProgress, this.swing);
252                triggerEvent(this.el, this.options.doubleTapEventName);
253            },
254
255            /**
256             * Compute the initial offset
257             *
258             * the element should be centered in the container upon initialization
259             */
260            computeInitialOffset: function computeInitialOffset() {
261                this.initialOffset = {
262                    x: -Math.abs(this.el.offsetWidth * this.getInitialZoomFactor() - this.container.offsetWidth) / 2,
263                    y: -Math.abs(this.el.offsetHeight * this.getInitialZoomFactor() - this.container.offsetHeight) / 2
264                };
265            },
266
267            /**
268             * Determine if image is loaded
269             */
270            isImageLoaded: function isImageLoaded(el) {
271                if (el.nodeName === 'IMG') {
272                    return el.complete && el.naturalHeight !== 0;
273                } else {
274                    return Array.from(el.querySelectorAll('img')).every(this.isImageLoaded);
275                }
276            },
277
278            setupInitialOffset: function setupInitialOffset() {
279                if (this._initialOffsetSetup) {
280                    return;
281                }
282
283                this._initialOffsetSetup = true;
284
285                this.computeInitialOffset();
286                this.offset.x = this.initialOffset.x;
287                this.offset.y = this.initialOffset.y;
288            },
289
290            /**
291             * Max / min values for the offset
292             * @param offset
293             * @return {Object} the sanitized offset
294             */
295            sanitizeOffset: function sanitizeOffset(offset) {
296                var elWidth = this.el.offsetWidth * this.getInitialZoomFactor() * this.zoomFactor;
297                var elHeight = this.el.offsetHeight * this.getInitialZoomFactor() * this.zoomFactor;
298                var maxX = elWidth - this.getContainerX() + this.options.horizontalPadding,
299                    maxY = elHeight - this.getContainerY() + this.options.verticalPadding,
300                    maxOffsetX = Math.max(maxX, 0),
301                    maxOffsetY = Math.max(maxY, 0),
302                    minOffsetX = Math.min(maxX, 0) - this.options.horizontalPadding,
303                    minOffsetY = Math.min(maxY, 0) - this.options.verticalPadding;
304
305                return {
306                    x: Math.min(Math.max(offset.x, minOffsetX), maxOffsetX),
307                    y: Math.min(Math.max(offset.y, minOffsetY), maxOffsetY)
308                };
309            },
310
311            /**
312             * Scale to a specific zoom factor (not relative)
313             * @param zoomFactor
314             * @param center
315             */
316            scaleTo: function scaleTo(zoomFactor, center) {
317                this.scale(zoomFactor / this.zoomFactor, center);
318            },
319
320            /**
321             * Scales the element from specified center
322             * @param scale
323             * @param center
324             */
325            scale: function scale(_scale, center) {
326                _scale = this.scaleZoomFactor(_scale);
327                this.addOffset({
328                    x: (_scale - 1) * (center.x + this.offset.x),
329                    y: (_scale - 1) * (center.y + this.offset.y)
330                });
331                triggerEvent(this.el, this.options.zoomUpdateEventName);
332            },
333
334            /**
335             * Scales the zoom factor relative to current state
336             * @param scale
337             * @return the actual scale (can differ because of max min zoom factor)
338             */
339            scaleZoomFactor: function scaleZoomFactor(scale) {
340                var originalZoomFactor = this.zoomFactor;
341                this.zoomFactor *= scale;
342                this.zoomFactor = Math.min(this.options.maxZoom, Math.max(this.zoomFactor, this.options.minZoom));
343                return this.zoomFactor / originalZoomFactor;
344            },
345
346            /**
347             * Determine if the image is in a draggable state
348             *
349             * When the image can be dragged, the drag event is acted upon and cancelled.
350             * When not draggable, the drag event bubbles through this component.
351             *
352             * @return {Boolean}
353             */
354            canDrag: function canDrag() {
355                return this.options.draggableUnzoomed || !isCloseTo(this.zoomFactor, 1);
356            },
357
358            /**
359             * Drags the element
360             * @param center
361             * @param lastCenter
362             */
363            drag: function drag(center, lastCenter) {
364                if (lastCenter) {
365                    if (this.options.lockDragAxis) {
366                        // lock scroll to position that was changed the most
367                        if (Math.abs(center.x - lastCenter.x) > Math.abs(center.y - lastCenter.y)) {
368                            this.addOffset({
369                                x: -(center.x - lastCenter.x),
370                                y: 0
371                            });
372                        } else {
373                            this.addOffset({
374                                y: -(center.y - lastCenter.y),
375                                x: 0
376                            });
377                        }
378                    } else {
379                        this.addOffset({
380                            y: -(center.y - lastCenter.y),
381                            x: -(center.x - lastCenter.x)
382                        });
383                    }
384                    triggerEvent(this.el, this.options.dragUpdateEventName);
385                }
386            },
387
388            /**
389             * Calculates the touch center of multiple touches
390             * @param touches
391             * @return {Object}
392             */
393            getTouchCenter: function getTouchCenter(touches) {
394                return this.getVectorAvg(touches);
395            },
396
397            /**
398             * Calculates the average of multiple vectors (x, y values)
399             */
400            getVectorAvg: function getVectorAvg(vectors) {
401                return {
402                    x: vectors.map(function (v) {
403                        return v.x;
404                    }).reduce(sum) / vectors.length,
405                    y: vectors.map(function (v) {
406                        return v.y;
407                    }).reduce(sum) / vectors.length
408                };
409            },
410
411            /**
412             * Adds an offset
413             * @param offset the offset to add
414             * @return return true when the offset change was accepted
415             */
416            addOffset: function addOffset(offset) {
417                this.offset = {
418                    x: this.offset.x + offset.x,
419                    y: this.offset.y + offset.y
420                };
421            },
422
423            sanitize: function sanitize() {
424                if (this.zoomFactor < this.options.zoomOutFactor) {
425                    this.zoomOutAnimation();
426                } else if (this.isInsaneOffset(this.offset)) {
427                    this.sanitizeOffsetAnimation();
428                }
429            },
430
431            /**
432             * Checks if the offset is ok with the current zoom factor
433             * @param offset
434             * @return {Boolean}
435             */
436            isInsaneOffset: function isInsaneOffset(offset) {
437                var sanitizedOffset = this.sanitizeOffset(offset);
438                return sanitizedOffset.x !== offset.x || sanitizedOffset.y !== offset.y;
439            },
440
441            /**
442             * Creates an animation moving to a sane offset
443             */
444            sanitizeOffsetAnimation: function sanitizeOffsetAnimation() {
445                var targetOffset = this.sanitizeOffset(this.offset),
446                    startOffset = {
447                    x: this.offset.x,
448                    y: this.offset.y
449                },
450                    updateProgress = function (progress) {
451                    this.offset.x = startOffset.x + progress * (targetOffset.x - startOffset.x);
452                    this.offset.y = startOffset.y + progress * (targetOffset.y - startOffset.y);
453                    this.update();
454                }.bind(this);
455
456                this.animate(this.options.animationDuration, updateProgress, this.swing);
457            },
458
459            /**
460             * Zooms back to the original position,
461             * (no offset and zoom factor 1)
462             */
463            zoomOutAnimation: function zoomOutAnimation() {
464                if (this.zoomFactor === 1) {
465                    return;
466                }
467
468                var startZoomFactor = this.zoomFactor,
469                    zoomFactor = 1,
470                    center = this.getCurrentZoomCenter(),
471                    updateProgress = function (progress) {
472                    this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center);
473                }.bind(this);
474
475                this.animate(this.options.animationDuration, updateProgress, this.swing);
476            },
477
478            /**
479             * Updates the aspect ratio
480             */
481            updateAspectRatio: function updateAspectRatio() {
482                this.setContainerY(this.container.parentElement.offsetHeight);
483            },
484
485            /**
486             * Calculates the initial zoom factor (for the element to fit into the container)
487             * @return {number} the initial zoom factor
488             */
489            getInitialZoomFactor: function getInitialZoomFactor() {
490                var xZoomFactor = this.container.offsetWidth / this.el.offsetWidth;
491                var yZoomFactor = this.container.offsetHeight / this.el.offsetHeight;
492
493                return Math.min(xZoomFactor, yZoomFactor);
494            },
495
496            /**
497             * Calculates the aspect ratio of the element
498             * @return the aspect ratio
499             */
500            getAspectRatio: function getAspectRatio() {
501                return this.el.offsetWidth / this.el.offsetHeight;
502            },
503
504            /**
505             * Calculates the virtual zoom center for the current offset and zoom factor
506             * (used for reverse zoom)
507             * @return {Object} the current zoom center
508             */
509            getCurrentZoomCenter: function getCurrentZoomCenter() {
510                var offsetLeft = this.offset.x - this.initialOffset.x;
511                var centerX = -1 * this.offset.x - offsetLeft / (1 / this.zoomFactor - 1);
512
513                var offsetTop = this.offset.y - this.initialOffset.y;
514                var centerY = -1 * this.offset.y - offsetTop / (1 / this.zoomFactor - 1);
515
516                return {
517                    x: centerX,
518                    y: centerY
519                };
520            },
521
522            /**
523             * Returns the touches of an event relative to the container offset
524             * @param event
525             * @return array touches
526             */
527            getTouches: function getTouches(event) {
528                var rect = this.container.getBoundingClientRect();
529                var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
530                var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
531                var posTop = rect.top + scrollTop;
532                var posLeft = rect.left + scrollLeft;
533
534                return Array.prototype.slice.call(event.touches).map(function (touch) {
535                    return {
536                        x: touch.pageX - posLeft,
537                        y: touch.pageY - posTop
538                    };
539                });
540            },
541
542            /**
543             * Animation loop
544             * does not support simultaneous animations
545             * @param duration
546             * @param framefn
547             * @param timefn
548             * @param callback
549             */
550            animate: function animate(duration, framefn, timefn, callback) {
551                var startTime = new Date().getTime(),
552                    renderFrame = function () {
553                    if (!this.inAnimation) {
554                        return;
555                    }
556                    var frameTime = new Date().getTime() - startTime,
557                        progress = frameTime / duration;
558                    if (frameTime >= duration) {
559                        framefn(1);
560                        if (callback) {
561                            callback();
562                        }
563                        this.update();
564                        this.stopAnimation();
565                        this.update();
566                    } else {
567                        if (timefn) {
568                            progress = timefn(progress);
569                        }
570                        framefn(progress);
571                        this.update();
572                        requestAnimationFrame(renderFrame);
573                    }
574                }.bind(this);
575                this.inAnimation = true;
576                requestAnimationFrame(renderFrame);
577            },
578
579            /**
580             * Stops the animation
581             */
582            stopAnimation: function stopAnimation() {
583                this.inAnimation = false;
584            },
585
586            /**
587             * Swing timing function for animations
588             * @param p
589             * @return {Number}
590             */
591            swing: function swing(p) {
592                return -Math.cos(p * Math.PI) / 2 + 0.5;
593            },
594
595            getContainerX: function getContainerX() {
596                return this.container.offsetWidth;
597            },
598
599            getContainerY: function getContainerY() {
600                return this.container.offsetHeight;
601            },
602
603            setContainerY: function setContainerY(y) {
604                return this.container.style.height = y + 'px';
605            },
606
607            /**
608             * Creates the expected html structure
609             */
610            setupMarkup: function setupMarkup() {
611                this.container = buildElement('<div class="pinch-zoom-container"></div>');
612                this.el.parentNode.insertBefore(this.container, this.el);
613                this.container.appendChild(this.el);
614
615                this.container.style.overflow = 'hidden';
616                this.container.style.position = 'relative';
617
618                this.el.style.webkitTransformOrigin = '0% 0%';
619                this.el.style.mozTransformOrigin = '0% 0%';
620                this.el.style.msTransformOrigin = '0% 0%';
621                this.el.style.oTransformOrigin = '0% 0%';
622                this.el.style.transformOrigin = '0% 0%';
623
624                this.el.style.position = 'absolute';
625            },
626
627            end: function end() {
628                this.hasInteraction = false;
629                this.sanitize();
630                this.update();
631            },
632
633            /**
634             * Binds all required event listeners
635             */
636            bindEvents: function bindEvents() {
637                var self = this;
638                detectGestures(this.container, this);
639
640                window.addEventListener('resize', this.update.bind(this));
641                Array.from(this.el.querySelectorAll('img')).forEach(function (imgEl) {
642                    imgEl.addEventListener('load', self.update.bind(self));
643                });
644
645                if (this.el.nodeName === 'IMG') {
646                    this.el.addEventListener('load', this.update.bind(this));
647                }
648            },
649
650            /**
651             * Updates the css values according to the current zoom factor and offset
652             */
653            update: function update(event) {
654                if (this.updatePlaned) {
655                    return;
656                }
657                this.updatePlaned = true;
658
659                window.setTimeout(function () {
660                    this.updatePlaned = false;
661                    this.updateAspectRatio();
662
663                    if (event && event.type === 'resize') {
664                        this.computeInitialOffset();
665                    }
666
667                    if (event && event.type === 'load') {
668                        this.setupInitialOffset();
669                    }
670
671                    var zoomFactor = this.getInitialZoomFactor() * this.zoomFactor,
672                        offsetX = -this.offset.x / zoomFactor,
673                        offsetY = -this.offset.y / zoomFactor,
674                        transform3d = 'scale3d(' + zoomFactor + ', ' + zoomFactor + ',1) ' + 'translate3d(' + offsetX + 'px,' + offsetY + 'px,0px)',
675                        transform2d = 'scale(' + zoomFactor + ', ' + zoomFactor + ') ' + 'translate(' + offsetX + 'px,' + offsetY + 'px)',
676                        removeClone = function () {
677                        if (this.clone) {
678                            this.clone.parentNode.removeChild(this.clone);
679                            delete this.clone;
680                        }
681                    }.bind(this);
682
683                    // Scale 3d and translate3d are faster (at least on ios)
684                    // but they also reduce the quality.
685                    // PinchZoom uses the 3d transformations during interactions
686                    // after interactions it falls back to 2d transformations
687                    if (!this.options.use2d || this.hasInteraction || this.inAnimation) {
688                        this.is3d = true;
689                        removeClone();
690
691                        this.el.style.webkitTransform = transform3d;
692                        this.el.style.mozTransform = transform2d;
693                        this.el.style.msTransform = transform2d;
694                        this.el.style.oTransform = transform2d;
695                        this.el.style.transform = transform3d;
696                    } else {
697                        // When changing from 3d to 2d transform webkit has some glitches.
698                        // To avoid this, a copy of the 3d transformed element is displayed in the
699                        // foreground while the element is converted from 3d to 2d transform
700                        if (this.is3d) {
701                            this.clone = this.el.cloneNode(true);
702                            this.clone.style.pointerEvents = 'none';
703                            this.container.appendChild(this.clone);
704                            window.setTimeout(removeClone, 200);
705                        }
706
707                        this.el.style.webkitTransform = transform2d;
708                        this.el.style.mozTransform = transform2d;
709                        this.el.style.msTransform = transform2d;
710                        this.el.style.oTransform = transform2d;
711                        this.el.style.transform = transform2d;
712
713                        this.is3d = false;
714                    }
715                }.bind(this), 0);
716            },
717
718            /**
719             * Enables event handling for gestures
720             */
721            enable: function enable() {
722                this.enabled = true;
723            },
724
725            /**
726             * Disables event handling for gestures
727             */
728            disable: function disable() {
729                this.enabled = false;
730            }
731        };
732
733        var detectGestures = function detectGestures(el, target) {
734            var interaction = null,
735                fingers = 0,
736                lastTouchStart = null,
737                startTouches = null,
738                setInteraction = function setInteraction(newInteraction, event) {
739                if (interaction !== newInteraction) {
740
741                    if (interaction && !newInteraction) {
742                        switch (interaction) {
743                            case "zoom":
744                                target.handleZoomEnd(event);
745                                break;
746                            case 'drag':
747                                target.handleDragEnd(event);
748                                break;
749                        }
750                    }
751
752                    switch (newInteraction) {
753                        case 'zoom':
754                            target.handleZoomStart(event);
755                            break;
756                        case 'drag':
757                            target.handleDragStart(event);
758                            break;
759                    }
760                }
761                interaction = newInteraction;
762            },
763                updateInteraction = function updateInteraction(event) {
764                if (fingers === 2) {
765                    setInteraction('zoom');
766                } else if (fingers === 1 && target.canDrag()) {
767                    setInteraction('drag', event);
768                } else {
769                    setInteraction(null, event);
770                }
771            },
772                targetTouches = function targetTouches(touches) {
773                return Array.from(touches).map(function (touch) {
774                    return {
775                        x: touch.pageX,
776                        y: touch.pageY
777                    };
778                });
779            },
780                getDistance = function getDistance(a, b) {
781                var x, y;
782                x = a.x - b.x;
783                y = a.y - b.y;
784                return Math.sqrt(x * x + y * y);
785            },
786                calculateScale = function calculateScale(startTouches, endTouches) {
787                var startDistance = getDistance(startTouches[0], startTouches[1]),
788                    endDistance = getDistance(endTouches[0], endTouches[1]);
789                return endDistance / startDistance;
790            },
791                cancelEvent = function cancelEvent(event) {
792                event.stopPropagation();
793                event.preventDefault();
794            },
795                detectDoubleTap = function detectDoubleTap(event) {
796                var time = new Date().getTime();
797
798                if (fingers > 1) {
799                    lastTouchStart = null;
800                }
801
802                if (time - lastTouchStart < 300) {
803                    cancelEvent(event);
804
805                    target.handleDoubleTap(event);
806                    switch (interaction) {
807                        case "zoom":
808                            target.handleZoomEnd(event);
809                            break;
810                        case 'drag':
811                            target.handleDragEnd(event);
812                            break;
813                    }
814                } else {
815                    target.isDoubleTap = false;
816                }
817
818                if (fingers === 1) {
819                    lastTouchStart = time;
820                }
821            },
822                firstMove = true;
823
824            el.addEventListener('touchstart', function (event) {
825                if (target.enabled) {
826                    firstMove = true;
827                    fingers = event.touches.length;
828                    detectDoubleTap(event);
829                }
830            });
831
832            el.addEventListener('touchmove', function (event) {
833                if (target.enabled && !target.isDoubleTap) {
834                    if (firstMove) {
835                        updateInteraction(event);
836                        if (interaction) {
837                            cancelEvent(event);
838                        }
839                        startTouches = targetTouches(event.touches);
840                    } else {
841                        switch (interaction) {
842                            case 'zoom':
843                                target.handleZoom(event, calculateScale(startTouches, targetTouches(event.touches)));
844                                break;
845                            case 'drag':
846                                target.handleDrag(event);
847                                break;
848                        }
849                        if (interaction) {
850                            cancelEvent(event);
851                            target.update();
852                        }
853                    }
854
855                    firstMove = false;
856                }
857            });
858
859            el.addEventListener('touchend', function (event) {
860                if (target.enabled) {
861                    fingers = event.touches.length;
862                    updateInteraction(event);
863                }
864            });
865        };
866
867        return PinchZoom;
868    };
869
870    var PinchZoom = definePinchZoom();
871
872    exports.default = PinchZoom;
873});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.