You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

636 lines
23 KiB

7 years ago
  1. // Generated by CoffeeScript 1.10.0
  2. /*
  3. jQuery SVG Pan Zoom v1.0.2, June 2015
  4. Author: Daniel Hoffmann Bernardes (daniel.hoffmann.bernardes@gmail.com)
  5. Repository: https://github.com/DanielHoffmann/jquery-svg-pan-zoom/
  6. jQuery plugin to enable pan and zoom in SVG images either programmatically or through mouse/touch events.
  7. [Demo page](http://danielhoffmann.github.io/jquery-svg-pan-zoom/)
  8. * Features
  9. - Programmatically manipulate the SVG viewBox
  10. - Mouse and touch events to pan the SVG viewBox
  11. - Mousewheel events to zoom in or out the SVG viewBox
  12. - Animations
  13. - Mousewheel zooming keeps the cursor over the same coordinates relative to the image (A.K.A. GoogleMaps-like zoom)
  14. - Limiting the navigable area
  15. * Requirements
  16. jQuery
  17. SVG-enabled browser (does not work with SVG work-arounds that use Flash)
  18. * The viewBox
  19. The viewBox is an attribute of SVG images that defines the area of the SVG that is visible, it is defined by 4 numbers: X, Y, Width, Height. These numbers together specify the visible area. This plugin works by manipulating these four numbers. For example, moving the image to the right alters the X value while zooming in reduces Width and Height.
  20. * Usage
  21. ```javascript
  22. var svgPanZoom= $("svg").svgPanZoom(options)
  23. ```
  24. If the selection has more than one element `svgPanZoom` will return an array with a SvgPanZoom object for each image in the same order of the selection. If only one element is selected then the return is a single SvgPanZoom object. If no elements are selected the above call returns `null`
  25. The returned SvgPanZoom object contains all options, these options can be overriden at any time directly, for example to disable mouseWheel events simply:
  26. ```javascript
  27. svgPanZoom.events.mouseWheel= false
  28. ```
  29. the SvgPanZoom object also has methods for manipulating the viewBox programmatically. For example:
  30. ```javascript
  31. svgPanZoom.zoomIn()
  32. ```
  33. will zoomIn the image using options.zoomFactor.
  34. * Building
  35. This project requires coffeescript to be installed in order to build.
  36. `coffee -m --compile --output compiled/ src/`
  37. * Options
  38. ```javascript
  39. Options:
  40. {
  41. events: {
  42. mouseWheel: boolean (true), // enables mouse wheel zooming events
  43. doubleClick: boolean (true), // enables double-click to zoom-in events
  44. drag: boolean (true), // enables drag and drop to move the SVG events
  45. dragCursor: string "move" // cursor to use while dragging the SVG
  46. },
  47. animationTime: number (300), // time in milliseconds to use as default for animations. Set 0 to remove the animation
  48. zoomFactor: number (0.25), // how much to zoom-in or zoom-out
  49. maxZoom: number (3), //maximum zoom in, must be a number bigger than 1
  50. panFactor: (number (100), // how much to move the viewBox when calling .panDirection() methods
  51. initialViewBox: { // the initial viewBox, if null or undefined will try to use the viewBox set in the svg tag. Also accepts string in the format "X Y Width Height"
  52. x: number (0) // the top-left corner X coordinate
  53. y: number (0) // the top-left corner Y coordinate
  54. width: number (1000) // the width of the viewBox
  55. height: number (1000) // the height of the viewBox
  56. },
  57. limits: { // the limits in which the image can be moved. If null or undefined will use the initialViewBox plus 15% in each direction
  58. x: number (-150)
  59. y: number (-150)
  60. x2: number (1150)
  61. y2: number (1150)
  62. }
  63. }
  64. ```
  65. * Methods
  66. - pan
  67. ```javascript
  68. svgPanZoom.panLeft(amount, animationTime)
  69. svgPanZoom.panRight(amount, animationTime)
  70. svgPanZoom.panUp(amount, animationTime)
  71. svgPanZoom.panDown(amount, animationTime)
  72. ```
  73. Moves the SVG viewBox in the specified direction. Parameters:
  74. - amount: Number, optional. How much to move the viewBox, defaults to options.panFactor.
  75. - animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
  76. - zoom
  77. ```javascript
  78. svgPanZoom.zoomIn(animationTime)
  79. svgPanZoom.zoomOut(animationTime)
  80. ```
  81. Zooms the viewBox. Parameters:
  82. - animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
  83. - reset
  84. ```javascript
  85. svgPanZoom.reset()
  86. ```
  87. Resets the SVG to options.initialViewBox values.
  88. - getViewBox
  89. ```javascript
  90. svgPanZoom.getViewBox()
  91. ```
  92. Returns the viewbox in this format:
  93. ```javascript
  94. {
  95. x: number
  96. y: number
  97. width: number
  98. height: number
  99. }
  100. ```
  101. - setViewBox
  102. ```javascript
  103. svgPanZoom.setViewBox(x, y, width, height, animationTime)
  104. ```
  105. Changes the viewBox to the specified coordinates. Will respect the `options.limits` adapting the viewBox if needed (moving or reducing it to fit into `options.limits`
  106. - x: Number, the new x coodinate of the top-left corner
  107. - y: Number, the new y coodinate of the top-left corner
  108. - width: Number, the new width of the viewBox
  109. - height: Number, the new height of the viewBox
  110. - animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
  111. - setCenter
  112. ```javascript
  113. svgPanZoom.setCenter(x, y, animationTime)
  114. ```
  115. Sets the center of the SVG. Parameters:
  116. - x: Number, the new x coordinate of the center
  117. - y: Number, the new y coordinate of the center
  118. - animationTime: Number, optional. How long the animation should last, defaults to options.animationTime.
  119. * Notes:
  120. - Only works in SVGs inlined in the HTML. You can use $.load() to load the SVG image in the page using AJAX and call $().svgPanZoom() in the callback
  121. - Touch pinch events to zoom not yet supported
  122. - This plugin does not create any controls (like arrows to move the image) on top of the SVG. These controls are simple to create manually and they can call the methods to move the image.
  123. - Do not manipulate the SVG viewBox attribute manually, use SvgPanZoom.setViewBox() instead
  124. Copyright (C) 2014 Daniel Hoffmann Bernardes, Ícaro Technologies
  125. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  126. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  127. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  128. */
  129. (function() {
  130. var hasProp = {}.hasOwnProperty;
  131. (function($) {
  132. var checkLimits, defaultOptions, defaultViewBox, getViewBoxCoordinatesFromEvent, parseViewBoxString;
  133. defaultOptions = {
  134. events: {
  135. mouseWheel: true,
  136. doubleClick: true,
  137. drag: true,
  138. dragCursor: "move"
  139. },
  140. animationTime: 300,
  141. zoomFactor: 0.25,
  142. maxZoom: 3,
  143. panFactor: 100,
  144. initialViewBox: null,
  145. limits: null
  146. };
  147. defaultViewBox = {
  148. x: 0,
  149. y: 0,
  150. width: 1000,
  151. height: 1000
  152. };
  153. /**
  154. * Check the limits of the view box, return a new viewBox that respects the limits while keeping
  155. * the original view box size if possible. If the view box needs to be reduced, the returned view
  156. * box will keep the aspect ratio of the original view box.
  157. *
  158. * @param {Object} viewBox
  159. * The original view box. Takes numbers, in the format `{x, y, width, height}`.
  160. *
  161. * @param {Object} limits
  162. * Extents which can be shown, in the view box coordinate system. Takes numbers in the format
  163. * `{x, y, x2, y2}`.
  164. *
  165. * @return {Object} viewBox
  166. * A new view box object, squeezed into the limits. Contains numbers, in the format `{x, y,
  167. * width, height}`.
  168. */
  169. checkLimits = function(viewBox, limits) {
  170. var limitsHeight, limitsWidth, reductionFactor, vb;
  171. vb = $.extend({}, viewBox);
  172. limitsWidth = Math.abs(limits.x2 - limits.x);
  173. limitsHeight = Math.abs(limits.y2 - limits.y);
  174. if (vb.width > limitsWidth) {
  175. if (vb.height > limitsHeight) {
  176. if (limitsWidth > limitsHeight) {
  177. reductionFactor = limitsHeight / vb.height;
  178. vb.height = limitsHeight;
  179. vb.width = vb.width * reductionFactor;
  180. } else {
  181. reductionFactor = limitsWidth / vb.width;
  182. vb.width = limitsWidth;
  183. vb.height = vb.height * reductionFactor;
  184. }
  185. } else {
  186. reductionFactor = limitsWidth / vb.width;
  187. vb.width = limitsWidth;
  188. vb.height = vb.height * reductionFactor;
  189. }
  190. } else if (vb.height > limitsHeight) {
  191. reductionFactor = limitsHeight / vb.height;
  192. vb.height = limitsHeight;
  193. vb.width = vb.width * reductionFactor;
  194. }
  195. if (vb.x < limits.x) {
  196. vb.x = limits.x;
  197. }
  198. if (vb.y < limits.y) {
  199. vb.y = limits.y;
  200. }
  201. if (vb.x + vb.width > limits.x2) {
  202. vb.x = limits.x2 - vb.width;
  203. }
  204. if (vb.y + vb.height > limits.y2) {
  205. vb.y = limits.y2 - vb.height;
  206. }
  207. return vb;
  208. };
  209. /**
  210. * Parse the viewbox string as defined in the spec for the svg tag.
  211. *
  212. * @param {String} viewBoxString
  213. * A valid value of the `viewBox` attribute.
  214. *
  215. * @return {Object} viewBox
  216. * A view box object. Contains numbers, in the format `{x, y, width, height}`.
  217. */
  218. parseViewBoxString = function(string) {
  219. var vb;
  220. vb = string.replace("\s+", " ").split(" ");
  221. return vb = {
  222. x: parseFloat(vb[0]),
  223. y: parseFloat(vb[1]),
  224. width: parseFloat(vb[2]),
  225. height: parseFloat(vb[3])
  226. };
  227. };
  228. /**
  229. * Get the mouse or first touch position from the `event`, relative to the SVG viewBox.
  230. *
  231. * @param {SVGElement} svgRoot
  232. * The `<svg>` DOM object
  233. *
  234. * @param {MouseEvent|TouchEvent|jQueryEvent} event
  235. * The DOM or jQuery event.
  236. *
  237. * @return {Object}
  238. * Coordinates of the event. Contains numbers, in the format `{x, y}`.
  239. */
  240. getViewBoxCoordinatesFromEvent = function(svgRoot, event) {
  241. var ctm, foo, pos;
  242. foo = {
  243. x: null,
  244. y: null
  245. };
  246. if (event.type === "touchstart" || event.type === "touchmove") {
  247. if ((event.originalEvent != null) && (event.touches == null)) {
  248. foo.x = event.originalEvent.touches[0].clientX;
  249. foo.y = event.originalEvent.touches[0].clientY;
  250. } else {
  251. foo.x = event.touches[0].clientX;
  252. foo.y = event.touches[0].clientY;
  253. }
  254. } else {
  255. if (event.clientX != null) {
  256. foo.x = event.clientX;
  257. foo.y = event.clientY;
  258. } else {
  259. foo.x = event.originalEvent.clientX;
  260. foo.y = event.originalEvent.clientY;
  261. }
  262. }
  263. pos = svgRoot.createSVGPoint();
  264. pos.x = parseInt(foo.x, 10);
  265. pos.y = parseInt(foo.y, 10);
  266. ctm = svgRoot.getScreenCTM();
  267. ctm = ctm.inverse();
  268. pos = pos.matrixTransform(ctm);
  269. return pos;
  270. };
  271. return $.fn.svgPanZoom = function(options) {
  272. var ret;
  273. ret = [];
  274. this.each(function() {
  275. var $animationDiv, dragStarted, horizontalSizeIncrement, key, opts, preventClick, value, vb, verticalSizeIncrement, viewBox;
  276. opts = $.extend(true, {}, defaultOptions, options);
  277. opts.$svg = $(this);
  278. if (opts.animationTime == null) {
  279. opts.animationTime = 0;
  280. }
  281. opts.$svg[0].setAttribute("preserveAspectRatio", "xMidYMid meet");
  282. vb = $.extend({}, this.viewBox.baseVal);
  283. if (vb.x == null) {
  284. vb.x = 0;
  285. }
  286. if (vb.y == null) {
  287. vb.y = 0;
  288. }
  289. if (vb.width == null) {
  290. vb.width = 0;
  291. }
  292. if (vb.height == null) {
  293. vb.height = 0;
  294. }
  295. if (opts.initialViewBox != null) {
  296. if (typeof opts.initialViewBox === "string") {
  297. vb = parseViewBoxString(opts.initialViewBox);
  298. } else if (typeof opts.initialViewBox === "object") {
  299. vb = $.extend({}, defaultViewBox, opts.initialViewBox);
  300. } else {
  301. throw "initialViewBox is of invalid type";
  302. }
  303. } else if (vb.x === 0 && vb.y === 0 && vb.width === 0 && vb.height === 0) {
  304. vb = defaultViewBox;
  305. }
  306. viewBox = vb;
  307. opts.initialViewBox = $.extend({}, viewBox);
  308. if (opts.limits == null) {
  309. horizontalSizeIncrement = viewBox.width * 0.15;
  310. verticalSizeIncrement = viewBox.height * 0.15;
  311. opts.limits = {
  312. x: viewBox.x - horizontalSizeIncrement,
  313. y: viewBox.y - verticalSizeIncrement,
  314. x2: viewBox.x + viewBox.width + horizontalSizeIncrement,
  315. y2: viewBox.y + viewBox.height + verticalSizeIncrement
  316. };
  317. }
  318. opts.reset = function() {
  319. var inivb;
  320. inivb = this.initialViewBox;
  321. this.setViewBox(inivb.x, inivb.y, inivb.width, inivb.height, 0);
  322. };
  323. opts.getViewBox = function() {
  324. return $.extend({}, viewBox);
  325. };
  326. $animationDiv = $("<div></div>");
  327. opts.setViewBox = function(x, y, width, height, animationTime) {
  328. if (animationTime == null) {
  329. animationTime = this.animationTime;
  330. }
  331. if (animationTime > 0) {
  332. $animationDiv.css({
  333. left: viewBox.x + "px",
  334. top: viewBox.y + "px",
  335. width: viewBox.width + "px",
  336. height: viewBox.height + "px"
  337. });
  338. }
  339. viewBox = {
  340. x: x != null ? x : viewBox.x,
  341. y: y != null ? y : viewBox.y,
  342. width: width ? width : viewBox.width,
  343. height: height ? height : viewBox.height
  344. };
  345. viewBox = checkLimits(viewBox, this.limits);
  346. if (animationTime > 0) {
  347. $animationDiv.stop().animate({
  348. left: viewBox.x,
  349. top: viewBox.y,
  350. width: viewBox.width,
  351. height: viewBox.height
  352. }, {
  353. duration: animationTime,
  354. easing: "linear",
  355. step: (function(value, properties) {
  356. var $div;
  357. $div = $animationDiv;
  358. this.$svg[0].setAttribute("viewBox", ($div.css("left").slice(0, -2)) + " " + ($div.css("top").slice(0, -2)) + " " + ($div.css("width").slice(0, -2)) + " " + ($div.css("height").slice(0, -2)));
  359. }).bind(this)
  360. });
  361. } else {
  362. this.$svg[0].setAttribute("viewBox", viewBox.x + " " + viewBox.y + " " + viewBox.width + " " + viewBox.height);
  363. }
  364. };
  365. opts.panLeft = function(amount, animationTime) {
  366. if (amount == null) {
  367. amount = this.panFactor;
  368. }
  369. if (animationTime == null) {
  370. animationTime = this.animationTime;
  371. }
  372. this.panRight(-amount, animationTime);
  373. };
  374. opts.panRight = function(amount, animationTime) {
  375. if (amount == null) {
  376. amount = this.panFactor;
  377. }
  378. if (animationTime == null) {
  379. animationTime = this.animationTime;
  380. }
  381. this.setViewBox(viewBox.x + amount, null, null, null, animationTime);
  382. };
  383. opts.panUp = function(amount, animationTime) {
  384. if (amount == null) {
  385. amount = this.panFactor;
  386. }
  387. if (animationTime == null) {
  388. animationTime = this.animationTime;
  389. }
  390. this.panDown(-amount, animationTime);
  391. };
  392. opts.panDown = function(amount, animationTime) {
  393. if (amount == null) {
  394. amount = this.panFactor;
  395. }
  396. if (animationTime == null) {
  397. animationTime = this.animationTime;
  398. }
  399. this.setViewBox(null, viewBox.y + amount, null, null, animationTime);
  400. };
  401. opts.zoomIn = function(amount, animationTime) {
  402. if (amount == null) {
  403. amount = this.zoomFactor;
  404. }
  405. if (animationTime == null) {
  406. animationTime = this.animationTime;
  407. }
  408. this.zoomOut(-amount, animationTime);
  409. };
  410. opts.zoomOut = function(amount, animationTime) {
  411. var center, newHeight, newWidth;
  412. if (amount == null) {
  413. amount = this.zoomFactor;
  414. }
  415. if (animationTime == null) {
  416. animationTime = this.animationTime;
  417. }
  418. if (amount === 0) {
  419. return;
  420. } else if (amount < 0) {
  421. amount = Math.abs(amount);
  422. newWidth = viewBox.width / (1 + amount);
  423. newHeight = viewBox.height / (1 + amount);
  424. } else {
  425. newWidth = viewBox.width * (1 + amount);
  426. newHeight = viewBox.height * (1 + amount);
  427. }
  428. center = {
  429. x: viewBox.x + viewBox.width / 2,
  430. y: viewBox.y + viewBox.height / 2
  431. };
  432. this.setViewBox(center.x - newWidth / 2, center.y - newWidth / 2, newWidth, newHeight, animationTime);
  433. };
  434. opts.setCenter = function(x, y, animationTime) {
  435. if (animationTime == null) {
  436. animationTime = this.animationTime;
  437. }
  438. this.setViewBox(x - viewBox.width / 2, y - viewBox.height / 2, viewBox.width, viewBox.height, animationTime);
  439. };
  440. for (key in opts) {
  441. if (!hasProp.call(opts, key)) continue;
  442. value = opts[key];
  443. if (typeof value === "function") {
  444. opts.key = value.bind(opts);
  445. }
  446. }
  447. opts.$svg.on("mousewheel DOMMouseScroll MozMousePixelScroll", (function(ev) {
  448. var delta, minHeight, minWidth, newMousePosition, newViewBox, newcenter, oldDistanceFromCenter, oldMousePosition, oldViewBox, oldcenter, reductionFactor;
  449. delta = parseInt(ev.originalEvent.wheelDelta || -ev.originalEvent.detail);
  450. if (delta === 0 || opts.events.mouseWheel !== true) {
  451. return;
  452. }
  453. oldViewBox = this.getViewBox();
  454. ev.preventDefault();
  455. ev.stopPropagation();
  456. oldMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev);
  457. oldcenter = {
  458. x: viewBox.x + viewBox.width / 2,
  459. y: viewBox.y + viewBox.height / 2
  460. };
  461. oldDistanceFromCenter = {
  462. x: oldcenter.x - oldMousePosition.x,
  463. y: oldcenter.y - oldMousePosition.y
  464. };
  465. if (delta > 0) {
  466. this.zoomIn(void 0, 0);
  467. minWidth = this.initialViewBox.width / this.maxZoom;
  468. minHeight = this.initialViewBox.height / this.maxZoom;
  469. if (viewBox.width < minWidth) {
  470. reductionFactor = minWidth / viewBox.width;
  471. viewBox.width = minWidth;
  472. viewBox.height = viewBox.height * reductionFactor;
  473. }
  474. if (viewBox.height < minHeight) {
  475. reductionFactor = minHeight / viewBox.height;
  476. viewBox.height = minHeight;
  477. viewBox.width = viewBox.width * reductionFactor;
  478. }
  479. } else {
  480. this.zoomOut(void 0, 0);
  481. }
  482. newMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev);
  483. newcenter = {
  484. x: oldcenter.x + (oldMousePosition.x - newMousePosition.x),
  485. y: oldcenter.y + (oldMousePosition.y - newMousePosition.y)
  486. };
  487. this.setCenter(newcenter.x, newcenter.y, 0);
  488. newViewBox = this.getViewBox();
  489. this.setViewBox(oldViewBox.x, oldViewBox.y, oldViewBox.width, oldViewBox.height, 0);
  490. this.setViewBox(newViewBox.x, newViewBox.y, newViewBox.width, newViewBox.height);
  491. }).bind(opts));
  492. opts.$svg.dblclick((function(ev) {
  493. if (opts.events.doubleClick !== true) {
  494. return;
  495. }
  496. ev.preventDefault();
  497. ev.stopPropagation();
  498. return this.zoomIn();
  499. }).bind(opts));
  500. opts.$svg[0].addEventListener("click", function(ev) {
  501. var preventClick;
  502. if (preventClick) {
  503. preventClick = false;
  504. ev.stopPropagation();
  505. return ev.preventDefault();
  506. }
  507. }, true);
  508. dragStarted = false;
  509. preventClick = false;
  510. opts.$svg.on("mousedown touchstart", (function(ev) {
  511. var $body, domBody, initialViewBox, mouseMoveCallback, mouseUpCallback, oldCursor;
  512. if (dragStarted) {
  513. return;
  514. }
  515. if (opts.events.drag !== true || (ev.type === "mousedown" && ev.which !== 1)) {
  516. return;
  517. }
  518. dragStarted = true;
  519. preventClick = false;
  520. ev.preventDefault();
  521. ev.stopPropagation();
  522. initialViewBox = $.extend({}, viewBox);
  523. $body = $(window.document.body);
  524. domBody = $body[0];
  525. oldCursor = $body.css("cursor");
  526. if (this.events.dragCursor != null) {
  527. $body.css("cursor", this.events.dragCursor);
  528. }
  529. mouseMoveCallback = (function(ev2) {
  530. var currentMousePosition, initialMousePosition;
  531. ev2.preventDefault();
  532. ev2.stopPropagation();
  533. initialMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev);
  534. currentMousePosition = getViewBoxCoordinatesFromEvent(this.$svg[0], ev2);
  535. if (Math.sqrt(Math.pow(ev.pageX + ev2.pageX, 2) + Math.pow(ev.pageY + ev2.pageY, 2)) > 3) {
  536. preventClick = true;
  537. }
  538. this.setViewBox(initialViewBox.x + initialMousePosition.x - currentMousePosition.x, initialViewBox.y + initialMousePosition.y - currentMousePosition.y, null, null, 0);
  539. }).bind(opts);
  540. mouseUpCallback = (function(ev2) {
  541. if (ev2.type === "mouseout" && ev2.target !== ev2.currentTarget) {
  542. return;
  543. }
  544. ev2.preventDefault();
  545. ev2.stopPropagation();
  546. domBody.removeEventListener("mousemove", mouseMoveCallback, true);
  547. domBody.removeEventListener("touchmove", mouseMoveCallback, true);
  548. domBody.removeEventListener("mouseup", mouseUpCallback, true);
  549. domBody.removeEventListener("touchend", mouseUpCallback, true);
  550. domBody.removeEventListener("touchcancel", mouseUpCallback, true);
  551. domBody.removeEventListener("mouseout", mouseUpCallback, true);
  552. if (this.events.dragCursor != null) {
  553. $body.css("cursor", oldCursor);
  554. }
  555. dragStarted = false;
  556. }).bind(opts);
  557. domBody.addEventListener("mousemove", mouseMoveCallback, true);
  558. domBody.addEventListener("touchmove", mouseMoveCallback, true);
  559. domBody.addEventListener("mouseup", mouseUpCallback, true);
  560. domBody.addEventListener("touchend", mouseUpCallback, true);
  561. domBody.addEventListener("touchcancel", mouseUpCallback, true);
  562. domBody.addEventListener("mouseout", mouseUpCallback, true);
  563. }).bind(opts));
  564. opts.setViewBox(vb.x, vb.y, vb.width, vb.height, 0);
  565. ret.push(opts);
  566. });
  567. if (ret.length === 0) {
  568. return null;
  569. }
  570. if (ret.length === 1) {
  571. return ret[0];
  572. } else {
  573. return ret;
  574. }
  575. };
  576. })(jQuery);
  577. }).call(this);
  578. //# sourceMappingURL=jquery.svg.pan.zoom.js.map