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