1// SVG pan and zoom library. 2// See copyright notice in string constant below. 3 4package svgpan 5 6// https://github.com/aleofreddi/svgpan 7 8// JSSource returns the svgpan.js file 9const 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 86var enablePan = 1; // 1 or 0: enable or disable panning (default enabled) 87var enableZoom = 1; // 1 or 0: enable or disable zooming (default enabled) 88var enableDrag = 0; // 1 or 0: enable or disable dragging (default disabled) 89var zoomScale = 0.2; // Zoom sensitivity 90 91/// <==== 92/// END OF CONFIGURATION 93 94var root = document.documentElement; 95 96var state = 'none', svgRoot = null, stateTarget, stateOrigin, stateTf; 97 98setupHandlers(root); 99 100/** 101 * Register handlers 102 */ 103function 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 */ 120function 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 */ 143function 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 */ 155function 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 */ 164function 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 */ 173function 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 */ 181function 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 */ 221function 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 */ 249function 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 */ 284function 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