+ value = dashes.join(",");
+ node.setAttribute("stroke-dasharray", value);
+ }
+ };
+ for (var att in params) {
+ if (!(att in availableAttrs)) {
+ continue;
+ }
+ var value = params[att];
+ attrs[att] = value;
+ switch (att) {
+ // Hyperlink
+ case "href":
+ case "title":
+ case "target":
+ var pn = node.parentNode;
+ if (pn.tagName.toLowerCase() != "a") {
+ var hl = doc.createElementNS(o.paper.svgns, "a");
+ pn.insertBefore(hl, node);
+ hl.appendChild(node);
+ pn = hl;
+ }
+ pn.setAttributeNS(o.paper.xlink, att, value);
+ break;
+ case "path":
+ if (o.type == "path") {
+ node.setAttribute("d", "M0,0");
+ paper.pathfinder(o, value);
+ }
+ case "width":
+ node.setAttribute(att, value);
+ if (attrs.fx) {
+ att = "x";
+ value = attrs.x;
+ } else {
+ break;
+ }
+ case "x":
+ if (attrs.fx) {
+ value = -attrs.x - (attrs.width || 0);
+ }
+ case "rx":
+ case "cx":
+ node.setAttribute(att, value);
+ updatePosition(o);
+ break;
+ case "height":
+ node.setAttribute(att, value);
+ if (attrs.fy) {
+ att = "y";
+ value = attrs.y;
+ } else {
+ break;
+ }
+ case "y":
+ if (attrs.fy) {
+ value = -attrs.y - (attrs.height || 0);
+ }
+ case "ry":
+ case "cy":
+ node.setAttribute(att, value);
+ updatePosition(o);
+ break;
+ case "r":
+ if (o.type == "rect") {
+ node.setAttribute("rx", value);
+ node.setAttribute("ry", value);
+ } else {
+ node.setAttribute(att, value);
+ }
+ break;
+ case "src":
+ if (o.type == "image") {
+ node.setAttributeNS(o.paper.xlink, "href", value);
+ }
+ break;
+ case "stroke-width":
+ node.style.strokeWidth = value;
+ // Need following line for Firefox
+ node.setAttribute(att, value);
+ if (attrs["stroke-dasharray"]) {
+ addDashes(o, attrs["stroke-dasharray"]);
+ }
+ break;
+ case "stroke-dasharray":
+ addDashes(o, value);
+ break;
+ case "rotation":
+ o.rotate(value, true);
+ break;
+ case "translation":
+ var xy = (value + "").split(separator);
+ o.translate((+xy[0] + 1 || 2) - 1, (+xy[1] + 1 || 2) - 1);
+ break;
+ case "scale":
+ var xy = (value + "").split(separator);
+ o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null);
+ break;
+ case "fill":
+ var isURL = value.match(/^url\(([^\)]+)\)$/i);
+ if (isURL) {
+ var el = doc.createElementNS(o.paper.svgns, "pattern");
+ var ig = doc.createElementNS(o.paper.svgns, "image");
+ el.id = "raphael-pattern-" + R.idGenerator++;
+ el.setAttribute("x", 0);
+ el.setAttribute("y", 0);
+ el.setAttribute("patternUnits", "userSpaceOnUse");
+ ig.setAttribute("x", 0);
+ ig.setAttribute("y", 0);
+ ig.setAttributeNS(o.paper.xlink, "href", isURL[1]);
+ el.appendChild(ig);
+
+ var img = doc.createElement("img");
+ img.style.position = "absolute";
+ img.style.top = "-9999em";
+ img.style.left = "-9999em";
+ img.onload = function () {
+ el.setAttribute("width", this.offsetWidth);
+ el.setAttribute("height", this.offsetHeight);
+ ig.setAttribute("width", this.offsetWidth);
+ ig.setAttribute("height", this.offsetHeight);
+ doc.body.removeChild(this);
+ paper.safari();
+ };
+ doc.body.appendChild(img);
+ img.src = isURL[1];
+ o.paper.defs.appendChild(el);
+ node.style.fill = "url(#" + el.id + ")";
+ node.setAttribute("fill", "url(#" + el.id + ")");
+ o.pattern = el;
+ updatePosition(o);
+ break;
+ }
+ delete params.gradient;
+ delete attrs.gradient;
+ if (typeof attrs.opacity != "undefined" && typeof params.opacity == "undefined" ) {
+ node.style.opacity = attrs.opacity;
+ // Need following line for Firefox
+ node.setAttribute("opacity", attrs.opacity);
+ }
+ if (typeof attrs["fill-opacity"] != "undefined" && typeof params["fill-opacity"] == "undefined" ) {
+ node.style.fillOpacity = o.attrs["fill-opacity"];
+ // Need following line for Firefox
+ node.setAttribute("fill-opacity", attrs["fill-opacity"]);
+ }
+ case "stroke":
+ node.style[att] = R.getRGB(value).hex;
+ // Need following line for Firefox
+ node.setAttribute(att, R.getRGB(value).hex);
+ break;
+ case "gradient":
+ addGradientFill(node, value, o.paper);
+ break;
+ case "opacity":
+ case "fill-opacity":
+ if (attrs.gradient) {
+ var gradient = doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, ""));
+ if (gradient) {
+ var stops = gradient.getElementsByTagName("stop");
+ stops[stops.length - 1].setAttribute("stop-opacity", value);
+ }
+ break;
+ }
+ default :
+ var cssrule = att.replace(/(\-.)/g, function (w) {
+ return w.substring(1).toUpperCase();
+ });
+ node.style[cssrule] = value;
+ // Need following line for Firefox
+ node.setAttribute(att, value);
+ break;
+ }
+ }
+
+ tuneText(o, params);
+ };
+ var leading = 1.2;
+ var tuneText = function (el, params) {
+ if (el.type != "text" || !("text" in params || "font" in params || "font-size" in params || "x" in params || "y" in params)) {
+ return;
+ }
+ var a = el.attrs,
+ node = el.node,
+ fontSize = node.firstChild ? parseInt(doc.defaultView.getComputedStyle(node.firstChild, "").getPropertyValue("font-size"), 10) : 10;
+
+ if ("text" in params) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ var texts = (params.text + "").split("\n");
+ for (var i = 0, ii = texts.length; i < ii; i++) {
+ var tspan = doc.createElementNS(el.paper.svgns, "tspan");
+ i && tspan.setAttribute("dy", fontSize * leading);
+ i && tspan.setAttribute("x", a.x);
+ tspan.appendChild(doc.createTextNode(texts[i]));
+ node.appendChild(tspan);
+ }
+ } else {
+ var texts = node.getElementsByTagName("tspan");
+ for (var i = 0, ii = texts.length; i < ii; i++) {
+ i && texts[i].setAttribute("dy", fontSize * leading);
+ i && texts[i].setAttribute("x", a.x);
+ }
+ }
+ node.setAttribute("y", a.y);
+ var bb = el.getBBox(),
+ dif = a.y - (bb.y + bb.height / 2);
+ dif && node.setAttribute("y", a.y + dif);
+ };
+ var Element = function (node, svg) {
+ var X = 0,
+ Y = 0;
+ this[0] = node;
+ this.node = node;
+ this.paper = svg;
+ this.attrs = this.attrs || {};
+ this.transformations = []; // rotate, translate, scale
+ this._ = {
+ tx: 0,
+ ty: 0,
+ rt: {deg: 0, x: 0, y: 0},
+ sx: 1,
+ sy: 1
+ };
+ };
+ Element.prototype.rotate = function (deg, cx, cy) {
+ if (deg == null) {
+ return this._.rt.deg;
+ }
+ var bbox = this.getBBox();
+ deg = deg.toString().split(separator);
+ if (deg.length - 1) {
+ cx = parseFloat(deg[1]);
+ cy = parseFloat(deg[2]);
+ }
+ deg = parseFloat(deg[0]);
+ if (cx != null) {
+ this._.rt.deg = deg;
+ } else {
+ this._.rt.deg += deg;
+ }
+ if (cy == null) {
+ cx = null;
+ }
+ cx = cx == null ? bbox.x + bbox.width / 2 : cx;
+ cy = cy == null ? bbox.y + bbox.height / 2 : cy;
+ if (this._.rt.deg) {
+ this.transformations[0] = ("rotate(" + this._.rt.deg + " " + cx + " " + cy + ")");
+ } else {
+ this.transformations[0] = "";
+ }
+ this.node.setAttribute("transform", this.transformations.join(" "));
+ return this;
+ };
+ Element.prototype.hide = function () {
+ this.node.style.display = "none";
+ return this;
+ };
+ Element.prototype.show = function () {
+ this.node.style.display = "block";
+ return this;
+ };
+ Element.prototype.remove = function () {
+ this.node.parentNode.removeChild(this.node);
+ };
+ Element.prototype.getBBox = function () {
+ var bbox = this.node.getBBox();
+ if (this.type == "text") {
+ bbox = {x: bbox.x, y: Infinity, width: bbox.width, height: 0};
+ for (var i = 0, ii = this.node.getNumberOfChars(); i < ii; i++) {
+ var bb = this.node.getExtentOfChar(i);
+ (bb.y < bbox.y) && (bbox.y = bb.y);
+ (bb.y + bb.height - bbox.y > bbox.height) && (bbox.height = bb.y + bb.height - bbox.y);
+ }
+ }
+ return bbox;
+ };
+ Element.prototype.attr = function () {
+ if (arguments.length == 1 && typeof arguments[0] == "string") {
+ if (arguments[0] == "translation") {
+ return this.translate();
+ }
+ return this.attrs[arguments[0]];
+ }
+ if (arguments.length == 1 && arguments[0] instanceof win.Array) {
+ var values = {};
+ for (var j in arguments[0]) {
+ values[arguments[0][j]] = this.attrs[arguments[0][j]];
+ }
+ return values;
+ }
+ if (arguments.length == 2) {
+ var params = {};
+ params[arguments[0]] = arguments[1];
+ setFillAndStroke(this, params);
+ } else if (arguments.length == 1 && typeof arguments[0] == "object") {
+ setFillAndStroke(this, arguments[0]);
+ }
+ return this;
+ };
+ Element.prototype.toFront = function () {
+ this.node.parentNode.appendChild(this.node);
+ return this;
+ };
+ Element.prototype.toBack = function () {
+ if (this.node.parentNode.firstChild != this.node) {
+ this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
+ }
+ return this;
+ };
+ Element.prototype.insertAfter = function (element) {
+ if (element.node.nextSibling) {
+ element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
+ } else {
+ element.node.parentNode.appendChild(this.node);
+ }
+ return this;
+ };
+ Element.prototype.insertBefore = function (element) {
+ element.node.parentNode.insertBefore(this.node, element.node);
+ return this;
+ };
+ var theCircle = function (svg, x, y, r) {
+ var el = doc.createElementNS(svg.svgns, "circle");
+ el.setAttribute("cx", x);
+ el.setAttribute("cy", y);
+ el.setAttribute("r", r);
+ el.setAttribute("fill", "none");
+ el.setAttribute("stroke", "#000");
+ if (svg.canvas) {
+ svg.canvas.appendChild(el);
+ }
+ var res = new Element(el, svg);
+ res.attrs = res.attrs || {};
+ res.attrs.cx = x;
+ res.attrs.cy = y;
+ res.attrs.r = r;
+ res.attrs.stroke = "#000";
+ res.type = "circle";
+ return res;
+ };
+ var theRect = function (svg, x, y, w, h, r) {
+ var el = doc.createElementNS(svg.svgns, "rect");
+ el.setAttribute("x", x);
+ el.setAttribute("y", y);
+ el.setAttribute("width", w);
+ el.setAttribute("height", h);
+ if (r) {
+ el.setAttribute("rx", r);
+ el.setAttribute("ry", r);
+ }
+ el.setAttribute("fill", "none");
+ el.setAttribute("stroke", "#000");
+ if (svg.canvas) {
+ svg.canvas.appendChild(el);
+ }
+ var res = new Element(el, svg);
+ res.attrs = res.attrs || {};
+ res.attrs.x = x;
+ res.attrs.y = y;
+ res.attrs.width = w;
+ res.attrs.height = h;
+ res.attrs.stroke = "#000";
+ if (r) {
+ res.attrs.rx = res.attrs.ry = r;
+ }
+ res.type = "rect";
+ return res;
+ };
+ var theEllipse = function (svg, x, y, rx, ry) {
+ var el = doc.createElementNS(svg.svgns, "ellipse");
+ el.setAttribute("cx", x);
+ el.setAttribute("cy", y);
+ el.setAttribute("rx", rx);
+ el.setAttribute("ry", ry);
+ el.setAttribute("fill", "none");
+ el.setAttribute("stroke", "#000");
+ if (svg.canvas) {
+ svg.canvas.appendChild(el);
+ }
+ var res = new Element(el, svg);
+ res.attrs = res.attrs || {};
+ res.attrs.cx = x;
+ res.attrs.cy = y;
+ res.attrs.rx = rx;
+ res.attrs.ry = ry;
+ res.attrs.stroke = "#000";
+ res.type = "ellipse";
+ return res;
+ };
+ var theImage = function (svg, src, x, y, w, h) {
+ var el = doc.createElementNS(svg.svgns, "image");
+ el.setAttribute("x", x);
+ el.setAttribute("y", y);
+ el.setAttribute("width", w);
+ el.setAttribute("height", h);
+ el.setAttribute("preserveAspectRatio", "none");
+ el.setAttributeNS(svg.xlink, "href", src);
+ if (svg.canvas) {
+ svg.canvas.appendChild(el);
+ }
+ var res = new Element(el, svg);
+ res.attrs = res.attrs || {};
+ res.attrs.x = x;
+ res.attrs.y = y;
+ res.attrs.width = w;
+ res.attrs.height = h;
+ res.type = "image";
+ return res;
+ };
+ var theText = function (svg, x, y, text) {
+ var el = doc.createElementNS(svg.svgns, "text");
+ el.setAttribute("x", x);
+ el.setAttribute("y", y);
+ el.setAttribute("text-anchor", "middle");
+ if (svg.canvas) {
+ svg.canvas.appendChild(el);
+ }
+ var res = new Element(el, svg);
+ res.attrs = res.attrs || {};
+ res.attrs.x = x;
+ res.attrs.y = y;
+ res.type = "text";
+ setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000", text: text});
+ return res;
+ };
+ var setSize = function (width, height) {
+ this.width = width || this.width;
+ this.height = height || this.height;
+ this.canvas.setAttribute("width", this.width);
+ this.canvas.setAttribute("height", this.height);
+ return this;
+ };
+ var create = function () {
+ var con = getContainer.apply(null, arguments);
+ var container = con.container,
+ x = con.x,
+ y = con.y,
+ width = con.width,
+ height = con.height;
+ if (!container) {
+ throw new Error("SVG container not found.");
+ }
+ paper.canvas = doc.createElementNS(paper.svgns, "svg");
+ paper.canvas.setAttribute("width", width || 512);
+ paper.width = width || 512;
+ paper.canvas.setAttribute("height", height || 342);
+ paper.height = height || 342;
+ if (container == 1) {
+ doc.body.appendChild(paper.canvas);
+ paper.canvas.style.position = "absolute";
+ paper.canvas.style.left = x + "px";
+ paper.canvas.style.top = y + "px";
+ } else {
+ if (container.firstChild) {
+ container.insertBefore(paper.canvas, container.firstChild);
+ } else {
+ container.appendChild(paper.canvas);
+ }
+ }
+ container = {
+ canvas: paper.canvas,
+ clear: function () {
+ while (this.canvas.firstChild) {
+ this.canvas.removeChild(this.canvas.firstChild);