0.7
[raphael] / raphael-svg.js
1 /*
2  * Raphael 0.7 - JavaScript Vector Library
3  *
4  * Copyright (c) 2008 – 2009 Dmitry Baranovskiy (http://raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 (function (R) {
8     if (R == null) {
9         throw new Error("raphael-svg.js should not be included into HTML. It has to be loaded with raphael.js");
10     }
11     R.type = "SVG";
12     R.vml = false;
13     R.svg = true;
14     R._.thePath = function (params, pathString, SVG) {
15         var el = document.createElementNS(SVG.svgns, "path");
16         el.setAttribute("fill", "none");
17         if (SVG.canvas) {
18             SVG.canvas.appendChild(el);
19         }
20         var p = new Element(el, SVG);
21         p.isAbsolute = true;
22         p.type = "path";
23         p.last = {x: 0, y: 0, bx: 0, by: 0};
24         p.absolutely = function () {
25             this.isAbsolute = true;
26             return this;
27         };
28         p.relatively = function () {
29             this.isAbsolute = false;
30             return this;
31         };
32         p.moveTo = function (x, y) {
33             var d = this.isAbsolute?"M":"m";
34             d += parseFloat(x, 10).toFixed(3) + " " + parseFloat(y, 10).toFixed(3) + " ";
35             var oldD = this[0].getAttribute("d") || "";
36             (oldD == "M0,0") && (oldD = "");
37             this[0].setAttribute("d", oldD + d);
38             this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
39             this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
40             this.attrs.path = oldD + d;
41             return this;
42         };
43         p.lineTo = function (x, y) {
44             this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
45             this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
46             var d = this.isAbsolute?"L":"l";
47             d += parseFloat(x, 10).toFixed(3) + " " + parseFloat(y, 10).toFixed(3) + " ";
48             var oldD = this[0].getAttribute("d") || "";
49             this[0].setAttribute("d", oldD + d);
50             this.attrs.path = oldD + d;
51             return this;
52         };
53         p.arcTo = function (rx, ry, large_arc_flag, sweep_flag, x, y) {
54             var d = this.isAbsolute ? "A" : "a";
55             d += [parseFloat(rx, 10).toFixed(3), parseFloat(ry, 10).toFixed(3), 0, large_arc_flag, sweep_flag, parseFloat(x, 10).toFixed(3), parseFloat(y, 10).toFixed(3)].join(" ");
56             var oldD = this[0].getAttribute("d") || "";
57             this[0].setAttribute("d", oldD + d);
58             this.last.x = parseFloat(x, 10);
59             this.last.y = parseFloat(y, 10);
60             this.attrs.path = oldD + d;
61             return this;
62         };
63         p.cplineTo = function (x1, y1, w1) {
64             if (!w1) {
65                 return this.lineTo(x1, y1);
66             } else {
67                 var p = {};
68                 var x = parseFloat(x1, 10);
69                 var y = parseFloat(y1, 10);
70                 var w = parseFloat(w1, 10);
71                 var d = this.isAbsolute?"C":"c";
72                 var attr = [+this.last.x + w, +this.last.y, x - w, y, x, y];
73                 for (var i = 0, ii = attr.length; i < ii; i++) {
74                     d += attr[i].toFixed(3) + " ";
75                 }
76                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + attr[4];
77                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + attr[5];
78                 this.last.bx = attr[2];
79                 this.last.by = attr[3];
80                 var oldD = this[0].getAttribute("d") || "";
81                 this[0].setAttribute("d", oldD + d);
82                 this.attrs.path = oldD + d;
83                 return this;
84             }
85         };
86         p.curveTo = function () {
87             var p = {},
88                 command = [0, 1, 2, 3, "s", 5, "c"];
89
90             var d = command[arguments.length];
91             if (this.isAbsolute) {
92                 d = d.toUpperCase();
93             }
94             for (var i = 0, ii = arguments.length; i < ii; i++) {
95                 d += parseFloat(arguments[i], 10).toFixed(3) + " ";
96             }
97             this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[arguments.length - 2], 10);
98             this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[arguments.length - 1], 10);
99             this.last.bx = parseFloat(arguments[arguments.length - 4], 10);
100             this.last.by = parseFloat(arguments[arguments.length - 3], 10);
101             var oldD = this.node.getAttribute("d") || "";
102             this.node.setAttribute("d", oldD + d);
103             this.attrs.path = oldD + d;
104             return this;
105         };
106         p.qcurveTo = function () {
107             var p = {},
108                 command = [0, 1, "t", 3, "q"];
109
110             var d = command[arguments.length];
111             if (this.isAbsolute) {
112                 d = d.toUpperCase();
113             }
114             for (var i = 0, ii = arguments.length; i < ii; i++) {
115                 d += parseFloat(arguments[i], 10).toFixed(3) + " ";
116             }
117             this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[arguments.length - 2], 10);
118             this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[arguments.length - 1], 10);
119             if (arguments.length != 2) {
120                 this.last.qx = parseFloat(arguments[arguments.length - 4], 10);
121                 this.last.qy = parseFloat(arguments[arguments.length - 3], 10);
122             }
123             var oldD = this.node.getAttribute("d") || "";
124             this.node.setAttribute("d", oldD + d);
125             this.attrs.path = oldD + d;
126             return this;
127         };
128         p.addRoundedCorner = function (r, dir) {
129             var R = .5522 * r, rollback = this.isAbsolute, o = this;
130             if (rollback) {
131                 this.relatively();
132                 rollback = function () {
133                     o.absolutely();
134                 };
135             } else {
136                 rollback = function () {};
137             }
138             var actions = {
139                 l: function () {
140                     return {
141                         u: function () {
142                             o.curveTo(-R, 0, -r, -(r - R), -r, -r);
143                         },
144                         d: function () {
145                             o.curveTo(-R, 0, -r, r - R, -r, r);
146                         }
147                     };
148                 },
149                 r: function () {
150                     return {
151                         u: function () {
152                             o.curveTo(R, 0, r, -(r - R), r, -r);
153                         },
154                         d: function () {
155                             o.curveTo(R, 0, r, r - R, r, r);
156                         }
157                     };
158                 },
159                 u: function () {
160                     return {
161                         r: function () {
162                             o.curveTo(0, -R, -(R - r), -r, r, -r);
163                         },
164                         l: function () {
165                             o.curveTo(0, -R, R - r, -r, -r, -r);
166                         }
167                     };
168                 },
169                 d: function () {
170                     return {
171                         r: function () {
172                             o.curveTo(0, R, -(R - r), r, r, r);
173                         },
174                         l: function () {
175                             o.curveTo(0, R, R - r, r, -r, r);
176                         }
177                     };
178                 }
179             };
180             actions[dir[0]]()[dir[1]]();
181             rollback();
182             return o;
183         };
184         p.andClose = function () {
185             var oldD = this[0].getAttribute("d") || "";
186             this[0].setAttribute("d", oldD + "Z ");
187             this.attrs.path = oldD + "Z ";
188             return this;
189         };
190         if (pathString) {
191             p.attrs.path = "" + pathString;
192             p.absolutely();
193             R._.paper.pathfinder(p, p.attrs.path);
194         }
195         if (params) {
196             R._.setFillAndStroke(p, params);
197         }
198         return p;
199     };
200     R._.addGrdientFill = function (o, gradient, SVG) {
201         var el = document.createElementNS(SVG.svgns, gradient.type + "Gradient");
202         el.id = "raphael-gradient-" + Raphael.idGenerator++;
203         if (gradient.vector && gradient.vector.length) {
204             el.setAttribute("x1", gradient.vector[0]);
205             el.setAttribute("y1", gradient.vector[1]);
206             el.setAttribute("x2", gradient.vector[2]);
207             el.setAttribute("y2", gradient.vector[3]);
208         }
209         SVG.defs.appendChild(el);
210         for (var i = 0, ii = gradient.dots.length; i < ii; i++) {
211             var stop = document.createElementNS(SVG.svgns, "stop");
212             stop.setAttribute("offset", gradient.dots[i].offset ? gradient.dots[i].offset : (i == 0) ? "0%" : "100%");
213             stop.setAttribute("stop-color", gradient.dots[i].color || "#fff");
214             if (typeof gradient.dots[i].opacity != "undefined") {
215                 stop.setAttribute("stop-opacity", gradient.dots[i].opacity);
216             }
217             el.appendChild(stop);
218         };
219         o.setAttribute("fill", "url(#" + el.id + ")");
220     };
221     R._.updatePosition = function (o) {
222         if (o.pattern) {
223             var bbox = o.node.getBBox();
224             o.pattern.setAttribute("patternTransform", "translate(" + [bbox.x, bbox.y].join(",") + ")");
225         }
226     };
227     R._.setFillAndStroke = function (o, params) {
228         var dasharray = {
229             "-": [3, 1],
230             ".": [1, 1],
231             "-.": [3, 1, 1, 1],
232             "-..": [3, 1, 1, 1, 1, 1],
233             ". ": [1, 3],
234             "- ": [4, 3],
235             "--": [8, 3],
236             "- .": [4, 3, 1, 3],
237             "--.": [8, 3, 1, 3],
238             "--..": [8, 3, 1, 3, 1, 3]
239         },
240         addDashes = function (o, value) {
241             value = dasharray[value.toString().toLowerCase()];
242             if (value) {
243                 var width = o.attrs["stroke-width"] || "1",
244                     butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
245                     dashes = [];
246                 for (var i = 0, ii = value.length; i < ii; i++) {
247                     dashes.push(value[i] * width + ((i % 2) ? 1 : -1) * butt);
248                 }
249                 value = dashes.join(",");
250                 o.node.setAttribute("stroke-dasharray", value);
251             }
252         };
253         for (var att in params) {
254             var value = params[att];
255             o.attrs[att] = value;
256             switch (att) {
257                 case "path":
258                     if (o.type == "path") {
259                         o.node.setAttribute("d", "M0,0");
260                         R._.paper.pathfinder(o, value);
261                     }
262                 case "rx":
263                 case "cx":
264                 case "x":
265                     o.node.setAttribute(att, value);
266                     R._.updatePosition(o);
267                     break;
268                 case "ry":
269                 case "cy":
270                 case "y":
271                     o.node.setAttribute(att, value);
272                     R._.updatePosition(o);
273                     break;
274                 case "width":
275                     o.node.setAttribute(att, value);
276                     break;
277                 case "height":
278                     o.node.setAttribute(att, value);
279                     break;
280                 case "gradient":
281                     R._.addGrdientFill(o.node, value, o.svg);
282                     break;
283                 case "stroke-width":
284                     o.node.style.strokeWidth = value;
285                     // Need following line for Firefox
286                     o.node.setAttribute(att, value);
287                     if (o.attrs["stroke-dasharray"]) {
288                         addDashes(o, o.attrs["stroke-dasharray"]);
289                     }
290                     break;
291                 case "stroke-dasharray":
292                     addDashes(o, value);
293                     break;
294                 case "text":
295                     if (o.type == "text") {
296                         o.node.childNodes.length && o.node.removeChild(o.node.firstChild);
297                         o.node.appendChild(document.createTextNode(value));
298                     }
299                     break;
300                 case "rotation":
301                     o.rotate(value, true);
302                     break;
303                 case "translation":
304                     var xy = value.split(/[, ]+/);
305                     o.translate(xy[0], xy[1]);
306                     break;
307                 case "scale":
308                     var xy = value.split(/[, ]+/);
309                     o.scale(xy[0], xy[1]);
310                     break;
311                 case "fill":
312                     var isURL = value.match(/^url\(([^\)]+)\)$/i);
313                     if (isURL) {
314                         var el = document.createElementNS(o.svg.svgns, "pattern");
315                         var ig = document.createElementNS(o.svg.svgns, "image");
316                         el.id = "raphael-pattern-" + Raphael.idGenerator++;
317                         el.setAttribute("x", 0);
318                         el.setAttribute("y", 0);
319                         el.setAttribute("patternUnits", "userSpaceOnUse");
320                         ig.setAttribute("x", 0);
321                         ig.setAttribute("y", 0);
322                         ig.setAttributeNS(o.svg.xlink, "href", isURL[1]);
323                         el.appendChild(ig);
324
325                         var img = document.createElement("img");
326                         img.style.position = "absolute";
327                         img.style.top = "-9999em";
328                         img.style.left = "-9999em";
329                         img.onload = function () {
330                             el.setAttribute("width", this.offsetWidth);
331                             el.setAttribute("height", this.offsetHeight);
332                             ig.setAttribute("width", this.offsetWidth);
333                             ig.setAttribute("height", this.offsetHeight);
334                             document.body.removeChild(this);
335                             R._.paper.safari();
336                         };
337                         document.body.appendChild(img);
338                         img.src = isURL[1];
339                         o.svg.defs.appendChild(el);
340                         o.node.style.fill = "url(#" + el.id + ")";
341                         o.node.setAttribute("fill", "url(#" + el.id + ")");
342                         o.pattern = el;
343                         R._.updatePosition(o);
344                         break;
345                     }
346                 default :
347                     var cssrule = att.replace(/(\-.)/g, function (w) {
348                         return w.substring(1).toUpperCase();
349                     });
350                     o.node.style[cssrule] = value;
351                     // Need following line for Firefox
352                     o.node.setAttribute(att, value);
353                     break;
354             }
355         }
356     };
357     var Element = function (node, svg) {
358         var X = 0,
359             Y = 0;
360         this[0] = node;
361         this.node = node;
362         this.svg = svg;
363         this.attrs = this.attrs || {};
364         this.transformations = []; // rotate, translate, scale
365         this._ = {
366             tx: 0,
367             ty: 0,
368             rt: {deg: 0, x: 0, y: 0},
369             sx: 1,
370             sy: 1
371         };
372     };
373     Element.prototype.translate = function (x, y) {
374         if (x == undefined && y == undefined) {
375             return {x: this._.tx, y: this._.ty};
376         }
377         this._.tx += +x;
378         this._.ty += +y;
379         switch (this.type) {
380             case "circle":
381             case "ellipse":
382                 this.attr({cx: this.attrs.cx + x, cy: this.attrs.cy + y});
383                 break;
384             case "rect":
385             case "image":
386             case "text":
387                 this.attr({x: this.attrs.x + x, y: this.attrs.y + y});
388                 break;
389             case "path":
390                 var path = Raphael.pathToRelative(this.attrs.path);
391                 path[0][1] += +x;
392                 path[0][2] += +y;
393                 this.attr({path: path.join(" ")});
394             break;
395         }
396         return this;
397     };
398     Element.prototype.rotate = function (deg, isAbsolute) {
399         if (deg == undefined) {
400             return this._.rt.deg;
401         }
402         var bbox = this.getBBox();
403         if (isAbsolute) {
404             this._.rt.deg = deg;
405         } else {
406             this._.rt.deg += deg;
407         }
408
409         if (this._.rt.deg) {
410             this.transformations[0] = ("rotate(" + this._.rt.deg + " " + (bbox.x + bbox.width / 2) + " " + (bbox.y + bbox.height / 2) + ")");
411         } else {
412             this.transformations[0] = "";
413         }
414         this.node.setAttribute("transform", this.transformations.join(" "));
415         return this;
416     };
417     Element.prototype.hide = function () {
418         this.node.style.display = "none";
419         return this;
420     };
421     Element.prototype.show = function () {
422         this.node.style.display = "block";
423         return this;
424     };
425     Element.prototype.remove = function () {
426         this.node.parentNode.removeChild(this.node);
427     };
428     Element.prototype.getBBox = function () {
429         return this.node.getBBox();
430     };
431     Element.prototype.attr = function () {
432         if (arguments.length == 1 && typeof arguments[0] == "string") {
433             if (arguments[0] == "translation") {
434                 return this.translate();
435             }
436             return this.attrs[arguments[0]];
437         }
438         if (arguments.length == 1 && arguments[0] instanceof Array) {
439             var values = {};
440             for (var j in arguments[0]) {
441                 values[arguments[0][j]] = this.attrs[arguments[0][j]];
442             }
443             return values;
444         }
445         if (arguments.length == 2) {
446             var params = {};
447             params[arguments[0]] = arguments[1];
448             R._.setFillAndStroke(this, params);
449         } else if (arguments.length == 1 && typeof arguments[0] == "object") {
450             R._.setFillAndStroke(this, arguments[0]);
451         }
452         return this;
453     };
454     Element.prototype.toFront = function () {
455         this.node.parentNode.appendChild(this.node);
456         return this;
457     };
458     Element.prototype.toBack = function () {
459         if (this.node.parentNode.firstChild != this.node) {
460             this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
461         }
462         return this;
463     };
464     Element.prototype.insertAfter = function (element) {
465         if (element.node.nextSibling) {
466             element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
467         } else {
468             element.node.parentNode.appendChild(this.node);
469         }
470         return this;
471     };
472     Element.prototype.insertBefore = function (element) {
473         element.node.parentNode.insertBefore(this.node, element.node);
474         return this;
475     };
476     for (var method in R._.element) {
477         Element.prototype[method] = R._.element[method];
478     }
479     R._.theCircle = function (svg, x, y, r) {
480         var el = document.createElementNS(svg.svgns, "circle");
481         el.setAttribute("cx", x);
482         el.setAttribute("cy", y);
483         el.setAttribute("r", r);
484         el.setAttribute("fill", "none");
485         el.setAttribute("stroke", "#000");
486         if (svg.canvas) {
487             svg.canvas.appendChild(el);
488         }
489         var res = new Element(el, svg);
490         res.attrs = res.attrs || {};
491         res.attrs.cx = x;
492         res.attrs.cy = y;
493         res.attrs.r = r;
494         res.attrs.stroke = "#000";
495         res.type = "circle";
496         return res;
497     };
498     R._.theRect = function (svg, x, y, w, h, r) {
499         var el = document.createElementNS(svg.svgns, "rect");
500         el.setAttribute("x", x);
501         el.setAttribute("y", y);
502         el.setAttribute("width", w);
503         el.setAttribute("height", h);
504         if (r) {
505             el.setAttribute("rx", r);
506             el.setAttribute("ry", r);
507         }
508         el.setAttribute("fill", "none");
509         el.setAttribute("stroke", "#000");
510         if (svg.canvas) {
511             svg.canvas.appendChild(el);
512         }
513         var res = new Element(el, svg);
514         res.attrs = res.attrs || {};
515         res.attrs.x = x;
516         res.attrs.y = y;
517         res.attrs.width = w;
518         res.attrs.height = h;
519         res.attrs.stroke = "#000";
520         if (r) {
521             res.attrs.rx = res.attrs.ry = r;
522         }
523         res.type = "rect";
524         return res;
525     };
526     R._.theEllipse = function (svg, x, y, rx, ry) {
527         var el = document.createElementNS(svg.svgns, "ellipse");
528         el.setAttribute("cx", x);
529         el.setAttribute("cy", y);
530         el.setAttribute("rx", rx);
531         el.setAttribute("ry", ry);
532         el.setAttribute("fill", "none");
533         el.setAttribute("stroke", "#000");
534         if (svg.canvas) {
535             svg.canvas.appendChild(el);
536         }
537         var res = new Element(el, svg);
538         res.attrs = res.attrs || {};
539         res.attrs.cx = x;
540         res.attrs.cy = y;
541         res.attrs.rx = rx;
542         res.attrs.ry = ry;
543         res.attrs.stroke = "#000";
544         res.type = "ellipse";
545         return res;
546     };
547     R._.theImage = function (svg, src, x, y, w, h) {
548         var el = document.createElementNS(svg.svgns, "image");
549         el.setAttribute("x", x);
550         el.setAttribute("y", y);
551         el.setAttribute("width", w);
552         el.setAttribute("height", h);
553         el.setAttribute("preserveAspectRatio", "none");
554         el.setAttributeNS(svg.xlink, "href", src);
555         if (svg.canvas) {
556             svg.canvas.appendChild(el);
557         }
558         var res = new Element(el, svg);
559         res.attrs = res.attrs || {};
560         res.attrs.x = x;
561         res.attrs.y = y;
562         res.attrs.width = w;
563         res.attrs.height = h;
564         res.type = "image";
565         return res;
566     };
567     R._.theText = function (svg, x, y, text) {
568         var el = document.createElementNS(svg.svgns, "text");
569         el.setAttribute("x", x);
570         el.setAttribute("y", y);
571         el.setAttribute("text-anchor", "middle");
572         el.setAttribute("fill", "#000");
573         if (text) {
574             el.appendChild(document.createTextNode(text));
575         }
576         if (svg.canvas) {
577             svg.canvas.appendChild(el);
578         }
579         var res = new Element(el, svg);
580         res.attrs = res.attrs || {};
581         res.attrs.x = x;
582         res.attrs.y = y;
583         res.attrs.fill = "#000";
584         res.type = "text";
585         return res;
586     };
587     R._.theGroup = function (svg) {
588         var el = document.createElementNS(svg.svgns, "g");
589         if (svg.canvas) {
590             svg.canvas.appendChild(el);
591         }
592         var i = new Element(el, svg);
593         for (var f in svg) {
594             if (f[0] != "_" && typeof svg[f] == "function") {
595                 i[f] = (function (f) {
596                     return function () {
597                         var e = svg[f].apply(svg, arguments);
598                         el.appendChild(e[0]);
599                         return e;
600                     };
601                 })(f);
602             }
603         }
604         i.type = "group";
605         return i;
606     };
607     R._create = function () {
608         // container, width, height
609         // x, y, width, height
610         if (typeof arguments[0] == "string") {
611             var container = document.getElementById(arguments[0]);
612             var width = arguments[1];
613             var height = arguments[2];
614         }
615         if (typeof arguments[0] == "object") {
616             var container = arguments[0];
617             var width = arguments[1];
618             var height = arguments[2];
619         }
620         if (typeof arguments[0] == "number") {
621             var container = 1,
622                 x = arguments[0],
623                 y = arguments[1],
624                 width = arguments[2],
625                 height = arguments[3];
626         }
627         if (!container) {
628             throw new Error("SVG container not found.");
629         }
630         R._.paper.canvas = document.createElementNS(R._.paper.svgns, "svg");
631         R._.paper.canvas.setAttribute("width", width || 320);
632         R._.paper.width = width || 320;
633         R._.paper.canvas.setAttribute("height", height || 200);
634         R._.paper.height = height || 200;
635         if (container == 1) {
636             document.body.appendChild(R._.paper.canvas);
637             R._.paper.canvas.style.position = "absolute";
638             R._.paper.canvas.style.left = x + "px";
639             R._.paper.canvas.style.top = y + "px";
640         } else {
641             if (container.firstChild) {
642                 container.insertBefore(R._.paper.canvas, container.firstChild);
643             } else {
644                 container.appendChild(R._.paper.canvas);
645             }
646         }
647         container = {
648             canvas: R._.paper.canvas,
649             clear: function () {
650                 while (this.canvas.firstChild) {
651                     this.canvas.removeChild(this.canvas.firstChild);
652                 }
653                 this.defs = document.createElementNS(R._.paper.svgns, "defs");
654                 this.canvas.appendChild(this.defs);
655             }
656         };
657         for (var prop in R._.paper) {
658             if (prop != "create") {
659                 container[prop] = R._.paper[prop];
660             }
661         }
662         container.clear();
663         return container;
664     };
665     R._.paper.remove = function () {
666         this.canvas.parentNode.removeChild(this.canvas);
667     };
668     R._.paper.svgns = "http://www.w3.org/2000/svg";
669     R._.paper.xlink = "http://www.w3.org/1999/xlink";
670     R._.paper.safari = function () {
671         if (navigator.vendor == "Apple Computer, Inc.") {
672             var rect = this.rect(-this.width, -this.height, this.width * 3, this.height * 3).attr({stroke: "none"});
673             setTimeout(function () {rect.remove();}, 0);
674         }
675     };
676 })(window.Raphael);