text: (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 eventify = require('ngraph.events');
  var kinetic = require('./lib/kinetic.js');
  var preventTextSelection = require('./lib/textSelectionInterceptor.js')();
  var Transform = require('./lib/transform.js');
  var makeSvgController = require('./lib/svgController.js');
  var makeDomController = require('./lib/domController.js');
  
  var defaultZoomSpeed = 0.065;
  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)
      }
  
      if (domElement instanceof HTMLElement) {
        panController = makeDomController(domElement)
      }
    }
  
    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;
    var realPinch = typeof options.realPinch === 'boolean' ? options.realPinch : false;
    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 speed = typeof options.zoomSpeed === 'number' ? options.zoomSpeed : defaultZoomSpeed;
  
    validateBounds(bounds);
  
    if (options.autocenter) {
      autocenter()
    }
  
    var frameAnimation;
  
    var lastTouchEndTime = 0;
  
    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,
      getTransform: getTransformModel,
      showRectangle: showRectangle,
  
      pause: pause,
      resume: resume,
      isPaused: isPaused,
    };
  
    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 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);
  
      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 to = {x: dx, y: dy};
      var lastX = 0;
      var lastY = 0;
  
      moveBy(to.x - lastX, to.y - lastY);
    }
  
    function scroll(x, y) {
      cancelZoomAnimation();
      moveTo(x, y)
    }
  
    function dispose() {
      releaseEvents();
    }
  
    function listenForEvents() {
      owner.addEventListener('mousedown', onMouseDown);
      owner.addEventListener('dblclick', onDoubleClick);
      owner.addEventListener('touchstart', onTouch);
      owner.addEventListener('keydown', onKeyDown);
  
      // Need to listen on the owner container, so that we are not limited
      // by the size of the scrollable domElement
      wheel.addWheelListener(owner, onMouseWheel);
  
      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();
  
      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);
        var ownerRect = owner.getBoundingClientRect();
        publicZoomTo(ownerRect.width / 2, ownerRect.height / 2, scaleMultiplier)
      }
    }
  
    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) {
      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) {
      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);
      mouseX = offset.x;
      mouseY = offset.y;
  
      smoothScroll.cancel();
      startTouchListenerIfNeeded()
    }
  
    function startTouchListenerIfNeeded() {
      if (!touchInProgress) {
        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 dx = offset.x - mouseX;
        var dy = offset.y - mouseY;
  
        if (dx !== 0 && dy !== 0) {
          triggerPanStart()
        }
        mouseX = offset.x;
        mouseY = offset.y;
        var point = transformToScreen(dx, dy);
        internalMoveBy(point.x, point.y)
      } 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);
  
        var scaleMultiplier = 1;
  
        if (realPinch) {
          scaleMultiplier = currentPinchLength / pinchZoomLength
        } else {
          var delta = 0;
          if (currentPinchLength < pinchZoomLength) {
            delta = 1
          } else if (currentPinchLength > pinchZoomLength) {
            delta = -1
          }
  
          scaleMultiplier = getScaleMultiplier(delta)
        }
  
        mouseX = (t1.clientX + t2.clientX) / 2;
        mouseY = (t1.clientY + t2.clientY) / 2;
  
        publicZoomTo(mouseX, mouseY, scaleMultiplier);
  
        pinchZoomLength = currentPinchLength;
        e.stopPropagation();
        e.preventDefault()
      }
    }
  
    function handleTouchEnd(e) {
      if (e.touches.length > 0) {
        var offset = getOffsetXY(e.touches[0]);
        mouseX = offset.x;
        mouseY = offset.y
      } else {
        var now = new Date();
        if (now - lastTouchEndTime < doubleTapSpeedInMS) {
          smoothZoom(mouseX, mouseY, zoomDoubleClickSpeed)
        }
  
        lastTouchEndTime = now;
  
        touchInProgress = false;
        triggerPanEnd();
        releaseTouches()
      }
    }
  
    function getPinchZoomLength(finger1, finger2) {
      return Math.sqrt((finger1.clientX - finger2.clientX) * (finger1.clientX - finger2.clientX) +
        (finger1.clientY - finger2.clientY) * (finger1.clientY - finger2.clientY))
    }
  
    function onDoubleClick(e) {
      beforeDoubleClick(e);
      var offset = getOffsetXY(e);
      smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed)
    }
  
    function onMouseDown(e) {
      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);
  
      preventTextSelection.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() {
      preventTextSelection.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 scaleMultiplier = getScaleMultiplier(e.deltaY);
  
      if (scaleMultiplier !== 1) {
        var offset = 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 to = {scale: scaleMultiplier * fromValue};
  
      smoothScroll.cancel();
      cancelZoomAnimation();
  
      zoomAbs(clientX, clientY, to.scale)
    }
  
    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 scaleMultiplier = 1;
      if (delta > 0) { // zoom out
        scaleMultiplier = (1 - speed)
      } else if (delta < 0) { // zoom in
        scaleMultiplier = (1 + speed)
      }
  
      return scaleMultiplier
    }
  
    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 triggerEvent(name) {
      api.fire(name, api);
    }
  }
  
  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;
  
    Array.from(scripts).forEach(function (x) {
      if (x.src && x.src.match(/\bpanzoom(\.min)?\.js/)) {
        panzoomScript = x
      }
    });
  
    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/domController.js":2,"./lib/kinetic.js":3,"./lib/svgController.js":4,"./lib/textSelectionInterceptor.js":5,"./lib/transform.js":6,"ngraph.events":7,"wheel":8}],2:[function(require,module,exports){
  module.exports = makeDomController
  
  function makeDomController(domElement) {
    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;
    owner.setAttribute('tabindex', 1); // TODO: not sure if this is really polite
  
    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 + ')'
    }
  }
  
  },{}],3:[function(require,module,exports){
  /**
   * 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 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() {
      window.clearInterval(ticker)
      window.cancelAnimationFrame(raf)
    }
  
    function start() {
      lastPoint = getPoint()
  
      ax = ay = vx = vy = 0
      timestamp = new Date()
  
      window.clearInterval(ticker)
      window.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 = window.setInterval(track, 100);
    }
  
    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
    }
  
    function stop() {
      window.clearInterval(ticker);
      window.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 = window.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 = window.requestAnimationFrame(autoScroll);
      }
    }
  
  }
  
  },{}],4:[function(require,module,exports){
  module.exports = makeSvgController
  
  function makeSvgController(svgElement) {
    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')
    }
  
    owner.setAttribute('tabindex', 1); // TODO: not sure if this is really polite
  
    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() {
      return owner.getScreenCTM()
    }
  
    function initTransform(transform) {
      var screenCTM = svgElement.getScreenCTM()
      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 + ')')
    }
  }
  },{}],5:[function(require,module,exports){
  /**
   * Disallows selecting text.
   */
  module.exports = createTextSelectionInterceptor
  
  function createTextSelectionInterceptor() {
    var dragObject
    var prevSelectStart
    var prevDragStart
  
    return {
      capture: capture,
      release: release
    }
  
    function capture(domObject) {
      prevSelectStart = window.document.onselectstart
      prevDragStart = window.document.ondragstart
  
      window.document.onselectstart = disabled
  
      dragObject = domObject
      dragObject.ondragstart = disabled
    }
  
    function release() {
      window.document.onselectstart = prevSelectStart
      if (dragObject) dragObject.ondragstart = prevDragStart
    }
  }
  
  function disabled(e) {
    e.stopPropagation()
    return false
  }
  
  },{}],6:[function(require,module,exports){
  module.exports = Transform;
  
  function Transform() {
    this.x = 0;
    this.y = 0;
    this.scale = 1;
  }
  
  },{}],7:[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] + "'");
      }
    }
  }
  
  },{}],8:[function(require,module,exports){
  /**
   * This module unifies handling of mouse whee event across different browsers
   *
   * See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel?redirectlocale=en-US&redirectslug=DOM%2FMozilla_event_reference%2Fwheel
   * for more details
   *
   * Usage:
   *  var addWheelListener = require('wheel').addWheelListener;
   *  var removeWheelListener = require('wheel').removeWheelListener;
   *  addWheelListener(domElement, function (e) {
   *    // mouse wheel event
   *  });
   *  removeWheelListener(domElement, function);
   */
  // by default we shortcut to 'addEventListener':
  
  module.exports = addWheelListener;
  
  // But also expose "advanced" api with unsubscribe:
  module.exports.addWheelListener = addWheelListener;
  module.exports.removeWheelListener = removeWheelListener;
  
  
  var prefix = "", _addEventListener, _removeEventListener,  support;
  
  detectEventModel(typeof window !== 'undefined' && window,
                  typeof document !== 'undefined' && document);
  
  function addWheelListener( elem, callback, useCapture ) {
      _addWheelListener( elem, support, callback, useCapture );
  
      // handle MozMousePixelScroll in older Firefox
      if( support == "DOMMouseScroll" ) {
          _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
      }
  }
  
  function removeWheelListener( elem, callback, useCapture ) {
      _removeWheelListener( elem, support, callback, useCapture );
  
      // handle MozMousePixelScroll in older Firefox
      if( support == "DOMMouseScroll" ) {
          _removeWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
      }
  }
  
    // TODO: in theory this anonymous function may result in incorrect
    // unsubscription in some browsers. But in practice, I don't think we should
    // worry too much about it (those browsers are on the way out)
  function _addWheelListener( elem, eventName, callback, useCapture ) {
    elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
      !originalEvent && ( originalEvent = window.event );
  
      // create a normalized event object
      var event = {
        // keep a ref to the original event object
        originalEvent: originalEvent,
        target: originalEvent.target || originalEvent.srcElement,
        type: "wheel",
        deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
        deltaX: 0,
        deltaY: 0,
        deltaZ: 0,
        clientX: originalEvent.clientX,
        clientY: originalEvent.clientY,
        preventDefault: function() {
          originalEvent.preventDefault ?
              originalEvent.preventDefault() :
              originalEvent.returnValue = false;
        },
        stopPropagation: function() {
          if(originalEvent.stopPropagation)
            originalEvent.stopPropagation();
        },
        stopImmediatePropagation: function() {
          if(originalEvent.stopImmediatePropagation)
            originalEvent.stopImmediatePropagation();
        }
      };
  
      // calculate deltaY (and deltaX) according to the event
      if ( support == "mousewheel" ) {
        event.deltaY = - 1/40 * originalEvent.wheelDelta;
        // Webkit also support wheelDeltaX
        originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
      } else {
        event.deltaY = originalEvent.detail;
      }
  
      // it's time to fire the callback
      return callback( event );
  
    }, useCapture || false );
  }
  
  function _removeWheelListener( elem, eventName, callback, useCapture ) {
    elem[ _removeEventListener ]( prefix + eventName, callback, useCapture || false );
  }
  
  function detectEventModel(window, document) {
    if ( window && window.addEventListener ) {
        _addEventListener = "addEventListener";
        _removeEventListener = "removeEventListener";
    } else {
        _addEventListener = "attachEvent";
        _removeEventListener = "detachEvent";
        prefix = "on";
    }
  
    if (document) {
      // detect available wheel event
      support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
                document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
                "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
    } else {
      support = "wheel";
    }
  }
  
  },{}]},{},[1])(1)
  });
  