1
2
3
4 package svgpan
5
6
7
8
9 const JSSource = `
10 /**
11 * SVGPan library 1.2.2
12 * ======================
13 *
14 * Given an unique existing element with id "viewport" (or when missing, the
15 * first g-element), including the library into any SVG adds the following
16 * capabilities:
17 *
18 * - Mouse panning
19 * - Mouse zooming (using the wheel)
20 * - Object dragging
21 *
22 * You can configure the behaviour of the pan/zoom/drag with the variables
23 * listed in the CONFIGURATION section of this file.
24 *
25 * Known issues:
26 *
27 * - Zooming (while panning) on Safari has still some issues
28 *
29 * Releases:
30 *
31 * 1.2.2, Tue Aug 30 17:21:56 CEST 2011, Andrea Leofreddi
32 * - Fixed viewBox on root tag (#7)
33 * - Improved zoom speed (#2)
34 *
35 * 1.2.1, Mon Jul 4 00:33:18 CEST 2011, Andrea Leofreddi
36 * - Fixed a regression with mouse wheel (now working on Firefox 5)
37 * - Working with viewBox attribute (#4)
38 * - Added "use strict;" and fixed resulting warnings (#5)
39 * - Added configuration variables, dragging is disabled by default (#3)
40 *
41 * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
42 * Fixed a bug with browser mouse handler interaction
43 *
44 * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui
45 * Updated the zoom code to support the mouse wheel on Safari/Chrome
46 *
47 * 1.0, Andrea Leofreddi
48 * First release
49 *
50 * This code is licensed under the following BSD license:
51 *
52 * Copyright 2009-2017 Andrea Leofreddi <a.leofreddi@vleo.net>. All rights reserved.
53 *
54 * Redistribution and use in source and binary forms, with or without modification, are
55 * permitted provided that the following conditions are met:
56 *
57 * 1. Redistributions of source code must retain the above copyright
58 * notice, this list of conditions and the following disclaimer.
59 * 2. Redistributions in binary form must reproduce the above copyright
60 * notice, this list of conditions and the following disclaimer in the
61 * documentation and/or other materials provided with the distribution.
62 * 3. Neither the name of the copyright holder nor the names of its
63 * contributors may be used to endorse or promote products derived from
64 * this software without specific prior written permission.
65 *
66 * THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS
67 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
68 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR
69 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
70 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
71 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
72 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
73 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
74 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75 *
76 * The views and conclusions contained in the software and documentation are those of the
77 * authors and should not be interpreted as representing official policies, either expressed
78 * or implied, of Andrea Leofreddi.
79 */
80
81 "use strict";
82
83 /// CONFIGURATION
84 /// ====>
85
86 var enablePan = 1; // 1 or 0: enable or disable panning (default enabled)
87 var enableZoom = 1; // 1 or 0: enable or disable zooming (default enabled)
88 var enableDrag = 0; // 1 or 0: enable or disable dragging (default disabled)
89 var zoomScale = 0.2; // Zoom sensitivity
90
91 /// <====
92 /// END OF CONFIGURATION
93
94 var root = document.documentElement;
95
96 var state = 'none', svgRoot = null, stateTarget, stateOrigin, stateTf;
97
98 setupHandlers(root);
99
100 /**
101 * Register handlers
102 */
103 function setupHandlers(root){
104 setAttributes(root, {
105 "onmouseup" : "handleMouseUp(evt)",
106 "onmousedown" : "handleMouseDown(evt)",
107 "onmousemove" : "handleMouseMove(evt)",
108 //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
109 });
110
111 if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
112 window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
113 else
114 window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
115 }
116
117 /**
118 * Retrieves the root element for SVG manipulation. The element is then cached into the svgRoot global variable.
119 */
120 function getRoot(root) {
121 if(svgRoot == null) {
122 var r = root.getElementById("viewport") ? root.getElementById("viewport") : root.documentElement, t = r;
123
124 while(t != root) {
125 if(t.getAttribute("viewBox")) {
126 setCTM(r, t.getCTM());
127
128 t.removeAttribute("viewBox");
129 }
130
131 t = t.parentNode;
132 }
133
134 svgRoot = r;
135 }
136
137 return svgRoot;
138 }
139
140 /**
141 * Instance an SVGPoint object with given event coordinates.
142 */
143 function getEventPoint(evt) {
144 var p = root.createSVGPoint();
145
146 p.x = evt.clientX;
147 p.y = evt.clientY;
148
149 return p;
150 }
151
152 /**
153 * Sets the current transform matrix of an element.
154 */
155 function setCTM(element, matrix) {
156 var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
157
158 element.setAttribute("transform", s);
159 }
160
161 /**
162 * Dumps a matrix to a string (useful for debug).
163 */
164 function dumpMatrix(matrix) {
165 var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]";
166
167 return s;
168 }
169
170 /**
171 * Sets attributes of an element.
172 */
173 function setAttributes(element, attributes){
174 for (var i in attributes)
175 element.setAttributeNS(null, i, attributes[i]);
176 }
177
178 /**
179 * Handle mouse wheel event.
180 */
181 function handleMouseWheel(evt) {
182 if(!enableZoom)
183 return;
184
185 if(evt.preventDefault)
186 evt.preventDefault();
187
188 evt.returnValue = false;
189
190 var svgDoc = evt.target.ownerDocument;
191
192 var delta;
193
194 if(evt.wheelDelta)
195 delta = evt.wheelDelta / 360; // Chrome/Safari
196 else
197 delta = evt.detail / -9; // Mozilla
198
199 var z = Math.pow(1 + zoomScale, delta);
200
201 var g = getRoot(svgDoc);
202
203 var p = getEventPoint(evt);
204
205 p = p.matrixTransform(g.getCTM().inverse());
206
207 // Compute new scale matrix in current mouse position
208 var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
209
210 setCTM(g, g.getCTM().multiply(k));
211
212 if(typeof(stateTf) == "undefined")
213 stateTf = g.getCTM().inverse();
214
215 stateTf = stateTf.multiply(k.inverse());
216 }
217
218 /**
219 * Handle mouse move event.
220 */
221 function handleMouseMove(evt) {
222 if(evt.preventDefault)
223 evt.preventDefault();
224
225 evt.returnValue = false;
226
227 var svgDoc = evt.target.ownerDocument;
228
229 var g = getRoot(svgDoc);
230
231 if(state == 'pan' && enablePan) {
232 // Pan mode
233 var p = getEventPoint(evt).matrixTransform(stateTf);
234
235 setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
236 } else if(state == 'drag' && enableDrag) {
237 // Drag mode
238 var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
239
240 setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
241
242 stateOrigin = p;
243 }
244 }
245
246 /**
247 * Handle click event.
248 */
249 function handleMouseDown(evt) {
250 if(evt.preventDefault)
251 evt.preventDefault();
252
253 evt.returnValue = false;
254
255 var svgDoc = evt.target.ownerDocument;
256
257 var g = getRoot(svgDoc);
258
259 if(
260 evt.target.tagName == "svg"
261 || !enableDrag // Pan anyway when drag is disabled and the user clicked on an element
262 ) {
263 // Pan mode
264 state = 'pan';
265
266 stateTf = g.getCTM().inverse();
267
268 stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
269 } else {
270 // Drag mode
271 state = 'drag';
272
273 stateTarget = evt.target;
274
275 stateTf = g.getCTM().inverse();
276
277 stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
278 }
279 }
280
281 /**
282 * Handle mouse button release event.
283 */
284 function handleMouseUp(evt) {
285 if(evt.preventDefault)
286 evt.preventDefault();
287
288 evt.returnValue = false;
289
290 var svgDoc = evt.target.ownerDocument;
291
292 if(state == 'pan' || state == 'drag') {
293 // Quit pan mode
294 state = '';
295 }
296 }
297 `
298
View as plain text