1(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2module.exports = {
3  enable: function(instance) {
4    // Select (and create if necessary) defs
5    var defs = instance.svg.querySelector('defs')
6    if (!defs) {
7      defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')
8      instance.svg.appendChild(defs)
9    }
10
11    // Create style element
12    var style = document.createElementNS('http://www.w3.org/2000/svg', 'style')
13    style.setAttribute('type', 'text/css')
14    style.textContent = '.svg-pan-zoom-control { cursor: pointer; fill: black; fill-opacity: 0.333; } .svg-pan-zoom-control:hover { fill-opacity: 0.8; } .svg-pan-zoom-control-background { fill: white; fill-opacity: 0.5; } .svg-pan-zoom-control-background { fill-opacity: 0.8; }'
15    defs.appendChild(style)
16
17    // Zoom Group
18    var zoomGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
19    zoomGroup.setAttribute('id', 'svg-pan-zoom-controls');
20    zoomGroup.setAttribute('transform', 'translate(' + ( instance.width - 70 ) + ' ' + ( instance.height - 76 ) + ') scale(0.75)');
21    zoomGroup.setAttribute('class', 'svg-pan-zoom-control');
22
23    // Control elements
24    zoomGroup.appendChild(this._createZoomIn(instance))
25    zoomGroup.appendChild(this._createZoomReset(instance))
26    zoomGroup.appendChild(this._createZoomOut(instance))
27
28    // Finally append created element
29    instance.svg.appendChild(zoomGroup)
30
31    // Cache control instance
32    instance.controlIcons = zoomGroup
33  }
34
35, _createZoomIn: function(instance) {
36    var zoomIn = document.createElementNS('http://www.w3.org/2000/svg', 'g');
37    zoomIn.setAttribute('id', 'svg-pan-zoom-zoom-in');
38    zoomIn.setAttribute('transform', 'translate(30.5 5) scale(0.015)');
39    zoomIn.setAttribute('class', 'svg-pan-zoom-control');
40    zoomIn.addEventListener('click', function() {instance.getPublicInstance().zoomIn()}, false)
41
42    var zoomInBackground = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
43    zoomInBackground.setAttribute('x', '0');
44    zoomInBackground.setAttribute('y', '0');
45    zoomInBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
46    zoomInBackground.setAttribute('height', '1400');
47    zoomInBackground.setAttribute('class', 'svg-pan-zoom-control-background');
48    zoomIn.appendChild(zoomInBackground);
49
50    var zoomInShape = document.createElementNS('http://www.w3.org/2000/svg', 'path');
51    zoomInShape.setAttribute('d', 'M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z');
52    zoomInShape.setAttribute('class', 'svg-pan-zoom-control-element');
53    zoomIn.appendChild(zoomInShape);
54
55    return zoomIn
56  }
57
58, _createZoomReset: function(instance){
59    // reset
60    var resetPanZoomControl = document.createElementNS('http://www.w3.org/2000/svg', 'g');
61    resetPanZoomControl.setAttribute('id', 'svg-pan-zoom-reset-pan-zoom');
62    resetPanZoomControl.setAttribute('transform', 'translate(5 35) scale(0.4)');
63    resetPanZoomControl.setAttribute('class', 'svg-pan-zoom-control');
64    resetPanZoomControl.addEventListener('click', function() {instance.getPublicInstance().resetZoom()}, false);
65
66    var resetPanZoomControlBackground = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
67    resetPanZoomControlBackground.setAttribute('x', '2');
68    resetPanZoomControlBackground.setAttribute('y', '2');
69    resetPanZoomControlBackground.setAttribute('width', '182'); // larger than expected because the whole group is transformed to scale down
70    resetPanZoomControlBackground.setAttribute('height', '58');
71    resetPanZoomControlBackground.setAttribute('class', 'svg-pan-zoom-control-background');
72    resetPanZoomControl.appendChild(resetPanZoomControlBackground);
73
74    var resetPanZoomControlShape1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
75    resetPanZoomControlShape1.setAttribute('d', 'M33.051,20.632c-0.742-0.406-1.854-0.609-3.338-0.609h-7.969v9.281h7.769c1.543,0,2.701-0.188,3.473-0.562c1.365-0.656,2.048-1.953,2.048-3.891C35.032,22.757,34.372,21.351,33.051,20.632z');
76    resetPanZoomControlShape1.setAttribute('class', 'svg-pan-zoom-control-element');
77    resetPanZoomControl.appendChild(resetPanZoomControlShape1);
78
79    var resetPanZoomControlShape2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
80    resetPanZoomControlShape2.setAttribute('d', 'M170.231,0.5H15.847C7.102,0.5,0.5,5.708,0.5,11.84v38.861C0.5,56.833,7.102,61.5,15.847,61.5h154.384c8.745,0,15.269-4.667,15.269-10.798V11.84C185.5,5.708,178.976,0.5,170.231,0.5z M42.837,48.569h-7.969c-0.219-0.766-0.375-1.383-0.469-1.852c-0.188-0.969-0.289-1.961-0.305-2.977l-0.047-3.211c-0.03-2.203-0.41-3.672-1.142-4.406c-0.732-0.734-2.103-1.102-4.113-1.102h-7.05v13.547h-7.055V14.022h16.524c2.361,0.047,4.178,0.344,5.45,0.891c1.272,0.547,2.351,1.352,3.234,2.414c0.731,0.875,1.31,1.844,1.737,2.906s0.64,2.273,0.64,3.633c0,1.641-0.414,3.254-1.242,4.84s-2.195,2.707-4.102,3.363c1.594,0.641,2.723,1.551,3.387,2.73s0.996,2.98,0.996,5.402v2.32c0,1.578,0.063,2.648,0.19,3.211c0.19,0.891,0.635,1.547,1.333,1.969V48.569z M75.579,48.569h-26.18V14.022h25.336v6.117H56.454v7.336h16.781v6H56.454v8.883h19.125V48.569z M104.497,46.331c-2.44,2.086-5.887,3.129-10.34,3.129c-4.548,0-8.125-1.027-10.731-3.082s-3.909-4.879-3.909-8.473h6.891c0.224,1.578,0.662,2.758,1.316,3.539c1.196,1.422,3.246,2.133,6.15,2.133c1.739,0,3.151-0.188,4.236-0.562c2.058-0.719,3.087-2.055,3.087-4.008c0-1.141-0.504-2.023-1.512-2.648c-1.008-0.609-2.607-1.148-4.796-1.617l-3.74-0.82c-3.676-0.812-6.201-1.695-7.576-2.648c-2.328-1.594-3.492-4.086-3.492-7.477c0-3.094,1.139-5.664,3.417-7.711s5.623-3.07,10.036-3.07c3.685,0,6.829,0.965,9.431,2.895c2.602,1.93,3.966,4.73,4.093,8.402h-6.938c-0.128-2.078-1.057-3.555-2.787-4.43c-1.154-0.578-2.587-0.867-4.301-0.867c-1.907,0-3.428,0.375-4.565,1.125c-1.138,0.75-1.706,1.797-1.706,3.141c0,1.234,0.561,2.156,1.682,2.766c0.721,0.406,2.25,0.883,4.589,1.43l6.063,1.43c2.657,0.625,4.648,1.461,5.975,2.508c2.059,1.625,3.089,3.977,3.089,7.055C108.157,41.624,106.937,44.245,104.497,46.331z M139.61,48.569h-26.18V14.022h25.336v6.117h-18.281v7.336h16.781v6h-16.781v8.883h19.125V48.569z M170.337,20.14h-10.336v28.43h-7.266V20.14h-10.383v-6.117h27.984V20.14z');
81    resetPanZoomControlShape2.setAttribute('class', 'svg-pan-zoom-control-element');
82    resetPanZoomControl.appendChild(resetPanZoomControlShape2);
83
84    return resetPanZoomControl
85  }
86
87, _createZoomOut: function(instance){
88    // zoom out
89    var zoomOut = document.createElementNS('http://www.w3.org/2000/svg', 'g');
90    zoomOut.setAttribute('id', 'svg-pan-zoom-zoom-out');
91    zoomOut.setAttribute('transform', 'translate(30.5 70) scale(0.015)');
92    zoomOut.setAttribute('class', 'svg-pan-zoom-control');
93    zoomOut.addEventListener('click', function() {instance.getPublicInstance().zoomOut()}, false);
94
95    var zoomOutBackground = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
96    zoomOutBackground.setAttribute('x', '0');
97    zoomOutBackground.setAttribute('y', '0');
98    zoomOutBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
99    zoomOutBackground.setAttribute('height', '1400');
100    zoomOutBackground.setAttribute('class', 'svg-pan-zoom-control-background');
101    zoomOut.appendChild(zoomOutBackground);
102
103    var zoomOutShape = document.createElementNS('http://www.w3.org/2000/svg', 'path');
104    zoomOutShape.setAttribute('d', 'M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z');
105    zoomOutShape.setAttribute('class', 'svg-pan-zoom-control-element');
106    zoomOut.appendChild(zoomOutShape);
107
108    return zoomOut
109  }
110
111, disable: function(instance) {
112    if (instance.controlIcons) {
113      instance.controlIcons.parentNode.removeChild(instance.controlIcons)
114      instance.controlIcons = null
115    }
116  }
117}
118
119},{}],2:[function(require,module,exports){
120// Cross-browser wheel event, from: https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel
121if (!window.hasOwnProperty('addWheelListener')) {
122  // creates a global "addWheelListener" method
123  // example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } );
124  (function(window,document) {
125
126    var prefix = "", _addEventListener, onwheel, support;
127
128    // detect event model
129    if ( window.addEventListener ) {
130      _addEventListener = "addEventListener";
131    } else {
132      _addEventListener = "attachEvent";
133      prefix = "on";
134    }
135
136    // detect available wheel event
137    support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
138      document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
139      "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
140
141        window.addWheelListener = function( elem, callback, useCapture ) {
142      _addWheelListener( elem, support, callback, useCapture );
143
144      // handle MozMousePixelScroll in older Firefox
145      if( support == "DOMMouseScroll" ) {
146        _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
147      }
148    };
149
150    function _addWheelListener( elem, eventName, callback, useCapture ) {
151      elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
152        !originalEvent && ( originalEvent = window.event );
153
154        // create a normalized event object
155        var event = {
156          // keep a ref to the original event object
157          originalEvent: originalEvent,
158          // NOTE: clientX and clientY are not in Mozilla example, but are needed for svg-pan-zoom
159          clientX: originalEvent.clientX,
160          clientY: originalEvent.clientY,
161          target: originalEvent.target || originalEvent.srcElement,
162          type: "wheel",
163          deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
164          deltaX: 0,
165          deltaZ: 0,
166          preventDefault: function() {
167            originalEvent.preventDefault ?
168              originalEvent.preventDefault() :
169              originalEvent.returnValue = false;
170          }
171        };
172
173        // calculate deltaY (and deltaX) according to the event
174        if ( support == "mousewheel" ) {
175          event.deltaY = - 1/40 * originalEvent.wheelDelta;
176          // Webkit also support wheelDeltaX
177          originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
178        } else {
179          event.deltaY = originalEvent.detail;
180        }
181
182        // it's time to fire the callback
183        return callback(event);
184
185      }, useCapture || false );
186    }
187
188  })(window,document);
189}
190
191},{}],3:[function(require,module,exports){
192var Mousewheel = require('./mousewheel')  // Keep it here so that mousewheel is initialised
193  , ControlIcons = require('./control-icons')
194  , Utils = require('./utilities')
195  , SvgUtils = require('./svg-utilities')
196
197;(function(window, document){
198  'use strict';
199
200  var SvgPanZoom = function(svg, options) {
201    this.init(svg, options)
202  }
203
204  var optionsDefaults = {
205    panEnabled: true // enable or disable panning (default enabled)
206  , dragEnabled: false // enable or disable dragging (default disabled)
207  , controlIconsEnabled: false // insert icons to give user an option in addition to mouse events to control pan/zoom (default disabled)
208  , zoomEnabled: true // enable or disable zooming (default enabled)
209  , zoomScaleSensitivity: 0.2 // Zoom sensitivity
210  , minZoom: 0.5 // Minimum Zoom level
211  , maxZoom: 10 // Maximum Zoom level
212  , fit: true // enable or disable viewport fit in SVG (default true)
213  , center: true // enable or disable viewport centering in SVG (default true)
214  , beforeZoom: null
215  , onZoom: function(){}
216  , beforePan: null
217  , onPan: function(){}
218  }
219
220  SvgPanZoom.prototype.init = function(svg, options) {
221    this.svg = svg
222
223    // Set options
224    this.options = Utils.extend(Utils.extend({}, optionsDefaults), options)
225
226    // Set default state
227    this.state = 'none'
228
229    // Get dimensions
230    var dimensions = SvgUtils.getSvgDimensions(svg)
231    this.width = dimensions.width
232    this.height = dimensions.height
233
234    // Get viewport
235    this.viewport = SvgUtils.getOrCreateViewport(svg)
236
237    // Create zoom and pan cache
238    this._zoom = 1
239    this._pan = {x: 0, y: 0}
240
241    // Sets initialCTM
242    this.processCTM()
243
244    if (this.options.controlIconsEnabled && this.options.zoomEnabled) {
245      ControlIcons.enable(this)
246    }
247
248    // Add default attributes to SVG
249    SvgUtils.setupSvgAttributes(this.svg)
250
251    // Init events handlers
252    this.setupHandlers()
253
254    // TODO what for do we need this?
255    // It is replacing window.svgPanZoom constructor with this instance
256    //
257    // if (this.svg.ownerDocument.documentElement.tagName.toLowerCase() !== 'svg') {
258    //   this.svg.ownerDocument.defaultView.svgPanZoom = this
259    // }
260  }
261
262  /**
263   * Get CTM and save it into initialCTM attribute
264   * Parse viewBox and if any update initialCTM based on this values
265   */
266  SvgPanZoom.prototype.processCTM = function() {
267    var svgViewBox = this.svg.getAttribute('viewBox')
268
269    this.cacheViewBox()
270    this.svg.removeAttribute('viewBox')
271
272    if (this.options.fit) {
273      var newCTM = this.viewport.getCTM()
274        , newScale = Math.min(this.width/(this._viewBox.width - this._viewBox.x), this.height/(this._viewBox.height - this._viewBox.y));
275
276      newCTM.a = newCTM.a * newScale; //x-scale
277      newCTM.d = newCTM.d * newScale; //y-scale
278      newCTM.e = (newCTM.e - this._viewBox.x) * newScale; //x-transform
279      newCTM.f = (newCTM.f - this._viewBox.y) * newScale; //y-transform
280      this.initialCTM = newCTM;
281
282      // Update viewport CTM
283      SvgUtils.setCTM(this.viewport, this.initialCTM);
284    } else {
285      // Leave sizes as they are
286      this.svg.removeAttribute('viewBox')
287      this.initialCTM = this.viewport.getCTM();
288    }
289
290    // Cache zoom level
291    this._zoom = this.initialCTM.a
292
293    // Cache pan level
294    this._pan.x = this.initialCTM.e
295    this._pan.y = this.initialCTM.f
296
297    if (this.options.center) {
298      this.center()
299    }
300  }
301
302  /**
303   * Cache initial viewBox value
304   * If nok viewBox is defined than use viewport sizes as viewBox values
305   */
306  SvgPanZoom.prototype.cacheViewBox = function() {
307    // ViewBox cache
308    this._viewBox = {x: 0, y: 0, width: 0, height: 0}
309
310    var svgViewBox = this.svg.getAttribute('viewBox')
311
312    if (svgViewBox) {
313      var viewBoxValues = svgViewBox.split(' ').map(parseFloat)
314
315      // Cache viewbox x and y offset
316      this._viewBox.x = viewBoxValues[0]
317      this._viewBox.y = viewBoxValues[1]
318      this._viewBox.width = viewBoxValues[2]
319      this._viewBox.height = viewBoxValues[3]
320    } else {
321      var boundingClientRect = this.viewport.getBoundingClientRect()
322
323      // Cache viewbox sizes
324      this._viewBox.width = boundingClientRect.width
325      this._viewBox.height = boundingClientRect.height
326    }
327  }
328
329  /**
330   * Recalculate viewport sizes and update viewBox cache
331   */
332  SvgPanZoom.prototype.recacheViewBox = function() {
333    var boundingClientRect = this.viewport.getBoundingClientRect()
334      , viewBoxWidth = boundingClientRect.width / this.getZoom()
335      , viewBoxHeight = boundingClientRect.height / this.getZoom()
336
337    // Cache viewbox
338    this._viewBox.x = 0
339    this._viewBox.y = 0
340    this._viewBox.width = viewBoxWidth
341    this._viewBox.height = viewBoxHeight
342  }
343
344  /**
345   * Register event handlers
346   */
347  SvgPanZoom.prototype.setupHandlers = function() {
348    var that = this
349
350    // Mouse down group
351    this.svg.addEventListener("mousedown", function(evt) {
352      return that.handleMouseDown(evt);
353    }, false);
354    this.svg.addEventListener("touchstart", function(evt) {
355      return that.handleMouseDown(evt);
356    }, false);
357
358    // Mouse up group
359    this.svg.addEventListener("mouseup", function(evt) {
360      return that.handleMouseUp(evt);
361    }, false);
362    this.svg.addEventListener("touchend", function(evt) {
363      return that.handleMouseUp(evt);
364    }, false);
365
366    // Mouse move group
367    this.svg.addEventListener("mousemove", function(evt) {
368      return that.handleMouseMove(evt);
369    }, false);
370    this.svg.addEventListener("touchmove", function(evt) {
371      return that.handleMouseMove(evt);
372    }, false);
373
374    // Mouse leave group
375    this.svg.addEventListener("mouseleave", function(evt) {
376      return that.handleMouseUp(evt);
377    }, false);
378    this.svg.addEventListener("touchleave", function(evt) {
379      return that.handleMouseUp(evt);
380    }, false);
381    this.svg.addEventListener("touchcancel", function(evt) {
382      return that.handleMouseUp(evt);
383    }, false);
384
385    // Mouse wheel listener
386    window.addWheelListener(this.svg, function(evt) {
387      return that.handleMouseWheel(evt);
388    })
389  }
390
391  /**
392   * Handle mouse wheel event
393   *
394   * @param  {object} evt Event object
395   */
396  SvgPanZoom.prototype.handleMouseWheel = function(evt) {
397    if (!this.options.zoomEnabled) {
398      return;
399    }
400
401    if (evt.preventDefault) {
402      evt.preventDefault();
403    } else {
404      evt.returnValue = false;
405    }
406
407    var delta = 0
408
409    if ('deltaMode' in evt && evt.deltaMode === 0) {
410      // Make empirical adjustments for browsers that give deltaY in pixels (deltaMode=0)
411
412      if (evt.wheelDelta) {
413        // Normalizer for Chrome
414        delta = evt.deltaY / Math.abs(evt.wheelDelta/3)
415      } else {
416        // Others. Possibly tablets? Use a value just in case
417        delta = evt.deltaY / 120
418      }
419    } else if ('mozPressure' in evt) {
420      // Normalizer for newer Firefox
421      // NOTE: May need to change detection at some point if mozPressure disappears.
422      delta = evt.deltaY / 3;
423    } else {
424      // Others should be reasonably normalized by the mousewheel code at the end of the file.
425      delta = evt.deltaY;
426    }
427
428    var svg = (evt.target.tagName === 'svg' || evt.target.tagName === 'SVG') ? evt.target : evt.target.ownerSVGElement || evt.target.correspondingElement.ownerSVGElement
429      , relativeMousePoint = SvgUtils.getRelativeMousePoint(svg, evt)
430      , zoom = Math.pow(1 + this.options.zoomScaleSensitivity, (-1) * delta); // multiplying by neg. 1 so as to make zoom in/out behavior match Google maps behavior
431
432    this.zoomAtPoint(svg, relativeMousePoint, zoom)
433  }
434
435  /**
436   * Zoom in at an SVG point
437   *
438   * @param  {object} svg          SVG Element
439   * @param  {object} point        SVG Point
440   * @param  {float} zoomScale    Number representing how much to zoom
441   * @param  {bool} zoomAbsolute [description]
442   * @return {[type]}              Default false. If true, zoomScale is treated as an absolute value.
443   *                               Otherwise, zoomScale is treated as a multiplied (e.g. 1.10 would zoom in 10%)
444   */
445  SvgPanZoom.prototype.zoomAtPoint = function(svg, point, zoomScale, zoomAbsolute) {
446    if (Utils.isFunction(this.options.beforeZoom)) {
447      this.options.beforeZoom()
448    }
449
450    var viewportCTM = this.viewport.getCTM()
451
452    point = point.matrixTransform(viewportCTM.inverse())
453
454    var k = svg.createSVGMatrix().translate(point.x, point.y).scale(zoomScale).translate(-point.x, -point.y)
455      , wasZoom = viewportCTM
456      , setZoom = viewportCTM.multiply(k)
457
458    if (zoomAbsolute) {
459      setZoom.a = setZoom.d = zoomScale
460    }
461
462    if (setZoom.a < this.options.minZoom * this.initialCTM.a) {setZoom.a = setZoom.d = wasZoom.a}
463    if (setZoom.a > this.options.maxZoom * this.initialCTM.a) {setZoom.a = setZoom.d = wasZoom.a}
464    if (setZoom.a !== wasZoom.a) {
465      SvgUtils.setCTM(this.viewport, setZoom)
466
467      // Cache zoom level
468      this._zoom = setZoom.a
469    }
470
471    if (!this.stateTf) {
472      this.stateTf = setZoom.inverse()
473    }
474
475    this.stateTf = this.stateTf.multiply(k.inverse())
476
477    if (this.options.onZoom) {
478      this.options.onZoom(setZoom.a)
479    }
480  }
481
482  SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) {
483    // If not a SVGPoint but has x and y than create new point
484    if (Utils.getType(point) !== 'SVGPoint' && 'x' in point && 'y' in point) {
485      var _point = this.svg.createSVGPoint()
486      _point.x = point.x
487      _point.y = point.y
488      point = _point
489    } else {
490      throw new Error('Given point is invalid')
491      return
492    }
493
494    this.zoomAtPoint(this.svg, point, scale, absolute)
495  }
496
497  /**
498   * Get zoom scale/level
499   *
500   * @return {float} zoom scale
501   */
502  SvgPanZoom.prototype.getZoom = function() {
503    return this._zoom
504  }
505
506  SvgPanZoom.prototype.resetZoom = function() {
507    this.getPublicInstance().zoom(this.initialCTM.a)
508    this.getPublicInstance().pan({x: this.initialCTM.e, y: this.initialCTM.f})
509
510    // Cache zoom level
511    this._zoom = this.initialCTM.a
512
513    // Cache pan level
514    this._pan.x = this.initialCTM.e
515    this._pan.y = this.initialCTM.f
516  }
517
518  /**
519   * Handle mouse move event
520   *
521   * @param  {object} evt Event
522   */
523  SvgPanZoom.prototype.handleMouseMove = function(evt) {
524    if (evt.preventDefault) {
525      evt.preventDefault()
526    } else {
527      evt.returnValue = false
528    }
529
530    var svg = (evt.target.tagName === 'svg' || evt.target.tagName === 'SVG') ? evt.target : evt.target.ownerSVGElement || evt.target.correspondingElement.ownerSVGElement
531
532    var point;
533    if (this.state === 'pan' && this.options.panEnabled) {
534      // Trigger beforePan
535      if (Utils.isFunction(this.options.beforePan)) {
536        this.options.beforePan()
537      }
538
539      // Pan mode
540      point = SvgUtils.getEventPoint(evt).matrixTransform(this.stateTf)
541      var viewportCTM = this.stateTf.inverse().translate(point.x - this.stateOrigin.x, point.y - this.stateOrigin.y)
542
543      SvgUtils.setCTM(this.viewport, viewportCTM)
544
545      // Cache pan level
546      this._pan.x = viewportCTM.e
547      this._pan.y = viewportCTM.f
548
549      // Trigger onPan
550      this.options.onPan(this._pan.x, this._pan.y)
551    } else if (this.state === 'drag' && this.options.dragEnabled) {
552      // Drag mode
553      point = SvgUtils.getEventPoint(evt).matrixTransform(this.viewport.getCTM().inverse())
554
555      SvgUtils.setCTM(this.stateTarget, svg.createSVGMatrix().translate(point.x - this.stateOrigin.x, point.y - this.stateOrigin.y).multiply(this.viewport.getCTM().inverse()).multiply(this.stateTarget.getCTM()))
556
557      this.stateOrigin = point;
558    }
559  }
560
561  /**
562   * Handle double click event
563   * See handleMouseDown() for alternate detection method
564   *
565   * @param {object} evt Event
566   */
567  SvgPanZoom.prototype.handleDblClick = function(evt) {
568    var target = evt.target
569      , svg = (target.tagName === 'svg' || target.tagName === 'SVG') ? target : target.ownerSVGElement || target.correspondingElement.ownerSVGElement
570
571    if (evt.preventDefault) {
572      evt.preventDefault()
573    } else {
574      evt.returnValue = false
575    }
576
577    // Check if target was a control button
578    if (this.options.controlIconsEnabled) {
579      var targetClass = target.getAttribute('class') || ''
580      if (targetClass.indexOf('svg-pan-zoom-control') > -1) {
581        return false
582      }
583    }
584
585    var zoomFactor
586
587    if (evt.shiftKey) {
588      zoomFactor = 1/((1 + this.options.zoomScaleSensitivity) * 2) // zoom out when shift key pressed
589    }
590    else {
591      zoomFactor = (1 + this.options.zoomScaleSensitivity) * 2
592    }
593
594    var point = SvgUtils.getRelativeMousePoint(svg, evt)
595    this.zoomAtPoint(svg, point, zoomFactor)
596  }
597
598  /**
599   * Handle click event
600   *
601   * @param {object} evt Event
602   */
603  SvgPanZoom.prototype.handleMouseDown = function(evt) {
604    // Double click detection; more consistent than ondblclick
605    if (evt.detail === 2){
606      this.handleDblClick(evt)
607    }
608
609    if (evt.preventDefault) {
610      evt.preventDefault()
611    } else {
612      evt.returnValue = false
613    }
614
615    var svg = (evt.target.tagName === 'svg' || evt.target.tagName === 'SVG') ? evt.target : evt.target.ownerSVGElement || evt.target.correspondingElement.ownerSVGElement
616
617    if (evt.target.tagName === 'svg' || !this.options.dragEnabled) { // Pan anyway when drag is disabled and the user clicked on an element
618      // Pan mode
619      this.state = 'pan'
620      this.stateTf = this.viewport.getCTM().inverse()
621      this.stateOrigin = SvgUtils.getEventPoint(evt).matrixTransform(this.stateTf)
622    } else {
623      // Drag mode
624      this.state = 'drag'
625      this.stateTarget = evt.target
626      this.stateTf = this.viewport.getCTM().inverse()
627      this.stateOrigin = SvgUtils.getEventPoint(evt).matrixTransform(this.stateTf)
628    }
629  }
630
631  /**
632   * Handle mouse button release event
633   *
634   * @param {object} evt Event
635   */
636  SvgPanZoom.prototype.handleMouseUp = function(evt) {
637    if (evt.preventDefault) {
638      evt.preventDefault()
639    } else {
640      evt.returnValue = false
641    }
642
643    var svg = (evt.target.tagName === 'svg' || evt.target.tagName === 'SVG') ? evt.target : evt.target.ownerSVGElement || evt.target.correspondingElement.ownerSVGElement
644
645    if (this.state === 'pan' || this.state === 'drag') {
646      // Quit pan mode
647      this.state = 'none'
648    }
649  }
650
651  /**
652   * Adjust viewport size (only) so it will fit in SVG
653   * Does not center image
654   *
655   * @param  {bool} dropCache drop viewBox cache and recalculate SVG's viewport sizes. Default false
656   */
657  SvgPanZoom.prototype.fit = function(dropCache) {
658    if (dropCache) {
659      this.recacheViewBox()
660    }
661
662    var newScale = Math.min(this.width/(this._viewBox.width - this._viewBox.x), this.height/(this._viewBox.height - this._viewBox.y))
663
664    this.getPublicInstance().zoom(newScale)
665  }
666
667  /**
668   * Adjust viewport pan (only) so it will be centered in SVG
669   * Does not zoom/fit image
670   *
671   * @param  {bool} dropCache drop viewBox cache and recalculate SVG's viewport sizes. Default false
672   */
673  SvgPanZoom.prototype.center = function(dropCache) {
674    if (dropCache) {
675      this.recacheViewBox()
676    }
677
678    var offsetX = (this.width - (this._viewBox.width + this._viewBox.x) * this.getZoom()) * 0.5
679      , offsetY = (this.height - (this._viewBox.height + this._viewBox.y) * this.getZoom()) * 0.5
680
681    this.getPublicInstance().pan({x: offsetX, y: offsetY})
682  }
683
684  /**
685   * Pan to a rendered position
686   *
687   * @param  {object} point {x: 0, y: 0}
688   */
689  SvgPanZoom.prototype.pan = function(point) {
690    // Trigger beforePan
691    if (Utils.isFunction(this.options.beforePan)) {
692      this.options.beforePan()
693    }
694
695    var viewportCTM = this.viewport.getCTM()
696    viewportCTM.e = point.x
697    viewportCTM.f = point.y
698    SvgUtils.setCTM(this.viewport, viewportCTM)
699
700    // Cache pan level
701    this._pan.x = viewportCTM.e
702    this._pan.y = viewportCTM.f
703
704    // Trigger onPan
705    this.options.onPan(this._pan.x, this._pan.y)
706  }
707
708  /**
709   * Relatively pan the graph by a specified rendered position vector
710   *
711   * @param  {object} point {x: 0, y: 0}
712   */
713  SvgPanZoom.prototype.panBy = function(point) {
714    // Trigger beforePan
715    if (Utils.isFunction(this.options.beforePan)) {
716      this.options.beforePan()
717    }
718
719    var viewportCTM = this.viewport.getCTM()
720    viewportCTM.e += point.x
721    viewportCTM.f += point.y
722    SvgUtils.setCTM(this.viewport, viewportCTM)
723
724    // Cache pan level
725    this._pan.x = viewportCTM.e
726    this._pan.y = viewportCTM.f
727
728    // Trigger onPan
729    this.options.onPan(this._pan.x, this._pan.y)
730  }
731
732  /**
733   * Get pan vector
734   *
735   * @return {object} {x: 0, y: 0}
736   */
737  SvgPanZoom.prototype.getPan = function() {
738    // Do not return object directly because it will be possible to modify it using the reference
739    return {x: this._pan.x, y: this._pan.y}
740  }
741
742  /**
743   * Returns a public instance object
744   * @return {object} Public instance object
745   */
746  SvgPanZoom.prototype.getPublicInstance = function() {
747    var that = this
748
749    // Create cache
750    if (!this.publicInstance) {
751      this.publicInstance = {
752        // Pan
753        enablePan: function() {that.options.panEnabled = true}
754      , disablePan: function() {that.options.panEnabled = false}
755      , isPanEnabled: function() {return !!that.options.panEnabled}
756      , pan: function(point) {that.pan(point)}
757      , panBy: function(point) {that.panBy(point)}
758      , getPan: function() {return that.getPan()}
759        // Pan event
760      , setBeforePan: function(fn) {that.options.beforePan = Utils.proxy(fn, that.publicInstance)}
761      , setOnPan: function(fn) {that.options.onPan = Utils.proxy(fn, that.publicInstance)}
762        // Drag
763      , enableDrag: function() {that.options.dragEnabled = true}
764      , disableDrag: function() {that.options.dragEnabled = false}
765      , isDragEnabled: function() {return !!that.options.dragEnabled}
766        // Zoom and Control Icons
767      , enableZoom: function() {
768          if (that.options.controlIconsEnabled && !that.options.zoomEnabled) {
769            ControlIcons.enable(that)
770          }
771          that.options.zoomEnabled = true;
772        }
773      , disableZoom: function() {
774          if (that.options.controlIconsEnabled && that.options.zoomEnabled) {
775            ControlIcons.disable(that)
776          }
777          that.options.zoomEnabled = false;
778        }
779      , isZoomEnabled: function() {return !!that.options.zoomEnabled}
780      , enableControlIcons: function() {
781          if (that.options.zoomEnabled && !that.options.controlIconsEnabled) {
782            that.options.controlIconsEnabled = true
783            ControlIcons.enable(that)
784          }
785        }
786      , disableControlIcons: function() {
787          if (that.options.controlIconsEnabled) {
788            that.options.controlIconsEnabled = false;
789            ControlIcons.disable(that)
790          }
791        }
792      , isControlIconsEnabled: function() {return !!that.options.controlIconsEnabled}
793        // Zoom scale and bounds
794      , setZoomScaleSensitivity: function(scale) {that.options.zoomScaleSensitivity = scale}
795      , setMinZoom: function(zoom) {that.options.minZoom = zoom}
796      , setMaxZoom: function(zoom) {that.options.maxZoom = zoom}
797        // Zoom event
798      , setBeforeZoom: function(fn) {that.options.beforeZoom = Utils.proxy(fn, that.publicInstance)}
799      , setOnZoom: function(fn) {that.options.onZoom = Utils.proxy(fn, that.publicInstance)}
800        // Zooming
801      , zoom: function(scale) {
802          that.zoomAtPoint(that.svg, SvgUtils.getSvgCenterPoint(that.svg), scale, true)
803        }
804      , zoomBy: function(scale) {
805          that.zoomAtPoint(that.svg, SvgUtils.getSvgCenterPoint(that.svg), scale, false)
806        }
807      , zoomAtPoint: function(scale, point) {
808          that.publicZoomAtPoint(scale, point, true)
809        }
810      , zoomAtPointBy: function(scale, point) {
811          that.publicZoomAtPoint(scale, point, false)
812        }
813      , zoomIn: function() {
814          this.zoomBy(1 + that.options.zoomScaleSensitivity)
815        }
816      , zoomOut: function() {
817          this.zoomBy(1 / (1 + that.options.zoomScaleSensitivity))
818        }
819      , resetZoom: function() {that.resetZoom()}
820      , getZoom: function() {return that.getZoom()}
821      , fit: function(dropCache) {return that.fit(dropCache)}
822      , center: function(dropCache) {return that.center(dropCache)}
823      }
824    }
825
826    return this.publicInstance
827  }
828
829  /**
830   * Stores pairs of instances of SvgPanZoom and SVG
831   * Each pair is represented by an object {svg: SVG, instance: SvgPanZoom}
832   *
833   * @type {Array}
834   */
835  var instancesStore = []
836
837  window.svgPanZoom = function(elementOrSelector, options){
838    var svg = Utils.getSvg(elementOrSelector)
839
840    if (svg === null) {
841      return null
842    } else {
843      // Look for existent instance
844      for(var i = instancesStore.length - 1; i >= 0; i--) {
845        if (instancesStore[i].svg === svg) {
846          return instancesStore[i].instance.getPublicInstance()
847        }
848      }
849
850      // If instance not found - create one
851      instancesStore.push({
852        svg: svg
853      , instance: new SvgPanZoom(svg, options)
854      })
855
856      // Return just pushed instance
857      return instancesStore[instancesStore.length - 1].instance.getPublicInstance()
858    }
859  }
860})(window, document)
861
862},{"./control-icons":1,"./mousewheel":2,"./svg-utilities":4,"./utilities":5}],4:[function(require,module,exports){
863module.exports = {
864  /**
865   * Get svg dimensions: width and height
866   *
867   * @param  {object} svg
868   * @return {object}     {width: 0, height: 0}
869   */
870  getSvgDimensions: function(svg) {
871    var width = 0
872      , height = 0
873      , svgClientRects = svg.getClientRects()
874
875    // thanks to http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
876    var isFirefox = typeof InstallTrigger !== 'undefined';
877
878    // Firefox has no nice way of detecting SVG size, so we'll check for
879    // width/height from getComputedStyle, specified in pixels,
880    // and if they don't exist, we'll use the parent dimensions.
881    // TODO: check whether this method would be a better method for other browsers too.
882    if (isFirefox) {
883      var svgComputedStyle = window.getComputedStyle(svg, null);
884      width = parseFloat(svgComputedStyle.width) - (parseFloat(svgComputedStyle.borderLeftWidth) + parseFloat(svgComputedStyle.paddingLeft) + parseFloat(svgComputedStyle.borderRightWidth) + parseFloat(svgComputedStyle.paddingRight));
885      height = parseFloat(svgComputedStyle.height) - (parseFloat(svgComputedStyle.borderTopWidth) + parseFloat(svgComputedStyle.paddingTop) + parseFloat(svgComputedStyle.borderBottomWidth) + parseFloat(svgComputedStyle.paddingBottom));
886      if (!width || !height) {
887        var parentStyle = window.getComputedStyle(svg.parentElement, null);
888        var parentDimensions = svg.parentElement.getBoundingClientRect();
889        width = parentDimensions.width - (parseFloat(parentStyle.borderLeftWidth) + parseFloat(parentStyle.paddingLeft) + parseFloat(parentStyle.borderRightWidth) + parseFloat(parentStyle.paddingRight));
890        height = parentDimensions.height - (parseFloat(parentStyle.borderTopWidth) + parseFloat(parentStyle.paddingTop) + parseFloat(parentStyle.borderBottomWidth) + parseFloat(parentStyle.paddingBottom));
891      }
892    } else {
893      if (typeof svgClientRects !== 'undefined' && svgClientRects.length > 0) {
894        var svgClientRect = svgClientRects[0];
895
896        width = parseFloat(svgClientRect.width);
897        height = parseFloat(svgClientRect.height);
898      } else {
899        var svgBoundingClientRect = svg.getBoundingClientRect();
900
901        if (!!svgBoundingClientRect) {
902          width = parseFloat(svgBoundingClientRect.width);
903          height = parseFloat(svgBoundingClientRect.height);
904        } else {
905          throw new Error('Cannot determine SVG width and height.');
906        }
907      }
908    }
909
910    return {
911      width: width
912    , height: height
913    }
914  }
915
916  /**
917   * Gets g.viewport element or creates it if it doesn't exist
918   * @param  {object} svg
919   * @return {object}     g element
920   */
921, getOrCreateViewport: function(svg) {
922    var viewport = svg.querySelector('g.viewport')
923
924    // If no g container with id 'viewport' exists, create one
925    if (!viewport) {
926      var viewport = document.createElementNS('http://www.w3.org/2000/svg', 'g');
927      viewport.setAttribute('class', 'viewport');
928
929      var svgChildren = svg.childNodes || svg.children;
930      do {
931        viewport.appendChild(svgChildren[0]);
932      } while (svgChildren.length > 0);
933      svg.appendChild(viewport);
934    }
935
936    return viewport
937  }
938
939, setupSvgAttributes: function(svg) {
940    // Setting default attributes
941    svg.setAttribute('xmlns', 'http://www.w3.org/1999/xlink');
942    svg.setAttributeNS('xmlns', 'xlink', 'http://www.w3.org/1999/xlink');
943    svg.setAttributeNS('xmlns', 'ev', 'http://www.w3.org/2001/xml-events');
944
945    // Needed for Internet Explorer, otherwise the viewport overflows
946    if (svg.parentNode !== null) {
947      var style = svg.getAttribute('style') || '';
948      if (style.toLowerCase().indexOf('overflow') === -1) {
949        svg.setAttribute('style', 'overflow: hidden; ' + style);
950      }
951    }
952  }
953
954  /**
955   * Sets the current transform matrix of an element
956   * @param {object} element SVG Element
957   * @param {object} matrix  CTM
958   */
959, setCTM: function(element, matrix) {
960    var s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')';
961    element.setAttribute('transform', s);
962  }
963
964  /**
965   * Time-based cache for svg.getScreenCTM().
966   * Needed because getScreenCTM() is very slow on Firefox (FF 28 at time of writing).
967   * The cache expires every 300ms... this is a pretty safe time because it's only called
968   * when we're zooming, when the screenCTM is unlikely/impossible to change.
969   *
970   * @param {object} svg SVG Element
971   * @return {[type]} [description]
972   */
973, getScreenCTMCached: (function() {
974    var svgs = {};
975    return function(svg) {
976      var cur = Date.now();
977      if (svgs.hasOwnProperty(svg)) {
978        var cached = svgs[svg];
979        if (cur - cached.time > 300) {
980          // Cache expired
981          cached.time = cur;
982          cached.ctm = svg.getScreenCTM();
983        }
984        return cached.ctm;
985      } else {
986        var ctm = svg.getScreenCTM();
987        svgs[svg] = {time: cur, ctm: ctm};
988        return ctm;
989      }
990    };
991  })()
992
993  /**
994   * Get an SVGPoint of the mouse co-ordinates of the event, relative to the SVG element
995   *
996   * @param  {object} svg SVG Element
997   * @param  {object} evt Event
998   * @return {object}     point
999   */
1000, getRelativeMousePoint: function(svg, evt) {
1001    var point = svg.createSVGPoint()
1002
1003    point.x = evt.clientX
1004    point.y = evt.clientY
1005
1006    return point.matrixTransform(this.getScreenCTMCached(svg).inverse())
1007  }
1008
1009  /**
1010   * Instantiate an SVGPoint object with given event coordinates
1011   *
1012   * @param {object} evt Event
1013   */
1014, getEventPoint: function(evt) {
1015    var svg = (evt.target.tagName === 'svg' || evt.target.tagName === 'SVG') ? evt.target : evt.target.ownerSVGElement || evt.target.correspondingElement.ownerSVGElement
1016      , point = svg.createSVGPoint()
1017
1018    point.x = evt.clientX
1019    point.y = evt.clientY
1020
1021    return point
1022  }
1023
1024  /**
1025   * Get SVG center point
1026   *
1027   * @param  {object} svg SVG Element
1028   * @return {object}     SVG Point
1029   */
1030, getSvgCenterPoint: function(svg) {
1031    var boundingClientRect = svg.getBoundingClientRect()
1032      , width = boundingClientRect.width
1033      , height = boundingClientRect.height
1034      , point = svg.createSVGPoint()
1035
1036    point.x = width / 2
1037    point.y = height / 2
1038
1039    return point
1040  }
1041}
1042
1043},{}],5:[function(require,module,exports){
1044module.exports = {
1045  /**
1046   * Extends an object
1047   *
1048   * @param  {object} target object to extend
1049   * @param  {object} source object to take properties from
1050   * @return {object}        extended object
1051   */
1052  extend: function(target, source) {
1053    target = target || {};
1054    for (var prop in source) {
1055      // Go recursively
1056      if (this.isObject(source[prop])) {
1057        target[prop] = this.extend(target[prop], source[prop])
1058      } else {
1059        target[prop] = source[prop]
1060      }
1061    }
1062    return target;
1063  }
1064
1065  /**
1066   * Checks if an object is a DOM element
1067   *
1068   * @param  {object}  o HTML element or String
1069   * @return {Boolean}   returns true if object is a DOM element
1070   */
1071, isElement: function(o){
1072    return (
1073      typeof HTMLElement === "object" ? (o instanceof HTMLElement || o instanceof SVGElement || o instanceof SVGSVGElement) : //DOM2
1074      o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
1075    );
1076  }
1077
1078  /**
1079   * Checks if an object is an Object
1080   *
1081   * @param  {object}  o Object
1082   * @return {Boolean}   returns true if object is an Object
1083   */
1084, isObject: function(o){
1085    return Object.prototype.toString.call(o) === '[object Object]';
1086  }
1087
1088  /**
1089   * Checks if an object is a Function
1090   *
1091   * @param  {object}  f Function
1092   * @return {Boolean}   returns true if object is a Function
1093   */
1094, isFunction: function(f){
1095    return Object.prototype.toString.call(f) === '[object Function]';
1096  }
1097
1098  /**
1099   * Search for an SVG element
1100   *
1101   * @param  {object|string} elementOrSelector DOM Element or selector String
1102   * @return {object|null}                   SVG or null
1103   */
1104, getSvg: function(elementOrSelector) {
1105    var element
1106      , svg;
1107
1108    if (!this.isElement(elementOrSelector)) {
1109      // If selector provided
1110      if (typeof elementOrSelector == 'string' || elementOrSelector instanceof String) {
1111        // Try to find the element
1112        element = document.querySelector(elementOrSelector)
1113
1114        if (!element) {
1115          throw new Error('Provided selector did not find any elements')
1116          return null
1117        }
1118
1119      } else {
1120        throw new Error('Provided selector is not an HTML object nor String')
1121        return null
1122      }
1123    } else {
1124      element = elementOrSelector
1125    }
1126
1127    if (element.tagName.toLowerCase() === 'svg') {
1128      svg = element;
1129    } else {
1130      if (element.tagName.toLowerCase() === 'object') {
1131        svg = element.contentDocument.documentElement;
1132      } else {
1133        if (element.tagName.toLowerCase() === 'embed') {
1134          svg = element.getSVGDocument().documentElement;
1135        } else {
1136          if (element.tagName.toLowerCase() === 'img') {
1137            throw new Error('Cannot script an SVG in an "img" element. Please use an "object" element or an in-line SVG.');
1138          } else {
1139            throw new Error('Cannot get SVG.');
1140          }
1141          return null
1142        }
1143      }
1144    }
1145
1146    return svg
1147  }
1148
1149  /**
1150   * Attach a given context to a function
1151   * @param  {Function} fn      Function
1152   * @param  {object}   context Context
1153   * @return {Function}           Function with certain context
1154   */
1155, proxy: function(fn, context) {
1156    return function() {
1157      fn.apply(context, arguments)
1158    }
1159  }
1160
1161  /**
1162   * Returns object type
1163   * Uses toString that returns [object SVGPoint]
1164   * And than parses object type from string
1165   *
1166   * @param  {object} o Any object
1167   * @return {string}   Object type
1168   */
1169, getType: function(o) {
1170    return Object.prototype.toString.apply(o).replace(/^\[object\s/, '').replace(/\]$/, '')
1171  }
1172}
1173
1174},{}]},{},[3])