SVGGraphic.js revision 09688ec5ffb8b9cf9883a770e2f9ebd60b28888d
2521N/A/**
2521N/A * Graphic is a simple drawing api that allows for basic drawing operations.
2521N/A *
2521N/A * @class Graphic
2521N/A * @constructor
2521N/A */
2892N/Afunction SVGGraphic(config) {
2521N/A
2521N/A this.initializer.apply(this, arguments);
2892N/A}
2892N/A
2892N/ASVGGraphic.prototype = {
2892N/A /**
2892N/A * Gets the current position of the node's parentNode in page coordinates.
2892N/A *
2892N/A * @method getXY
2521N/A * @return Array The XY position of the shape.
2521N/A */
2521N/A getXY: function()
2521N/A {
2521N/A var parentNode = Y.one(this.parentNode),
2521N/A parentXY;
2521N/A if(parentNode)
2521N/A {
2521N/A parentXY = parentNode.getXY();
2521N/A }
2521N/A return parentXY;
2521N/A },
2521N/A
2521N/A /**
2521N/A * Indicates whether or not the instance will size itself based on its contents.
2521N/A *
2521N/A * @property autoSize
2521N/A * @type String
2892N/A */
2521N/A autoSize: true,
2521N/A
2521N/A /**
2521N/A * Indicates whether or not the instance will automatically redraw after a change is made to a shape.
2892N/A * This property will get set to false when batching operations.
2892N/A *
2892N/A * @property autoDraw
2892N/A * @type Boolean
2892N/A * @default true
2892N/A */
2892N/A autoDraw: true,
2892N/A
2892N/A /**
2892N/A * Initializes the class.
2892N/A *
2892N/A * @method initializer
2892N/A * @private
2892N/A */
2892N/A initializer: function(config) {
2892N/A this._shapeInstances = {
2892N/A ellipse: null,
2892N/A circle: null,
2892N/A path: null,
2892N/A rect: null
2892N/A };
2521N/A this._shapes = {};
2521N/A this._redrawQueue = {};
2521N/A config = config || {};
2521N/A var w = config.width || 0,
2521N/A h = config.height || 0;
2521N/A this._gradients = {};
2521N/A this.id = Y.guid();
2521N/A this.node = Y.config.doc.createElement('div');
2521N/A this.node.style.position = "absolute";
2521N/A this.group = this._createGraphics();
2521N/A this.group.setAttribute("id", this.id);
2521N/A this.node.appendChild(this.group);
2521N/A this.setSize(w, h);
2521N/A
2521N/A if(config.render)
2663N/A {
2521N/A this.render(config.render);
2521N/A }
2521N/A },
2521N/A
2521N/A /**
2521N/A * Removes all nodes.
2521N/A *
2521N/A * @method destroy
2521N/A */
2521N/A destroy: function()
2521N/A {
2521N/A this._removeChildren(this.node);
2521N/A if(this.node && this.node.parentNode)
2521N/A {
2521N/A this.node.parentNode.removeChild(this.node);
2892N/A }
2521N/A },
2521N/A
2521N/A /**
2521N/A * Removes all child nodes.
2521N/A *
2521N/A * @method _removeChildren
2521N/A * @param {HTMLElement} node
2521N/A * @private
2521N/A */
2521N/A _removeChildren: function(node)
2521N/A {
2892N/A if(node.hasChildNodes())
2521N/A {
2521N/A var child;
2521N/A while(node.firstChild)
2521N/A {
2521N/A child = node.firstChild;
2521N/A this._removeChildren(child);
2521N/A node.removeChild(child);
2521N/A }
2521N/A }
2521N/A },
2521N/A
2521N/A /**
2521N/A * Shows and and hides a the graphic instance.
2521N/A *
2521N/A * @method toggleVisible
2521N/A * @param val {Boolean} indicates whether the instance should be visible.
2521N/A */
2521N/A toggleVisible: function(val)
2521N/A {
2521N/A this._toggleVisible(this.node, val);
2521N/A },
2521N/A
2521N/A /**
2521N/A * Toggles visibility
2521N/A *
2521N/A * @method _toggleVisible
2521N/A * @param {HTMLElement} node element to toggle
2521N/A * @param {Boolean} val indicates visibilitye
2521N/A * @private
2521N/A */
2521N/A _toggleVisible: function(node, val)
2521N/A {
2521N/A var children = Y.Selector.query(">/*", node),
2521N/A visibility = val ? "visible" : "hidden",
2521N/A i = 0,
2521N/A len;
2892N/A if(children)
2521N/A {
2521N/A len = children.length;
2521N/A for(; i < len; ++i)
2521N/A {
2892N/A this._toggleVisible(children[i], val);
2892N/A }
2892N/A }
2521N/A node.style.visibility = visibility;
2521N/A },
2521N/A
2521N/A /**
2521N/A * Clears the graphics object.
2521N/A *
2521N/A * @method clear
2521N/A */
2521N/A clear: function() {
2892N/A if(this._graphicsList)
2892N/A {
2892N/A while(this._graphicsList.length > 0)
2892N/A {
2521N/A this.group.removeChild(this._graphicsList.shift());
2521N/A }
2925N/A }
2925N/A },
2925N/A
2925N/A /**
2521N/A * Sets the size of the graphics object.
2925N/A *
2892N/A * @method setSize
2521N/A * @param w {Number} width to set for the instance.
2892N/A * @param h {Number} height to set for the instance.
2892N/A */
2892N/A setSize: function(w, h) {
2892N/A if(this.autoSize)
2892N/A {
2892N/A if(w > this.node.getAttribute("width"))
2892N/A {
2892N/A this.group.setAttribute("width", w);
2892N/A }
2892N/A if(h > this.group.getAttribute("height"))
2892N/A {
2892N/A this.group.setAttribute("height", h);
2892N/A }
2521N/A }
2521N/A },
2892N/A
2892N/A /**
2892N/A * Updates the size of the graphics object
2892N/A *
2892N/A * @method _trackSize
2892N/A * @param {Number} w width
2892N/A * @param {Number} h height
2892N/A * @private
2892N/A */
2892N/A _trackSize: function(w, h) {
2892N/A if (w > this._right) {
2892N/A this._right = w;
2892N/A }
2892N/A if(w < this._left)
2892N/A {
2892N/A this._left = w;
2892N/A }
2521N/A if (h < this._top)
2521N/A {
2521N/A this._top = h;
2892N/A }
2521N/A if (h > this._bottom)
2521N/A {
2892N/A this._bottom = h;
2892N/A }
2892N/A this._width = this._right - this._left;
2892N/A this._height = this._bottom - this._top;
2892N/A this.node.style.left = this._left + "px";
2892N/A this.node.style.top = this._top + "px";
2892N/A this.setSize(this._width, this._height);
2521N/A },
2521N/A
2521N/A /**
2521N/A * Adds the graphics node to the dom.
2521N/A *
2521N/A * @method render
2521N/A * @param {HTMLElement} parentNode node in which to render the graphics node into.
2521N/A */
2521N/A render: function(render) {
2521N/A var parentNode = Y.one(render),
2892N/A w = parseInt(parentNode.getComputedStyle("width"), 10),
2892N/A h = parseInt(parentNode.getComputedStyle("height"), 10);
2892N/A parentNode = parentNode || Y.config.doc.body;
2892N/A parentNode.appendChild(this.node);
2892N/A this.setSize(w, h);
2892N/A this.parentNode = parentNode;
2892N/A return this;
2892N/A },
2892N/A
2892N/A /**
2892N/A * Creates a group element
2892N/A *
2892N/A * @method _createGraphics
2892N/A * @private
2892N/A */
2892N/A _createGraphics: function() {
2892N/A var group = this._createGraphicNode("svg");
2892N/A this._styleGroup(group);
2892N/A return group;
2892N/A },
2892N/A
2892N/A /**
2892N/A * Styles a group element
2892N/A *
2892N/A * @method _styleGroup
2892N/A * @private
2892N/A */
2892N/A _styleGroup: function(group)
2892N/A {
2892N/A group.style.position = "absolute";
2521N/A group.style.top = "0px";
2521N/A group.style.left = "0px";
2521N/A group.style.overflow = "auto";
2521N/A group.setAttribute("overflow", "auto");
2521N/A group.setAttribute("pointer-events", "none");
2521N/A },
2521N/A
2521N/A /**
2521N/A * Creates a graphic node
2521N/A *
2521N/A * @method _createGraphicNode
2521N/A * @param {String} type node type to create
2521N/A * @param {String} pe specified pointer-events value
2521N/A * @return HTMLElement
2521N/A * @private
2521N/A */
2521N/A _createGraphicNode: function(type, pe)
2521N/A {
2521N/A var node = document.createElementNS("http://www.w3.org/2000/svg", "svg:" + type),
2521N/A v = pe || "none";
2521N/A if(type !== "defs" && type !== "stop" && type !== "linearGradient" && type != "radialGradient")
2521N/A {
2521N/A node.setAttribute("pointer-events", v);
2521N/A }
2521N/A return node;
2521N/A },
2521N/A
2521N/A /**
2521N/A * Adds a shape instance to the graphic instance.
2521N/A *
2521N/A * @method addShape
2892N/A * @param {Shape} shape The shape instance to be added to the graphic.
2521N/A */
2521N/A addShape: function(shape)
2521N/A {
2521N/A var node = shape.node,
2892N/A parentNode = this._frag || this.group;
2521N/A parentNode.appendChild(node);
2521N/A if(!this._graphicsList)
2521N/A {
2521N/A this._graphicsList = [];
2892N/A }
2892N/A this._graphicsList.push(node);
2892N/A if(this.autoDraw)
2892N/A {
2892N/A this.updateCoordSpace();
2892N/A }
2521N/A },
2521N/A
2521N/A /**
2521N/A * Generates a shape instance by type.
2892N/A *
2521N/A * @method getShape
2521N/A * @param {String} type type of shape to generate.
2521N/A * @param {Object} cfg attributes for the shape
2521N/A * @return Shape
2892N/A */
2892N/A getShape: function(cfg)
2892N/A {
2892N/A cfg.graphic = this;
2892N/A var shape = new this._shapeClass[cfg.type](cfg);
2892N/A this._shapes[shape.get("id")] = shape;
2892N/A this.addShape(shape);
2892N/A return shape;
2892N/A },
2892N/A
2892N/A /**
2892N/A * When overflow is set to true, by default, the viewBox will resize to greater values but not values. (for performance)
2892N/A * When resizing the viewBox down is desirable, set the resizeDown value to true.
2892N/A *
2892N/A * @property resizeDown
2892N/A * @type Boolean
2892N/A */
2892N/A resizeDown: false,
2892N/A
2892N/A /**
2892N/A * @private
2892N/A */
2892N/A _shapeClass: {
2892N/A circle: Y.SVGCircle,
2892N/A rect: Y.SVGRect,
2892N/A path: Y.SVGPath,
2892N/A ellipse: Y.SVGEllipse
2892N/A },
2892N/A
2892N/A /**
2892N/A * @private
2521N/A */
2521N/A _shapeIntances: null,
2521N/A
2521N/A /**
2892N/A * Returns a shape based on the id of its dom node.
2521N/A *
2892N/A * @method getShapeById
2892N/A * @param {String} id Dom id of the shape's node attribute.
2892N/A * @return Shape
2892N/A */
2892N/A getShapeById: function(id)
2892N/A {
2892N/A var shape = this._shapes[id];
2892N/A return shape;
2892N/A },
2892N/A
2521N/A /**
2521N/A * Allows for creating multiple shapes in order to batch appending and redraw operations.
2892N/A *
2892N/A * @method batch
2892N/A * @param {Function} method Method to execute.
2892N/A */
2892N/A batch: function(method)
2892N/A {
2892N/A var node = this.group,
2892N/A frag = document.createDocumentFragment();
2892N/A this._frag = frag;
2892N/A this.autoDraw = false;
2892N/A method();
2892N/A this.updateCoordSpace();
2892N/A node.appendChild(frag);
2892N/A this._frag = null;
2892N/A this.autoDraw = true;
2892N/A },
2892N/A
2892N/A /**
2892N/A * Updates the size of the graphics container and the position of its children.
2892N/A *
2892N/A * @method updateCoordSpace
2892N/A */
2892N/A updateCoordSpace: function(e)
2892N/A {
2892N/A var bounds,
2892N/A i,
2892N/A shape,
2892N/A queue = this.resizeDown ? this._shapes : this._redrawQueue;
2892N/A for(i in queue)
2892N/A {
2892N/A if(queue.hasOwnProperty(i))
2892N/A {
2892N/A shape = queue[i];
2892N/A bounds = shape.getBounds();
2892N/A this._left = Math.min(this._left, bounds.left);
2892N/A this._top = Math.min(this._top, bounds.top);
2892N/A this._right = Math.max(this._right, bounds.right);
2892N/A this._bottom = Math.max(this._bottom, bounds.bottom);
2892N/A }
2892N/A }
2892N/A
2892N/A this._redrawQueue = {};
2892N/A this._width = this._right - this._left;
2892N/A this._height = this._bottom - this._top;
2892N/A this.node.style.width = this._width + "px";
2892N/A this.node.style.height = this._height + "px";
2892N/A this.node.style.left = this._left + "px";
2892N/A this.node.style.top = this._top + "px";
2892N/A this.group.setAttribute("width", this._width);
2892N/A this.group.setAttribute("height", this._height);
2892N/A this.group.style.width = this._width + "px";
2892N/A this.group.style.height = this._height + "px";
2892N/A this.group.setAttribute("viewBox", "" + this._left + " " + this._top + " " + this._width + " " + this._height + "");
2892N/A },
2892N/A
2892N/A /**
2892N/A * Adds a shape to the redraw queue.
2892N/A *
2892N/A * @method addToRedrawQueue
2892N/A * @param shape {SVGShape}
2892N/A */
2892N/A addToRedrawQueue: function(shape)
2892N/A {
2892N/A var id = shape.get("id");
2892N/A this._redrawQueue[id] = shape;
2892N/A if(this.autoDraw)
2892N/A {
2892N/A this.updateCoordSpace();
2892N/A }
2892N/A },
2892N/A
2892N/A /**
2892N/A * @private
2892N/A */
2892N/A _left: 0,
2892N/A
2892N/A /**
2892N/A * @private
2892N/A */
2892N/A _right: 0,
2892N/A
2892N/A /**
2892N/A * @private
2892N/A */
2892N/A _top: 0,
2892N/A
2892N/A /**
2892N/A * @private
2892N/A */
2892N/A _bottom: 0,
2892N/A
2892N/A /**
2892N/A * Returns a reference to a gradient definition based on an id and type.
2892N/A *
2892N/A * @method getGradientNode
2892N/A * @key {String} id that references the gradient definition
2892N/A * @type {String} description of the gradient type
2892N/A * @return HTMLElement
2892N/A */
2892N/A getGradientNode: function(key, type)
2892N/A {
2892N/A var gradients = this._gradients,
2892N/A gradient,
2892N/A nodeType = type + "Gradient";
2892N/A if(gradients.hasOwnProperty(key) && gradients[key].tagName.indexOf(type) > -1)
{
gradient = this._gradients[key];
}
else
{
gradient = this._createGraphicNode(nodeType);
if(!this._defs)
{
this._defs = this._createGraphicNode("defs");
this.group.appendChild(this._defs);
}
this._defs.appendChild(gradient);
key = key || "gradient" + Math.round(100000 * Math.random());
gradient.setAttribute("id", key);
if(gradients.hasOwnProperty(key))
{
this._defs.removeChild(gradients[key]);
}
gradients[key] = gradient;
}
return gradient;
}
};
Y.SVGGraphic = SVGGraphic;