PageSourceSearch

https://vitaverzum.hu/js/panzoom.js

js vitaverzum.hu collected 2026-10-08 23:44:19 UTC 51,581 bytes, 1,818 lines download raw bytes

vendor: 51,581 bytes, lines 1-1818
1(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.panzoom = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2'use strict';
3/**
4 * Allows to drag and zoom svg elements
5 */
6var wheel = require('wheel');
7var animate = require('amator');
8var eventify = require('ngraph.events');
9var kinetic = require('./lib/kinetic.js');
10var createTextSelectionInterceptor = require('./lib/makeTextSelectionInterceptor.js');
11var domTextSelectionInterceptor = createTextSelectionInterceptor();
12var fakeTextSelectorInterceptor = createTextSelectionInterceptor(true);
13var Transform = require('./lib/transform.js');
14var makeSvgController = require('./lib/makeSvgController.js');
15var makeDomController = require('./lib/makeDomController.js');
16
17var defaultZoomSpeed = 1;
18var defaultDoubleTapZoomSpeed = 1.75;
19var doubleTapSpeedInMS = 300;
20var clickEventTimeInMS = 200;
21
22module.exports = createPanZoom;
23
24/**
25 * Creates a new instance of panzoom, so that an object can be panned and zoomed
26 *
27 * @param {DOMElement} domElement where panzoom should be attached.
28 * @param {Object} options that configure behavior.
29 */
30function createPanZoom(domElement, options) {
31  options = options || {};
32
33  var panController = options.controller;
34
35  if (!panController) {
36    if (makeSvgController.canAttach(domElement)) {
37      panController = makeSvgController(domElement, options);
38    } else if (makeDomController.canAttach(domElement)) {
39      panController = makeDomController(domElement, options);
40    }
41  }
42
43  if (!panController) {
44    throw new Error(
45      'Cannot create panzoom for the current type of dom element'
46    );
47  }
48  var owner = panController.getOwner();
49  // just to avoid GC pressure, every time we do intermediate transform
50  // we return this object. For internal use only. Never give it back to the consumer of this library
51  var storedCTMResult = { x: 0, y: 0 };
52
53  var isDirty = false;
54  var transform = new Transform();
55
56  if (panController.initTransform) {
57    panController.initTransform(transform);
58  }
59
60  var filterKey = typeof options.filterKey === 'function' ? options.filterKey : noop;
61  // TODO: likely need to unite pinchSpeed with zoomSpeed
62  var pinchSpeed = typeof options.pinchSpeed === 'number' ? options.pinchSpeed : 1;
63  var bounds = options.bounds;
64  var maxZoom = typeof options.maxZoom === 'number' ? options.maxZoom : Number.POSITIVE_INFINITY;
65  var minZoom = typeof options.minZoom === 'number' ? options.minZoom : 0;
66
67  var boundsPadding = typeof options.boundsPadding === 'number' ? options.boundsPadding : 0.05;
68  var zoomDoubleClickSpeed = typeof options.zoomDoubleClickSpeed === 'number' ? options.zoomDoubleClickSpeed : defaultDoubleTapZoomSpeed;
69  var beforeWheel = options.beforeWheel || noop;
70  var beforeMouseDown = options.beforeMouseDown || noop;
71  var speed = typeof options.zoomSpeed === 'number' ? options.zoomSpeed : defaultZoomSpeed;
72  var transformOrigin = parseTransformOrigin(options.transformOrigin);
73  var textSelection = options.enableTextSelection ? fakeTextSelectorInterceptor : domTextSelectionInterceptor;
74
75  validateBounds(bounds);
76
77  if (options.autocenter) {
78    autocenter();
79  }
80
81  var frameAnimation;
82  var lastTouchEndTime = 0;
83  var lastTouchStartTime = 0;
84  var pendingClickEventTimeout = 0;
85  var lastMouseDownedEvent = null;
86  var lastMouseDownTime = new Date();
87  var lastSingleFingerOffset;
88  var touchInProgress = false;
89
90  // We only need to fire panstart when actual move happens
91  var panstartFired = false;
92
93  // cache mouse coordinates here
94  var mouseX;
95  var mouseY;
96
97  // Where the first click has happened, so that we can differentiate
98  // between pan and click
99  var clickX;
100  var clickY;
101
102  var pinchZoomLength;
103
104  var smoothScroll;
105  if ('smoothScroll' in options && !options.smoothScroll) {
106    // If user explicitly asked us not to use smooth scrolling, we obey
107    smoothScroll = rigidScroll();
108  } else {
109    // otherwise we use forward smoothScroll settings to kinetic API
110    // which makes scroll smoothing.
111    smoothScroll = kinetic(getPoint, scroll, options.smoothScroll);
112  }
113
114  var moveByAnimation;
115  var zoomToAnimation;
116
117  var multiTouch;
118  var paused = false;
119
120  listenForEvents();
121
122  var api = {
123    dispose: dispose,
124    moveBy: internalMoveBy,
125    moveTo: moveTo,
126    smoothMoveTo: smoothMoveTo,
127    centerOn: centerOn,
128    zoomTo: publicZoomTo,
129    zoomAbs: zoomAbs,
130    smoothZoom: smoothZoom,
131    smoothZoomAbs: smoothZoomAbs,
132    showRectangle: showRectangle,
133
134    pause: pause,
135    resume: resume,
136    isPaused: isPaused,
137
138    getTransform: getTransformModel,
139
140    getMinZoom: getMinZoom,
141    setMinZoom: setMinZoom,
142
143    getMaxZoom: getMaxZoom,
144    setMaxZoom: setMaxZoom,
145
146    getTransformOrigin: getTransformOrigin,
147    setTransformOrigin: setTransformOrigin,
148
149    getZoomSpeed: getZoomSpeed,
150    setZoomSpeed: setZoomSpeed
151  };
152
153  eventify(api);
154
155  var initialX = typeof options.initialX === 'number' ? options.initialX : transform.x;
156  var initialY = typeof options.initialY === 'number' ? options.initialY : transform.y;
157  var initialZoom = typeof options.initialZoom === 'number' ? options.initialZoom : transform.scale;
158
159  if(initialX != transform.x || initialY != transform.y || initialZoom != transform.scale){
160    zoomAbs(initialX, initialY, initialZoom);
161  }
162
163  return api;
164
165  function pause() {
166    releaseEvents();
167    paused = true;
168  }
169
170  function resume() {
171    if (paused) {
172      listenForEvents();
173      paused = false;
174    }
175  }
176
177  function isPaused() {
178    return paused;
179  }
180
181  function showRectangle(rect) {
182    // TODO: this duplicates autocenter. I think autocenter should go.
183    var clientRect = owner.getBoundingClientRect();
184    var size = transformToScreen(clientRect.width, clientRect.height);
185
186    var rectWidth = rect.right - rect.left;
187    var rectHeight = rect.bottom - rect.top;
188    if (!Number.isFinite(rectWidth) || !Number.isFinite(rectHeight)) {
189      throw new Error('Invalid rectangle');
190    }
191
192    var dw = size.x / rectWidth;
193    var dh = size.y / rectHeight;
194    var scale = Math.min(dw, dh);
195    transform.x = -(rect.left + rectWidth / 2) * scale + size.x / 2;
196    transform.y = -(rect.top + rectHeight / 2) * scale + size.y / 2;
197    transform.scale = scale;
198  }
199
200  function transformToScreen(x, y) {
201    if (panController.getScreenCTM) {
202      var parentCTM = panController.getScreenCTM();
203      var parentScaleX = parentCTM.a;
204      var parentScaleY = parentCTM.d;
205      var parentOffsetX = parentCTM.e;
206      var parentOffsetY = parentCTM.f;
207      storedCTMResult.x = x * parentScaleX - parentOffsetX;
208      storedCTMResult.y = y * parentScaleY - parentOffsetY;
209    } else {
210      storedCTMResult.x = x;
211      storedCTMResult.y = y;
212    }
213
214    return storedCTMResult;
215  }
216
217  function autocenter() {
218    var w; // width of the parent
219    var h; // height of the parent
220    var left = 0;
221    var top = 0;
222    var sceneBoundingBox = getBoundingBox();
223    if (sceneBoundingBox) {
224      // If we have bounding box - use it.
225      left = sceneBoundingBox.left;
226      top = sceneBoundingBox.top;
227      w = sceneBoundingBox.right - sceneBoundingBox.left;
228      h = sceneBoundingBox.bottom - sceneBoundingBox.top;
229    } else {
230      // otherwise just use whatever space we have
231      var ownerRect = owner.getBoundingClientRect();
232      w = ownerRect.width;
233      h = ownerRect.height;
234    }
235    var bbox = panController.getBBox();
236    if (bbox.width === 0 || bbox.height === 0) {
237      // we probably do not have any elements in the SVG
238      // just bail out;
239      return;
240    }
241    var dh = h / bbox.height;
242    var dw = w / bbox.width;
243    var scale = Math.min(dw, dh);
244    transform.x = -(bbox.left + bbox.width / 2) * scale + w / 2 + left;
245    transform.y = -(bbox.top + bbox.height / 2) * scale + h / 2 + top;
246    transform.scale = scale;
247  }
248
249  function getTransformModel() {
250    // TODO: should this be read only?
251    return transform;
252  }
253
254  function getMinZoom() {
255    return minZoom;
256  }
257
258  function setMinZoom(newMinZoom) {
259    minZoom = newMinZoom;
260  }
261
262  function getMaxZoom() {
263    return maxZoom;
264  }
265
266  function setMaxZoom(newMaxZoom) {
267    maxZoom = newMaxZoom;
268  }
269
270  function getTransformOrigin() {
271    return transformOrigin;
272  }
273
274  function setTransformOrigin(newTransformOrigin) {
275    transformOrigin = parseTransformOrigin(newTransformOrigin);
276  }
277
278  function getZoomSpeed() {
279    return speed;
280  }
281
282  function setZoomSpeed(newSpeed) {
283    if (!Number.isFinite(newSpeed)) {
284      throw new Error('Zoom speed should be a number');
285    }
286    speed = newSpeed;
287  }
288
289  function getPoint() {
290    return {
291      x: transform.x,
292      y: transform.y
293    };
294  }
295
296  function moveTo(x, y) {
297    transform.x = x;
298    transform.y = y;
299
300    keepTransformInsideBounds();
301
302    triggerEvent('pan');
303    makeDirty();
304  }
305
306  function moveBy(dx, dy) {
307    moveTo(transform.x + dx, transform.y + dy);
308  }
309
310  function keepTransformInsideBounds() {
311    var boundingBox = getBoundingBox();
312    if (!boundingBox) return;
313
314    var adjusted = false;
315    var clientRect = getClientRect();
316
317    var diff = boundingBox.left - clientRect.right;
318    if (diff > 0) {
319      transform.x += diff;
320      adjusted = true;
321    }
322    // check the other side:
323    diff = boundingBox.right - clientRect.left;
324    if (diff < 0) {
325      transform.x += diff;
326      adjusted = true;
327    }
328
329    // y axis:
330    diff = boundingBox.top - clientRect.bottom;
331    if (diff > 0) {
332      // we adjust transform, so that it matches exactly our bounding box:
333      // transform.y = boundingBox.top - (boundingBox.height + boundingBox.y) * transform.scale =>
334      // transform.y = boundingBox.top - (clientRect.bottom - transform.y) =>
335      // transform.y = diff + transform.y =>
336      transform.y += diff;
337      adjusted = true;
338    }
339
340    diff = boundingBox.bottom - clientRect.top;
341    if (diff < 0) {
342      transform.y += diff;
343      adjusted = true;
344    }
345    return adjusted;
346  }
347
348  /**
349   * Returns bounding box that should be used to restrict scene movement.
350   */
351  function getBoundingBox() {
352    if (!bounds) return; // client does not want to restrict movement
353
354    if (typeof bounds === 'boolean') {
355      // for boolean type we use parent container bounds
356      var ownerRect = owner.getBoundingClientRect();
357      var sceneWidth = ownerRect.width;
358      var sceneHeight = ownerRect.height;
359
360      return {
361        left: sceneWidth * boundsPadding,
362        top: sceneHeight * boundsPadding,
363        right: sceneWidth * (1 - boundsPadding),
364        bottom: sceneHeight * (1 - boundsPadding)
365      };
366    }
367
368    return bounds;
369  }
370
371  function getClientRect() {
372    var bbox = panController.getBBox();
373    var leftTop = client(bbox.left, bbox.top);
374
375    return {
376      left: leftTop.x,
377      top: leftTop.y,
378      right: bbox.width * transform.scale + leftTop.x,
379      bottom: bbox.height * transform.scale + leftTop.y
380    };
381  }
382
383  function client(x, y) {
384    return {
385      x: x * transform.scale + transform.x,
386      y: y * transform.scale + transform.y
387    };
388  }
389
390  function makeDirty() {
391    isDirty = true;
392    frameAnimation = window.requestAnimationFrame(frame);
393  }
394
395  function zoomByRatio(clientX, clientY, ratio) {
396    if (isNaN(clientX) || isNaN(clientY) || isNaN(ratio)) {
397      throw new Error('zoom requires valid numbers');
398    }
399
400    var newScale = transform.scale * ratio;
401
402    if (newScale < minZoom) {
403      if (transform.scale === minZoom) return;
404
405      ratio = minZoom / transform.scale;
406    }
407    if (newScale > maxZoom) {
408      if (transform.scale === maxZoom) return;
409
410      ratio = maxZoom / transform.scale;
411    }
412
413    var size = transformToScreen(clientX, clientY);
414
415    transform.x = size.x - ratio * (size.x - transform.x);
416    transform.y = size.y - ratio * (size.y - transform.y);
417
418    // TODO: https://github.com/anvaka/panzoom/issues/112
419    if (bounds && boundsPadding === 1 && minZoom === 1) {
420      transform.scale *= ratio;
421      keepTransformInsideBounds();
422    } else {
423      var transformAdjusted = keepTransformInsideBounds();
424      if (!transformAdjusted) transform.scale *= ratio;
425    }
426
427    triggerEvent('zoom');
428
429    makeDirty();
430  }
431
432  function zoomAbs(clientX, clientY, zoomLevel) {
433    var ratio = zoomLevel / transform.scale;
434    zoomByRatio(clientX, clientY, ratio);
435  }
436
437  function centerOn(ui) {
438    var parent = ui.ownerSVGElement;
439    if (!parent)
440      throw new Error('ui element is required to be within the scene');
441
442    // TODO: should i use controller's screen CTM?
443    var clientRect = ui.getBoundingClientRect();
444    var cx = clientRect.left + clientRect.width / 2;
445    var cy = clientRect.top + clientRect.height / 2;
446
447    var container = parent.getBoundingClientRect();
448    var dx = container.width / 2 - cx;
449    var dy = container.height / 2 - cy;
450
451    internalMoveBy(dx, dy, true);
452  }
453
454  function smoothMoveTo(x, y){
455    internalMoveBy(x - transform.x, y - transform.y, true);
456  }
457
458  function internalMoveBy(dx, dy, smooth) {
459    if (!smooth) {
460      return moveBy(dx, dy);
461    }
462
463    if (moveByAnimation) moveByAnimation.cancel();
464
465    var from = { x: 0, y: 0 };
466    var to = { x: dx, y: dy };
467    var lastX = 0;
468    var lastY = 0;
469
470    moveByAnimation = animate(from, to, {
471      step: function (v) {
472        moveBy(v.x - lastX, v.y - lastY);
473
474        lastX = v.x;
475        lastY = v.y;
476      }
477    });
478  }
479
480  function scroll(x, y) {
481    cancelZoomAnimation();
482    moveTo(x, y);
483  }
484
485  function dispose() {
486    releaseEvents();
487  }
488
489  function listenForEvents() {
490    owner.addEventListener('mousedown', onMouseDown, { passive: false });
491    owner.addEventListener('dblclick', onDoubleClick, { passive: false });
492    owner.addEventListener('touchstart', onTouch, { passive: false });
493    owner.addEventListener('keydown', onKeyDown, { passive: false });
494
495    // Need to listen on the owner container, so that we are not limited
496    // by the size of the scrollable domElement
497    wheel.addWheelListener(owner, onMouseWheel, { passive: false });
498
499    makeDirty();
500  }
501
502  function releaseEvents() {
503    wheel.removeWheelListener(owner, onMouseWheel);
504    owner.removeEventListener('mousedown', onMouseDown);
505    owner.removeEventListener('keydown', onKeyDown);
506    owner.removeEventListener('dblclick', onDoubleClick);
507    owner.removeEventListener('touchstart', onTouch);
508
509    if (frameAnimation) {
510      window.cancelAnimationFrame(frameAnimation);
511      frameAnimation = 0;
512    }
513
514    smoothScroll.cancel();
515
516    releaseDocumentMouse();
517    releaseTouches();
518    textSelection.release();
519
520    triggerPanEnd();
521  }
522
523  function frame() {
524    if (isDirty) applyTransform();
525  }
526
527  function applyTransform() {
528    isDirty = false;
529
530    // TODO: Should I allow to cancel this?
531    panController.applyTransform(transform);
532
533    triggerEvent('transform');
534    frameAnimation = 0;
535  }
536
537  function onKeyDown(e) {
538    var x = 0,
539      y = 0,
540      z = 0;
541    if (e.keyCode === 38) {
542      y = 1; // up
543    } else if (e.keyCode === 40) {
544      y = -1; // down
545    } else if (e.keyCode === 37) {
546      x = 1; // left
547    } else if (e.keyCode === 39) {
548      x = -1; // right
549    } else if (e.keyCode === 189 || e.keyCode === 109) {
550      // DASH or SUBTRACT
551      z = 1; // `-` -  zoom out
552    } else if (e.keyCode === 187 || e.keyCode === 107) {
553      // EQUAL SIGN or ADD
554      z = -1; // `=` - zoom in (equal sign on US layout is under `+`)
555    }
556
557    if (filterKey(e, x, y, z)) {
558      // They don't want us to handle the key: https://github.com/anvaka/panzoom/issues/45
559      return;
560    }
561
562    if (x || y) {
563      e.preventDefault();
564      e.stopPropagation();
565
566      var clientRect = owner.getBoundingClientRect();
567      // movement speed should be the same in both X and Y direction:
568      var offset = Math.min(clientRect.width, clientRect.height);
569      var moveSpeedRatio = 0.05;
570      var dx = offset * moveSpeedRatio * x;
571      var dy = offset * moveSpeedRatio * y;
572
573      // TODO: currently we do not animate this. It could be better to have animation
574      internalMoveBy(dx, dy);
575    }
576
577    if (z) {
578      var scaleMultiplier = getScaleMultiplier(z * 100);
579      var offset = transformOrigin ? getTransformOriginOffset() : midPoint();
580      publicZoomTo(offset.x, offset.y, scaleMultiplier);
581    }
582  }
583
584  function midPoint() {
585    var ownerRect = owner.getBoundingClientRect();
586    return {
587      x: ownerRect.width / 2,
588      y: ownerRect.height / 2
589    };
590  }
591
592  function onTouch(e) {
593    // let them override the touch behavior
594    beforeTouch(e);
595    clearPendingClickEventTimeout();
596
597    if (e.touches.length === 1) {
598      return handleSingleFingerTouch(e, e.touches[0]);
599    } else if (e.touches.length === 2) {
600      // handleTouchMove() will care about pinch zoom.
601      pinchZoomLength = getPinchZoomLength(e.touches[0], e.touches[1]);
602      multiTouch = true;
603      startTouchListenerIfNeeded();
604    }
605  }
606
607  function beforeTouch(e) {
608    // TODO: Need to unify this filtering names. E.g. use `beforeTouch`
609    if (options.onTouch && !options.onTouch(e)) {
610      // if they return `false` from onTouch, we don't want to stop
611      // events propagation. Fixes https://github.com/anvaka/panzoom/issues/12
612      return;
613    }
614
615    e.stopPropagation();
616    e.preventDefault();
617  }
618
619  function beforeDoubleClick(e) {
620    clearPendingClickEventTimeout();
621
622    // TODO: Need to unify this filtering names. E.g. use `beforeDoubleClick``
623    if (options.onDoubleClick && !options.onDoubleClick(e)) {
624      // if they return `false` from onTouch, we don't want to stop
625      // events propagation. Fixes https://github.com/anvaka/panzoom/issues/46
626      return;
627    }
628
629    e.preventDefault();
630    e.stopPropagation();
631  }
632
633  function handleSingleFingerTouch(e) {
634    lastTouchStartTime = new Date();
635    var touch = e.touches[0];
636    var offset = getOffsetXY(touch);
637    lastSingleFingerOffset = offset;
638    var point = transformToScreen(offset.x, offset.y);
639    mouseX = point.x;
640    mouseY = point.y;
641    clickX = mouseX;
642    clickY = mouseY;
643
644    smoothScroll.cancel();
645    startTouchListenerIfNeeded();
646  }
647
648  function startTouchListenerIfNeeded() {
649    if (touchInProgress) {
650      // no need to do anything, as we already listen to events;
651      return;
652    }
653
654    touchInProgress = true;
655    document.addEventListener('touchmove', handleTouchMove);
656    document.addEventListener('touchend', handleTouchEnd);
657    document.addEventListener('touchcancel', handleTouchEnd);
658  }
659
660  function handleTouchMove(e) {
661    if (e.touches.length === 1) {
662      e.stopPropagation();
663      var touch = e.touches[0];
664
665      var offset = getOffsetXY(touch);
666      var point = transformToScreen(offset.x, offset.y);
667
668      var dx = point.x - mouseX;
669      var dy = point.y - mouseY;
670
671      if (dx !== 0 && dy !== 0) {
672        triggerPanStart();
673      }
674      mouseX = point.x;
675      mouseY = point.y;
676      internalMoveBy(dx, dy);
677    } else if (e.touches.length === 2) {
678      // it's a zoom, let's find direction
679      multiTouch = true;
680      var t1 = e.touches[0];
681      var t2 = e.touches[1];
682      var currentPinchLength = getPinchZoomLength(t1, t2);
683
684      // since the zoom speed is always based on distance from 1, we need to apply
685      // pinch speed only on that distance from 1:
686      var scaleMultiplier =
687        1 + (currentPinchLength / pinchZoomLength - 1) * pinchSpeed;
688
689      var firstTouchPoint = getOffsetXY(t1);
690      var secondTouchPoint = getOffsetXY(t2);
691      mouseX = (firstTouchPoint.x + secondTouchPoint.x) / 2;
692      mouseY = (firstTouchPoint.y + secondTouchPoint.y) / 2;
693      if (transformOrigin) {
694        var offset = getTransformOriginOffset();
695        mouseX = offset.x;
696        mouseY = offset.y;
697      }
698
699      publicZoomTo(mouseX, mouseY, scaleMultiplier);
700
701      pinchZoomLength = currentPinchLength;
702      e.stopPropagation();
703      e.preventDefault();
704    }
705  }
706
707  function clearPendingClickEventTimeout() {
708    if (pendingClickEventTimeout) {
709      clearTimeout(pendingClickEventTimeout);
710      pendingClickEventTimeout = 0;
711    }
712  }
713
714  function handlePotentialClickEvent(e) {
715    // we could still be in the double tap mode, let's wait until double tap expires,
716    // and then notify:
717    if (!options.onClick) return;
718    clearPendingClickEventTimeout();
719    var dx = mouseX - clickX;
720    var dy = mouseY - clickY;
721    var l = Math.sqrt(dx * dx + dy * dy);
722    if (l > 5) return; // probably they are panning, ignore it
723
724    pendingClickEventTimeout = setTimeout(function() {
725      pendingClickEventTimeout = 0;
726      options.onClick(e);
727    }, doubleTapSpeedInMS);
728  }
729
730  function handleTouchEnd(e) {
731    clearPendingClickEventTimeout();
732    if (e.touches.length > 0) {
733      var offset = getOffsetXY(e.touches[0]);
734      var point = transformToScreen(offset.x, offset.y);
735      mouseX = point.x;
736      mouseY = point.y;
737    } else {
738      var now = new Date();
739      if (now - lastTouchEndTime < doubleTapSpeedInMS) {
740        // They did a double tap here
741        if (transformOrigin) {
742          var offset = getTransformOriginOffset();
743          smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed);
744        } else {
745          // We want untransformed x/y here.
746          smoothZoom(lastSingleFingerOffset.x, lastSingleFingerOffset.y, zoomDoubleClickSpeed);
747        }
748      } else if (now - lastTouchStartTime < clickEventTimeInMS) {
749        handlePotentialClickEvent(e);
750      }
751
752      lastTouchEndTime = now;
753
754      triggerPanEnd();
755      releaseTouches();
756    }
757  }
758
759  function getPinchZoomLength(finger1, finger2) {
760    var dx = finger1.clientX - finger2.clientX;
761    var dy = finger1.clientY - finger2.clientY;
762    return Math.sqrt(dx * dx + dy * dy);
763  }
764
765  function onDoubleClick(e) {
766    beforeDoubleClick(e);
767    var offset = getOffsetXY(e);
768    if (transformOrigin) {
769      // TODO: looks like this is duplicated in the file.
770      // Need to refactor
771      offset = getTransformOriginOffset();
772    }
773    smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed);
774  }
775
776  function onMouseDown(e) {
777    clearPendingClickEventTimeout();
778
779    // if client does not want to handle this event - just ignore the call
780    if (beforeMouseDown(e)) return;
781
782    lastMouseDownedEvent = e;
783    lastMouseDownTime = new Date();
784
785    if (touchInProgress) {
786      // modern browsers will fire mousedown for touch events too
787      // we do not want this: touch is handled separately.
788      e.stopPropagation();
789      return false;
790    }
791    // for IE, left click == 1
792    // for Firefox, left click == 0
793    var isLeftButton =
794      (e.button === 1 && window.event !== null) || e.button === 0;
795    if (!isLeftButton) return;
796
797    smoothScroll.cancel();
798
799    var offset = getOffsetXY(e);
800    var point = transformToScreen(offset.x, offset.y);
801    clickX = mouseX = point.x;
802    clickY = mouseY = point.y;
803
804    // We need to listen on document itself, since mouse can go outside of the
805    // window, and we will loose it
806    document.addEventListener('mousemove', onMouseMove);
807    document.addEventListener('mouseup', onMouseUp);
808    textSelection.capture(e.target || e.srcElement);
809
810    return false;
811  }
812
813  function onMouseMove(e) {
814    // no need to worry about mouse events when touch is happening
815    if (touchInProgress) return;
816
817    triggerPanStart();
818
819    var offset = getOffsetXY(e);
820    var point = transformToScreen(offset.x, offset.y);
821    var dx = point.x - mouseX;
822    var dy = point.y - mouseY;
823
824    mouseX = point.x;
825    mouseY = point.y;
826
827    internalMoveBy(dx, dy);
828  }
829
830  function onMouseUp() {
831    var now = new Date();
832    if (now - lastMouseDownTime < clickEventTimeInMS) handlePotentialClickEvent(lastMouseDownedEvent);
833    textSelection.release();
834    triggerPanEnd();
835    releaseDocumentMouse();
836  }
837
838  function releaseDocumentMouse() {
839    document.removeEventListener('mousemove', onMouseMove);
840    document.removeEventListener('mouseup', onMouseUp);
841    panstartFired = false;
842  }
843
844  function releaseTouches() {
845    document.removeEventListener('touchmove', handleTouchMove);
846    document.removeEventListener('touchend', handleTouchEnd);
847    document.removeEventListener('touchcancel', handleTouchEnd);
848    panstartFired = false;
849    multiTouch = false;
850    touchInProgress = false;
851  }
852
853  function onMouseWheel(e) {
854    // if client does not want to handle this event - just ignore the call
855    if (beforeWheel(e)) return;
856
857    smoothScroll.cancel();
858
859    var delta = e.deltaY;
860    if (e.deltaMode > 0) delta *= 100;
861
862    var scaleMultiplier = getScaleMultiplier(delta);
863
864    if (scaleMultiplier !== 1) {
865      var offset = transformOrigin
866        ? getTransformOriginOffset()
867        : getOffsetXY(e);
868      publicZoomTo(offset.x, offset.y, scaleMultiplier);
869      e.preventDefault();
870    }
871  }
872
873  function getOffsetXY(e) {
874    var offsetX, offsetY;
875    // I tried using e.offsetX, but that gives wrong results for svg, when user clicks on a path.
876    var ownerRect = owner.getBoundingClientRect();
877    offsetX = e.clientX - ownerRect.left;
878    offsetY = e.clientY - ownerRect.top;
879
880    return { x: offsetX, y: offsetY };
881  }
882
883  function smoothZoom(clientX, clientY, scaleMultiplier) {
884    var fromValue = transform.scale;
885    var from = { scale: fromValue };
886    var to = { scale: scaleMultiplier * fromValue };
887
888    smoothScroll.cancel();
889    cancelZoomAnimation();
890
891    zoomToAnimation = animate(from, to, {
892      step: function (v) {
893        zoomAbs(clientX, clientY, v.scale);
894      },
895      done: triggerZoomEnd
896    });
897  }
898
899  function smoothZoomAbs(clientX, clientY, toScaleValue) {
900    var fromValue = transform.scale;
901    var from = { scale: fromValue };
902    var to = { scale: toScaleValue };
903
904    smoothScroll.cancel();
905    cancelZoomAnimation();
906
907    zoomToAnimation = animate(from, to, {
908      step: function (v) {
909        zoomAbs(clientX, clientY, v.scale);
910      }
911    });
912  }
913
914  function getTransformOriginOffset() {
915    var ownerRect = owner.getBoundingClientRect();
916    return {
917      x: ownerRect.width * transformOrigin.x,
918      y: ownerRect.height * transformOrigin.y
919    };
920  }
921
922  function publicZoomTo(clientX, clientY, scaleMultiplier) {
923    smoothScroll.cancel();
924    cancelZoomAnimation();
925    return zoomByRatio(clientX, clientY, scaleMultiplier);
926  }
927
928  function cancelZoomAnimation() {
929    if (zoomToAnimation) {
930      zoomToAnimation.cancel();
931      zoomToAnimation = null;
932    }
933  }
934
935  function getScaleMultiplier(delta) {
936    var sign = Math.sign(delta);
937    var deltaAdjustedSpeed = Math.min(0.25, Math.abs(speed * delta / 128));
938    return 1 - sign * deltaAdjustedSpeed;
939  }
940
941  function triggerPanStart() {
942    if (!panstartFired) {
943      triggerEvent('panstart');
944      panstartFired = true;
945      smoothScroll.start();
946    }
947  }
948
949  function triggerPanEnd() {
950    if (panstartFired) {
951      // we should never run smooth scrolling if it was multiTouch (pinch zoom animation):
952      if (!multiTouch) smoothScroll.stop();
953      triggerEvent('panend');
954    }
955  }
956
957  function triggerZoomEnd() {
958    triggerEvent('zoomend');
959  }
960
961  function triggerEvent(name) {
962    api.fire(name, api);
963  }
964}
965
966function parseTransformOrigin(options) {
967  if (!options) return;
968  if (typeof options === 'object') {
969    if (!isNumber(options.x) || !isNumber(options.y))
970      failTransformOrigin(options);
971    return options;
972  }
973
974  failTransformOrigin();
975}
976
977function failTransformOrigin(options) {
978  console.error(options);
979  throw new Error(
980    [
981      'Cannot parse transform origin.',
982      'Some good examples:',
983      '  "center center" can be achieved with {x: 0.5, y: 0.5}',
984      '  "top center" can be achieved with {x: 0.5, y: 0}',
985      '  "bottom right" can be achieved with {x: 1, y: 1}'
986    ].join('\n')
987  );
988}
989
990function noop() { }
991
992function validateBounds(bounds) {
993  var boundsType = typeof bounds;
994  if (boundsType === 'undefined' || boundsType === 'boolean') return; // this is okay
995  // otherwise need to be more thorough:
996  var validBounds =
997    isNumber(bounds.left) &&
998    isNumber(bounds.top) &&
999    isNumber(bounds.bottom) &&
1000    isNumber(bounds.right);
1001
1002  if (!validBounds)
1003    throw new Error(
1004      'Bounds object is not valid. It can be: ' +
1005      'undefined, boolean (true|false) or an object {left, top, right, bottom}'
1006    );
1007}
1008
1009function isNumber(x) {
1010  return Number.isFinite(x);
1011}
1012
1013// IE 11 does not support isNaN:
1014function isNaN(value) {
1015  if (Number.isNaN) {
1016    return Number.isNaN(value);
1017  }
1018
1019  return value !== value;
1020}
1021
1022function rigidScroll() {
1023  return {
1024    start: noop,
1025    stop: noop,
1026    cancel: noop
1027  };
1028}
1029
1030function autoRun() {
1031  if (typeof document === 'undefined') return;
1032
1033  var scripts = document.getElementsByTagName('script');
1034  if (!scripts) return;
1035  var panzoomScript;
1036
1037  for (var i = 0; i < scripts.length; ++i) {
1038    var x = scripts[i];
1039    if (x.src && x.src.match(/\bpanzoom(\.min)?\.js/)) {
1040      panzoomScript = x;
1041      break;
1042    }
1043  }
1044
1045  if (!panzoomScript) return;
1046
1047  var query = panzoomScript.getAttribute('query');
1048  if (!query) return;
1049
1050  var globalName = panzoomScript.getAttribute('name') || 'pz';
1051  var started = Date.now();
1052
1053  tryAttach();
1054
1055  function tryAttach() {
1056    var el = document.querySelector(query);
1057    if (!el) {
1058      var now = Date.now();
1059      var elapsed = now - started;
1060      if (elapsed < 2000) {
1061        // Let's wait a bit
1062        setTimeout(tryAttach, 100);
1063        return;
1064      }
1065      // If we don't attach within 2 seconds to the target element, consider it a failure
1066      console.error('Cannot find the panzoom element', globalName);
1067      return;
1068    }
1069    var options = collectOptions(panzoomScript);
1070    console.log(options);
1071    window[globalName] = createPanZoom(el, options);
1072  }
1073
1074  function collectOptions(script) {
1075    var attrs = script.attributes;
1076    var options = {};
1077    for (var j = 0; j < attrs.length; ++j) {
1078      var attr = attrs[j];
1079      var nameValue = getPanzoomAttributeNameValue(attr);
1080      if (nameValue) {
1081        options[nameValue.name] = nameValue.value;
1082      }
1083    }
1084
1085    return options;
1086  }
1087
1088  function getPanzoomAttributeNameValue(attr) {
1089    if (!attr.name) return;
1090    var isPanZoomAttribute =
1091      attr.name[0] === 'p' && attr.name[1] === 'z' && attr.name[2] === '-';
1092
1093    if (!isPanZoomAttribute) return;
1094
1095    var name = attr.name.substr(3);
1096    var value = JSON.parse(attr.value);
1097    return { name: name, value: value };
1098  }
1099}
1100
1101autoRun();
1102
1103},{"./lib/kinetic.js":2,"./lib/makeDomController.js":3,"./lib/makeSvgController.js":4,"./lib/makeTextSelectionInterceptor.js":5,"./lib/transform.js":6,"amator":7,"ngraph.events":9,"wheel":10}],2:[function(require,module,exports){
1104/**
1105 * Allows smooth kinetic scrolling of the surface
1106 */
1107module.exports = kinetic;
1108
1109function kinetic(getPoint, scroll, settings) {
1110  if (typeof settings !== 'object') {
1111    // setting could come as boolean, we should ignore it, and use an object.
1112    settings = {};
1113  }
1114
1115  var minVelocity = typeof settings.minVelocity === 'number' ? settings.minVelocity : 5;
1116  var amplitude = typeof settings.amplitude === 'number' ? settings.amplitude : 0.25;
1117  var cancelAnimationFrame = typeof settings.cancelAnimationFrame === 'function' ? settings.cancelAnimationFrame : getCancelAnimationFrame();
1118  var requestAnimationFrame = typeof settings.requestAnimationFrame === 'function' ? settings.requestAnimationFrame : getRequestAnimationFrame();
1119
1120  var lastPoint;
1121  var timestamp;
1122  var timeConstant = 342;
1123
1124  var ticker;
1125  var vx, targetX, ax;
1126  var vy, targetY, ay;
1127
1128  var raf;
1129
1130  return {
1131    start: start,
1132    stop: stop,
1133    cancel: dispose
1134  };
1135
1136  function dispose() {
1137    cancelAnimationFrame(ticker);
1138    cancelAnimationFrame(raf);
1139  }
1140
1141  function start() {
1142    lastPoint = getPoint();
1143
1144    ax = ay = vx = vy = 0;
1145    timestamp = new Date();
1146
1147    cancelAnimationFrame(ticker);
1148    cancelAnimationFrame(raf);
1149
1150    // we start polling the point position to accumulate velocity
1151    // Once we stop(), we will use accumulated velocity to keep scrolling
1152    // an object.
1153    ticker = requestAnimationFrame(track);
1154  }
1155
1156  function track() {
1157    var now = Date.now();
1158    var elapsed = now - timestamp;
1159    timestamp = now;
1160
1161    var currentPoint = getPoint();
1162
1163    var dx = currentPoint.x - lastPoint.x;
1164    var dy = currentPoint.y - lastPoint.y;
1165
1166    lastPoint = currentPoint;
1167
1168    var dt = 1000 / (1 + elapsed);
1169
1170    // moving average
1171    vx = 0.8 * dx * dt + 0.2 * vx;
1172    vy = 0.8 * dy * dt + 0.2 * vy;
1173
1174    ticker = requestAnimationFrame(track);
1175  }
1176
1177  function stop() {
1178    cancelAnimationFrame(ticker);
1179    cancelAnimationFrame(raf);
1180
1181    var currentPoint = getPoint();
1182
1183    targetX = currentPoint.x;
1184    targetY = currentPoint.y;
1185    timestamp = Date.now();
1186
1187    if (vx < -minVelocity || vx > minVelocity) {
1188      ax = amplitude * vx;
1189      targetX += ax;
1190    }
1191
1192    if (vy < -minVelocity || vy > minVelocity) {
1193      ay = amplitude * vy;
1194      targetY += ay;
1195    }
1196
1197    raf = requestAnimationFrame(autoScroll);
1198  }
1199
1200  function autoScroll() {
1201    var elapsed = Date.now() - timestamp;
1202
1203    var moving = false;
1204    var dx = 0;
1205    var dy = 0;
1206
1207    if (ax) {
1208      dx = -ax * Math.exp(-elapsed / timeConstant);
1209
1210      if (dx > 0.5 || dx < -0.5) moving = true;
1211      else dx = ax = 0;
1212    }
1213
1214    if (ay) {
1215      dy = -ay * Math.exp(-elapsed / timeConstant);
1216
1217      if (dy > 0.5 || dy < -0.5) moving = true;
1218      else dy = ay = 0;
1219    }
1220
1221    if (moving) {
1222      scroll(targetX + dx, targetY + dy);
1223      raf = requestAnimationFrame(autoScroll);
1224    }
1225  }
1226}
1227
1228function getCancelAnimationFrame() {
1229  if (typeof cancelAnimationFrame === 'function') return cancelAnimationFrame;
1230  return clearTimeout;
1231}
1232
1233function getRequestAnimationFrame() {
1234  if (typeof requestAnimationFrame === 'function') return requestAnimationFrame;
1235
1236  return function (handler) {
1237    return setTimeout(handler, 16);
1238  };
1239}
1240},{}],3:[function(require,module,exports){
1241module.exports = makeDomController;
1242
1243module.exports.canAttach = isDomElement;
1244
1245function makeDomController(domElement, options) {
1246  var elementValid = isDomElement(domElement);
1247  if (!elementValid) {
1248    throw new Error('panzoom requires DOM element to be attached to the DOM tree');
1249  }
1250
1251  var owner = domElement.parentElement;
1252  domElement.scrollTop = 0;
1253
1254  if (!options.disableKeyboardInteraction) {
1255    owner.setAttribute('tabindex', 0);
1256  }
1257
1258  var api = {
1259    getBBox: getBBox,
1260    getOwner: getOwner,
1261    applyTransform: applyTransform,
1262  };
1263
1264  return api;
1265
1266  function getOwner() {
1267    return owner;
1268  }
1269
1270  function getBBox() {
1271    // TODO: We should probably cache this?
1272    return  {
1273      left: 0,
1274      top: 0,
1275      width: domElement.clientWidth,
1276      height: domElement.clientHeight
1277    };
1278  }
1279
1280  function applyTransform(transform) {
1281    // TODO: Should we cache this?
1282    domElement.style.transformOrigin = '0 0 0';
1283    domElement.style.transform = 'matrix(' +
1284      transform.scale + ', 0, 0, ' +
1285      transform.scale + ', ' +
1286      transform.x + ', ' + transform.y + ')';
1287  }
1288}
1289
1290function isDomElement(element) {
1291  return element && element.parentElement && element.style;
1292}
1293
1294},{}],4:[function(require,module,exports){
1295module.exports = makeSvgController;
1296module.exports.canAttach = isSVGElement;
1297
1298function makeSvgController(svgElement, options) {
1299  if (!isSVGElement(svgElement)) {
1300    throw new Error('svg element is required for svg.panzoom to work');
1301  }
1302
1303  var owner = svgElement.ownerSVGElement;
1304  if (!owner) {
1305    throw new Error(
1306      'Do not apply panzoom to the root <svg> element. ' +
1307      'Use its child instead (e.g. <g></g>). ' +
1308      'As of March 2016 only FireFox supported transform on the root element');
1309  }
1310
1311  if (!options.disableKeyboardInteraction) {
1312    owner.setAttribute('tabindex', 0);
1313  }
1314
1315  var api = {
1316    getBBox: getBBox,
1317    getScreenCTM: getScreenCTM,
1318    getOwner: getOwner,
1319    applyTransform: applyTransform,
1320    initTransform: initTransform
1321  };
1322
1323  return api;
1324
1325  function getOwner() {
1326    return owner;
1327  }
1328
1329  function getBBox() {
1330    var bbox =  svgElement.getBBox();
1331    return {
1332      left: bbox.x,
1333      top: bbox.y,
1334      width: bbox.width,
1335      height: bbox.height,
1336    };
1337  }
1338
1339  function getScreenCTM() {
1340    var ctm = owner.getCTM();
1341    if (!ctm) {
1342      // This is likely firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=873106
1343      // The code below is not entirely correct, but still better than nothing
1344      return owner.getScreenCTM();
1345    }
1346    return ctm;
1347  }
1348
1349  function initTransform(transform) {
1350    var screenCTM = svgElement.getCTM();
1351
1352    // The above line returns null on Firefox
1353    if (screenCTM === null) {
1354      screenCTM = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix();
1355    }
1356
1357    transform.x = screenCTM.e;
1358    transform.y = screenCTM.f;
1359    transform.scale = screenCTM.a;
1360    owner.removeAttributeNS(null, 'viewBox');
1361  }
1362
1363  function applyTransform(transform) {
1364    svgElement.setAttribute('transform', 'matrix(' +
1365      transform.scale + ' 0 0 ' +
1366      transform.scale + ' ' +
1367      transform.x + ' ' + transform.y + ')');
1368  }
1369}
1370
1371function isSVGElement(element) {
1372  return element && element.ownerSVGElement && element.getCTM;
1373}
1374},{}],5:[function(require,module,exports){
1375/**
1376 * Disallows selecting text.
1377 */
1378module.exports = makeTextSelectionInterceptor;
1379
1380function makeTextSelectionInterceptor(useFake) {
1381  if (useFake) {
1382    return {
1383      capture: noop,
1384      release: noop
1385    };
1386  }
1387
1388  var dragObject;
1389  var prevSelectStart;
1390  var prevDragStart;
1391  var wasCaptured = false;
1392
1393  return {
1394    capture: capture,
1395    release: release
1396  };
1397
1398  function capture(domObject) {
1399    wasCaptured = true;
1400    prevSelectStart = window.document.onselectstart;
1401    prevDragStart = window.document.ondragstart;
1402
1403    window.document.onselectstart = disabled;
1404
1405    dragObject = domObject;
1406    dragObject.ondragstart = disabled;
1407  }
1408
1409  function release() {
1410    if (!wasCaptured) return;
1411
1412    wasCaptured = false;
1413    window.document.onselectstart = prevSelectStart;
1414    if (dragObject) dragObject.ondragstart = prevDragStart;
1415  }
1416}
1417
1418function disabled(e) {
1419  e.stopPropagation();
1420  return false;
1421}
1422
1423function noop() {}
1424
1425},{}],6:[function(require,module,exports){
1426module.exports = Transform;
1427
1428function Transform() {
1429  this.x = 0;
1430  this.y = 0;
1431  this.scale = 1;
1432}
1433
1434},{}],7:[function(require,module,exports){
1435var BezierEasing = require('bezier-easing')
1436
1437// Predefined set of animations. Similar to CSS easing functions
1438var animations = {
1439  ease:  BezierEasing(0.25, 0.1, 0.25, 1),
1440  easeIn: BezierEasing(0.42, 0, 1, 1),
1441  easeOut: BezierEasing(0, 0, 0.58, 1),
1442  easeInOut: BezierEasing(0.42, 0, 0.58, 1),
1443  linear: BezierEasing(0, 0, 1, 1)
1444}
1445
1446
1447module.exports = animate;
1448module.exports.makeAggregateRaf = makeAggregateRaf;
1449module.exports.sharedScheduler = makeAggregateRaf();
1450
1451
1452function animate(source, target, options) {
1453  var start = Object.create(null)
1454  var diff = Object.create(null)
1455  options = options || {}
1456  // We let clients specify their own easing function
1457  var easing = (typeof options.easing === 'function') ? options.easing : animations[options.easing]
1458
1459  // if nothing is specified, default to ease (similar to CSS animations)
1460  if (!easing) {
1461    if (options.easing) {
1462      console.warn('Unknown easing function in amator: ' + options.easing);
1463    }
1464    easing = animations.ease
1465  }
1466
1467  var step = typeof options.step === 'function' ? options.step : noop
1468  var done = typeof options.done === 'function' ? options.done : noop
1469
1470  var scheduler = getScheduler(options.scheduler)
1471
1472  var keys = Object.keys(target)
1473  keys.forEach(function(key) {
1474    start[key] = source[key]
1475    diff[key] = target[key] - source[key]
1476  })
1477
1478  var durationInMs = typeof options.duration === 'number' ? options.duration : 400
1479  var durationInFrames = Math.max(1, durationInMs * 0.06) // 0.06 because 60 frames pers 1,000 ms
1480  var previousAnimationId
1481  var frame = 0
1482
1483  previousAnimationId = scheduler.next(loop)
1484
1485  return {
1486    cancel: cancel
1487  }
1488
1489  function cancel() {
1490    scheduler.cancel(previousAnimationId)
1491    previousAnimationId = 0
1492  }
1493
1494  function loop() {
1495    var t = easing(frame/durationInFrames)
1496    frame += 1
1497    setValues(t)
1498    if (frame <= durationInFrames) {
1499      previousAnimationId = scheduler.next(loop)
1500      step(source)
1501    } else {
1502      previousAnimationId = 0
1503      setTimeout(function() { done(source) }, 0)
1504    }
1505  }
1506
1507  function setValues(t) {
1508    keys.forEach(function(key) {
1509      source[key] = diff[key] * t + start[key]
1510    })
1511  }
1512}
1513
1514function noop() { }
1515
1516function getScheduler(scheduler) {
1517  if (!scheduler) {
1518    var canRaf = typeof window !== 'undefined' && window.requestAnimationFrame
1519    return canRaf ? rafScheduler() : timeoutScheduler()
1520  }
1521  if (typeof scheduler.next !== 'function') throw new Error('Scheduler is supposed to have next(cb) function')
1522  if (typeof scheduler.cancel !== 'function') throw new Error('Scheduler is supposed to have cancel(handle) function')
1523
1524  return scheduler
1525}
1526
1527function rafScheduler() {
1528  return {
1529    next: window.requestAnimationFrame.bind(window),
1530    cancel: window.cancelAnimationFrame.bind(window)
1531  }
1532}
1533
1534function timeoutScheduler() {
1535  return {
1536    next: function(cb) {
1537      return setTimeout(cb, 1000/60)
1538    },
1539    cancel: function (id) {
1540      return clearTimeout(id)
1541    }
1542  }
1543}
1544
1545function makeAggregateRaf() {
1546  var frontBuffer = new Set();
1547  var backBuffer = new Set();
1548  var frameToken = 0;
1549
1550  return {
1551    next: next,
1552    cancel: next,
1553    clearAll: clearAll
1554  }
1555
1556  function clearAll() {
1557    frontBuffer.clear();
1558    backBuffer.clear();
1559    cancelAnimationFrame(frameToken);
1560    frameToken = 0;
1561  }
1562
1563  function next(callback) {
1564    backBuffer.add(callback);
1565    renderNextFrame();
1566  }
1567
1568  function renderNextFrame() {
1569    if (!frameToken) frameToken = requestAnimationFrame(renderFrame);
1570  }
1571
1572  function renderFrame() {
1573    frameToken = 0;
1574
1575    var t = backBuffer;
1576    backBuffer = frontBuffer;
1577    frontBuffer = t;
1578
1579    frontBuffer.forEach(function(callback) {
1580      callback();
1581    });
1582    frontBuffer.clear();
1583  }
1584
1585  function cancel(callback) {
1586    backBuffer.delete(callback);
1587  }
1588}
1589
1590},{"bezier-easing":8}],8:[function(require,module,exports){
1591/**
1592 * https://github.com/gre/bezier-easing
1593 * BezierEasing - use bezier curve for transition easing function
1594 * by Gaëtan Renaudeau 2014 - 2015 – MIT License
1595 */
1596
1597// These values are established by empiricism with tests (tradeoff: performance VS precision)
1598var NEWTON_ITERATIONS = 4;
1599var NEWTON_MIN_SLOPE = 0.001;
1600var SUBDIVISION_PRECISION = 0.0000001;
1601var SUBDIVISION_MAX_ITERATIONS = 10;
1602
1603var kSplineTableSize = 11;
1604var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
1605
1606var float32ArraySupported = typeof Float32Array === 'function';
1607
1608function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
1609function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
1610function C (aA1)      { return 3.0 * aA1; }
1611
1612// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
1613function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
1614
1615// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
1616function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
1617
1618function binarySubdivide (aX, aA, aB, mX1, mX2) {
1619  var currentX, currentT, i = 0;
1620  do {
1621    currentT = aA + (aB - aA) / 2.0;
1622    currentX = calcBezier(currentT, mX1, mX2) - aX;
1623    if (currentX > 0.0) {
1624      aB = currentT;
1625    } else {
1626      aA = currentT;
1627    }
1628  } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
1629  return currentT;
1630}
1631
1632function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
1633 for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
1634   var currentSlope = getSlope(aGuessT, mX1, mX2);
1635   if (currentSlope === 0.0) {
1636     return aGuessT;
1637   }
1638   var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
1639   aGuessT -= currentX / currentSlope;
1640 }
1641 return aGuessT;
1642}
1643
1644function LinearEasing (x) {
1645  return x;
1646}
1647
1648module.exports = function bezier (mX1, mY1, mX2, mY2) {
1649  if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
1650    throw new Error('bezier x values must be in [0, 1] range');
1651  }
1652
1653  if (mX1 === mY1 && mX2 === mY2) {
1654    return LinearEasing;
1655  }
1656
1657  // Precompute samples table
1658  var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
1659  for (var i = 0; i < kSplineTableSize; ++i) {
1660    sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
1661  }
1662
1663  function getTForX (aX) {
1664    var intervalStart = 0.0;
1665    var currentSample = 1;
1666    var lastSample = kSplineTableSize - 1;
1667
1668    for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
1669      intervalStart += kSampleStepSize;
1670    }
1671    --currentSample;
1672
1673    // Interpolate to provide an initial guess for t
1674    var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
1675    var guessForT = intervalStart + dist * kSampleStepSize;
1676
1677    var initialSlope = getSlope(guessForT, mX1, mX2);
1678    if (initialSlope >= NEWTON_MIN_SLOPE) {
1679      return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
1680    } else if (initialSlope === 0.0) {
1681      return guessForT;
1682    } else {
1683      return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
1684    }
1685  }
1686
1687  return function BezierEasing (x) {
1688    // Because JavaScript number are imprecise, we should guarantee the extremes are right.
1689    if (x === 0) {
1690      return 0;
1691    }
1692    if (x === 1) {
1693      return 1;
1694    }
1695    return calcBezier(getTForX(x), mY1, mY2);
1696  };
1697};
1698
1699},{}],9:[function(require,module,exports){
1700module.exports = function eventify(subject) {
1701  validateSubject(subject);
1702
1703  var eventsStorage = createEventsStorage(subject);
1704  subject.on = eventsStorage.on;
1705  subject.off = eventsStorage.off;
1706  subject.fire = eventsStorage.fire;
1707  return subject;
1708};
1709
1710function createEventsStorage(subject) {
1711  // Store all event listeners to this hash. Key is event name, value is array
1712  // of callback records.
1713  //
1714  // A callback record consists of callback function and its optional context:
1715  // { 'eventName' => [{callback: function, ctx: object}] }
1716  var registeredEvents = Object.create(null);
1717
1718  return {
1719    on: function (eventName, callback, ctx) {
1720      if (typeof callback !== 'function') {
1721        throw new Error('callback is expected to be a function');
1722      }
1723      var handlers = registeredEvents[eventName];
1724      if (!handlers) {
1725        handlers = registeredEvents[eventName] = [];
1726      }
1727      handlers.push({callback: callback, ctx: ctx});
1728
1729      return subject;
1730    },
1731
1732    off: function (eventName, callback) {
1733      var wantToRemoveAll = (typeof eventName === 'undefined');
1734      if (wantToRemoveAll) {
1735        // Killing old events storage should be enough in this case:
1736        registeredEvents = Object.create(null);
1737        return subject;
1738      }
1739
1740      if (registeredEvents[eventName]) {
1741        var deleteAllCallbacksForEvent = (typeof callback !== 'function');
1742        if (deleteAllCallbacksForEvent) {
1743          delete registeredEvents[eventName];
1744        } else {
1745          var callbacks = registeredEvents[eventName];
1746          for (var i = 0; i < callbacks.length; ++i) {
1747            if (callbacks[i].callback === callback) {
1748              callbacks.splice(i, 1);
1749            }
1750          }
1751        }
1752      }
1753
1754      return subject;
1755    },
1756
1757    fire: function (eventName) {
1758      var callbacks = registeredEvents[eventName];
1759      if (!callbacks) {
1760        return subject;
1761      }
1762
1763      var fireArguments;
1764      if (arguments.length > 1) {
1765        fireArguments = Array.prototype.splice.call(arguments, 1);
1766      }
1767      for(var i = 0; i < callbacks.length; ++i) {
1768        var callbackInfo = callbacks[i];
1769        callbackInfo.callback.apply(callbackInfo.ctx, fireArguments);
1770      }
1771
1772      return subject;
1773    }
1774  };
1775}
1776
1777function validateSubject(subject) {
1778  if (!subject) {
1779    throw new Error('Eventify cannot use falsy object as events subject');
1780  }
1781  var reservedWords = ['on', 'fire', 'off'];
1782  for (var i = 0; i < reservedWords.length; ++i) {
1783    if (subject.hasOwnProperty(reservedWords[i])) {
1784      throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'");
1785    }
1786  }
1787}
1788
1789},{}],10:[function(require,module,exports){
1790/**
1791 * This module used to unify mouse wheel behavior between different browsers in 2014
1792 * Now it's just a wrapper around addEventListener('wheel');
1793 *
1794 * Usage:
1795 *  var addWheelListener = require('wheel').addWheelListener;
1796 *  var removeWheelListener = require('wheel').removeWheelListener;
1797 *  addWheelListener(domElement, function (e) {
1798 *    // mouse wheel event
1799 *  });
1800 *  removeWheelListener(domElement, function);
1801 */
1802
1803module.exports = addWheelListener;
1804
1805// But also expose "advanced" api with unsubscribe:
1806module.exports.addWheelListener = addWheelListener;
1807module.exports.removeWheelListener = removeWheelListener;
1808
1809
1810function addWheelListener(element, listener, useCapture) {
1811  element.addEventListener('wheel', listener, useCapture);
1812}
1813
1814function removeWheelListener( element, listener, useCapture ) {
1815  element.removeEventListener('wheel', listener, useCapture);
1816}
1817},{}]},{},[1])(1)
1818});

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.