svg_pan_zoom 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b0b9debf9c52d1ae16b1a175292d65102ad9622f
4
+ data.tar.gz: d690e1ca76ec0c72ab061ab884e6fbbba0b8e228
5
+ SHA512:
6
+ metadata.gz: 0227cca3841c7675fdbadc66b50f909aea5dbadab04b5f432cd96f4ccb066edbbabf37535562fdedf7bc75945392fb17516f465f6d520c372f4bab2ff0dd8053
7
+ data.tar.gz: 1fc3440107c1ad583658b89a9f8281db952ad1d4bb1427de9433c00c9370686e644e8d522318ad719d1f7c40ffd5492f7a5d529ec9b04e333f25d5d07a990831
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in svg_pan_zoom.gemspec
4
+ gemspec
@@ -0,0 +1,40 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Peter Le
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ ----------------------------------------------------------------------------
24
+
25
+ The code from the SVGPan library is licensed under the following BSD license:
26
+
27
+ Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
28
+
29
+ Redistribution and use in source and binary forms, with or without modification, are
30
+ permitted provided that the following conditions are met:
31
+
32
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
33
+
34
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materialsprovided with the distribution.
35
+
36
+ THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ * The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Andrea Leofreddi.
39
+
40
+ The code from the updates and changes to SVGPan are licensed under the same BSD license, with the copyright for the code from each change held by the author of that code.
@@ -0,0 +1,51 @@
1
+ # SvgPanZoom
2
+
3
+ Rails gem for the svg-pan-zoom library at [github.com/ariutta/svg-pan-zoom](https://github.com/ariutta/svg-pan-zoom)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'svg_pan_zoom'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install svg_pan_zoom
20
+
21
+ Finally, add this line to your `app/assets/javascripts/application.js` file before `//require_tree .`:
22
+
23
+ ```javascript
24
+ //= require svg-pan-zoom
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Instructions on how to use: [github.com/ariutta/svg-pan-zoom](https://github.com/ariutta/svg-pan-zoom)
30
+
31
+ ## License
32
+
33
+ The code from the SVGPan library is licensed under the following BSD license:
34
+
35
+ ```
36
+ Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
37
+
38
+ Redistribution and use in source and binary forms, with or without modification, are
39
+ permitted provided that the following conditions are met:
40
+
41
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
42
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
43
+
44
+ THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
+
46
+ * The views and conclusions contained in the software and documentation are those of the
47
+ authors and should not be interpreted as representing official policies, either expressed
48
+ or implied, of Andrea Leofreddi.
49
+ ```
50
+
51
+ The code from the updates and changes to SVGPan are licensed under the same BSD license, with the copyright for the code from each change held by the author of that code.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "svg_pan_zoom"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ require "svg_pan_zoom/version"
2
+
3
+ module SvgPanZoom
4
+ class Engine < ::Rails::Engine; end
5
+ end
@@ -0,0 +1,3 @@
1
+ module SvgPanZoom
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'svg_pan_zoom/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "svg_pan_zoom"
8
+ spec.version = SvgPanZoom::VERSION
9
+ spec.authors = ["Peter Le"]
10
+ spec.email = ["peter@peterkle.com"]
11
+
12
+ spec.summary = %q{Rails gem that wraps the svg-pan-zoom.js library}
13
+ spec.description = %q{Visit https://github.com/ariutta/svg-pan-zoom for how to use.}
14
+ spec.homepage = "https://github.com/peterkle/svg-pan-zoom-gem"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ end
@@ -0,0 +1,1867 @@
1
+ // svg-pan-zoom v3.2.5
2
+ // https://github.com/ariutta/svg-pan-zoom
3
+ (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.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){
4
+ var svgPanZoom = require('./svg-pan-zoom.js');
5
+
6
+ // UMD module definition
7
+ (function(window, document){
8
+ // AMD
9
+ if (typeof define === 'function' && define.amd) {
10
+ define('svg-pan-zoom', function () {
11
+ return svgPanZoom;
12
+ });
13
+ // CMD
14
+ } else if (typeof module !== 'undefined' && module.exports) {
15
+ module.exports = svgPanZoom;
16
+
17
+ // Browser
18
+ // Keep exporting globally as module.exports is available because of browserify
19
+ window.svgPanZoom = svgPanZoom;
20
+ }
21
+ })(window, document)
22
+
23
+ },{"./svg-pan-zoom.js":4}],2:[function(require,module,exports){
24
+ var SvgUtils = require('./svg-utilities');
25
+
26
+ module.exports = {
27
+ enable: function(instance) {
28
+ // Select (and create if necessary) defs
29
+ var defs = instance.svg.querySelector('defs')
30
+ if (!defs) {
31
+ defs = document.createElementNS(SvgUtils.svgNS, 'defs')
32
+ instance.svg.appendChild(defs)
33
+ }
34
+
35
+ // Create style element
36
+ var style = document.createElementNS(SvgUtils.svgNS, 'style')
37
+ style.setAttribute('type', 'text/css')
38
+ 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; }'
39
+ defs.appendChild(style)
40
+
41
+
42
+ // Zoom Group
43
+ var zoomGroup = document.createElementNS(SvgUtils.svgNS, 'g');
44
+ zoomGroup.setAttribute('id', 'svg-pan-zoom-controls');
45
+ zoomGroup.setAttribute('transform', 'translate(' + ( instance.width - 70 ) + ' ' + ( instance.height - 76 ) + ') scale(0.75)');
46
+ zoomGroup.setAttribute('class', 'svg-pan-zoom-control');
47
+
48
+ // Control elements
49
+ zoomGroup.appendChild(this._createZoomIn(instance))
50
+ zoomGroup.appendChild(this._createZoomReset(instance))
51
+ zoomGroup.appendChild(this._createZoomOut(instance))
52
+
53
+ // Finally append created element
54
+ instance.svg.appendChild(zoomGroup)
55
+
56
+ // Cache control instance
57
+ instance.controlIcons = zoomGroup
58
+ }
59
+
60
+ , _createZoomIn: function(instance) {
61
+ var zoomIn = document.createElementNS(SvgUtils.svgNS, 'g');
62
+ zoomIn.setAttribute('id', 'svg-pan-zoom-zoom-in');
63
+ zoomIn.setAttribute('transform', 'translate(30.5 5) scale(0.015)');
64
+ zoomIn.setAttribute('class', 'svg-pan-zoom-control');
65
+ zoomIn.addEventListener('click', function() {instance.getPublicInstance().zoomIn()}, false)
66
+ zoomIn.addEventListener('touchstart', function() {instance.getPublicInstance().zoomIn()}, false)
67
+
68
+ var zoomInBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
69
+ zoomInBackground.setAttribute('x', '0');
70
+ zoomInBackground.setAttribute('y', '0');
71
+ zoomInBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
72
+ zoomInBackground.setAttribute('height', '1400');
73
+ zoomInBackground.setAttribute('class', 'svg-pan-zoom-control-background');
74
+ zoomIn.appendChild(zoomInBackground);
75
+
76
+ var zoomInShape = document.createElementNS(SvgUtils.svgNS, 'path');
77
+ 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');
78
+ zoomInShape.setAttribute('class', 'svg-pan-zoom-control-element');
79
+ zoomIn.appendChild(zoomInShape);
80
+
81
+ return zoomIn
82
+ }
83
+
84
+ , _createZoomReset: function(instance){
85
+ // reset
86
+ var resetPanZoomControl = document.createElementNS(SvgUtils.svgNS, 'g');
87
+ resetPanZoomControl.setAttribute('id', 'svg-pan-zoom-reset-pan-zoom');
88
+ resetPanZoomControl.setAttribute('transform', 'translate(5 35) scale(0.4)');
89
+ resetPanZoomControl.setAttribute('class', 'svg-pan-zoom-control');
90
+ resetPanZoomControl.addEventListener('click', function() {instance.getPublicInstance().reset()}, false);
91
+ resetPanZoomControl.addEventListener('touchstart', function() {instance.getPublicInstance().reset()}, false);
92
+
93
+ var resetPanZoomControlBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
94
+ resetPanZoomControlBackground.setAttribute('x', '2');
95
+ resetPanZoomControlBackground.setAttribute('y', '2');
96
+ resetPanZoomControlBackground.setAttribute('width', '182'); // larger than expected because the whole group is transformed to scale down
97
+ resetPanZoomControlBackground.setAttribute('height', '58');
98
+ resetPanZoomControlBackground.setAttribute('class', 'svg-pan-zoom-control-background');
99
+ resetPanZoomControl.appendChild(resetPanZoomControlBackground);
100
+
101
+ var resetPanZoomControlShape1 = document.createElementNS(SvgUtils.svgNS, 'path');
102
+ 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');
103
+ resetPanZoomControlShape1.setAttribute('class', 'svg-pan-zoom-control-element');
104
+ resetPanZoomControl.appendChild(resetPanZoomControlShape1);
105
+
106
+ var resetPanZoomControlShape2 = document.createElementNS(SvgUtils.svgNS, 'path');
107
+ 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');
108
+ resetPanZoomControlShape2.setAttribute('class', 'svg-pan-zoom-control-element');
109
+ resetPanZoomControl.appendChild(resetPanZoomControlShape2);
110
+
111
+ return resetPanZoomControl
112
+ }
113
+
114
+ , _createZoomOut: function(instance){
115
+ // zoom out
116
+ var zoomOut = document.createElementNS(SvgUtils.svgNS, 'g');
117
+ zoomOut.setAttribute('id', 'svg-pan-zoom-zoom-out');
118
+ zoomOut.setAttribute('transform', 'translate(30.5 70) scale(0.015)');
119
+ zoomOut.setAttribute('class', 'svg-pan-zoom-control');
120
+ zoomOut.addEventListener('click', function() {instance.getPublicInstance().zoomOut()}, false);
121
+ zoomOut.addEventListener('touchstart', function() {instance.getPublicInstance().zoomOut()}, false);
122
+
123
+ var zoomOutBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
124
+ zoomOutBackground.setAttribute('x', '0');
125
+ zoomOutBackground.setAttribute('y', '0');
126
+ zoomOutBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
127
+ zoomOutBackground.setAttribute('height', '1400');
128
+ zoomOutBackground.setAttribute('class', 'svg-pan-zoom-control-background');
129
+ zoomOut.appendChild(zoomOutBackground);
130
+
131
+ var zoomOutShape = document.createElementNS(SvgUtils.svgNS, 'path');
132
+ 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');
133
+ zoomOutShape.setAttribute('class', 'svg-pan-zoom-control-element');
134
+ zoomOut.appendChild(zoomOutShape);
135
+
136
+ return zoomOut
137
+ }
138
+
139
+ , disable: function(instance) {
140
+ if (instance.controlIcons) {
141
+ instance.controlIcons.parentNode.removeChild(instance.controlIcons)
142
+ instance.controlIcons = null
143
+ }
144
+ }
145
+ }
146
+
147
+ },{"./svg-utilities":5}],3:[function(require,module,exports){
148
+ var SvgUtils = require('./svg-utilities')
149
+ , Utils = require('./utilities')
150
+ ;
151
+
152
+ var ShadowViewport = function(viewport, options){
153
+ this.init(viewport, options)
154
+ }
155
+
156
+ /**
157
+ * Initialization
158
+ *
159
+ * @param {SVGElement} viewport
160
+ * @param {Object} options
161
+ */
162
+ ShadowViewport.prototype.init = function(viewport, options) {
163
+ // DOM Elements
164
+ this.viewport = viewport
165
+ this.options = options
166
+
167
+ // State cache
168
+ this.originalState = {zoom: 1, x: 0, y: 0}
169
+ this.activeState = {zoom: 1, x: 0, y: 0}
170
+
171
+ this.updateCTMCached = Utils.proxy(this.updateCTM, this)
172
+
173
+ // Create a custom requestAnimationFrame taking in account refreshRate
174
+ this.requestAnimationFrame = Utils.createRequestAnimationFrame(this.options.refreshRate)
175
+
176
+ // ViewBox
177
+ this.viewBox = {x: 0, y: 0, width: 0, height: 0}
178
+ this.cacheViewBox()
179
+
180
+ // Process CTM
181
+ this.processCTM()
182
+
183
+ // Update CTM in this frame
184
+ this.updateCTM()
185
+ }
186
+
187
+ /**
188
+ * Cache initial viewBox value
189
+ * If no viewBox is defined, then use viewport size/position instead for viewBox values
190
+ */
191
+ ShadowViewport.prototype.cacheViewBox = function() {
192
+ var svgViewBox = this.options.svg.getAttribute('viewBox')
193
+
194
+ if (svgViewBox) {
195
+ var viewBoxValues = svgViewBox.split(/[\s\,]/).filter(function(v){return v}).map(parseFloat)
196
+
197
+ // Cache viewbox x and y offset
198
+ this.viewBox.x = viewBoxValues[0]
199
+ this.viewBox.y = viewBoxValues[1]
200
+ this.viewBox.width = viewBoxValues[2]
201
+ this.viewBox.height = viewBoxValues[3]
202
+
203
+ var zoom = Math.min(this.options.width / this.viewBox.width, this.options.height / this.viewBox.height)
204
+
205
+ // Update active state
206
+ this.activeState.zoom = zoom
207
+ this.activeState.x = (this.options.width - this.viewBox.width * zoom) / 2
208
+ this.activeState.y = (this.options.height - this.viewBox.height * zoom) / 2
209
+
210
+ // Force updating CTM
211
+ this.updateCTMOnNextFrame()
212
+
213
+ this.options.svg.removeAttribute('viewBox')
214
+ } else {
215
+ var bBox = this.viewport.getBBox();
216
+
217
+ // Cache viewbox sizes
218
+ this.viewBox.x = bBox.x;
219
+ this.viewBox.y = bBox.y;
220
+ this.viewBox.width = bBox.width
221
+ this.viewBox.height = bBox.height
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Recalculate viewport sizes and update viewBox cache
227
+ */
228
+ ShadowViewport.prototype.recacheViewBox = function() {
229
+ var boundingClientRect = this.viewport.getBoundingClientRect()
230
+ , viewBoxWidth = boundingClientRect.width / this.getZoom()
231
+ , viewBoxHeight = boundingClientRect.height / this.getZoom()
232
+
233
+ // Cache viewbox
234
+ this.viewBox.x = 0
235
+ this.viewBox.y = 0
236
+ this.viewBox.width = viewBoxWidth
237
+ this.viewBox.height = viewBoxHeight
238
+ }
239
+
240
+ /**
241
+ * Returns a viewbox object. Safe to alter
242
+ *
243
+ * @return {Object} viewbox object
244
+ */
245
+ ShadowViewport.prototype.getViewBox = function() {
246
+ return Utils.extend({}, this.viewBox)
247
+ }
248
+
249
+ /**
250
+ * Get initial zoom and pan values. Save them into originalState
251
+ * Parses viewBox attribute to alter initial sizes
252
+ */
253
+ ShadowViewport.prototype.processCTM = function() {
254
+ var newCTM = this.getCTM()
255
+
256
+ if (this.options.fit || this.options.contain) {
257
+ var newScale;
258
+ if (this.options.fit) {
259
+ newScale = Math.min(this.options.width/this.viewBox.width, this.options.height/this.viewBox.height);
260
+ } else {
261
+ newScale = Math.max(this.options.width/this.viewBox.width, this.options.height/this.viewBox.height);
262
+ }
263
+
264
+ newCTM.a = newScale; //x-scale
265
+ newCTM.d = newScale; //y-scale
266
+ newCTM.e = -this.viewBox.x * newScale; //x-transform
267
+ newCTM.f = -this.viewBox.y * newScale; //y-transform
268
+ }
269
+
270
+ if (this.options.center) {
271
+ var offsetX = (this.options.width - (this.viewBox.width + this.viewBox.x * 2) * newCTM.a) * 0.5
272
+ , offsetY = (this.options.height - (this.viewBox.height + this.viewBox.y * 2) * newCTM.a) * 0.5
273
+
274
+ newCTM.e = offsetX
275
+ newCTM.f = offsetY
276
+ }
277
+
278
+ // Cache initial values. Based on activeState and fix+center opitons
279
+ this.originalState.zoom = newCTM.a
280
+ this.originalState.x = newCTM.e
281
+ this.originalState.y = newCTM.f
282
+
283
+ // Update viewport CTM and cache zoom and pan
284
+ this.setCTM(newCTM);
285
+ }
286
+
287
+ /**
288
+ * Return originalState object. Safe to alter
289
+ *
290
+ * @return {Object}
291
+ */
292
+ ShadowViewport.prototype.getOriginalState = function() {
293
+ return Utils.extend({}, this.originalState)
294
+ }
295
+
296
+ /**
297
+ * Return actualState object. Safe to alter
298
+ *
299
+ * @return {Object}
300
+ */
301
+ ShadowViewport.prototype.getState = function() {
302
+ return Utils.extend({}, this.activeState)
303
+ }
304
+
305
+ /**
306
+ * Get zoom scale
307
+ *
308
+ * @return {Float} zoom scale
309
+ */
310
+ ShadowViewport.prototype.getZoom = function() {
311
+ return this.activeState.zoom
312
+ }
313
+
314
+ /**
315
+ * Get zoom scale for pubilc usage
316
+ *
317
+ * @return {Float} zoom scale
318
+ */
319
+ ShadowViewport.prototype.getRelativeZoom = function() {
320
+ return this.activeState.zoom / this.originalState.zoom
321
+ }
322
+
323
+ /**
324
+ * Compute zoom scale for pubilc usage
325
+ *
326
+ * @return {Float} zoom scale
327
+ */
328
+ ShadowViewport.prototype.computeRelativeZoom = function(scale) {
329
+ return scale / this.originalState.zoom
330
+ }
331
+
332
+ /**
333
+ * Get pan
334
+ *
335
+ * @return {Object}
336
+ */
337
+ ShadowViewport.prototype.getPan = function() {
338
+ return {x: this.activeState.x, y: this.activeState.y}
339
+ }
340
+
341
+ /**
342
+ * Return cached viewport CTM value that can be safely modified
343
+ *
344
+ * @return {SVGMatrix}
345
+ */
346
+ ShadowViewport.prototype.getCTM = function() {
347
+ var safeCTM = this.options.svg.createSVGMatrix()
348
+
349
+ // Copy values manually as in FF they are not itterable
350
+ safeCTM.a = this.activeState.zoom
351
+ safeCTM.b = 0
352
+ safeCTM.c = 0
353
+ safeCTM.d = this.activeState.zoom
354
+ safeCTM.e = this.activeState.x
355
+ safeCTM.f = this.activeState.y
356
+
357
+ return safeCTM
358
+ }
359
+
360
+ /**
361
+ * Set a new CTM
362
+ *
363
+ * @param {SVGMatrix} newCTM
364
+ */
365
+ ShadowViewport.prototype.setCTM = function(newCTM) {
366
+ var willZoom = this.isZoomDifferent(newCTM)
367
+ , willPan = this.isPanDifferent(newCTM)
368
+
369
+ if (willZoom || willPan) {
370
+ // Before zoom
371
+ if (willZoom) {
372
+ // If returns false then cancel zooming
373
+ if (this.options.beforeZoom(this.getRelativeZoom(), this.computeRelativeZoom(newCTM.a)) === false) {
374
+ newCTM.a = newCTM.d = this.activeState.zoom
375
+ willZoom = false
376
+ }
377
+ }
378
+
379
+ // Before pan
380
+ if (willPan) {
381
+ var preventPan = this.options.beforePan(this.getPan(), {x: newCTM.e, y: newCTM.f})
382
+ // If prevent pan is an object
383
+ , preventPanX = false
384
+ , preventPanY = false
385
+
386
+ // If prevent pan is Boolean false
387
+ if (preventPan === false) {
388
+ // Set x and y same as before
389
+ newCTM.e = this.getPan().x
390
+ newCTM.f = this.getPan().y
391
+
392
+ preventPanX = preventPanY = true
393
+ } else if (Utils.isObject(preventPan)) {
394
+ // Check for X axes attribute
395
+ if (preventPan.x === false) {
396
+ // Prevent panning on x axes
397
+ newCTM.e = this.getPan().x
398
+ preventPanX = true
399
+ } else if (Utils.isNumber(preventPan.x)) {
400
+ // Set a custom pan value
401
+ newCTM.e = preventPan.x
402
+ }
403
+
404
+ // Check for Y axes attribute
405
+ if (preventPan.y === false) {
406
+ // Prevent panning on x axes
407
+ newCTM.f = this.getPan().y
408
+ preventPanY = true
409
+ } else if (Utils.isNumber(preventPan.y)) {
410
+ // Set a custom pan value
411
+ newCTM.f = preventPan.y
412
+ }
413
+ }
414
+
415
+ // Update willPan flag
416
+ if (preventPanX && preventPanY) {
417
+ willPan = false
418
+ }
419
+ }
420
+
421
+ // Check again if should zoom or pan
422
+ if (willZoom || willPan) {
423
+ this.updateCache(newCTM)
424
+
425
+ this.updateCTMOnNextFrame()
426
+
427
+ // After callbacks
428
+ if (willZoom) {this.options.onZoom(this.getRelativeZoom())}
429
+ if (willPan) {this.options.onPan(this.getPan())}
430
+ }
431
+ }
432
+ }
433
+
434
+ ShadowViewport.prototype.isZoomDifferent = function(newCTM) {
435
+ return this.activeState.zoom !== newCTM.a
436
+ }
437
+
438
+ ShadowViewport.prototype.isPanDifferent = function(newCTM) {
439
+ return this.activeState.x !== newCTM.e || this.activeState.y !== newCTM.f
440
+ }
441
+
442
+
443
+ /**
444
+ * Update cached CTM and active state
445
+ *
446
+ * @param {SVGMatrix} newCTM
447
+ */
448
+ ShadowViewport.prototype.updateCache = function(newCTM) {
449
+ this.activeState.zoom = newCTM.a
450
+ this.activeState.x = newCTM.e
451
+ this.activeState.y = newCTM.f
452
+ }
453
+
454
+ ShadowViewport.prototype.pendingUpdate = false
455
+
456
+ /**
457
+ * Place a request to update CTM on next Frame
458
+ */
459
+ ShadowViewport.prototype.updateCTMOnNextFrame = function() {
460
+ if (!this.pendingUpdate) {
461
+ // Lock
462
+ this.pendingUpdate = true
463
+
464
+ // Throttle next update
465
+ this.requestAnimationFrame.call(window, this.updateCTMCached)
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Update viewport CTM with cached CTM
471
+ */
472
+ ShadowViewport.prototype.updateCTM = function() {
473
+ // Updates SVG element
474
+ SvgUtils.setCTM(this.viewport, this.getCTM(), this.defs)
475
+
476
+ // Free the lock
477
+ this.pendingUpdate = false
478
+ }
479
+
480
+ module.exports = function(viewport, options){
481
+ return new ShadowViewport(viewport, options)
482
+ }
483
+
484
+ },{"./svg-utilities":5,"./utilities":7}],4:[function(require,module,exports){
485
+ var Wheel = require('./uniwheel')
486
+ , ControlIcons = require('./control-icons')
487
+ , Utils = require('./utilities')
488
+ , SvgUtils = require('./svg-utilities')
489
+ , ShadowViewport = require('./shadow-viewport')
490
+
491
+ var SvgPanZoom = function(svg, options) {
492
+ this.init(svg, options)
493
+ }
494
+
495
+ var optionsDefaults = {
496
+ viewportSelector: '.svg-pan-zoom_viewport' // Viewport selector. Can be querySelector string or SVGElement
497
+ , panEnabled: true // enable or disable panning (default enabled)
498
+ , controlIconsEnabled: false // insert icons to give user an option in addition to mouse events to control pan/zoom (default disabled)
499
+ , zoomEnabled: true // enable or disable zooming (default enabled)
500
+ , dblClickZoomEnabled: true // enable or disable zooming by double clicking (default enabled)
501
+ , mouseWheelZoomEnabled: true // enable or disable zooming by mouse wheel (default enabled)
502
+ , preventMouseEventsDefault: true // enable or disable preventDefault for mouse events
503
+ , zoomScaleSensitivity: 0.1 // Zoom sensitivity
504
+ , minZoom: 0.5 // Minimum Zoom level
505
+ , maxZoom: 10 // Maximum Zoom level
506
+ , fit: true // enable or disable viewport fit in SVG (default true)
507
+ , contain: false // enable or disable viewport contain the svg (default false)
508
+ , center: true // enable or disable viewport centering in SVG (default true)
509
+ , refreshRate: 'auto' // Maximum number of frames per second (altering SVG's viewport)
510
+ , beforeZoom: null
511
+ , onZoom: null
512
+ , beforePan: null
513
+ , onPan: null
514
+ , customEventsHandler: null
515
+ , eventsListenerElement: null
516
+ }
517
+
518
+ SvgPanZoom.prototype.init = function(svg, options) {
519
+ var that = this
520
+
521
+ this.svg = svg
522
+ this.defs = svg.querySelector('defs')
523
+
524
+ // Add default attributes to SVG
525
+ SvgUtils.setupSvgAttributes(this.svg)
526
+
527
+ // Set options
528
+ this.options = Utils.extend(Utils.extend({}, optionsDefaults), options)
529
+
530
+ // Set default state
531
+ this.state = 'none'
532
+
533
+ // Get dimensions
534
+ var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(svg)
535
+ this.width = boundingClientRectNormalized.width
536
+ this.height = boundingClientRectNormalized.height
537
+
538
+ // Init shadow viewport
539
+ this.viewport = ShadowViewport(SvgUtils.getOrCreateViewport(this.svg, this.options.viewportSelector), {
540
+ svg: this.svg
541
+ , width: this.width
542
+ , height: this.height
543
+ , fit: this.options.fit
544
+ , contain: this.options.contain
545
+ , center: this.options.center
546
+ , refreshRate: this.options.refreshRate
547
+ // Put callbacks into functions as they can change through time
548
+ , beforeZoom: function(oldScale, newScale) {
549
+ if (that.viewport && that.options.beforeZoom) {return that.options.beforeZoom(oldScale, newScale)}
550
+ }
551
+ , onZoom: function(scale) {
552
+ if (that.viewport && that.options.onZoom) {return that.options.onZoom(scale)}
553
+ }
554
+ , beforePan: function(oldPoint, newPoint) {
555
+ if (that.viewport && that.options.beforePan) {return that.options.beforePan(oldPoint, newPoint)}
556
+ }
557
+ , onPan: function(point) {
558
+ if (that.viewport && that.options.onPan) {return that.options.onPan(point)}
559
+ }
560
+ })
561
+
562
+ // Wrap callbacks into public API context
563
+ var publicInstance = this.getPublicInstance()
564
+ publicInstance.setBeforeZoom(this.options.beforeZoom)
565
+ publicInstance.setOnZoom(this.options.onZoom)
566
+ publicInstance.setBeforePan(this.options.beforePan)
567
+ publicInstance.setOnPan(this.options.onPan)
568
+
569
+ if (this.options.controlIconsEnabled) {
570
+ ControlIcons.enable(this)
571
+ }
572
+
573
+ // Init events handlers
574
+ this.lastMouseWheelEventTime = Date.now()
575
+ this.setupHandlers()
576
+ }
577
+
578
+ /**
579
+ * Register event handlers
580
+ */
581
+ SvgPanZoom.prototype.setupHandlers = function() {
582
+ var that = this
583
+ , prevEvt = null // use for touchstart event to detect double tap
584
+ ;
585
+
586
+ this.eventListeners = {
587
+ // Mouse down group
588
+ mousedown: function(evt) {
589
+ return that.handleMouseDown(evt, null);
590
+ }
591
+ , touchstart: function(evt) {
592
+ var result = that.handleMouseDown(evt, prevEvt);
593
+ prevEvt = evt
594
+ return result;
595
+ }
596
+
597
+ // Mouse up group
598
+ , mouseup: function(evt) {
599
+ return that.handleMouseUp(evt);
600
+ }
601
+ , touchend: function(evt) {
602
+ return that.handleMouseUp(evt);
603
+ }
604
+
605
+ // Mouse move group
606
+ , mousemove: function(evt) {
607
+ return that.handleMouseMove(evt);
608
+ }
609
+ , touchmove: function(evt) {
610
+ return that.handleMouseMove(evt);
611
+ }
612
+
613
+ // Mouse leave group
614
+ , mouseleave: function(evt) {
615
+ return that.handleMouseUp(evt);
616
+ }
617
+ , touchleave: function(evt) {
618
+ return that.handleMouseUp(evt);
619
+ }
620
+ , touchcancel: function(evt) {
621
+ return that.handleMouseUp(evt);
622
+ }
623
+ }
624
+
625
+ // Init custom events handler if available
626
+ if (this.options.customEventsHandler != null) { // jshint ignore:line
627
+ this.options.customEventsHandler.init({
628
+ svgElement: this.svg
629
+ , eventsListenerElement: this.options.eventsListenerElement
630
+ , instance: this.getPublicInstance()
631
+ })
632
+
633
+ // Custom event handler may halt builtin listeners
634
+ var haltEventListeners = this.options.customEventsHandler.haltEventListeners
635
+ if (haltEventListeners && haltEventListeners.length) {
636
+ for (var i = haltEventListeners.length - 1; i >= 0; i--) {
637
+ if (this.eventListeners.hasOwnProperty(haltEventListeners[i])) {
638
+ delete this.eventListeners[haltEventListeners[i]]
639
+ }
640
+ }
641
+ }
642
+ }
643
+
644
+ // Bind eventListeners
645
+ for (var event in this.eventListeners) {
646
+ // Attach event to eventsListenerElement or SVG if not available
647
+ (this.options.eventsListenerElement || this.svg)
648
+ .addEventListener(event, this.eventListeners[event], false)
649
+ }
650
+
651
+ // Zoom using mouse wheel
652
+ if (this.options.mouseWheelZoomEnabled) {
653
+ this.options.mouseWheelZoomEnabled = false // set to false as enable will set it back to true
654
+ this.enableMouseWheelZoom()
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Enable ability to zoom using mouse wheel
660
+ */
661
+ SvgPanZoom.prototype.enableMouseWheelZoom = function() {
662
+ if (!this.options.mouseWheelZoomEnabled) {
663
+ var that = this
664
+
665
+ // Mouse wheel listener
666
+ this.wheelListener = function(evt) {
667
+ return that.handleMouseWheel(evt);
668
+ }
669
+
670
+ // Bind wheelListener
671
+ Wheel.on(this.options.eventsListenerElement || this.svg, this.wheelListener, false)
672
+
673
+ this.options.mouseWheelZoomEnabled = true
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Disable ability to zoom using mouse wheel
679
+ */
680
+ SvgPanZoom.prototype.disableMouseWheelZoom = function() {
681
+ if (this.options.mouseWheelZoomEnabled) {
682
+ Wheel.off(this.options.eventsListenerElement || this.svg, this.wheelListener, false)
683
+ this.options.mouseWheelZoomEnabled = false
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Handle mouse wheel event
689
+ *
690
+ * @param {Event} evt
691
+ */
692
+ SvgPanZoom.prototype.handleMouseWheel = function(evt) {
693
+ if (!this.options.zoomEnabled || this.state !== 'none') {
694
+ return;
695
+ }
696
+
697
+ if (this.options.preventMouseEventsDefault){
698
+ if (evt.preventDefault) {
699
+ evt.preventDefault();
700
+ } else {
701
+ evt.returnValue = false;
702
+ }
703
+ }
704
+
705
+ // Default delta in case that deltaY is not available
706
+ var delta = evt.deltaY || 1
707
+ , timeDelta = Date.now() - this.lastMouseWheelEventTime
708
+ , divider = 3 + Math.max(0, 30 - timeDelta)
709
+
710
+ // Update cache
711
+ this.lastMouseWheelEventTime = Date.now()
712
+
713
+ // Make empirical adjustments for browsers that give deltaY in pixels (deltaMode=0)
714
+ if ('deltaMode' in evt && evt.deltaMode === 0 && evt.wheelDelta) {
715
+ delta = evt.deltaY === 0 ? 0 : Math.abs(evt.wheelDelta) / evt.deltaY
716
+ }
717
+
718
+ delta = -0.3 < delta && delta < 0.3 ? delta : (delta > 0 ? 1 : -1) * Math.log(Math.abs(delta) + 10) / divider
719
+
720
+ var inversedScreenCTM = this.svg.getScreenCTM().inverse()
721
+ , relativeMousePoint = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(inversedScreenCTM)
722
+ , 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
723
+
724
+ this.zoomAtPoint(zoom, relativeMousePoint)
725
+ }
726
+
727
+ /**
728
+ * Zoom in at a SVG point
729
+ *
730
+ * @param {SVGPoint} point
731
+ * @param {Float} zoomScale Number representing how much to zoom
732
+ * @param {Boolean} zoomAbsolute Default false. If true, zoomScale is treated as an absolute value.
733
+ * Otherwise, zoomScale is treated as a multiplied (e.g. 1.10 would zoom in 10%)
734
+ */
735
+ SvgPanZoom.prototype.zoomAtPoint = function(zoomScale, point, zoomAbsolute) {
736
+ var originalState = this.viewport.getOriginalState()
737
+
738
+ if (!zoomAbsolute) {
739
+ // Fit zoomScale in set bounds
740
+ if (this.getZoom() * zoomScale < this.options.minZoom * originalState.zoom) {
741
+ zoomScale = (this.options.minZoom * originalState.zoom) / this.getZoom()
742
+ } else if (this.getZoom() * zoomScale > this.options.maxZoom * originalState.zoom) {
743
+ zoomScale = (this.options.maxZoom * originalState.zoom) / this.getZoom()
744
+ }
745
+ } else {
746
+ // Fit zoomScale in set bounds
747
+ zoomScale = Math.max(this.options.minZoom * originalState.zoom, Math.min(this.options.maxZoom * originalState.zoom, zoomScale))
748
+ // Find relative scale to achieve desired scale
749
+ zoomScale = zoomScale/this.getZoom()
750
+ }
751
+
752
+ var oldCTM = this.viewport.getCTM()
753
+ , relativePoint = point.matrixTransform(oldCTM.inverse())
754
+ , modifier = this.svg.createSVGMatrix().translate(relativePoint.x, relativePoint.y).scale(zoomScale).translate(-relativePoint.x, -relativePoint.y)
755
+ , newCTM = oldCTM.multiply(modifier)
756
+
757
+ if (newCTM.a !== oldCTM.a) {
758
+ this.viewport.setCTM(newCTM)
759
+ }
760
+ }
761
+
762
+ /**
763
+ * Zoom at center point
764
+ *
765
+ * @param {Float} scale
766
+ * @param {Boolean} absolute Marks zoom scale as relative or absolute
767
+ */
768
+ SvgPanZoom.prototype.zoom = function(scale, absolute) {
769
+ this.zoomAtPoint(scale, SvgUtils.getSvgCenterPoint(this.svg, this.width, this.height), absolute)
770
+ }
771
+
772
+ /**
773
+ * Zoom used by public instance
774
+ *
775
+ * @param {Float} scale
776
+ * @param {Boolean} absolute Marks zoom scale as relative or absolute
777
+ */
778
+ SvgPanZoom.prototype.publicZoom = function(scale, absolute) {
779
+ if (absolute) {
780
+ scale = this.computeFromRelativeZoom(scale)
781
+ }
782
+
783
+ this.zoom(scale, absolute)
784
+ }
785
+
786
+ /**
787
+ * Zoom at point used by public instance
788
+ *
789
+ * @param {Float} scale
790
+ * @param {SVGPoint|Object} point An object that has x and y attributes
791
+ * @param {Boolean} absolute Marks zoom scale as relative or absolute
792
+ */
793
+ SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) {
794
+ if (absolute) {
795
+ // Transform zoom into a relative value
796
+ scale = this.computeFromRelativeZoom(scale)
797
+ }
798
+
799
+ // If not a SVGPoint but has x and y than create a SVGPoint
800
+ if (Utils.getType(point) !== 'SVGPoint' && 'x' in point && 'y' in point) {
801
+ point = SvgUtils.createSVGPoint(this.svg, point.x, point.y)
802
+ } else {
803
+ throw new Error('Given point is invalid')
804
+ return
805
+ }
806
+
807
+ this.zoomAtPoint(scale, point, absolute)
808
+ }
809
+
810
+ /**
811
+ * Get zoom scale
812
+ *
813
+ * @return {Float} zoom scale
814
+ */
815
+ SvgPanZoom.prototype.getZoom = function() {
816
+ return this.viewport.getZoom()
817
+ }
818
+
819
+ /**
820
+ * Get zoom scale for public usage
821
+ *
822
+ * @return {Float} zoom scale
823
+ */
824
+ SvgPanZoom.prototype.getRelativeZoom = function() {
825
+ return this.viewport.getRelativeZoom()
826
+ }
827
+
828
+ /**
829
+ * Compute actual zoom from public zoom
830
+ *
831
+ * @param {Float} zoom
832
+ * @return {Float} zoom scale
833
+ */
834
+ SvgPanZoom.prototype.computeFromRelativeZoom = function(zoom) {
835
+ return zoom * this.viewport.getOriginalState().zoom
836
+ }
837
+
838
+ /**
839
+ * Set zoom to initial state
840
+ */
841
+ SvgPanZoom.prototype.resetZoom = function() {
842
+ var originalState = this.viewport.getOriginalState()
843
+
844
+ this.zoom(originalState.zoom, true);
845
+ }
846
+
847
+ /**
848
+ * Set pan to initial state
849
+ */
850
+ SvgPanZoom.prototype.resetPan = function() {
851
+ this.pan(this.viewport.getOriginalState());
852
+ }
853
+
854
+ /**
855
+ * Set pan and zoom to initial state
856
+ */
857
+ SvgPanZoom.prototype.reset = function() {
858
+ this.resetZoom()
859
+ this.resetPan()
860
+ }
861
+
862
+ /**
863
+ * Handle double click event
864
+ * See handleMouseDown() for alternate detection method
865
+ *
866
+ * @param {Event} evt
867
+ */
868
+ SvgPanZoom.prototype.handleDblClick = function(evt) {
869
+ if (this.options.preventMouseEventsDefault) {
870
+ if (evt.preventDefault) {
871
+ evt.preventDefault()
872
+ } else {
873
+ evt.returnValue = false
874
+ }
875
+ }
876
+
877
+ // Check if target was a control button
878
+ if (this.options.controlIconsEnabled) {
879
+ var targetClass = evt.target.getAttribute('class') || ''
880
+ if (targetClass.indexOf('svg-pan-zoom-control') > -1) {
881
+ return false
882
+ }
883
+ }
884
+
885
+ var zoomFactor
886
+
887
+ if (evt.shiftKey) {
888
+ zoomFactor = 1/((1 + this.options.zoomScaleSensitivity) * 2) // zoom out when shift key pressed
889
+ } else {
890
+ zoomFactor = (1 + this.options.zoomScaleSensitivity) * 2
891
+ }
892
+
893
+ var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.svg.getScreenCTM().inverse())
894
+ this.zoomAtPoint(zoomFactor, point)
895
+ }
896
+
897
+ /**
898
+ * Handle click event
899
+ *
900
+ * @param {Event} evt
901
+ */
902
+ SvgPanZoom.prototype.handleMouseDown = function(evt, prevEvt) {
903
+ if (this.options.preventMouseEventsDefault) {
904
+ if (evt.preventDefault) {
905
+ evt.preventDefault()
906
+ } else {
907
+ evt.returnValue = false
908
+ }
909
+ }
910
+
911
+ Utils.mouseAndTouchNormalize(evt, this.svg)
912
+
913
+ // Double click detection; more consistent than ondblclick
914
+ if (this.options.dblClickZoomEnabled && Utils.isDblClick(evt, prevEvt)){
915
+ this.handleDblClick(evt)
916
+ } else {
917
+ // Pan mode
918
+ this.state = 'pan'
919
+ this.firstEventCTM = this.viewport.getCTM()
920
+ this.stateOrigin = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse())
921
+ }
922
+ }
923
+
924
+ /**
925
+ * Handle mouse move event
926
+ *
927
+ * @param {Event} evt
928
+ */
929
+ SvgPanZoom.prototype.handleMouseMove = function(evt) {
930
+ if (this.options.preventMouseEventsDefault) {
931
+ if (evt.preventDefault) {
932
+ evt.preventDefault()
933
+ } else {
934
+ evt.returnValue = false
935
+ }
936
+ }
937
+
938
+ if (this.state === 'pan' && this.options.panEnabled) {
939
+ // Pan mode
940
+ var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse())
941
+ , viewportCTM = this.firstEventCTM.translate(point.x - this.stateOrigin.x, point.y - this.stateOrigin.y)
942
+
943
+ this.viewport.setCTM(viewportCTM)
944
+ }
945
+ }
946
+
947
+ /**
948
+ * Handle mouse button release event
949
+ *
950
+ * @param {Event} evt
951
+ */
952
+ SvgPanZoom.prototype.handleMouseUp = function(evt) {
953
+ if (this.options.preventMouseEventsDefault) {
954
+ if (evt.preventDefault) {
955
+ evt.preventDefault()
956
+ } else {
957
+ evt.returnValue = false
958
+ }
959
+ }
960
+
961
+ if (this.state === 'pan') {
962
+ // Quit pan mode
963
+ this.state = 'none'
964
+ }
965
+ }
966
+
967
+ /**
968
+ * Adjust viewport size (only) so it will fit in SVG
969
+ * Does not center image
970
+ */
971
+ SvgPanZoom.prototype.fit = function() {
972
+ var viewBox = this.viewport.getViewBox()
973
+ , newScale = Math.min(this.width/viewBox.width, this.height/viewBox.height)
974
+
975
+ this.zoom(newScale, true)
976
+ }
977
+
978
+ /**
979
+ * Adjust viewport size (only) so it will contain the SVG
980
+ * Does not center image
981
+ */
982
+ SvgPanZoom.prototype.contain = function() {
983
+ var viewBox = this.viewport.getViewBox()
984
+ , newScale = Math.max(this.width/viewBox.width, this.height/viewBox.height)
985
+
986
+ this.zoom(newScale, true)
987
+ }
988
+
989
+ /**
990
+ * Adjust viewport pan (only) so it will be centered in SVG
991
+ * Does not zoom/fit/contain image
992
+ */
993
+ SvgPanZoom.prototype.center = function() {
994
+ var viewBox = this.viewport.getViewBox()
995
+ , offsetX = (this.width - (viewBox.width + viewBox.x * 2) * this.getZoom()) * 0.5
996
+ , offsetY = (this.height - (viewBox.height + viewBox.y * 2) * this.getZoom()) * 0.5
997
+
998
+ this.getPublicInstance().pan({x: offsetX, y: offsetY})
999
+ }
1000
+
1001
+ /**
1002
+ * Update content cached BorderBox
1003
+ * Use when viewport contents change
1004
+ */
1005
+ SvgPanZoom.prototype.updateBBox = function() {
1006
+ this.viewport.recacheViewBox()
1007
+ }
1008
+
1009
+ /**
1010
+ * Pan to a rendered position
1011
+ *
1012
+ * @param {Object} point {x: 0, y: 0}
1013
+ */
1014
+ SvgPanZoom.prototype.pan = function(point) {
1015
+ var viewportCTM = this.viewport.getCTM()
1016
+ viewportCTM.e = point.x
1017
+ viewportCTM.f = point.y
1018
+ this.viewport.setCTM(viewportCTM)
1019
+ }
1020
+
1021
+ /**
1022
+ * Relatively pan the graph by a specified rendered position vector
1023
+ *
1024
+ * @param {Object} point {x: 0, y: 0}
1025
+ */
1026
+ SvgPanZoom.prototype.panBy = function(point) {
1027
+ var viewportCTM = this.viewport.getCTM()
1028
+ viewportCTM.e += point.x
1029
+ viewportCTM.f += point.y
1030
+ this.viewport.setCTM(viewportCTM)
1031
+ }
1032
+
1033
+ /**
1034
+ * Get pan vector
1035
+ *
1036
+ * @return {Object} {x: 0, y: 0}
1037
+ */
1038
+ SvgPanZoom.prototype.getPan = function() {
1039
+ var state = this.viewport.getState()
1040
+
1041
+ return {x: state.x, y: state.y}
1042
+ }
1043
+
1044
+ /**
1045
+ * Recalculates cached svg dimensions and controls position
1046
+ */
1047
+ SvgPanZoom.prototype.resize = function() {
1048
+ // Get dimensions
1049
+ var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(this.svg)
1050
+ this.width = boundingClientRectNormalized.width
1051
+ this.height = boundingClientRectNormalized.height
1052
+
1053
+ // Reposition control icons by re-enabling them
1054
+ if (this.options.controlIconsEnabled) {
1055
+ this.getPublicInstance().disableControlIcons()
1056
+ this.getPublicInstance().enableControlIcons()
1057
+ }
1058
+ }
1059
+
1060
+ /**
1061
+ * Unbind mouse events, free callbacks and destroy public instance
1062
+ */
1063
+ SvgPanZoom.prototype.destroy = function() {
1064
+ var that = this
1065
+
1066
+ // Free callbacks
1067
+ this.beforeZoom = null
1068
+ this.onZoom = null
1069
+ this.beforePan = null
1070
+ this.onPan = null
1071
+
1072
+ // Destroy custom event handlers
1073
+ if (this.options.customEventsHandler != null) { // jshint ignore:line
1074
+ this.options.customEventsHandler.destroy({
1075
+ svgElement: this.svg
1076
+ , eventsListenerElement: this.options.eventsListenerElement
1077
+ , instance: this.getPublicInstance()
1078
+ })
1079
+ }
1080
+
1081
+ // Unbind eventListeners
1082
+ for (var event in this.eventListeners) {
1083
+ (this.options.eventsListenerElement || this.svg)
1084
+ .removeEventListener(event, this.eventListeners[event], false)
1085
+ }
1086
+
1087
+ // Unbind wheelListener
1088
+ this.disableMouseWheelZoom()
1089
+
1090
+ // Remove control icons
1091
+ this.getPublicInstance().disableControlIcons()
1092
+
1093
+ // Reset zoom and pan
1094
+ this.reset()
1095
+
1096
+ // Remove instance from instancesStore
1097
+ instancesStore = instancesStore.filter(function(instance){
1098
+ return instance.svg !== that.svg
1099
+ })
1100
+
1101
+ // Delete options and its contents
1102
+ delete this.options
1103
+
1104
+ // Destroy public instance and rewrite getPublicInstance
1105
+ delete this.publicInstance
1106
+ delete this.pi
1107
+ this.getPublicInstance = function(){
1108
+ return null
1109
+ }
1110
+ }
1111
+
1112
+ /**
1113
+ * Returns a public instance object
1114
+ *
1115
+ * @return {Object} Public instance object
1116
+ */
1117
+ SvgPanZoom.prototype.getPublicInstance = function() {
1118
+ var that = this
1119
+
1120
+ // Create cache
1121
+ if (!this.publicInstance) {
1122
+ this.publicInstance = this.pi = {
1123
+ // Pan
1124
+ enablePan: function() {that.options.panEnabled = true; return that.pi}
1125
+ , disablePan: function() {that.options.panEnabled = false; return that.pi}
1126
+ , isPanEnabled: function() {return !!that.options.panEnabled}
1127
+ , pan: function(point) {that.pan(point); return that.pi}
1128
+ , panBy: function(point) {that.panBy(point); return that.pi}
1129
+ , getPan: function() {return that.getPan()}
1130
+ // Pan event
1131
+ , setBeforePan: function(fn) {that.options.beforePan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
1132
+ , setOnPan: function(fn) {that.options.onPan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
1133
+ // Zoom and Control Icons
1134
+ , enableZoom: function() {that.options.zoomEnabled = true; return that.pi}
1135
+ , disableZoom: function() {that.options.zoomEnabled = false; return that.pi}
1136
+ , isZoomEnabled: function() {return !!that.options.zoomEnabled}
1137
+ , enableControlIcons: function() {
1138
+ if (!that.options.controlIconsEnabled) {
1139
+ that.options.controlIconsEnabled = true
1140
+ ControlIcons.enable(that)
1141
+ }
1142
+ return that.pi
1143
+ }
1144
+ , disableControlIcons: function() {
1145
+ if (that.options.controlIconsEnabled) {
1146
+ that.options.controlIconsEnabled = false;
1147
+ ControlIcons.disable(that)
1148
+ }
1149
+ return that.pi
1150
+ }
1151
+ , isControlIconsEnabled: function() {return !!that.options.controlIconsEnabled}
1152
+ // Double click zoom
1153
+ , enableDblClickZoom: function() {that.options.dblClickZoomEnabled = true; return that.pi}
1154
+ , disableDblClickZoom: function() {that.options.dblClickZoomEnabled = false; return that.pi}
1155
+ , isDblClickZoomEnabled: function() {return !!that.options.dblClickZoomEnabled}
1156
+ // Mouse wheel zoom
1157
+ , enableMouseWheelZoom: function() {that.enableMouseWheelZoom(); return that.pi}
1158
+ , disableMouseWheelZoom: function() {that.disableMouseWheelZoom(); return that.pi}
1159
+ , isMouseWheelZoomEnabled: function() {return !!that.options.mouseWheelZoomEnabled}
1160
+ // Zoom scale and bounds
1161
+ , setZoomScaleSensitivity: function(scale) {that.options.zoomScaleSensitivity = scale; return that.pi}
1162
+ , setMinZoom: function(zoom) {that.options.minZoom = zoom; return that.pi}
1163
+ , setMaxZoom: function(zoom) {that.options.maxZoom = zoom; return that.pi}
1164
+ // Zoom event
1165
+ , setBeforeZoom: function(fn) {that.options.beforeZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
1166
+ , setOnZoom: function(fn) {that.options.onZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
1167
+ // Zooming
1168
+ , zoom: function(scale) {that.publicZoom(scale, true); return that.pi}
1169
+ , zoomBy: function(scale) {that.publicZoom(scale, false); return that.pi}
1170
+ , zoomAtPoint: function(scale, point) {that.publicZoomAtPoint(scale, point, true); return that.pi}
1171
+ , zoomAtPointBy: function(scale, point) {that.publicZoomAtPoint(scale, point, false); return that.pi}
1172
+ , zoomIn: function() {this.zoomBy(1 + that.options.zoomScaleSensitivity); return that.pi}
1173
+ , zoomOut: function() {this.zoomBy(1 / (1 + that.options.zoomScaleSensitivity)); return that.pi}
1174
+ , getZoom: function() {return that.getRelativeZoom()}
1175
+ // Reset
1176
+ , resetZoom: function() {that.resetZoom(); return that.pi}
1177
+ , resetPan: function() {that.resetPan(); return that.pi}
1178
+ , reset: function() {that.reset(); return that.pi}
1179
+ // Fit, Contain and Center
1180
+ , fit: function() {that.fit(); return that.pi}
1181
+ , contain: function() {that.contain(); return that.pi}
1182
+ , center: function() {that.center(); return that.pi}
1183
+ // Size and Resize
1184
+ , updateBBox: function() {that.updateBBox(); return that.pi}
1185
+ , resize: function() {that.resize(); return that.pi}
1186
+ , getSizes: function() {
1187
+ return {
1188
+ width: that.width
1189
+ , height: that.height
1190
+ , realZoom: that.getZoom()
1191
+ , viewBox: that.viewport.getViewBox()
1192
+ }
1193
+ }
1194
+ // Destroy
1195
+ , destroy: function() {that.destroy(); return that.pi}
1196
+ }
1197
+ }
1198
+
1199
+ return this.publicInstance
1200
+ }
1201
+
1202
+ /**
1203
+ * Stores pairs of instances of SvgPanZoom and SVG
1204
+ * Each pair is represented by an object {svg: SVGSVGElement, instance: SvgPanZoom}
1205
+ *
1206
+ * @type {Array}
1207
+ */
1208
+ var instancesStore = []
1209
+
1210
+ var svgPanZoom = function(elementOrSelector, options){
1211
+ var svg = Utils.getSvg(elementOrSelector)
1212
+
1213
+ if (svg === null) {
1214
+ return null
1215
+ } else {
1216
+ // Look for existent instance
1217
+ for(var i = instancesStore.length - 1; i >= 0; i--) {
1218
+ if (instancesStore[i].svg === svg) {
1219
+ return instancesStore[i].instance.getPublicInstance()
1220
+ }
1221
+ }
1222
+
1223
+ // If instance not found - create one
1224
+ instancesStore.push({
1225
+ svg: svg
1226
+ , instance: new SvgPanZoom(svg, options)
1227
+ })
1228
+
1229
+ // Return just pushed instance
1230
+ return instancesStore[instancesStore.length - 1].instance.getPublicInstance()
1231
+ }
1232
+ }
1233
+
1234
+ module.exports = svgPanZoom;
1235
+
1236
+ },{"./control-icons":2,"./shadow-viewport":3,"./svg-utilities":5,"./uniwheel":6,"./utilities":7}],5:[function(require,module,exports){
1237
+ var Utils = require('./utilities')
1238
+ , _browser = 'unknown'
1239
+ ;
1240
+
1241
+ // http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
1242
+ if (/*@cc_on!@*/false || !!document.documentMode) { // internet explorer
1243
+ _browser = 'ie';
1244
+ }
1245
+
1246
+ module.exports = {
1247
+ svgNS: 'http://www.w3.org/2000/svg'
1248
+ , xmlNS: 'http://www.w3.org/XML/1998/namespace'
1249
+ , xmlnsNS: 'http://www.w3.org/2000/xmlns/'
1250
+ , xlinkNS: 'http://www.w3.org/1999/xlink'
1251
+ , evNS: 'http://www.w3.org/2001/xml-events'
1252
+
1253
+ /**
1254
+ * Get svg dimensions: width and height
1255
+ *
1256
+ * @param {SVGSVGElement} svg
1257
+ * @return {Object} {width: 0, height: 0}
1258
+ */
1259
+ , getBoundingClientRectNormalized: function(svg) {
1260
+ if (svg.clientWidth && svg.clientHeight) {
1261
+ return {width: svg.clientWidth, height: svg.clientHeight}
1262
+ } else if (!!svg.getBoundingClientRect()) {
1263
+ return svg.getBoundingClientRect();
1264
+ } else {
1265
+ throw new Error('Cannot get BoundingClientRect for SVG.');
1266
+ }
1267
+ }
1268
+
1269
+ /**
1270
+ * Gets g element with class of "viewport" or creates it if it doesn't exist
1271
+ *
1272
+ * @param {SVGSVGElement} svg
1273
+ * @return {SVGElement} g (group) element
1274
+ */
1275
+ , getOrCreateViewport: function(svg, selector) {
1276
+ var viewport = null
1277
+
1278
+ if (Utils.isElement(selector)) {
1279
+ viewport = selector
1280
+ } else {
1281
+ viewport = svg.querySelector(selector)
1282
+ }
1283
+
1284
+ // Check if there is just one main group in SVG
1285
+ if (!viewport) {
1286
+ var childNodes = Array.prototype.slice.call(svg.childNodes || svg.children).filter(function(el){
1287
+ return el.nodeName !== 'defs' && el.nodeName !== '#text'
1288
+ })
1289
+
1290
+ // Node name should be SVGGElement and should have no transform attribute
1291
+ // Groups with transform are not used as viewport because it involves parsing of all transform possibilities
1292
+ if (childNodes.length === 1 && childNodes[0].nodeName === 'g' && childNodes[0].getAttribute('transform') === null) {
1293
+ viewport = childNodes[0]
1294
+ }
1295
+ }
1296
+
1297
+ // If no favorable group element exists then create one
1298
+ if (!viewport) {
1299
+ var viewportId = 'viewport-' + new Date().toISOString().replace(/\D/g, '');
1300
+ viewport = document.createElementNS(this.svgNS, 'g');
1301
+ viewport.setAttribute('id', viewportId);
1302
+
1303
+ // Internet Explorer (all versions?) can't use childNodes, but other browsers prefer (require?) using childNodes
1304
+ var svgChildren = svg.childNodes || svg.children;
1305
+ if (!!svgChildren && svgChildren.length > 0) {
1306
+ for (var i = svgChildren.length; i > 0; i--) {
1307
+ // Move everything into viewport except defs
1308
+ if (svgChildren[svgChildren.length - i].nodeName !== 'defs') {
1309
+ viewport.appendChild(svgChildren[svgChildren.length - i]);
1310
+ }
1311
+ }
1312
+ }
1313
+ svg.appendChild(viewport);
1314
+ }
1315
+
1316
+ // Parse class names
1317
+ var classNames = [];
1318
+ if (viewport.getAttribute('class')) {
1319
+ classNames = viewport.getAttribute('class').split(' ')
1320
+ }
1321
+
1322
+ // Set class (if not set already)
1323
+ if (!~classNames.indexOf('svg-pan-zoom_viewport')) {
1324
+ classNames.push('svg-pan-zoom_viewport')
1325
+ viewport.setAttribute('class', classNames.join(' '))
1326
+ }
1327
+
1328
+ return viewport
1329
+ }
1330
+
1331
+ /**
1332
+ * Set SVG attributes
1333
+ *
1334
+ * @param {SVGSVGElement} svg
1335
+ */
1336
+ , setupSvgAttributes: function(svg) {
1337
+ // Setting default attributes
1338
+ svg.setAttribute('xmlns', this.svgNS);
1339
+ svg.setAttributeNS(this.xmlnsNS, 'xmlns:xlink', this.xlinkNS);
1340
+ svg.setAttributeNS(this.xmlnsNS, 'xmlns:ev', this.evNS);
1341
+
1342
+ // Needed for Internet Explorer, otherwise the viewport overflows
1343
+ if (svg.parentNode !== null) {
1344
+ var style = svg.getAttribute('style') || '';
1345
+ if (style.toLowerCase().indexOf('overflow') === -1) {
1346
+ svg.setAttribute('style', 'overflow: hidden; ' + style);
1347
+ }
1348
+ }
1349
+ }
1350
+
1351
+ /**
1352
+ * How long Internet Explorer takes to finish updating its display (ms).
1353
+ */
1354
+ , internetExplorerRedisplayInterval: 300
1355
+
1356
+ /**
1357
+ * Forces the browser to redisplay all SVG elements that rely on an
1358
+ * element defined in a 'defs' section. It works globally, for every
1359
+ * available defs element on the page.
1360
+ * The throttling is intentionally global.
1361
+ *
1362
+ * This is only needed for IE. It is as a hack to make markers (and 'use' elements?)
1363
+ * visible after pan/zoom when there are multiple SVGs on the page.
1364
+ * See bug report: https://connect.microsoft.com/IE/feedback/details/781964/
1365
+ * also see svg-pan-zoom issue: https://github.com/ariutta/svg-pan-zoom/issues/62
1366
+ */
1367
+ , refreshDefsGlobal: Utils.throttle(function() {
1368
+ var allDefs = document.querySelectorAll('defs');
1369
+ var allDefsCount = allDefs.length;
1370
+ for (var i = 0; i < allDefsCount; i++) {
1371
+ var thisDefs = allDefs[i];
1372
+ thisDefs.parentNode.insertBefore(thisDefs, thisDefs);
1373
+ }
1374
+ }, this.internetExplorerRedisplayInterval)
1375
+
1376
+ /**
1377
+ * Sets the current transform matrix of an element
1378
+ *
1379
+ * @param {SVGElement} element
1380
+ * @param {SVGMatrix} matrix CTM
1381
+ * @param {SVGElement} defs
1382
+ */
1383
+ , setCTM: function(element, matrix, defs) {
1384
+ var that = this
1385
+ , s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')';
1386
+
1387
+ element.setAttributeNS(null, 'transform', s);
1388
+
1389
+ // IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
1390
+ // see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
1391
+ // and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
1392
+ if (_browser === 'ie' && !!defs) {
1393
+ // this refresh is intended for redisplaying the SVG during zooming
1394
+ defs.parentNode.insertBefore(defs, defs);
1395
+ // this refresh is intended for redisplaying the other SVGs on a page when panning a given SVG
1396
+ // it is also needed for the given SVG itself, on zoomEnd, if the SVG contains any markers that
1397
+ // are located under any other element(s).
1398
+ window.setTimeout(function() {
1399
+ that.refreshDefsGlobal();
1400
+ }, that.internetExplorerRedisplayInterval);
1401
+ }
1402
+ }
1403
+
1404
+ /**
1405
+ * Instantiate an SVGPoint object with given event coordinates
1406
+ *
1407
+ * @param {Event} evt
1408
+ * @param {SVGSVGElement} svg
1409
+ * @return {SVGPoint} point
1410
+ */
1411
+ , getEventPoint: function(evt, svg) {
1412
+ var point = svg.createSVGPoint()
1413
+
1414
+ Utils.mouseAndTouchNormalize(evt, svg)
1415
+
1416
+ point.x = evt.clientX
1417
+ point.y = evt.clientY
1418
+
1419
+ return point
1420
+ }
1421
+
1422
+ /**
1423
+ * Get SVG center point
1424
+ *
1425
+ * @param {SVGSVGElement} svg
1426
+ * @return {SVGPoint}
1427
+ */
1428
+ , getSvgCenterPoint: function(svg, width, height) {
1429
+ return this.createSVGPoint(svg, width / 2, height / 2)
1430
+ }
1431
+
1432
+ /**
1433
+ * Create a SVGPoint with given x and y
1434
+ *
1435
+ * @param {SVGSVGElement} svg
1436
+ * @param {Number} x
1437
+ * @param {Number} y
1438
+ * @return {SVGPoint}
1439
+ */
1440
+ , createSVGPoint: function(svg, x, y) {
1441
+ var point = svg.createSVGPoint()
1442
+ point.x = x
1443
+ point.y = y
1444
+
1445
+ return point
1446
+ }
1447
+ }
1448
+
1449
+ },{"./utilities":7}],6:[function(require,module,exports){
1450
+ // uniwheel 0.1.2 (customized)
1451
+ // A unified cross browser mouse wheel event handler
1452
+ // https://github.com/teemualap/uniwheel
1453
+
1454
+ module.exports = (function(){
1455
+
1456
+ //Full details: https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel
1457
+
1458
+ var prefix = "", _addEventListener, _removeEventListener, onwheel, support, fns = [];
1459
+
1460
+ // detect event model
1461
+ if ( window.addEventListener ) {
1462
+ _addEventListener = "addEventListener";
1463
+ _removeEventListener = "removeEventListener";
1464
+ } else {
1465
+ _addEventListener = "attachEvent";
1466
+ _removeEventListener = "detachEvent";
1467
+ prefix = "on";
1468
+ }
1469
+
1470
+ // detect available wheel event
1471
+ support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
1472
+ document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
1473
+ "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
1474
+
1475
+
1476
+ function createCallback(element,callback,capture) {
1477
+
1478
+ var fn = function(originalEvent) {
1479
+
1480
+ !originalEvent && ( originalEvent = window.event );
1481
+
1482
+ // create a normalized event object
1483
+ var event = {
1484
+ // keep a ref to the original event object
1485
+ originalEvent: originalEvent,
1486
+ target: originalEvent.target || originalEvent.srcElement,
1487
+ type: "wheel",
1488
+ deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
1489
+ deltaX: 0,
1490
+ delatZ: 0,
1491
+ preventDefault: function() {
1492
+ originalEvent.preventDefault ?
1493
+ originalEvent.preventDefault() :
1494
+ originalEvent.returnValue = false;
1495
+ }
1496
+ };
1497
+
1498
+ // calculate deltaY (and deltaX) according to the event
1499
+ if ( support == "mousewheel" ) {
1500
+ event.deltaY = - 1/40 * originalEvent.wheelDelta;
1501
+ // Webkit also support wheelDeltaX
1502
+ originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
1503
+ } else {
1504
+ event.deltaY = originalEvent.detail;
1505
+ }
1506
+
1507
+ // it's time to fire the callback
1508
+ return callback( event );
1509
+
1510
+ };
1511
+
1512
+ fns.push({
1513
+ element: element,
1514
+ fn: fn,
1515
+ capture: capture
1516
+ });
1517
+
1518
+ return fn;
1519
+ }
1520
+
1521
+ function getCallback(element,capture) {
1522
+ for (var i = 0; i < fns.length; i++) {
1523
+ if (fns[i].element === element && fns[i].capture === capture) {
1524
+ return fns[i].fn;
1525
+ }
1526
+ }
1527
+ return function(){};
1528
+ }
1529
+
1530
+ function removeCallback(element,capture) {
1531
+ for (var i = 0; i < fns.length; i++) {
1532
+ if (fns[i].element === element && fns[i].capture === capture) {
1533
+ return fns.splice(i,1);
1534
+ }
1535
+ }
1536
+ }
1537
+
1538
+ function _addWheelListener( elem, eventName, callback, useCapture ) {
1539
+
1540
+ var cb;
1541
+
1542
+ if (support === "wheel") {
1543
+ cb = callback;
1544
+ } else {
1545
+ cb = createCallback(elem,callback,useCapture);
1546
+ }
1547
+
1548
+ elem[ _addEventListener ]( prefix + eventName, cb, useCapture || false );
1549
+
1550
+ }
1551
+
1552
+ function _removeWheelListener( elem, eventName, callback, useCapture ) {
1553
+
1554
+ if (support === "wheel") {
1555
+ cb = callback;
1556
+ } else {
1557
+ cb = getCallback(elem,useCapture);
1558
+ }
1559
+
1560
+ elem[ _removeEventListener ]( prefix + eventName, cb, useCapture || false );
1561
+
1562
+ removeCallback(elem,useCapture);
1563
+
1564
+ }
1565
+
1566
+ function addWheelListener( elem, callback, useCapture ) {
1567
+ _addWheelListener( elem, support, callback, useCapture );
1568
+
1569
+ // handle MozMousePixelScroll in older Firefox
1570
+ if( support == "DOMMouseScroll" ) {
1571
+ _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture);
1572
+ }
1573
+ }
1574
+
1575
+ function removeWheelListener(elem,callback,useCapture){
1576
+ _removeWheelListener(elem,support,callback,useCapture);
1577
+
1578
+ // handle MozMousePixelScroll in older Firefox
1579
+ if( support == "DOMMouseScroll" ) {
1580
+ _removeWheelListener(elem, "MozMousePixelScroll", callback, useCapture);
1581
+ }
1582
+ }
1583
+
1584
+ return {
1585
+ on: addWheelListener,
1586
+ off: removeWheelListener
1587
+ };
1588
+
1589
+ })();
1590
+
1591
+ },{}],7:[function(require,module,exports){
1592
+ module.exports = {
1593
+ /**
1594
+ * Extends an object
1595
+ *
1596
+ * @param {Object} target object to extend
1597
+ * @param {Object} source object to take properties from
1598
+ * @return {Object} extended object
1599
+ */
1600
+ extend: function(target, source) {
1601
+ target = target || {};
1602
+ for (var prop in source) {
1603
+ // Go recursively
1604
+ if (this.isObject(source[prop])) {
1605
+ target[prop] = this.extend(target[prop], source[prop])
1606
+ } else {
1607
+ target[prop] = source[prop]
1608
+ }
1609
+ }
1610
+ return target;
1611
+ }
1612
+
1613
+ /**
1614
+ * Checks if an object is a DOM element
1615
+ *
1616
+ * @param {Object} o HTML element or String
1617
+ * @return {Boolean} returns true if object is a DOM element
1618
+ */
1619
+ , isElement: function(o){
1620
+ return (
1621
+ o instanceof HTMLElement || o instanceof SVGElement || o instanceof SVGSVGElement || //DOM2
1622
+ (o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string')
1623
+ );
1624
+ }
1625
+
1626
+ /**
1627
+ * Checks if an object is an Object
1628
+ *
1629
+ * @param {Object} o Object
1630
+ * @return {Boolean} returns true if object is an Object
1631
+ */
1632
+ , isObject: function(o){
1633
+ return Object.prototype.toString.call(o) === '[object Object]';
1634
+ }
1635
+
1636
+ /**
1637
+ * Checks if variable is Number
1638
+ *
1639
+ * @param {Integer|Float} n
1640
+ * @return {Boolean} returns true if variable is Number
1641
+ */
1642
+ , isNumber: function(n) {
1643
+ return !isNaN(parseFloat(n)) && isFinite(n);
1644
+ }
1645
+
1646
+ /**
1647
+ * Search for an SVG element
1648
+ *
1649
+ * @param {Object|String} elementOrSelector DOM Element or selector String
1650
+ * @return {Object|Null} SVG or null
1651
+ */
1652
+ , getSvg: function(elementOrSelector) {
1653
+ var element
1654
+ , svg;
1655
+
1656
+ if (!this.isElement(elementOrSelector)) {
1657
+ // If selector provided
1658
+ if (typeof elementOrSelector === 'string' || elementOrSelector instanceof String) {
1659
+ // Try to find the element
1660
+ element = document.querySelector(elementOrSelector)
1661
+
1662
+ if (!element) {
1663
+ throw new Error('Provided selector did not find any elements. Selector: ' + elementOrSelector)
1664
+ return null
1665
+ }
1666
+ } else {
1667
+ throw new Error('Provided selector is not an HTML object nor String')
1668
+ return null
1669
+ }
1670
+ } else {
1671
+ element = elementOrSelector
1672
+ }
1673
+
1674
+ if (element.tagName.toLowerCase() === 'svg') {
1675
+ svg = element;
1676
+ } else {
1677
+ if (element.tagName.toLowerCase() === 'object') {
1678
+ svg = element.contentDocument.documentElement;
1679
+ } else {
1680
+ if (element.tagName.toLowerCase() === 'embed') {
1681
+ svg = element.getSVGDocument().documentElement;
1682
+ } else {
1683
+ if (element.tagName.toLowerCase() === 'img') {
1684
+ throw new Error('Cannot script an SVG in an "img" element. Please use an "object" element or an in-line SVG.');
1685
+ } else {
1686
+ throw new Error('Cannot get SVG.');
1687
+ }
1688
+ return null
1689
+ }
1690
+ }
1691
+ }
1692
+
1693
+ return svg
1694
+ }
1695
+
1696
+ /**
1697
+ * Attach a given context to a function
1698
+ * @param {Function} fn Function
1699
+ * @param {Object} context Context
1700
+ * @return {Function} Function with certain context
1701
+ */
1702
+ , proxy: function(fn, context) {
1703
+ return function() {
1704
+ return fn.apply(context, arguments)
1705
+ }
1706
+ }
1707
+
1708
+ /**
1709
+ * Returns object type
1710
+ * Uses toString that returns [object SVGPoint]
1711
+ * And than parses object type from string
1712
+ *
1713
+ * @param {Object} o Any object
1714
+ * @return {String} Object type
1715
+ */
1716
+ , getType: function(o) {
1717
+ return Object.prototype.toString.apply(o).replace(/^\[object\s/, '').replace(/\]$/, '')
1718
+ }
1719
+
1720
+ /**
1721
+ * If it is a touch event than add clientX and clientY to event object
1722
+ *
1723
+ * @param {Event} evt
1724
+ * @param {SVGSVGElement} svg
1725
+ */
1726
+ , mouseAndTouchNormalize: function(evt, svg) {
1727
+ // If no cilentX and but touch objects are available
1728
+ if (evt.clientX === void 0 || evt.clientX === null) {
1729
+ // Fallback
1730
+ evt.clientX = 0
1731
+ evt.clientY = 0
1732
+
1733
+ // If it is a touch event
1734
+ if (evt.changedTouches !== void 0 && evt.changedTouches.length) {
1735
+ // If touch event has changedTouches
1736
+ if (evt.changedTouches[0].clientX !== void 0) {
1737
+ evt.clientX = evt.changedTouches[0].clientX
1738
+ evt.clientY = evt.changedTouches[0].clientY
1739
+ }
1740
+ // If changedTouches has pageX attribute
1741
+ else if (evt.changedTouches[0].pageX !== void 0) {
1742
+ var rect = svg.getBoundingClientRect();
1743
+
1744
+ evt.clientX = evt.changedTouches[0].pageX - rect.left
1745
+ evt.clientY = evt.changedTouches[0].pageY - rect.top
1746
+ }
1747
+ // If it is a custom event
1748
+ } else if (evt.originalEvent !== void 0) {
1749
+ if (evt.originalEvent.clientX !== void 0) {
1750
+ evt.clientX = evt.originalEvent.clientX
1751
+ evt.clientY = evt.originalEvent.clientY
1752
+ }
1753
+ }
1754
+ }
1755
+ }
1756
+
1757
+ /**
1758
+ * Check if an event is a double click/tap
1759
+ * TODO: For touch gestures use a library (hammer.js) that takes in account other events
1760
+ * (touchmove and touchend). It should take in account tap duration and traveled distance
1761
+ *
1762
+ * @param {Event} evt
1763
+ * @param {Event} prevEvt Previous Event
1764
+ * @return {Boolean}
1765
+ */
1766
+ , isDblClick: function(evt, prevEvt) {
1767
+ // Double click detected by browser
1768
+ if (evt.detail === 2) {
1769
+ return true;
1770
+ }
1771
+ // Try to compare events
1772
+ else if (prevEvt !== void 0 && prevEvt !== null) {
1773
+ var timeStampDiff = evt.timeStamp - prevEvt.timeStamp // should be lower than 250 ms
1774
+ , touchesDistance = Math.sqrt(Math.pow(evt.clientX - prevEvt.clientX, 2) + Math.pow(evt.clientY - prevEvt.clientY, 2))
1775
+
1776
+ return timeStampDiff < 250 && touchesDistance < 10
1777
+ }
1778
+
1779
+ // Nothing found
1780
+ return false;
1781
+ }
1782
+
1783
+ /**
1784
+ * Returns current timestamp as an integer
1785
+ *
1786
+ * @return {Number}
1787
+ */
1788
+ , now: Date.now || function() {
1789
+ return new Date().getTime();
1790
+ }
1791
+
1792
+ // From underscore.
1793
+ // Returns a function, that, when invoked, will only be triggered at most once
1794
+ // during a given window of time. Normally, the throttled function will run
1795
+ // as much as it can, without ever going more than once per `wait` duration;
1796
+ // but if you'd like to disable the execution on the leading edge, pass
1797
+ // `{leading: false}`. To disable execution on the trailing edge, ditto.
1798
+ // jscs:disable
1799
+ // jshint ignore:start
1800
+ , throttle: function(func, wait, options) {
1801
+ var that = this;
1802
+ var context, args, result;
1803
+ var timeout = null;
1804
+ var previous = 0;
1805
+ if (!options) options = {};
1806
+ var later = function() {
1807
+ previous = options.leading === false ? 0 : that.now();
1808
+ timeout = null;
1809
+ result = func.apply(context, args);
1810
+ if (!timeout) context = args = null;
1811
+ };
1812
+ return function() {
1813
+ var now = that.now();
1814
+ if (!previous && options.leading === false) previous = now;
1815
+ var remaining = wait - (now - previous);
1816
+ context = this;
1817
+ args = arguments;
1818
+ if (remaining <= 0 || remaining > wait) {
1819
+ clearTimeout(timeout);
1820
+ timeout = null;
1821
+ previous = now;
1822
+ result = func.apply(context, args);
1823
+ if (!timeout) context = args = null;
1824
+ } else if (!timeout && options.trailing !== false) {
1825
+ timeout = setTimeout(later, remaining);
1826
+ }
1827
+ return result;
1828
+ };
1829
+ }
1830
+ // jshint ignore:end
1831
+ // jscs:enable
1832
+
1833
+ /**
1834
+ * Create a requestAnimationFrame simulation
1835
+ *
1836
+ * @param {Number|String} refreshRate
1837
+ * @return {Function}
1838
+ */
1839
+ , createRequestAnimationFrame: function(refreshRate) {
1840
+ var timeout = null
1841
+
1842
+ // Convert refreshRate to timeout
1843
+ if (refreshRate !== 'auto' && refreshRate < 60 && refreshRate > 1) {
1844
+ timeout = Math.floor(1000 / refreshRate)
1845
+ }
1846
+
1847
+ if (timeout === null) {
1848
+ return window.requestAnimationFrame || requestTimeout(33)
1849
+ } else {
1850
+ return requestTimeout(timeout)
1851
+ }
1852
+ }
1853
+ }
1854
+
1855
+ /**
1856
+ * Create a callback that will execute after a given timeout
1857
+ *
1858
+ * @param {Function} timeout
1859
+ * @return {Function}
1860
+ */
1861
+ function requestTimeout(timeout) {
1862
+ return function(callback) {
1863
+ window.setTimeout(callback, timeout)
1864
+ }
1865
+ }
1866
+
1867
+ },{}]},{},[1]);