mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-02 19:36:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1754 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1754 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
(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){
 | 
						||
'use strict';
 | 
						||
 | 
						||
/* globals SVGElement */
 | 
						||
/**
 | 
						||
 * Allows to drag and zoom svg elements
 | 
						||
 */
 | 
						||
var wheel = require('wheel');
 | 
						||
var animate = require('amator');
 | 
						||
var eventify = require('ngraph.events');
 | 
						||
var kinetic = require('./lib/kinetic.js');
 | 
						||
var createTextSelectionInterceptor = require('./lib/createTextSelectionInterceptor.js');
 | 
						||
var domTextSelectionInterceptor = createTextSelectionInterceptor();
 | 
						||
var fakeTextSelectorInterceptor = createTextSelectionInterceptor(true);
 | 
						||
var Transform = require('./lib/transform.js');
 | 
						||
var makeSvgController = require('./lib/svgController.js');
 | 
						||
var makeDomController = require('./lib/domController.js');
 | 
						||
 | 
						||
var defaultZoomSpeed = 1;
 | 
						||
var defaultDoubleTapZoomSpeed = 1.75;
 | 
						||
var doubleTapSpeedInMS = 300;
 | 
						||
 | 
						||
module.exports = createPanZoom;
 | 
						||
 | 
						||
/**
 | 
						||
 * Creates a new instance of panzoom, so that an object can be panned and zoomed
 | 
						||
 *
 | 
						||
 * @param {DOMElement} domElement where panzoom should be attached.
 | 
						||
 * @param {Object} options that configure behavior.
 | 
						||
 */
 | 
						||
function createPanZoom(domElement, options) {
 | 
						||
  options = options || {};
 | 
						||
 | 
						||
  var panController = options.controller;
 | 
						||
 | 
						||
  if (!panController) {
 | 
						||
    if (domElement instanceof SVGElement) {
 | 
						||
      panController = makeSvgController(domElement, options);
 | 
						||
    }
 | 
						||
 | 
						||
    if (domElement instanceof HTMLElement) {
 | 
						||
      panController = makeDomController(domElement, options);
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  if (!panController) {
 | 
						||
    throw new Error(
 | 
						||
      'Cannot create panzoom for the current type of dom element'
 | 
						||
    );
 | 
						||
  }
 | 
						||
  var owner = panController.getOwner();
 | 
						||
  // just to avoid GC pressure, every time we do intermediate transform
 | 
						||
  // we return this object. For internal use only. Never give it back to the consumer of this library
 | 
						||
  var storedCTMResult = { x: 0, y: 0 };
 | 
						||
 | 
						||
  var isDirty = false;
 | 
						||
  var transform = new Transform();
 | 
						||
 | 
						||
  if (panController.initTransform) {
 | 
						||
    panController.initTransform(transform);
 | 
						||
  }
 | 
						||
 | 
						||
  var filterKey = typeof options.filterKey === 'function' ? options.filterKey : noop;
 | 
						||
  // TODO: likely need to unite pinchSpeed with zoomSpeed
 | 
						||
  var pinchSpeed = typeof options.pinchSpeed === 'number' ? options.pinchSpeed : 1;
 | 
						||
  var bounds = options.bounds;
 | 
						||
  var maxZoom = typeof options.maxZoom === 'number' ? options.maxZoom : Number.POSITIVE_INFINITY;
 | 
						||
  var minZoom = typeof options.minZoom === 'number' ? options.minZoom : 0;
 | 
						||
 | 
						||
  var boundsPadding = typeof options.boundsPadding === 'number' ? options.boundsPadding : 0.05;
 | 
						||
  var zoomDoubleClickSpeed = typeof options.zoomDoubleClickSpeed === 'number' ? options.zoomDoubleClickSpeed : defaultDoubleTapZoomSpeed;
 | 
						||
  var beforeWheel = options.beforeWheel || noop;
 | 
						||
  var beforeMouseDown = options.beforeMouseDown || noop;
 | 
						||
  var speed = typeof options.zoomSpeed === 'number' ? options.zoomSpeed : defaultZoomSpeed;
 | 
						||
  var transformOrigin = parseTransformOrigin(options.transformOrigin);
 | 
						||
  var textSelection = options.enableTextSelection ? fakeTextSelectorInterceptor : domTextSelectionInterceptor;
 | 
						||
 | 
						||
  validateBounds(bounds);
 | 
						||
 | 
						||
  if (options.autocenter) {
 | 
						||
    autocenter();
 | 
						||
  }
 | 
						||
 | 
						||
  var frameAnimation;
 | 
						||
  var lastTouchEndTime = 0;
 | 
						||
  var lastSingleFingerOffset;
 | 
						||
  var touchInProgress = false;
 | 
						||
 | 
						||
  // We only need to fire panstart when actual move happens
 | 
						||
  var panstartFired = false;
 | 
						||
 | 
						||
  // cache mouse coordinates here
 | 
						||
  var mouseX;
 | 
						||
  var mouseY;
 | 
						||
 | 
						||
  var pinchZoomLength;
 | 
						||
 | 
						||
  var smoothScroll;
 | 
						||
  if ('smoothScroll' in options && !options.smoothScroll) {
 | 
						||
    // If user explicitly asked us not to use smooth scrolling, we obey
 | 
						||
    smoothScroll = rigidScroll();
 | 
						||
  } else {
 | 
						||
    // otherwise we use forward smoothScroll settings to kinetic API
 | 
						||
    // which makes scroll smoothing.
 | 
						||
    smoothScroll = kinetic(getPoint, scroll, options.smoothScroll);
 | 
						||
  }
 | 
						||
 | 
						||
  var moveByAnimation;
 | 
						||
  var zoomToAnimation;
 | 
						||
 | 
						||
  var multiTouch;
 | 
						||
  var paused = false;
 | 
						||
 | 
						||
  listenForEvents();
 | 
						||
 | 
						||
  var api = {
 | 
						||
    dispose: dispose,
 | 
						||
    moveBy: internalMoveBy,
 | 
						||
    moveTo: moveTo,
 | 
						||
    centerOn: centerOn,
 | 
						||
    zoomTo: publicZoomTo,
 | 
						||
    zoomAbs: zoomAbs,
 | 
						||
    smoothZoom: smoothZoom,
 | 
						||
    smoothZoomAbs: smoothZoomAbs,
 | 
						||
    showRectangle: showRectangle,
 | 
						||
 | 
						||
    pause: pause,
 | 
						||
    resume: resume,
 | 
						||
    isPaused: isPaused,
 | 
						||
 | 
						||
    getTransform: getTransformModel,
 | 
						||
 | 
						||
    getMinZoom: getMinZoom,
 | 
						||
    setMinZoom: setMinZoom,
 | 
						||
 | 
						||
    getMaxZoom: getMaxZoom,
 | 
						||
    setMaxZoom: setMaxZoom,
 | 
						||
 | 
						||
    getTransformOrigin: getTransformOrigin,
 | 
						||
    setTransformOrigin: setTransformOrigin,
 | 
						||
 | 
						||
    getZoomSpeed: getZoomSpeed,
 | 
						||
    setZoomSpeed: setZoomSpeed,
 | 
						||
 | 
						||
    getOwner: () => owner
 | 
						||
  };
 | 
						||
 | 
						||
  eventify(api);
 | 
						||
 | 
						||
  return api;
 | 
						||
 | 
						||
  function pause() {
 | 
						||
    releaseEvents();
 | 
						||
    paused = true;
 | 
						||
  }
 | 
						||
 | 
						||
  function resume() {
 | 
						||
    if (paused) {
 | 
						||
      listenForEvents();
 | 
						||
      paused = false;
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function isPaused() {
 | 
						||
    return paused;
 | 
						||
  }
 | 
						||
 | 
						||
  function showRectangle(rect) {
 | 
						||
    // TODO: this duplicates autocenter. I think autocenter should go.
 | 
						||
    var clientRect = owner.getBoundingClientRect();
 | 
						||
    var size = transformToScreen(clientRect.width, clientRect.height);
 | 
						||
 | 
						||
    var rectWidth = rect.right - rect.left;
 | 
						||
    var rectHeight = rect.bottom - rect.top;
 | 
						||
    if (!Number.isFinite(rectWidth) || !Number.isFinite(rectHeight)) {
 | 
						||
      throw new Error('Invalid rectangle');
 | 
						||
    }
 | 
						||
 | 
						||
    var dw = size.x / rectWidth;
 | 
						||
    var dh = size.y / rectHeight;
 | 
						||
    var scale = Math.min(dw, dh);
 | 
						||
    transform.x = -(rect.left + rectWidth / 2) * scale + size.x / 2;
 | 
						||
    transform.y = -(rect.top + rectHeight / 2) * scale + size.y / 2;
 | 
						||
    transform.scale = scale;
 | 
						||
  }
 | 
						||
 | 
						||
  function transformToScreen(x, y) {
 | 
						||
    if (panController.getScreenCTM) {
 | 
						||
      var parentCTM = panController.getScreenCTM();
 | 
						||
      var parentScaleX = parentCTM.a;
 | 
						||
      var parentScaleY = parentCTM.d;
 | 
						||
      var parentOffsetX = parentCTM.e;
 | 
						||
      var parentOffsetY = parentCTM.f;
 | 
						||
      storedCTMResult.x = x * parentScaleX - parentOffsetX;
 | 
						||
      storedCTMResult.y = y * parentScaleY - parentOffsetY;
 | 
						||
    } else {
 | 
						||
      storedCTMResult.x = x;
 | 
						||
      storedCTMResult.y = y;
 | 
						||
    }
 | 
						||
 | 
						||
    return storedCTMResult;
 | 
						||
  }
 | 
						||
 | 
						||
  function autocenter() {
 | 
						||
    var w; // width of the parent
 | 
						||
    var h; // height of the parent
 | 
						||
    var left = 0;
 | 
						||
    var top = 0;
 | 
						||
    var sceneBoundingBox = getBoundingBox();
 | 
						||
    if (sceneBoundingBox) {
 | 
						||
      // If we have bounding box - use it.
 | 
						||
      left = sceneBoundingBox.left;
 | 
						||
      top = sceneBoundingBox.top;
 | 
						||
      w = sceneBoundingBox.right - sceneBoundingBox.left;
 | 
						||
      h = sceneBoundingBox.bottom - sceneBoundingBox.top;
 | 
						||
    } else {
 | 
						||
      // otherwise just use whatever space we have
 | 
						||
      var ownerRect = owner.getBoundingClientRect();
 | 
						||
      w = ownerRect.width;
 | 
						||
      h = ownerRect.height;
 | 
						||
    }
 | 
						||
    var bbox = panController.getBBox();
 | 
						||
    if (bbox.width === 0 || bbox.height === 0) {
 | 
						||
      // we probably do not have any elements in the SVG
 | 
						||
      // just bail out;
 | 
						||
      return;
 | 
						||
    }
 | 
						||
    var dh = h / bbox.height;
 | 
						||
    var dw = w / bbox.width;
 | 
						||
    var scale = Math.min(dw, dh);
 | 
						||
    transform.x = -(bbox.left + bbox.width / 2) * scale + w / 2 + left;
 | 
						||
    transform.y = -(bbox.top + bbox.height / 2) * scale + h / 2 + top;
 | 
						||
    transform.scale = scale;
 | 
						||
  }
 | 
						||
 | 
						||
  function getTransformModel() {
 | 
						||
    // TODO: should this be read only?
 | 
						||
    return transform;
 | 
						||
  }
 | 
						||
 | 
						||
  function getMinZoom() {
 | 
						||
    return minZoom;
 | 
						||
  }
 | 
						||
 | 
						||
  function setMinZoom(newMinZoom) {
 | 
						||
    minZoom = newMinZoom;
 | 
						||
  }
 | 
						||
 | 
						||
  function getMaxZoom() {
 | 
						||
    return maxZoom;
 | 
						||
  }
 | 
						||
 | 
						||
  function setMaxZoom(newMaxZoom) {
 | 
						||
    maxZoom = newMaxZoom;
 | 
						||
  }
 | 
						||
 | 
						||
  function getTransformOrigin() {
 | 
						||
    return transformOrigin;
 | 
						||
  }
 | 
						||
 | 
						||
  function setTransformOrigin(newTransformOrigin) {
 | 
						||
    transformOrigin = parseTransformOrigin(newTransformOrigin);
 | 
						||
  }
 | 
						||
 | 
						||
  function getZoomSpeed() {
 | 
						||
    return speed;
 | 
						||
  }
 | 
						||
 | 
						||
  function setZoomSpeed(newSpeed) {
 | 
						||
    if (!Number.isFinite(newSpeed)) {
 | 
						||
      throw new Error('Zoom speed should be a number');
 | 
						||
    }
 | 
						||
    speed = newSpeed;
 | 
						||
  }
 | 
						||
 | 
						||
  function getPoint() {
 | 
						||
    return {
 | 
						||
      x: transform.x,
 | 
						||
      y: transform.y
 | 
						||
    };
 | 
						||
  }
 | 
						||
 | 
						||
  function moveTo(x, y) {
 | 
						||
    transform.x = x;
 | 
						||
    transform.y = y;
 | 
						||
 | 
						||
    keepTransformInsideBounds();
 | 
						||
 | 
						||
    triggerEvent('pan');
 | 
						||
    makeDirty();
 | 
						||
  }
 | 
						||
 | 
						||
  function moveBy(dx, dy) {
 | 
						||
    moveTo(transform.x + dx, transform.y + dy);
 | 
						||
  }
 | 
						||
 | 
						||
  function keepTransformInsideBounds() {
 | 
						||
    var boundingBox = getBoundingBox();
 | 
						||
    if (!boundingBox) return;
 | 
						||
 | 
						||
    var adjusted = false;
 | 
						||
    var clientRect = getClientRect();
 | 
						||
 | 
						||
    var diff = boundingBox.left - clientRect.right;
 | 
						||
    if (diff > 0) {
 | 
						||
      transform.x += diff;
 | 
						||
      adjusted = true;
 | 
						||
    }
 | 
						||
    // check the other side:
 | 
						||
    diff = boundingBox.right - clientRect.left;
 | 
						||
    if (diff < 0) {
 | 
						||
      transform.x += diff;
 | 
						||
      adjusted = true;
 | 
						||
    }
 | 
						||
 | 
						||
    // y axis:
 | 
						||
    diff = boundingBox.top - clientRect.bottom;
 | 
						||
    if (diff > 0) {
 | 
						||
      // we adjust transform, so that it matches exactly our bounding box:
 | 
						||
      // transform.y = boundingBox.top - (boundingBox.height + boundingBox.y) * transform.scale =>
 | 
						||
      // transform.y = boundingBox.top - (clientRect.bottom - transform.y) =>
 | 
						||
      // transform.y = diff + transform.y =>
 | 
						||
      transform.y += diff;
 | 
						||
      adjusted = true;
 | 
						||
    }
 | 
						||
 | 
						||
    diff = boundingBox.bottom - clientRect.top;
 | 
						||
    if (diff < 0) {
 | 
						||
      transform.y += diff;
 | 
						||
      adjusted = true;
 | 
						||
    }
 | 
						||
    return adjusted;
 | 
						||
  }
 | 
						||
 | 
						||
  /**
 | 
						||
   * Returns bounding box that should be used to restrict scene movement.
 | 
						||
   */
 | 
						||
  function getBoundingBox() {
 | 
						||
    if (!bounds) return; // client does not want to restrict movement
 | 
						||
 | 
						||
    if (typeof bounds === 'boolean') {
 | 
						||
      // for boolean type we use parent container bounds
 | 
						||
      var ownerRect = owner.getBoundingClientRect();
 | 
						||
      var sceneWidth = ownerRect.width;
 | 
						||
      var sceneHeight = ownerRect.height;
 | 
						||
 | 
						||
      return {
 | 
						||
        left: sceneWidth * boundsPadding,
 | 
						||
        top: sceneHeight * boundsPadding,
 | 
						||
        right: sceneWidth * (1 - boundsPadding),
 | 
						||
        bottom: sceneHeight * (1 - boundsPadding)
 | 
						||
      };
 | 
						||
    }
 | 
						||
 | 
						||
    return bounds;
 | 
						||
  }
 | 
						||
 | 
						||
  function getClientRect() {
 | 
						||
    var bbox = panController.getBBox();
 | 
						||
    var leftTop = client(bbox.left, bbox.top);
 | 
						||
 | 
						||
    return {
 | 
						||
      left: leftTop.x,
 | 
						||
      top: leftTop.y,
 | 
						||
      right: bbox.width * transform.scale + leftTop.x,
 | 
						||
      bottom: bbox.height * transform.scale + leftTop.y
 | 
						||
    };
 | 
						||
  }
 | 
						||
 | 
						||
  function client(x, y) {
 | 
						||
    return {
 | 
						||
      x: x * transform.scale + transform.x,
 | 
						||
      y: y * transform.scale + transform.y
 | 
						||
    };
 | 
						||
  }
 | 
						||
 | 
						||
  function makeDirty() {
 | 
						||
    isDirty = true;
 | 
						||
    frameAnimation = window.requestAnimationFrame(frame);
 | 
						||
  }
 | 
						||
 | 
						||
  function zoomByRatio(clientX, clientY, ratio) {
 | 
						||
    if (isNaN(clientX) || isNaN(clientY) || isNaN(ratio)) {
 | 
						||
      throw new Error('zoom requires valid numbers');
 | 
						||
    }
 | 
						||
 | 
						||
    var newScale = transform.scale * ratio;
 | 
						||
 | 
						||
    if (newScale < minZoom) {
 | 
						||
      if (transform.scale === minZoom) return;
 | 
						||
 | 
						||
      ratio = minZoom / transform.scale;
 | 
						||
    }
 | 
						||
    if (newScale > maxZoom) {
 | 
						||
      if (transform.scale === maxZoom) return;
 | 
						||
 | 
						||
      ratio = maxZoom / transform.scale;
 | 
						||
    }
 | 
						||
 | 
						||
    var size = transformToScreen(clientX, clientY);
 | 
						||
 | 
						||
    transform.x = size.x - ratio * (size.x - transform.x);
 | 
						||
    transform.y = size.y - ratio * (size.y - transform.y);
 | 
						||
 | 
						||
    // TODO: https://github.com/anvaka/panzoom/issues/112
 | 
						||
    if (bounds && boundsPadding === 1 && minZoom === 1) {
 | 
						||
      transform.scale *= ratio;
 | 
						||
      keepTransformInsideBounds();
 | 
						||
    } else {
 | 
						||
      var transformAdjusted = keepTransformInsideBounds();
 | 
						||
      if (!transformAdjusted) transform.scale *= ratio;
 | 
						||
    }
 | 
						||
 | 
						||
    triggerEvent('zoom');
 | 
						||
 | 
						||
    makeDirty();
 | 
						||
  }
 | 
						||
 | 
						||
  function zoomAbs(clientX, clientY, zoomLevel) {
 | 
						||
    var ratio = zoomLevel / transform.scale;
 | 
						||
    zoomByRatio(clientX, clientY, ratio);
 | 
						||
  }
 | 
						||
 | 
						||
  function centerOn(ui) {
 | 
						||
    var parent = ui.ownerSVGElement;
 | 
						||
    if (!parent)
 | 
						||
      throw new Error('ui element is required to be within the scene');
 | 
						||
 | 
						||
    // TODO: should i use controller's screen CTM?
 | 
						||
    var clientRect = ui.getBoundingClientRect();
 | 
						||
    var cx = clientRect.left + clientRect.width / 2;
 | 
						||
    var cy = clientRect.top + clientRect.height / 2;
 | 
						||
 | 
						||
    var container = parent.getBoundingClientRect();
 | 
						||
    var dx = container.width / 2 - cx;
 | 
						||
    var dy = container.height / 2 - cy;
 | 
						||
 | 
						||
    internalMoveBy(dx, dy, true);
 | 
						||
  }
 | 
						||
 | 
						||
  function internalMoveBy(dx, dy, smooth) {
 | 
						||
    if (!smooth) {
 | 
						||
      return moveBy(dx, dy);
 | 
						||
    }
 | 
						||
 | 
						||
    if (moveByAnimation) moveByAnimation.cancel();
 | 
						||
 | 
						||
    var from = { x: 0, y: 0 };
 | 
						||
    var to = { x: dx, y: dy };
 | 
						||
    var lastX = 0;
 | 
						||
    var lastY = 0;
 | 
						||
 | 
						||
    moveByAnimation = animate(from, to, {
 | 
						||
      step: function (v) {
 | 
						||
        moveBy(v.x - lastX, v.y - lastY);
 | 
						||
 | 
						||
        lastX = v.x;
 | 
						||
        lastY = v.y;
 | 
						||
      }
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  function scroll(x, y) {
 | 
						||
    cancelZoomAnimation();
 | 
						||
    moveTo(x, y);
 | 
						||
  }
 | 
						||
 | 
						||
  function dispose() {
 | 
						||
    releaseEvents();
 | 
						||
  }
 | 
						||
 | 
						||
  function listenForEvents() {
 | 
						||
    owner.addEventListener('mousedown', onMouseDown, { passive: false });
 | 
						||
    owner.addEventListener('dblclick', onDoubleClick, { passive: false });
 | 
						||
    owner.addEventListener('touchstart', onTouch, { passive: false });
 | 
						||
    owner.addEventListener('keydown', onKeyDown, { passive: false });
 | 
						||
 | 
						||
    // Need to listen on the owner container, so that we are not limited
 | 
						||
    // by the size of the scrollable domElement
 | 
						||
    wheel.addWheelListener(owner, onMouseWheel, { passive: false });
 | 
						||
 | 
						||
    makeDirty();
 | 
						||
  }
 | 
						||
 | 
						||
  function releaseEvents() {
 | 
						||
    wheel.removeWheelListener(owner, onMouseWheel);
 | 
						||
    owner.removeEventListener('mousedown', onMouseDown);
 | 
						||
    owner.removeEventListener('keydown', onKeyDown);
 | 
						||
    owner.removeEventListener('dblclick', onDoubleClick);
 | 
						||
    owner.removeEventListener('touchstart', onTouch);
 | 
						||
 | 
						||
    if (frameAnimation) {
 | 
						||
      window.cancelAnimationFrame(frameAnimation);
 | 
						||
      frameAnimation = 0;
 | 
						||
    }
 | 
						||
 | 
						||
    smoothScroll.cancel();
 | 
						||
 | 
						||
    releaseDocumentMouse();
 | 
						||
    releaseTouches();
 | 
						||
    textSelection.release();
 | 
						||
 | 
						||
    triggerPanEnd();
 | 
						||
  }
 | 
						||
 | 
						||
  function frame() {
 | 
						||
    if (isDirty) applyTransform();
 | 
						||
  }
 | 
						||
 | 
						||
  function applyTransform() {
 | 
						||
    isDirty = false;
 | 
						||
 | 
						||
    // TODO: Should I allow to cancel this?
 | 
						||
    panController.applyTransform(transform);
 | 
						||
 | 
						||
    triggerEvent('transform');
 | 
						||
    frameAnimation = 0;
 | 
						||
  }
 | 
						||
 | 
						||
  function onKeyDown(e) {
 | 
						||
    var x = 0,
 | 
						||
      y = 0,
 | 
						||
      z = 0;
 | 
						||
    if (e.keyCode === 38) {
 | 
						||
      y = 1; // up
 | 
						||
    } else if (e.keyCode === 40) {
 | 
						||
      y = -1; // down
 | 
						||
    } else if (e.keyCode === 37) {
 | 
						||
      x = 1; // left
 | 
						||
    } else if (e.keyCode === 39) {
 | 
						||
      x = -1; // right
 | 
						||
    } else if (e.keyCode === 189 || e.keyCode === 109) {
 | 
						||
      // DASH or SUBTRACT
 | 
						||
      z = 1; // `-` -  zoom out
 | 
						||
    } else if (e.keyCode === 187 || e.keyCode === 107) {
 | 
						||
      // EQUAL SIGN or ADD
 | 
						||
      z = -1; // `=` - zoom in (equal sign on US layout is under `+`)
 | 
						||
    }
 | 
						||
 | 
						||
    if (filterKey(e, x, y, z)) {
 | 
						||
      // They don't want us to handle the key: https://github.com/anvaka/panzoom/issues/45
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
    if (x || y) {
 | 
						||
      e.preventDefault();
 | 
						||
      e.stopPropagation();
 | 
						||
 | 
						||
      var clientRect = owner.getBoundingClientRect();
 | 
						||
      // movement speed should be the same in both X and Y direction:
 | 
						||
      var offset = Math.min(clientRect.width, clientRect.height);
 | 
						||
      var moveSpeedRatio = 0.05;
 | 
						||
      var dx = offset * moveSpeedRatio * x;
 | 
						||
      var dy = offset * moveSpeedRatio * y;
 | 
						||
 | 
						||
      // TODO: currently we do not animate this. It could be better to have animation
 | 
						||
      internalMoveBy(dx, dy);
 | 
						||
    }
 | 
						||
 | 
						||
    if (z) {
 | 
						||
      var scaleMultiplier = getScaleMultiplier(z * 100);
 | 
						||
      var offset = transformOrigin ? getTransformOriginOffset() : midPoint();
 | 
						||
      publicZoomTo(offset.x, offset.y, scaleMultiplier);
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function midPoint() {
 | 
						||
    var ownerRect = owner.getBoundingClientRect();
 | 
						||
    return {
 | 
						||
      x: ownerRect.width / 2,
 | 
						||
      y: ownerRect.height / 2
 | 
						||
    };
 | 
						||
  }
 | 
						||
 | 
						||
  function onTouch(e) {
 | 
						||
    // let the override the touch behavior
 | 
						||
    beforeTouch(e);
 | 
						||
 | 
						||
    if (e.touches.length === 1) {
 | 
						||
      return handleSingleFingerTouch(e, e.touches[0]);
 | 
						||
    } else if (e.touches.length === 2) {
 | 
						||
      // handleTouchMove() will care about pinch zoom.
 | 
						||
      pinchZoomLength = getPinchZoomLength(e.touches[0], e.touches[1]);
 | 
						||
      multiTouch = true;
 | 
						||
      startTouchListenerIfNeeded();
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function beforeTouch(e) {
 | 
						||
    // TODO: Need to unify this filtering names. E.g. use `beforeTouch`
 | 
						||
    if (options.onTouch && !options.onTouch(e)) {
 | 
						||
      // if they return `false` from onTouch, we don't want to stop
 | 
						||
      // events propagation. Fixes https://github.com/anvaka/panzoom/issues/12
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
    e.stopPropagation();
 | 
						||
    e.preventDefault();
 | 
						||
  }
 | 
						||
 | 
						||
  function beforeDoubleClick(e) {
 | 
						||
    // TODO: Need to unify this filtering names. E.g. use `beforeDoubleClick``
 | 
						||
    if (options.onDoubleClick && !options.onDoubleClick(e)) {
 | 
						||
      // if they return `false` from onTouch, we don't want to stop
 | 
						||
      // events propagation. Fixes https://github.com/anvaka/panzoom/issues/46
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
    e.preventDefault();
 | 
						||
    e.stopPropagation();
 | 
						||
  }
 | 
						||
 | 
						||
  function handleSingleFingerTouch(e) {
 | 
						||
    var touch = e.touches[0];
 | 
						||
    var offset = getOffsetXY(touch);
 | 
						||
    lastSingleFingerOffset = offset;
 | 
						||
    var point = transformToScreen(offset.x, offset.y);
 | 
						||
    mouseX = point.x;
 | 
						||
    mouseY = point.y;
 | 
						||
 | 
						||
    smoothScroll.cancel();
 | 
						||
    startTouchListenerIfNeeded();
 | 
						||
  }
 | 
						||
 | 
						||
  function startTouchListenerIfNeeded() {
 | 
						||
    if (touchInProgress) {
 | 
						||
      // no need to do anything, as we already listen to events;
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
    touchInProgress = true;
 | 
						||
    document.addEventListener('touchmove', handleTouchMove);
 | 
						||
    document.addEventListener('touchend', handleTouchEnd);
 | 
						||
    document.addEventListener('touchcancel', handleTouchEnd);
 | 
						||
  }
 | 
						||
 | 
						||
  function handleTouchMove(e) {
 | 
						||
    if (e.touches.length === 1) {
 | 
						||
      e.stopPropagation();
 | 
						||
      var touch = e.touches[0];
 | 
						||
 | 
						||
      var offset = getOffsetXY(touch);
 | 
						||
      var point = transformToScreen(offset.x, offset.y);
 | 
						||
 | 
						||
      var dx = point.x - mouseX;
 | 
						||
      var dy = point.y - mouseY;
 | 
						||
 | 
						||
      if (dx !== 0 && dy !== 0) {
 | 
						||
        triggerPanStart();
 | 
						||
      }
 | 
						||
      mouseX = point.x;
 | 
						||
      mouseY = point.y;
 | 
						||
      internalMoveBy(dx, dy);
 | 
						||
    } else if (e.touches.length === 2) {
 | 
						||
      // it's a zoom, let's find direction
 | 
						||
      multiTouch = true;
 | 
						||
      var t1 = e.touches[0];
 | 
						||
      var t2 = e.touches[1];
 | 
						||
      var currentPinchLength = getPinchZoomLength(t1, t2);
 | 
						||
 | 
						||
      // since the zoom speed is always based on distance from 1, we need to apply
 | 
						||
      // pinch speed only on that distance from 1:
 | 
						||
      var scaleMultiplier =
 | 
						||
        1 + (currentPinchLength / pinchZoomLength - 1) * pinchSpeed;
 | 
						||
 | 
						||
      var firstTouchPoint = getOffsetXY(t1);
 | 
						||
      var secondTouchPoint = getOffsetXY(t2);
 | 
						||
      mouseX = (firstTouchPoint.x + secondTouchPoint.x) / 2;
 | 
						||
      mouseY = (firstTouchPoint.y + secondTouchPoint.y) / 2;
 | 
						||
      if (transformOrigin) {
 | 
						||
        var offset = getTransformOriginOffset();
 | 
						||
        mouseX = offset.x;
 | 
						||
        mouseY = offset.y;
 | 
						||
      }
 | 
						||
 | 
						||
      publicZoomTo(mouseX, mouseY, scaleMultiplier);
 | 
						||
 | 
						||
      pinchZoomLength = currentPinchLength;
 | 
						||
      e.stopPropagation();
 | 
						||
      e.preventDefault();
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function handleTouchEnd(e) {
 | 
						||
    if (e.touches.length > 0) {
 | 
						||
      var offset = getOffsetXY(e.touches[0]);
 | 
						||
      var point = transformToScreen(offset.x, offset.y);
 | 
						||
      mouseX = point.x;
 | 
						||
      mouseY = point.y;
 | 
						||
    } else {
 | 
						||
      var now = new Date();
 | 
						||
      if (now - lastTouchEndTime < doubleTapSpeedInMS) {
 | 
						||
        if (transformOrigin) {
 | 
						||
          var offset = getTransformOriginOffset();
 | 
						||
          smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed);
 | 
						||
        } else {
 | 
						||
          smoothZoom(lastSingleFingerOffset.x, lastSingleFingerOffset.y, zoomDoubleClickSpeed);
 | 
						||
        }
 | 
						||
      }
 | 
						||
 | 
						||
      lastTouchEndTime = now;
 | 
						||
 | 
						||
      touchInProgress = false;
 | 
						||
      triggerPanEnd();
 | 
						||
      releaseTouches();
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function getPinchZoomLength(finger1, finger2) {
 | 
						||
    var dx = finger1.clientX - finger2.clientX;
 | 
						||
    var dy = finger1.clientY - finger2.clientY;
 | 
						||
    return Math.sqrt(dx * dx + dy * dy);
 | 
						||
  }
 | 
						||
 | 
						||
  function onDoubleClick(e) {
 | 
						||
    beforeDoubleClick(e);
 | 
						||
    var offset = getOffsetXY(e);
 | 
						||
    if (transformOrigin) {
 | 
						||
      // TODO: looks like this is duplicated in the file.
 | 
						||
      // Need to refactor
 | 
						||
      offset = getTransformOriginOffset();
 | 
						||
    }
 | 
						||
    smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed);
 | 
						||
  }
 | 
						||
 | 
						||
  function onMouseDown(e) {
 | 
						||
    // if client does not want to handle this event - just ignore the call
 | 
						||
    if (beforeMouseDown(e)) return;
 | 
						||
 | 
						||
    if (touchInProgress) {
 | 
						||
      // modern browsers will fire mousedown for touch events too
 | 
						||
      // we do not want this: touch is handled separately.
 | 
						||
      e.stopPropagation();
 | 
						||
      return false;
 | 
						||
    }
 | 
						||
    // for IE, left click == 1
 | 
						||
    // for Firefox, left click == 0
 | 
						||
    var isLeftButton =
 | 
						||
      (e.button === 1 && window.event !== null) || e.button === 0;
 | 
						||
    if (!isLeftButton) return;
 | 
						||
 | 
						||
    smoothScroll.cancel();
 | 
						||
 | 
						||
    var offset = getOffsetXY(e);
 | 
						||
    var point = transformToScreen(offset.x, offset.y);
 | 
						||
    mouseX = point.x;
 | 
						||
    mouseY = point.y;
 | 
						||
 | 
						||
    // We need to listen on document itself, since mouse can go outside of the
 | 
						||
    // window, and we will loose it
 | 
						||
    document.addEventListener('mousemove', onMouseMove);
 | 
						||
    document.addEventListener('mouseup', onMouseUp);
 | 
						||
    textSelection.capture(e.target || e.srcElement);
 | 
						||
 | 
						||
    return false;
 | 
						||
  }
 | 
						||
 | 
						||
  function onMouseMove(e) {
 | 
						||
    // no need to worry about mouse events when touch is happening
 | 
						||
    if (touchInProgress) return;
 | 
						||
 | 
						||
    triggerPanStart();
 | 
						||
 | 
						||
    var offset = getOffsetXY(e);
 | 
						||
    var point = transformToScreen(offset.x, offset.y);
 | 
						||
    var dx = point.x - mouseX;
 | 
						||
    var dy = point.y - mouseY;
 | 
						||
 | 
						||
    mouseX = point.x;
 | 
						||
    mouseY = point.y;
 | 
						||
 | 
						||
    internalMoveBy(dx, dy);
 | 
						||
  }
 | 
						||
 | 
						||
  function onMouseUp() {
 | 
						||
    textSelection.release();
 | 
						||
    triggerPanEnd();
 | 
						||
    releaseDocumentMouse();
 | 
						||
  }
 | 
						||
 | 
						||
  function releaseDocumentMouse() {
 | 
						||
    document.removeEventListener('mousemove', onMouseMove);
 | 
						||
    document.removeEventListener('mouseup', onMouseUp);
 | 
						||
    panstartFired = false;
 | 
						||
  }
 | 
						||
 | 
						||
  function releaseTouches() {
 | 
						||
    document.removeEventListener('touchmove', handleTouchMove);
 | 
						||
    document.removeEventListener('touchend', handleTouchEnd);
 | 
						||
    document.removeEventListener('touchcancel', handleTouchEnd);
 | 
						||
    panstartFired = false;
 | 
						||
    multiTouch = false;
 | 
						||
  }
 | 
						||
 | 
						||
  function onMouseWheel(e) {
 | 
						||
    // if client does not want to handle this event - just ignore the call
 | 
						||
    if (beforeWheel(e)) return;
 | 
						||
 | 
						||
    smoothScroll.cancel();
 | 
						||
 | 
						||
    var delta = e.deltaY;
 | 
						||
    if (e.deltaMode > 0) delta *= 100;
 | 
						||
 | 
						||
    var scaleMultiplier = getScaleMultiplier(delta);
 | 
						||
 | 
						||
    if (scaleMultiplier !== 1) {
 | 
						||
      var offset = transformOrigin
 | 
						||
        ? getTransformOriginOffset()
 | 
						||
        : getOffsetXY(e);
 | 
						||
      publicZoomTo(offset.x, offset.y, scaleMultiplier);
 | 
						||
      e.preventDefault();
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function getOffsetXY(e) {
 | 
						||
    var offsetX, offsetY;
 | 
						||
    // I tried using e.offsetX, but that gives wrong results for svg, when user clicks on a path.
 | 
						||
    var ownerRect = owner.getBoundingClientRect();
 | 
						||
    offsetX = e.clientX - ownerRect.left;
 | 
						||
    offsetY = e.clientY - ownerRect.top;
 | 
						||
 | 
						||
    return { x: offsetX, y: offsetY };
 | 
						||
  }
 | 
						||
 | 
						||
  function smoothZoom(clientX, clientY, scaleMultiplier) {
 | 
						||
    var fromValue = transform.scale;
 | 
						||
    var from = { scale: fromValue };
 | 
						||
    var to = { scale: scaleMultiplier * fromValue };
 | 
						||
 | 
						||
    smoothScroll.cancel();
 | 
						||
    cancelZoomAnimation();
 | 
						||
 | 
						||
    zoomToAnimation = animate(from, to, {
 | 
						||
      step: function (v) {
 | 
						||
        zoomAbs(clientX, clientY, v.scale);
 | 
						||
      },
 | 
						||
      done: triggerZoomEnd
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  function smoothZoomAbs(clientX, clientY, toScaleValue) {
 | 
						||
    var fromValue = transform.scale;
 | 
						||
    var from = { scale: fromValue };
 | 
						||
    var to = { scale: toScaleValue };
 | 
						||
 | 
						||
    smoothScroll.cancel();
 | 
						||
    cancelZoomAnimation();
 | 
						||
 | 
						||
    zoomToAnimation = animate(from, to, {
 | 
						||
      step: function (v) {
 | 
						||
        zoomAbs(clientX, clientY, v.scale);
 | 
						||
      }
 | 
						||
    });
 | 
						||
  }
 | 
						||
 | 
						||
  function getTransformOriginOffset() {
 | 
						||
    var ownerRect = owner.getBoundingClientRect();
 | 
						||
    return {
 | 
						||
      x: ownerRect.width * transformOrigin.x,
 | 
						||
      y: ownerRect.height * transformOrigin.y
 | 
						||
    };
 | 
						||
  }
 | 
						||
 | 
						||
  function publicZoomTo(clientX, clientY, scaleMultiplier) {
 | 
						||
    smoothScroll.cancel();
 | 
						||
    cancelZoomAnimation();
 | 
						||
    return zoomByRatio(clientX, clientY, scaleMultiplier);
 | 
						||
  }
 | 
						||
 | 
						||
  function cancelZoomAnimation() {
 | 
						||
    if (zoomToAnimation) {
 | 
						||
      zoomToAnimation.cancel();
 | 
						||
      zoomToAnimation = null;
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function getScaleMultiplier(delta) {
 | 
						||
    var sign = Math.sign(delta);
 | 
						||
    var deltaAdjustedSpeed = Math.min(0.25, Math.abs(speed * delta / 128));
 | 
						||
    return 1 - sign * deltaAdjustedSpeed;
 | 
						||
  }
 | 
						||
 | 
						||
  function triggerPanStart() {
 | 
						||
    if (!panstartFired) {
 | 
						||
      triggerEvent('panstart');
 | 
						||
      panstartFired = true;
 | 
						||
      smoothScroll.start();
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function triggerPanEnd() {
 | 
						||
    if (panstartFired) {
 | 
						||
      // we should never run smooth scrolling if it was multiTouch (pinch zoom animation):
 | 
						||
      if (!multiTouch) smoothScroll.stop();
 | 
						||
      triggerEvent('panend');
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function triggerZoomEnd() {
 | 
						||
    triggerEvent('zoomend');
 | 
						||
  }
 | 
						||
 | 
						||
  function triggerEvent(name) {
 | 
						||
    api.fire(name, api);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function parseTransformOrigin(options) {
 | 
						||
  if (!options) return;
 | 
						||
  if (typeof options === 'object') {
 | 
						||
    if (!isNumber(options.x) || !isNumber(options.y))
 | 
						||
      failTransformOrigin(options);
 | 
						||
    return options;
 | 
						||
  }
 | 
						||
 | 
						||
  failTransformOrigin();
 | 
						||
}
 | 
						||
 | 
						||
function failTransformOrigin(options) {
 | 
						||
  console.error(options);
 | 
						||
  throw new Error(
 | 
						||
    [
 | 
						||
      'Cannot parse transform origin.',
 | 
						||
      'Some good examples:',
 | 
						||
      '  "center center" can be achieved with {x: 0.5, y: 0.5}',
 | 
						||
      '  "top center" can be achieved with {x: 0.5, y: 0}',
 | 
						||
      '  "bottom right" can be achieved with {x: 1, y: 1}'
 | 
						||
    ].join('\n')
 | 
						||
  );
 | 
						||
}
 | 
						||
 | 
						||
function noop() { }
 | 
						||
 | 
						||
function validateBounds(bounds) {
 | 
						||
  var boundsType = typeof bounds;
 | 
						||
  if (boundsType === 'undefined' || boundsType === 'boolean') return; // this is okay
 | 
						||
  // otherwise need to be more thorough:
 | 
						||
  var validBounds =
 | 
						||
    isNumber(bounds.left) &&
 | 
						||
    isNumber(bounds.top) &&
 | 
						||
    isNumber(bounds.bottom) &&
 | 
						||
    isNumber(bounds.right);
 | 
						||
 | 
						||
  if (!validBounds)
 | 
						||
    throw new Error(
 | 
						||
      'Bounds object is not valid. It can be: ' +
 | 
						||
      'undefined, boolean (true|false) or an object {left, top, right, bottom}'
 | 
						||
    );
 | 
						||
}
 | 
						||
 | 
						||
function isNumber(x) {
 | 
						||
  return Number.isFinite(x);
 | 
						||
}
 | 
						||
 | 
						||
// IE 11 does not support isNaN:
 | 
						||
function isNaN(value) {
 | 
						||
  if (Number.isNaN) {
 | 
						||
    return Number.isNaN(value);
 | 
						||
  }
 | 
						||
 | 
						||
  return value !== value;
 | 
						||
}
 | 
						||
 | 
						||
function rigidScroll() {
 | 
						||
  return {
 | 
						||
    start: noop,
 | 
						||
    stop: noop,
 | 
						||
    cancel: noop
 | 
						||
  };
 | 
						||
}
 | 
						||
 | 
						||
function autoRun() {
 | 
						||
  if (typeof document === 'undefined') return;
 | 
						||
 | 
						||
  var scripts = document.getElementsByTagName('script');
 | 
						||
  if (!scripts) return;
 | 
						||
  var panzoomScript;
 | 
						||
 | 
						||
  for (var i = 0; i < scripts.length; ++i) {
 | 
						||
    var x = scripts[i];
 | 
						||
    if (x.src && x.src.match(/\bpanzoom(\.min)?\.js/)) {
 | 
						||
      panzoomScript = x;
 | 
						||
      break;
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  if (!panzoomScript) return;
 | 
						||
 | 
						||
  var query = panzoomScript.getAttribute('query');
 | 
						||
  if (!query) return;
 | 
						||
 | 
						||
  var globalName = panzoomScript.getAttribute('name') || 'pz';
 | 
						||
  var started = Date.now();
 | 
						||
 | 
						||
  tryAttach();
 | 
						||
 | 
						||
  function tryAttach() {
 | 
						||
    var el = document.querySelector(query);
 | 
						||
    if (!el) {
 | 
						||
      var now = Date.now();
 | 
						||
      var elapsed = now - started;
 | 
						||
      if (elapsed < 2000) {
 | 
						||
        // Let's wait a bit
 | 
						||
        setTimeout(tryAttach, 100);
 | 
						||
        return;
 | 
						||
      }
 | 
						||
      // If we don't attach within 2 seconds to the target element, consider it a failure
 | 
						||
      console.error('Cannot find the panzoom element', globalName);
 | 
						||
      return;
 | 
						||
    }
 | 
						||
    var options = collectOptions(panzoomScript);
 | 
						||
    console.log(options);
 | 
						||
    window[globalName] = createPanZoom(el, options);
 | 
						||
  }
 | 
						||
 | 
						||
  function collectOptions(script) {
 | 
						||
    var attrs = script.attributes;
 | 
						||
    var options = {};
 | 
						||
    for (var i = 0; i < attrs.length; ++i) {
 | 
						||
      var attr = attrs[i];
 | 
						||
      var nameValue = getPanzoomAttributeNameValue(attr);
 | 
						||
      if (nameValue) {
 | 
						||
        options[nameValue.name] = nameValue.value;
 | 
						||
      }
 | 
						||
    }
 | 
						||
 | 
						||
    return options;
 | 
						||
  }
 | 
						||
 | 
						||
  function getPanzoomAttributeNameValue(attr) {
 | 
						||
    if (!attr.name) return;
 | 
						||
    var isPanZoomAttribute =
 | 
						||
      attr.name[0] === 'p' && attr.name[1] === 'z' && attr.name[2] === '-';
 | 
						||
 | 
						||
    if (!isPanZoomAttribute) return;
 | 
						||
 | 
						||
    var name = attr.name.substr(3);
 | 
						||
    var value = JSON.parse(attr.value);
 | 
						||
    return { name: name, value: value };
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
autoRun();
 | 
						||
 | 
						||
},{"./lib/createTextSelectionInterceptor.js":2,"./lib/domController.js":3,"./lib/kinetic.js":4,"./lib/svgController.js":5,"./lib/transform.js":6,"amator":7,"ngraph.events":9,"wheel":10}],2:[function(require,module,exports){
 | 
						||
/**
 | 
						||
 * Disallows selecting text.
 | 
						||
 */
 | 
						||
module.exports = createTextSelectionInterceptor;
 | 
						||
 | 
						||
function createTextSelectionInterceptor(useFake) {
 | 
						||
  if (useFake) {
 | 
						||
    return {
 | 
						||
      capture: noop,
 | 
						||
      release: noop
 | 
						||
    };
 | 
						||
  }
 | 
						||
 | 
						||
  var dragObject;
 | 
						||
  var prevSelectStart;
 | 
						||
  var prevDragStart;
 | 
						||
  var wasCaptured = false;
 | 
						||
 | 
						||
  return {
 | 
						||
    capture: capture,
 | 
						||
    release: release
 | 
						||
  };
 | 
						||
 | 
						||
  function capture(domObject) {
 | 
						||
    wasCaptured = true;
 | 
						||
    prevSelectStart = window.document.onselectstart;
 | 
						||
    prevDragStart = window.document.ondragstart;
 | 
						||
 | 
						||
    window.document.onselectstart = disabled;
 | 
						||
 | 
						||
    dragObject = domObject;
 | 
						||
    dragObject.ondragstart = disabled;
 | 
						||
  }
 | 
						||
 | 
						||
  function release() {
 | 
						||
    if (!wasCaptured) return;
 | 
						||
    
 | 
						||
    wasCaptured = false;
 | 
						||
    window.document.onselectstart = prevSelectStart;
 | 
						||
    if (dragObject) dragObject.ondragstart = prevDragStart;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function disabled(e) {
 | 
						||
  e.stopPropagation();
 | 
						||
  return false;
 | 
						||
}
 | 
						||
 | 
						||
function noop() {}
 | 
						||
 | 
						||
},{}],3:[function(require,module,exports){
 | 
						||
module.exports = makeDomController
 | 
						||
 | 
						||
function makeDomController(domElement, options) {
 | 
						||
  var elementValid = (domElement instanceof HTMLElement)
 | 
						||
  if (!elementValid) {
 | 
						||
    throw new Error('svg element is required for svg.panzoom to work')
 | 
						||
  }
 | 
						||
 | 
						||
  var owner = domElement.parentElement
 | 
						||
  if (!owner) {
 | 
						||
    throw new Error(
 | 
						||
      'Do not apply panzoom to the detached DOM element. '
 | 
						||
    )
 | 
						||
  }
 | 
						||
 | 
						||
  domElement.scrollTop = 0;
 | 
						||
  
 | 
						||
  if (!options.disableKeyboardInteraction) {
 | 
						||
    owner.setAttribute('tabindex', 0);
 | 
						||
  }
 | 
						||
 | 
						||
  var api = {
 | 
						||
    getBBox: getBBox,
 | 
						||
    getOwner: getOwner,
 | 
						||
    applyTransform: applyTransform,
 | 
						||
  }
 | 
						||
  
 | 
						||
  return api
 | 
						||
 | 
						||
  function getOwner() {
 | 
						||
    return owner
 | 
						||
  }
 | 
						||
 | 
						||
  function getBBox() {
 | 
						||
    // TODO: We should probably cache this?
 | 
						||
    return  {
 | 
						||
      left: 0,
 | 
						||
      top: 0,
 | 
						||
      width: domElement.clientWidth,
 | 
						||
      height: domElement.clientHeight
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function applyTransform(transform) {
 | 
						||
    // TODO: Should we cache this?
 | 
						||
    domElement.style.transformOrigin = '0 0 0';
 | 
						||
    domElement.style.transform = 'matrix(' +
 | 
						||
      transform.scale + ', 0, 0, ' +
 | 
						||
      transform.scale + ', ' +
 | 
						||
      transform.x + ', ' + transform.y + ')'
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
},{}],4:[function(require,module,exports){
 | 
						||
(function (global){
 | 
						||
/**
 | 
						||
 * Allows smooth kinetic scrolling of the surface
 | 
						||
 */
 | 
						||
module.exports = kinetic;
 | 
						||
 | 
						||
function kinetic(getPoint, scroll, settings) {
 | 
						||
  if (typeof settings !== 'object') {
 | 
						||
    // setting could come as boolean, we should ignore it, and use an object.
 | 
						||
    settings = {};
 | 
						||
  }
 | 
						||
 | 
						||
  var minVelocity = typeof settings.minVelocity === 'number' ? settings.minVelocity : 5;
 | 
						||
  var amplitude = typeof settings.amplitude === 'number' ? settings.amplitude : 0.25;
 | 
						||
  var cancelAnimationFrame = typeof settings.cancelAnimationFrame === 'function' ? settings.cancelAnimationFrame : getCancelAnimationFrame();
 | 
						||
  var requestAnimationFrame = typeof settings.requestAnimationFrame === 'function' ? settings.requestAnimationFrame : getRequestAnimationFrame();
 | 
						||
 | 
						||
  var lastPoint;
 | 
						||
  var timestamp;
 | 
						||
  var timeConstant = 342;
 | 
						||
 | 
						||
  var ticker;
 | 
						||
  var vx, targetX, ax;
 | 
						||
  var vy, targetY, ay;
 | 
						||
 | 
						||
  var raf;
 | 
						||
 | 
						||
  return {
 | 
						||
    start: start,
 | 
						||
    stop: stop,
 | 
						||
    cancel: dispose
 | 
						||
  };
 | 
						||
 | 
						||
  function dispose() {
 | 
						||
    cancelAnimationFrame(ticker);
 | 
						||
    cancelAnimationFrame(raf);
 | 
						||
  }
 | 
						||
 | 
						||
  function start() {
 | 
						||
    lastPoint = getPoint();
 | 
						||
 | 
						||
    ax = ay = vx = vy = 0;
 | 
						||
    timestamp = new Date();
 | 
						||
 | 
						||
    cancelAnimationFrame(ticker);
 | 
						||
    cancelAnimationFrame(raf);
 | 
						||
 | 
						||
    // we start polling the point position to accumulate velocity
 | 
						||
    // Once we stop(), we will use accumulated velocity to keep scrolling
 | 
						||
    // an object.
 | 
						||
    ticker = requestAnimationFrame(track);
 | 
						||
  }
 | 
						||
 | 
						||
  function track() {
 | 
						||
    var now = Date.now();
 | 
						||
    var elapsed = now - timestamp;
 | 
						||
    timestamp = now;
 | 
						||
 | 
						||
    var currentPoint = getPoint();
 | 
						||
 | 
						||
    var dx = currentPoint.x - lastPoint.x;
 | 
						||
    var dy = currentPoint.y - lastPoint.y;
 | 
						||
 | 
						||
    lastPoint = currentPoint;
 | 
						||
 | 
						||
    var dt = 1000 / (1 + elapsed);
 | 
						||
 | 
						||
    // moving average
 | 
						||
    vx = 0.8 * dx * dt + 0.2 * vx;
 | 
						||
    vy = 0.8 * dy * dt + 0.2 * vy;
 | 
						||
 | 
						||
    ticker = requestAnimationFrame(track);
 | 
						||
  }
 | 
						||
 | 
						||
  function stop() {
 | 
						||
    cancelAnimationFrame(ticker);
 | 
						||
    cancelAnimationFrame(raf);
 | 
						||
 | 
						||
    var currentPoint = getPoint();
 | 
						||
 | 
						||
    targetX = currentPoint.x;
 | 
						||
    targetY = currentPoint.y;
 | 
						||
    timestamp = Date.now();
 | 
						||
 | 
						||
    if (vx < -minVelocity || vx > minVelocity) {
 | 
						||
      ax = amplitude * vx;
 | 
						||
      targetX += ax;
 | 
						||
    }
 | 
						||
 | 
						||
    if (vy < -minVelocity || vy > minVelocity) {
 | 
						||
      ay = amplitude * vy;
 | 
						||
      targetY += ay;
 | 
						||
    }
 | 
						||
 | 
						||
    raf = requestAnimationFrame(autoScroll);
 | 
						||
  }
 | 
						||
 | 
						||
  function autoScroll() {
 | 
						||
    var elapsed = Date.now() - timestamp;
 | 
						||
 | 
						||
    var moving = false;
 | 
						||
    var dx = 0;
 | 
						||
    var dy = 0;
 | 
						||
 | 
						||
    if (ax) {
 | 
						||
      dx = -ax * Math.exp(-elapsed / timeConstant);
 | 
						||
 | 
						||
      if (dx > 0.5 || dx < -0.5) moving = true;
 | 
						||
      else dx = ax = 0;
 | 
						||
    }
 | 
						||
 | 
						||
    if (ay) {
 | 
						||
      dy = -ay * Math.exp(-elapsed / timeConstant);
 | 
						||
 | 
						||
      if (dy > 0.5 || dy < -0.5) moving = true;
 | 
						||
      else dy = ay = 0;
 | 
						||
    }
 | 
						||
 | 
						||
    if (moving) {
 | 
						||
      scroll(targetX + dx, targetY + dy);
 | 
						||
      raf = requestAnimationFrame(autoScroll);
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function getCancelAnimationFrame() {
 | 
						||
  if (typeof global.cancelAnimationFrame === 'function') return global.cancelAnimationFrame;
 | 
						||
 | 
						||
  return clearTimeout;
 | 
						||
}
 | 
						||
 | 
						||
function getRequestAnimationFrame() {
 | 
						||
  if (typeof global.requestAnimationFrame === 'function') return global.requestAnimationFrame;
 | 
						||
 | 
						||
  return function (handler) {
 | 
						||
    return setTimeout(handler, 16);
 | 
						||
  }
 | 
						||
}
 | 
						||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
 | 
						||
},{}],5:[function(require,module,exports){
 | 
						||
module.exports = makeSvgController
 | 
						||
 | 
						||
function makeSvgController(svgElement, options) {
 | 
						||
  var elementValid = (svgElement instanceof SVGElement)
 | 
						||
  if (!elementValid) {
 | 
						||
    throw new Error('svg element is required for svg.panzoom to work')
 | 
						||
  }
 | 
						||
 | 
						||
  var owner = svgElement.ownerSVGElement
 | 
						||
  if (!owner) {
 | 
						||
    throw new Error(
 | 
						||
      'Do not apply panzoom to the root <svg> element. ' +
 | 
						||
      'Use its child instead (e.g. <g></g>). ' +
 | 
						||
      'As of March 2016 only FireFox supported transform on the root element')
 | 
						||
  }
 | 
						||
 | 
						||
  if (!options.disableKeyboardInteraction) {
 | 
						||
    owner.setAttribute('tabindex', 0);
 | 
						||
  }
 | 
						||
 | 
						||
  var api = {
 | 
						||
    getBBox: getBBox,
 | 
						||
    getScreenCTM: getScreenCTM,
 | 
						||
    getOwner: getOwner,
 | 
						||
    applyTransform: applyTransform,
 | 
						||
    initTransform: initTransform
 | 
						||
  }
 | 
						||
  
 | 
						||
  return api
 | 
						||
 | 
						||
  function getOwner() {
 | 
						||
    return owner
 | 
						||
  }
 | 
						||
 | 
						||
  function getBBox() {
 | 
						||
    var bbox =  svgElement.getBBox()
 | 
						||
    return {
 | 
						||
      left: bbox.x,
 | 
						||
      top: bbox.y,
 | 
						||
      width: bbox.width,
 | 
						||
      height: bbox.height,
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function getScreenCTM() {
 | 
						||
    var ctm = owner.getCTM();
 | 
						||
    if (!ctm) {
 | 
						||
      // This is likely firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=873106
 | 
						||
      // The code below is not entirely correct, but still better than nothing
 | 
						||
      return owner.getScreenCTM();
 | 
						||
    }
 | 
						||
    return ctm;
 | 
						||
  }
 | 
						||
 | 
						||
  function initTransform(transform) {
 | 
						||
    var screenCTM = svgElement.getCTM()
 | 
						||
    transform.x = screenCTM.e;
 | 
						||
    transform.y = screenCTM.f;
 | 
						||
    transform.scale = screenCTM.a;
 | 
						||
    owner.removeAttributeNS(null, 'viewBox');
 | 
						||
  }
 | 
						||
 | 
						||
  function applyTransform(transform) {
 | 
						||
    svgElement.setAttribute('transform', 'matrix(' +
 | 
						||
      transform.scale + ' 0 0 ' +
 | 
						||
      transform.scale + ' ' +
 | 
						||
      transform.x + ' ' + transform.y + ')')
 | 
						||
  }
 | 
						||
}
 | 
						||
},{}],6:[function(require,module,exports){
 | 
						||
module.exports = Transform;
 | 
						||
 | 
						||
function Transform() {
 | 
						||
  this.x = 0;
 | 
						||
  this.y = 0;
 | 
						||
  this.scale = 1;
 | 
						||
}
 | 
						||
 | 
						||
},{}],7:[function(require,module,exports){
 | 
						||
var BezierEasing = require('bezier-easing')
 | 
						||
 | 
						||
// Predefined set of animations. Similar to CSS easing functions
 | 
						||
var animations = {
 | 
						||
  ease:  BezierEasing(0.25, 0.1, 0.25, 1),
 | 
						||
  easeIn: BezierEasing(0.42, 0, 1, 1),
 | 
						||
  easeOut: BezierEasing(0, 0, 0.58, 1),
 | 
						||
  easeInOut: BezierEasing(0.42, 0, 0.58, 1),
 | 
						||
  linear: BezierEasing(0, 0, 1, 1)
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
module.exports = animate;
 | 
						||
module.exports.makeAggregateRaf = makeAggregateRaf;
 | 
						||
module.exports.sharedScheduler = makeAggregateRaf();
 | 
						||
 | 
						||
 | 
						||
function animate(source, target, options) {
 | 
						||
  var start = Object.create(null)
 | 
						||
  var diff = Object.create(null)
 | 
						||
  options = options || {}
 | 
						||
  // We let clients specify their own easing function
 | 
						||
  var easing = (typeof options.easing === 'function') ? options.easing : animations[options.easing]
 | 
						||
 | 
						||
  // if nothing is specified, default to ease (similar to CSS animations)
 | 
						||
  if (!easing) {
 | 
						||
    if (options.easing) {
 | 
						||
      console.warn('Unknown easing function in amator: ' + options.easing);
 | 
						||
    }
 | 
						||
    easing = animations.ease
 | 
						||
  }
 | 
						||
 | 
						||
  var step = typeof options.step === 'function' ? options.step : noop
 | 
						||
  var done = typeof options.done === 'function' ? options.done : noop
 | 
						||
 | 
						||
  var scheduler = getScheduler(options.scheduler)
 | 
						||
 | 
						||
  var keys = Object.keys(target)
 | 
						||
  keys.forEach(function(key) {
 | 
						||
    start[key] = source[key]
 | 
						||
    diff[key] = target[key] - source[key]
 | 
						||
  })
 | 
						||
 | 
						||
  var durationInMs = typeof options.duration === 'number' ? options.duration : 400
 | 
						||
  var durationInFrames = Math.max(1, durationInMs * 0.06) // 0.06 because 60 frames pers 1,000 ms
 | 
						||
  var previousAnimationId
 | 
						||
  var frame = 0
 | 
						||
 | 
						||
  previousAnimationId = scheduler.next(loop)
 | 
						||
 | 
						||
  return {
 | 
						||
    cancel: cancel
 | 
						||
  }
 | 
						||
 | 
						||
  function cancel() {
 | 
						||
    scheduler.cancel(previousAnimationId)
 | 
						||
    previousAnimationId = 0
 | 
						||
  }
 | 
						||
 | 
						||
  function loop() {
 | 
						||
    var t = easing(frame/durationInFrames)
 | 
						||
    frame += 1
 | 
						||
    setValues(t)
 | 
						||
    if (frame <= durationInFrames) {
 | 
						||
      previousAnimationId = scheduler.next(loop)
 | 
						||
      step(source)
 | 
						||
    } else {
 | 
						||
      previousAnimationId = 0
 | 
						||
      setTimeout(function() { done(source) }, 0)
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  function setValues(t) {
 | 
						||
    keys.forEach(function(key) {
 | 
						||
      source[key] = diff[key] * t + start[key]
 | 
						||
    })
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function noop() { }
 | 
						||
 | 
						||
function getScheduler(scheduler) {
 | 
						||
  if (!scheduler) {
 | 
						||
    var canRaf = typeof window !== 'undefined' && window.requestAnimationFrame
 | 
						||
    return canRaf ? rafScheduler() : timeoutScheduler()
 | 
						||
  }
 | 
						||
  if (typeof scheduler.next !== 'function') throw new Error('Scheduler is supposed to have next(cb) function')
 | 
						||
  if (typeof scheduler.cancel !== 'function') throw new Error('Scheduler is supposed to have cancel(handle) function')
 | 
						||
 | 
						||
  return scheduler
 | 
						||
}
 | 
						||
 | 
						||
function rafScheduler() {
 | 
						||
  return {
 | 
						||
    next: window.requestAnimationFrame.bind(window),
 | 
						||
    cancel: window.cancelAnimationFrame.bind(window)
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function timeoutScheduler() {
 | 
						||
  return {
 | 
						||
    next: function(cb) {
 | 
						||
      return setTimeout(cb, 1000/60)
 | 
						||
    },
 | 
						||
    cancel: function (id) {
 | 
						||
      return clearTimeout(id)
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function makeAggregateRaf() {
 | 
						||
  var frontBuffer = new Set();
 | 
						||
  var backBuffer = new Set();
 | 
						||
  var frameToken = 0;
 | 
						||
 | 
						||
  return {
 | 
						||
    next: next,
 | 
						||
    cancel: next,
 | 
						||
    clearAll: clearAll
 | 
						||
  }
 | 
						||
 | 
						||
  function clearAll() {
 | 
						||
    frontBuffer.clear();
 | 
						||
    backBuffer.clear();
 | 
						||
    cancelAnimationFrame(frameToken);
 | 
						||
    frameToken = 0;
 | 
						||
  }
 | 
						||
 | 
						||
  function next(callback) {
 | 
						||
    backBuffer.add(callback);
 | 
						||
    renderNextFrame();
 | 
						||
  }
 | 
						||
 | 
						||
  function renderNextFrame() {
 | 
						||
    if (!frameToken) frameToken = requestAnimationFrame(renderFrame);
 | 
						||
  }
 | 
						||
 | 
						||
  function renderFrame() {
 | 
						||
    frameToken = 0;
 | 
						||
 | 
						||
    var t = backBuffer;
 | 
						||
    backBuffer = frontBuffer;
 | 
						||
    frontBuffer = t;
 | 
						||
 | 
						||
    frontBuffer.forEach(function(callback) {
 | 
						||
      callback();
 | 
						||
    });
 | 
						||
    frontBuffer.clear();
 | 
						||
  }
 | 
						||
 | 
						||
  function cancel(callback) {
 | 
						||
    backBuffer.delete(callback);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
},{"bezier-easing":8}],8:[function(require,module,exports){
 | 
						||
/**
 | 
						||
 * https://github.com/gre/bezier-easing
 | 
						||
 * BezierEasing - use bezier curve for transition easing function
 | 
						||
 * by Gaëtan Renaudeau 2014 - 2015 – MIT License
 | 
						||
 */
 | 
						||
 | 
						||
// These values are established by empiricism with tests (tradeoff: performance VS precision)
 | 
						||
var NEWTON_ITERATIONS = 4;
 | 
						||
var NEWTON_MIN_SLOPE = 0.001;
 | 
						||
var SUBDIVISION_PRECISION = 0.0000001;
 | 
						||
var SUBDIVISION_MAX_ITERATIONS = 10;
 | 
						||
 | 
						||
var kSplineTableSize = 11;
 | 
						||
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
 | 
						||
 | 
						||
var float32ArraySupported = typeof Float32Array === 'function';
 | 
						||
 | 
						||
function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
 | 
						||
function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
 | 
						||
function C (aA1)      { return 3.0 * aA1; }
 | 
						||
 | 
						||
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
 | 
						||
function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
 | 
						||
 | 
						||
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
 | 
						||
function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
 | 
						||
 | 
						||
function binarySubdivide (aX, aA, aB, mX1, mX2) {
 | 
						||
  var currentX, currentT, i = 0;
 | 
						||
  do {
 | 
						||
    currentT = aA + (aB - aA) / 2.0;
 | 
						||
    currentX = calcBezier(currentT, mX1, mX2) - aX;
 | 
						||
    if (currentX > 0.0) {
 | 
						||
      aB = currentT;
 | 
						||
    } else {
 | 
						||
      aA = currentT;
 | 
						||
    }
 | 
						||
  } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
 | 
						||
  return currentT;
 | 
						||
}
 | 
						||
 | 
						||
function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
 | 
						||
 for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
 | 
						||
   var currentSlope = getSlope(aGuessT, mX1, mX2);
 | 
						||
   if (currentSlope === 0.0) {
 | 
						||
     return aGuessT;
 | 
						||
   }
 | 
						||
   var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
 | 
						||
   aGuessT -= currentX / currentSlope;
 | 
						||
 }
 | 
						||
 return aGuessT;
 | 
						||
}
 | 
						||
 | 
						||
function LinearEasing (x) {
 | 
						||
  return x;
 | 
						||
}
 | 
						||
 | 
						||
module.exports = function bezier (mX1, mY1, mX2, mY2) {
 | 
						||
  if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
 | 
						||
    throw new Error('bezier x values must be in [0, 1] range');
 | 
						||
  }
 | 
						||
 | 
						||
  if (mX1 === mY1 && mX2 === mY2) {
 | 
						||
    return LinearEasing;
 | 
						||
  }
 | 
						||
 | 
						||
  // Precompute samples table
 | 
						||
  var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
 | 
						||
  for (var i = 0; i < kSplineTableSize; ++i) {
 | 
						||
    sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
 | 
						||
  }
 | 
						||
 | 
						||
  function getTForX (aX) {
 | 
						||
    var intervalStart = 0.0;
 | 
						||
    var currentSample = 1;
 | 
						||
    var lastSample = kSplineTableSize - 1;
 | 
						||
 | 
						||
    for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
 | 
						||
      intervalStart += kSampleStepSize;
 | 
						||
    }
 | 
						||
    --currentSample;
 | 
						||
 | 
						||
    // Interpolate to provide an initial guess for t
 | 
						||
    var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
 | 
						||
    var guessForT = intervalStart + dist * kSampleStepSize;
 | 
						||
 | 
						||
    var initialSlope = getSlope(guessForT, mX1, mX2);
 | 
						||
    if (initialSlope >= NEWTON_MIN_SLOPE) {
 | 
						||
      return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
 | 
						||
    } else if (initialSlope === 0.0) {
 | 
						||
      return guessForT;
 | 
						||
    } else {
 | 
						||
      return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  return function BezierEasing (x) {
 | 
						||
    // Because JavaScript number are imprecise, we should guarantee the extremes are right.
 | 
						||
    if (x === 0) {
 | 
						||
      return 0;
 | 
						||
    }
 | 
						||
    if (x === 1) {
 | 
						||
      return 1;
 | 
						||
    }
 | 
						||
    return calcBezier(getTForX(x), mY1, mY2);
 | 
						||
  };
 | 
						||
};
 | 
						||
 | 
						||
},{}],9:[function(require,module,exports){
 | 
						||
module.exports = function(subject) {
 | 
						||
  validateSubject(subject);
 | 
						||
 | 
						||
  var eventsStorage = createEventsStorage(subject);
 | 
						||
  subject.on = eventsStorage.on;
 | 
						||
  subject.off = eventsStorage.off;
 | 
						||
  subject.fire = eventsStorage.fire;
 | 
						||
  return subject;
 | 
						||
};
 | 
						||
 | 
						||
function createEventsStorage(subject) {
 | 
						||
  // Store all event listeners to this hash. Key is event name, value is array
 | 
						||
  // of callback records.
 | 
						||
  //
 | 
						||
  // A callback record consists of callback function and its optional context:
 | 
						||
  // { 'eventName' => [{callback: function, ctx: object}] }
 | 
						||
  var registeredEvents = Object.create(null);
 | 
						||
 | 
						||
  return {
 | 
						||
    on: function (eventName, callback, ctx) {
 | 
						||
      if (typeof callback !== 'function') {
 | 
						||
        throw new Error('callback is expected to be a function');
 | 
						||
      }
 | 
						||
      var handlers = registeredEvents[eventName];
 | 
						||
      if (!handlers) {
 | 
						||
        handlers = registeredEvents[eventName] = [];
 | 
						||
      }
 | 
						||
      handlers.push({callback: callback, ctx: ctx});
 | 
						||
 | 
						||
      return subject;
 | 
						||
    },
 | 
						||
 | 
						||
    off: function (eventName, callback) {
 | 
						||
      var wantToRemoveAll = (typeof eventName === 'undefined');
 | 
						||
      if (wantToRemoveAll) {
 | 
						||
        // Killing old events storage should be enough in this case:
 | 
						||
        registeredEvents = Object.create(null);
 | 
						||
        return subject;
 | 
						||
      }
 | 
						||
 | 
						||
      if (registeredEvents[eventName]) {
 | 
						||
        var deleteAllCallbacksForEvent = (typeof callback !== 'function');
 | 
						||
        if (deleteAllCallbacksForEvent) {
 | 
						||
          delete registeredEvents[eventName];
 | 
						||
        } else {
 | 
						||
          var callbacks = registeredEvents[eventName];
 | 
						||
          for (var i = 0; i < callbacks.length; ++i) {
 | 
						||
            if (callbacks[i].callback === callback) {
 | 
						||
              callbacks.splice(i, 1);
 | 
						||
            }
 | 
						||
          }
 | 
						||
        }
 | 
						||
      }
 | 
						||
 | 
						||
      return subject;
 | 
						||
    },
 | 
						||
 | 
						||
    fire: function (eventName) {
 | 
						||
      var callbacks = registeredEvents[eventName];
 | 
						||
      if (!callbacks) {
 | 
						||
        return subject;
 | 
						||
      }
 | 
						||
 | 
						||
      var fireArguments;
 | 
						||
      if (arguments.length > 1) {
 | 
						||
        fireArguments = Array.prototype.splice.call(arguments, 1);
 | 
						||
      }
 | 
						||
      for(var i = 0; i < callbacks.length; ++i) {
 | 
						||
        var callbackInfo = callbacks[i];
 | 
						||
        callbackInfo.callback.apply(callbackInfo.ctx, fireArguments);
 | 
						||
      }
 | 
						||
 | 
						||
      return subject;
 | 
						||
    }
 | 
						||
  };
 | 
						||
}
 | 
						||
 | 
						||
function validateSubject(subject) {
 | 
						||
  if (!subject) {
 | 
						||
    throw new Error('Eventify cannot use falsy object as events subject');
 | 
						||
  }
 | 
						||
  var reservedWords = ['on', 'fire', 'off'];
 | 
						||
  for (var i = 0; i < reservedWords.length; ++i) {
 | 
						||
    if (subject.hasOwnProperty(reservedWords[i])) {
 | 
						||
      throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'");
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
},{}],10:[function(require,module,exports){
 | 
						||
/**
 | 
						||
 * This module used to unify mouse wheel behavior between different browsers in 2014
 | 
						||
 * Now it's just a wrapper around addEventListener('wheel');
 | 
						||
 *
 | 
						||
 * Usage:
 | 
						||
 *  var addWheelListener = require('wheel').addWheelListener;
 | 
						||
 *  var removeWheelListener = require('wheel').removeWheelListener;
 | 
						||
 *  addWheelListener(domElement, function (e) {
 | 
						||
 *    // mouse wheel event
 | 
						||
 *  });
 | 
						||
 *  removeWheelListener(domElement, function);
 | 
						||
 */
 | 
						||
 | 
						||
module.exports = addWheelListener;
 | 
						||
 | 
						||
// But also expose "advanced" api with unsubscribe:
 | 
						||
module.exports.addWheelListener = addWheelListener;
 | 
						||
module.exports.removeWheelListener = removeWheelListener;
 | 
						||
 | 
						||
 | 
						||
function addWheelListener(element, listener, useCapture) {
 | 
						||
  element.addEventListener('wheel', listener, useCapture);
 | 
						||
}
 | 
						||
 | 
						||
function removeWheelListener( element, listener, useCapture ) {
 | 
						||
  element.removeEventListener('wheel', listener, useCapture);
 | 
						||
}
 | 
						||
},{}]},{},[1])(1)
 | 
						||
});
 |