123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /**
- * SVGPan library 1.2.2
- * ======================
- *
- * Given an unique existing element with a given id (or by default, the first
- * g-element), including the library into any SVG adds the following
- * capabilities:
- *
- * - Mouse panning
- * - Mouse zooming (using the wheel)
- * - Object dragging
- *
- * You can configure the behaviour of the pan/zoom/drag via setOptions().
- *
- * Known issues:
- *
- * - Zooming (while panning) on Safari has still some issues
- *
- * Releases:
- *
- * 1.2.2, Tue Aug 30 17:21:56 CEST 2011, Andrea Leofreddi
- * - Fixed viewBox on root tag (#7)
- * - Improved zoom speed (#2)
- *
- * 1.2.1, Mon Jul 4 00:33:18 CEST 2011, Andrea Leofreddi
- * - Fixed a regression with mouse wheel (now working on Firefox 5)
- * - Working with viewBox attribute (#4)
- * - Added "use strict;" and fixed resulting warnings (#5)
- * - Added configuration variables, dragging is disabled by default (#3)
- *
- * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
- * Fixed a bug with browser mouse handler interaction
- *
- * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui
- * Updated the zoom code to support the mouse wheel on Safari/Chrome
- *
- * 1.0, Andrea Leofreddi
- * First release
- */
- /**
- * @license
- * This code is licensed under the following BSD license:
- * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 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.
- *
- * 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.
- *
- * 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.
- *
- */
- goog.provide('svgpan.SvgPan');
- goog.require('goog.Disposable');
- goog.require('goog.events');
- goog.require('goog.events.EventType');
- goog.require('goog.events.MouseWheelHandler');
- /**
- * Instantiates an SvgPan object.
- * @param {string=} opt_graphElementId The id of the graph element.
- * @param {Element=} opt_root An optional document root.
- * @constructor
- * @extends {goog.Disposable}
- */
- svgpan.SvgPan = function(opt_graphElementId, opt_root) {
- svgpan.SvgPan.base(this, 'constructor');
- /** @private {Element} */
- this.root_ = opt_root || document.documentElement;
- /** @private {?string} */
- this.graphElementId_ = opt_graphElementId || null;
- /** @private {boolean} */
- this.cancelNextClick_ = false;
- /** @private {boolean} */
- this.enablePan_ = true;
- /** @private {boolean} */
- this.enableZoom_ = true;
- /** @private {boolean} */
- this.enableDrag_ = false;
- /** @private {number} */
- this.zoomScale_ = 0.4;
- /** @private {svgpan.SvgPan.State} */
- this.state_ = svgpan.SvgPan.State.NONE;
- /** @private {Element} */
- this.svgRoot_ = null;
- /** @private {Element} */
- this.stateTarget_ = null;
- /** @private {SVGPoint} */
- this.stateOrigin_ = null;
- /** @private {SVGMatrix} */
- this.stateTf_ = null;
- /** @private {goog.events.MouseWheelHandler} */
- this.mouseWheelHandler_ = null;
- this.setupHandlers_();
- };
- goog.inherits(svgpan.SvgPan, goog.Disposable);
- /** @override */
- svgpan.SvgPan.prototype.disposeInternal = function() {
- svgpan.SvgPan.base(this, 'disposeInternal');
- goog.events.removeAll(this.root_);
- this.mouseWheelHandler_.dispose();
- };
- /**
- * @enum {string}
- */
- svgpan.SvgPan.State = {
- NONE: 'none',
- PAN: 'pan',
- DRAG: 'drag'
- };
- /**
- * Enables/disables panning the entire SVG (default = true).
- * @param {boolean} enabled Whether or not to allow panning.
- */
- svgpan.SvgPan.prototype.setPanEnabled = function(enabled) {
- this.enablePan_ = enabled;
- };
- /**
- * Enables/disables zooming (default = true).
- * @param {boolean} enabled Whether or not to allow zooming (default = true).
- */
- svgpan.SvgPan.prototype.setZoomEnabled = function(enabled) {
- this.enableZoom_ = enabled;
- };
- /**
- * Enables/disables dragging individual SVG objects (default = false).
- * @param {boolean} enabled Whether or not to allow dragging of objects.
- */
- svgpan.SvgPan.prototype.setDragEnabled = function(enabled) {
- this.enableDrag_ = enabled;
- };
- /**
- * Sets the sensitivity of mousewheel zooming (default = 0.4).
- * @param {number} scale The new zoom scale.
- */
- svgpan.SvgPan.prototype.setZoomScale = function(scale) {
- this.zoomScale_ = scale;
- };
- /**
- * Registers mouse event handlers.
- * @private
- */
- svgpan.SvgPan.prototype.setupHandlers_ = function() {
- goog.events.listen(this.root_, goog.events.EventType.CLICK,
- goog.bind(this.handleMouseClick_, this));
- goog.events.listen(this.root_, goog.events.EventType.MOUSEUP,
- goog.bind(this.handleMouseUp_, this));
- goog.events.listen(this.root_, goog.events.EventType.MOUSEDOWN,
- goog.bind(this.handleMouseDown_, this));
- goog.events.listen(this.root_, goog.events.EventType.MOUSEMOVE,
- goog.bind(this.handleMouseMove_, this));
- this.mouseWheelHandler_ = new goog.events.MouseWheelHandler(this.root_);
- goog.events.listen(this.mouseWheelHandler_,
- goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
- goog.bind(this.handleMouseWheel_, this));
- };
- /**
- * Retrieves the root element for SVG manipulation. The element is then cached.
- * @param {Document} svgDoc The document.
- * @return {Element} The svg root.
- * @private
- */
- svgpan.SvgPan.prototype.getRoot_ = function(svgDoc) {
- if (!this.svgRoot_) {
- var r = this.graphElementId_ ?
- svgDoc.getElementById(this.graphElementId_) : svgDoc.documentElement;
- var t = r;
- while (t != svgDoc) {
- if (t.getAttribute('viewBox')) {
- this.setCtm_(r, r.getCTM());
- t.removeAttribute('viewBox');
- }
- t = t.parentNode;
- }
- this.svgRoot_ = r;
- }
- return this.svgRoot_;
- };
- /**
- * Instantiates an SVGPoint object with given event coordinates.
- * @param {!goog.events.Event} evt The event with coordinates.
- * @return {SVGPoint} The created point.
- * @private
- */
- svgpan.SvgPan.prototype.getEventPoint_ = function(evt) {
- return this.newPoint_(evt.clientX, evt.clientY);
- };
- /**
- * Instantiates an SVGPoint object with given coordinates.
- * @param {number} x The x coordinate.
- * @param {number} y The y coordinate.
- * @return {SVGPoint} The created point.
- * @private
- */
- svgpan.SvgPan.prototype.newPoint_ = function(x, y) {
- var p = this.root_.createSVGPoint();
- p.x = x;
- p.y = y;
- return p;
- };
- /**
- * Sets the current transform matrix of an element.
- * @param {Element} element The element.
- * @param {SVGMatrix} matrix The transform matrix.
- * @private
- */
- svgpan.SvgPan.prototype.setCtm_ = function(element, matrix) {
- var s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' +
- matrix.d + ',' + matrix.e + ',' + matrix.f + ')';
- element.setAttribute('transform', s);
- };
- /**
- * Handle mouse wheel event.
- * @param {!goog.events.Event} evt The event.
- * @private
- */
- svgpan.SvgPan.prototype.handleMouseWheel_ = function(evt) {
- if (!this.enableZoom_)
- return;
- // Prevents scrolling.
- evt.preventDefault();
- var svgDoc = evt.target.ownerDocument;
- var delta = evt.deltaY / -9;
- var z = Math.pow(1 + this.zoomScale_, delta);
- var g = this.getRoot_(svgDoc);
- var p = this.getEventPoint_(evt);
- p = p.matrixTransform(g.getCTM().inverse());
- // Compute new scale matrix in current mouse position
- var k = this.root_.createSVGMatrix().translate(
- p.x, p.y).scale(z).translate(-p.x, -p.y);
- this.setCtm_(g, g.getCTM().multiply(k));
- if (typeof(this.stateTf_) == 'undefined') {
- this.stateTf_ = g.getCTM().inverse();
- }
- this.stateTf_ =
- this.stateTf_ ? this.stateTf_.multiply(k.inverse()) : this.stateTf_;
- };
- /**
- * Handle mouse move event.
- * @param {!goog.events.Event} evt The event.
- * @private
- */
- svgpan.SvgPan.prototype.handleMouseMove_ = function(evt) {
- if (evt.button != 0) {
- return;
- }
- this.handleMove(evt.clientX, evt.clientY, evt.target.ownerDocument);
- };
- /**
- * Handles mouse motion for the given coordinates.
- * @param {number} x The x coordinate.
- * @param {number} y The y coordinate.
- * @param {Document} svgDoc The svg document.
- */
- svgpan.SvgPan.prototype.handleMove = function(x, y, svgDoc) {
- var g = this.getRoot_(svgDoc);
- if (this.state_ == svgpan.SvgPan.State.PAN && this.enablePan_) {
- // Pan mode
- var p = this.newPoint_(x, y).matrixTransform(
- /** @type {!SVGMatrix} */ (this.stateTf_));
- this.setCtm_(g, this.stateTf_.inverse().translate(
- p.x - this.stateOrigin_.x, p.y - this.stateOrigin_.y));
- this.cancelNextClick_ = true;
- } else if (this.state_ == svgpan.SvgPan.State.DRAG && this.enableDrag_) {
- // Drag mode
- var p = this.newPoint_(x, y).matrixTransform(g.getCTM().inverse());
- this.setCtm_(this.stateTarget_, this.root_.createSVGMatrix().translate(
- p.x - this.stateOrigin_.x, p.y - this.stateOrigin_.y).multiply(
- g.getCTM().inverse()).multiply(this.stateTarget_.getCTM()));
- this.stateOrigin_ = p;
- }
- };
- /**
- * Handle click event.
- * @param {!goog.events.Event} evt The event.
- * @private
- */
- svgpan.SvgPan.prototype.handleMouseDown_ = function(evt) {
- if (evt.button != 0) {
- return;
- }
- // Prevent selection while dragging.
- evt.preventDefault();
- var svgDoc = evt.target.ownerDocument;
- var g = this.getRoot_(svgDoc);
- if (evt.target.tagName == 'svg' || !this.enableDrag_) {
- // Pan mode
- this.state_ = svgpan.SvgPan.State.PAN;
- this.stateTf_ = g.getCTM().inverse();
- this.stateOrigin_ = this.getEventPoint_(evt).matrixTransform(this.stateTf_);
- } else {
- // Drag mode
- this.state_ = svgpan.SvgPan.State.DRAG;
- this.stateTarget_ = /** @type {Element} */ (evt.target);
- this.stateTf_ = g.getCTM().inverse();
- this.stateOrigin_ = this.getEventPoint_(evt).matrixTransform(this.stateTf_);
- }
- };
- /**
- * Handle mouse button release event.
- * @param {!goog.events.Event} evt The event.
- * @private
- */
- svgpan.SvgPan.prototype.handleMouseUp_ = function(evt) {
- if (this.state_ != svgpan.SvgPan.State.NONE) {
- this.endPanOrDrag();
- }
- };
- /**
- * Ends pan/drag mode.
- */
- svgpan.SvgPan.prototype.endPanOrDrag = function() {
- if (this.state_ != svgpan.SvgPan.State.NONE) {
- this.state_ = svgpan.SvgPan.State.NONE;
- }
- };
- /**
- * Handle mouse clicks.
- * @param {!goog.events.Event} evt The event.
- * @private
- */
- svgpan.SvgPan.prototype.handleMouseClick_ = function(evt) {
- // We only set cancelNextClick_ after panning occurred, and use it to prevent
- // the default action that would otherwise take place when clicking on the
- // element (for instance, navigation on clickable links, but also any click
- // handler that may be set on an SVG element, in the case of active SVG
- // content)
- if (this.cancelNextClick_) {
- // Cancel potential click handler on active SVG content.
- evt.stopPropagation();
- // Cancel navigation when panning on clickable links.
- evt.preventDefault();
- }
- this.cancelNextClick_ = false;
- };
- /**
- * Returns the current state.
- * @return {!svgpan.SvgPan.State}
- */
- svgpan.SvgPan.prototype.getState = function() {
- return this.state_;
- };
|