Fixed bug in VML part (thanks to Alfonso Moreno).
[raphael] / raphael.js
1 /*
2  * Raphael 0.5.4b - JavaScript Vector Library
3  *
4  * Copyright (c) 2008 Dmitry Baranovskiy (raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 var Raphael = (function (type) {
8         var r = function () {
9             return r._create.apply(r, arguments);
10         };
11         r.version = "0.5.4b";
12         r.type = type;
13         var C = {};
14         function Matrix(m11, m12, m21, m22, dx, dy) {
15             this.m = [
16                 [m11 || 1, m12 || 0, 0],
17                 [m21 || 0, m22 || 1, 0],
18                 [dx || 0, dy || 0, 1],
19             ];
20         }
21
22         C._getX = C._getY = C._getW = C._getH = function (x) { return x; };
23
24         if (type == "VML") {
25             Matrix.prototype.toString = function () {
26                 return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.m[0][0] +
27                     ", M12=" + this.m[1][0] + ", M21=" + this.m[0][1] + ", M22=" + this.m[1][1] +
28                     ", Dx=" + this.m[2][0] + ", Dy=" + this.m[2][1] + ", sizingmethod='auto expand', filtertype='bilinear')";
29             };
30             var thePath = function (params, pathString, VML) {
31                 var g = document.createElement("rvml:group"), gl = g.style;
32                 gl.position = "absolute";
33                 gl.left = 0;
34                 gl.top = 0;
35                 gl.width = VML.width + "px";
36                 gl.height = VML.height + "px";
37                 var el = document.createElement("rvml:shape"), ol = el.style;
38                 ol.width = VML.width + "px";
39                 ol.height = VML.height + "px";
40                 el.path = "";
41                 if (params["class"]) {
42                     el.className = params["class"];
43                 }
44                 el.coordsize = this.coordsize;
45                 el.coordorigin = this.coordorigin;
46                 g.appendChild(el);
47                 VML.canvas.appendChild(g);
48                 var p = new Element(el, g, VML);
49                 setFillAndStroke(p, params);
50                 if (params.gradient) {
51                     addGrdientFill(p, params.gradient);
52                 }
53                 p.isAbsolute = true;
54                 p.type = "path";
55                 p.path = [];
56                 p.last = {x: 0, y: 0, bx: 0, by: 0, isAbsolute: true};
57                 p.Path = "";
58                 p.absolutely = function () {
59                     this.isAbsolute = true;
60                     return this;
61                 };
62                 p.relatively = function () {
63                     this.isAbsolute = false;
64                     return this;
65                 };
66                 p.redraw = function () {
67                     this.Path = "";
68                     var oldPath = this.path;
69                     this.path = [];
70                     for (var i = 0, ii = oldPath.length; i < ii; i++) {
71                         if (oldPath[i].type != "end") {
72                             this[oldPath[i].type + "To"].apply(this, oldPath[i].arg);
73                         } else {
74                             this.andClose();
75                         }
76                     };
77                 };
78                 p.moveTo = function (x, y) {
79                     var d = this.isAbsolute?"m":"t";
80                     var _getX = this.isAbsolute ? VML._getX : VML._getW;
81                     var _getY = this.isAbsolute ? VML._getY : VML._getH;
82                     d += Math.round(_getX(parseFloat(x, 10))) + " " + Math.round(_getY(parseFloat(y, 10)));
83                     this[0].path = this.Path += d;
84                     this.last.x = Math.round(_getX(parseFloat(x, 10)));
85                     this.last.y = Math.round(_getY(parseFloat(y, 10)));
86                     this.last.isAbsolute = this.isAbsolute;
87                     this.path.push({type: "move", arg: [].slice.call(arguments, 0), pos: this.isAbsolute});
88                     return this;
89                 };
90                 p.lineTo = function (x, y) {
91                     var d = this.isAbsolute?"l":"r";
92                     var _getX = this.isAbsolute ? VML._getX : VML._getW;
93                     var _getY = this.isAbsolute ? VML._getY : VML._getH;
94                     d += Math.round(_getX(parseFloat(x, 10))) + " " + Math.round(_getY(parseFloat(y, 10)));
95                     this[0].path = this.Path += d;
96                     this.last.x = Math.round(_getX(parseFloat(x, 10)));
97                     this.last.y = Math.round(_getY(parseFloat(y, 10)));
98                     this.last.isAbsolute = this.isAbsolute;
99                     this.path.push({type: "line", arg: [].slice.call(arguments, 0), pos: this.isAbsolute});
100                     return this;
101                 };
102                 p.cplineTo = function (x1, y1, w1) {
103                     if (!w1) {
104                         return this.lineTo(x1, y1);
105                     } else {
106                         var p = {};
107                         p._getX = this.isAbsolute ? VML._getX : VML._getW;
108                         p._getY = this.isAbsolute ? VML._getY : VML._getH;
109                         var x = Math.round(p._getX(Math.round(parseFloat(x1, 10) * 100) / 100));
110                         var y = Math.round(p._getY(Math.round(parseFloat(y1, 10) * 100) / 100));
111                         var w = Math.round(VML._getW(Math.round(parseFloat(w1, 10) * 100) / 100));
112                         var d = this.isAbsolute?"c":"v";
113                         var attr = [this.last.x + w, this.last.y, x - w, y, x, y];
114                         d += attr.join(" ") + " ";
115                         this.last.x = attr[4];
116                         this.last.y = attr[5];
117                         this.last.bx = attr[2];
118                         this.last.by = attr[3];
119                         this[0].path = this.Path += d;
120                         this.path.push({type: "cpline", arg: [].slice.call(arguments, 0), pos: this.isAbsolute});
121                         return this;
122                     }
123                 };
124                 p.curveTo = function () {
125                     var d = this.isAbsolute?"c":"v";
126                     var _getX = this.isAbsolute ? VML._getX : VML._getW;
127                     var _getY = this.isAbsolute ? VML._getY : VML._getH;
128                     if (arguments.length == 6) {
129                         this.last.x = Math.round(_getX(parseFloat(arguments[4], 10)));
130                         this.last.y = Math.round(_getY(parseFloat(arguments[5], 10)));
131                         this.last.bx = Math.round(_getX(parseFloat(arguments[2], 10)));
132                         this.last.by = Math.round(_getY(parseFloat(arguments[3], 10)));
133                         d += Math.round(_getX(parseFloat(arguments[0], 10))) + " " + Math.round(_getY(parseFloat(arguments[1], 10))) + " " +
134                              Math.round(_getX(parseFloat(arguments[2], 10))) + " " + Math.round(_getY(parseFloat(arguments[3], 10))) + " " +
135                              Math.round(_getX(parseFloat(arguments[4], 10))) + " " + Math.round(_getY(parseFloat(arguments[5], 10))) + " ";
136                         this.last.isAbsolute = this.isAbsolute;
137                     }
138                     this[0].path = this.Path += d;
139                     this.path.push({type: "curve", arg: [].slice.call(arguments, 0), pos: this.isAbsolute});
140                     return this;
141                 };
142                 p.addRoundedCorner = function (r, dir) {
143                     var R = .5522 * r, rollback = this.isAbsolute, o = this;
144                     if (rollback) {
145                         this.relatively();
146                         rollback = function () {
147                             o.absolutely();
148                         };
149                     } else {
150                         rollback = function () {};
151                     }
152                     var actions = {
153                         l: function () {
154                             return {
155                                 u: function () {
156                                     o.curveTo(-R, 0, -r, -(r - R), -r, -r);
157                                 },
158                                 d: function () {
159                                     o.curveTo(-R, 0, -r, r - R, -r, r);
160                                 }
161                             };
162                         },
163                         r: function () {
164                             return {
165                                 u: function () {
166                                     o.curveTo(R, 0, r, -(r - R), r, -r);
167                                 },
168                                 d: function () {
169                                     o.curveTo(R, 0, r, r - R, r, r);
170                                 }
171                             };
172                         },
173                         u: function () {
174                             return {
175                                 r: function () {
176                                     o.curveTo(0, -R, -(R - r), -r, r, -r);
177                                 },
178                                 l: function () {
179                                     o.curveTo(0, -R, R - r, -r, -r, -r);
180                                 }
181                             };
182                         },
183                         d: function () {
184                             return {
185                                 r: function () {
186                                     o.curveTo(0, R, -(R - r), r, r, r);
187                                 },
188                                 l: function () {
189                                     o.curveTo(0, R, R - r, r, -r, r);
190                                 }
191                             };
192                         }
193                     };
194                     actions[dir.charAt(0)]()[dir.charAt(1)]();
195                     rollback();
196                     return o;
197                 };
198                 p.andClose = function () {
199                     this[0].path = (this.Path += "x e");
200                     return this;
201                 };
202                 if (typeof pathString == "string") {
203                     pathString = pathString.replace(/([mzlhvcsqta])/ig, ",$1,").replace(/([^,])\-/ig, "$1,-");
204                     path = pathString.split(",");
205                     var i = 1, ii = path.length;
206                     while (i < ii) {
207                         switch (path[i]) {
208                             case "M":
209                                 p.absolutely().moveTo(path[++i], path[++i]);
210                                 break;
211                             case "m":
212                                 p.relatively().moveTo(path[++i], path[++i]);
213                                 break;
214                             case "C":
215                                 p.absolutely().curveTo(path[++i], path[++i], path[++i], path[++i], path[++i], path[++i]);
216                                 break;
217                             case "c":
218                                 p.relatively().curveTo(path[++i], path[++i], path[++i], path[++i], path[++i], path[++i]);
219                                 break;
220                             case "L":
221                                 p.absolutely().lineTo(path[++i], path[++i]);
222                                 break;
223                             case "l":
224                                 p.relatively().lineTo(path[++i], path[++i]);
225                                 break;
226                             case "H":
227                                 p.absolutely().lineTo(path[++i], 0);
228                                 break;
229                             case "h":
230                                 p.relatively().lineTo(path[++i], 0);
231                                 break;
232                             case "V":
233                                 p.absolutely().lineTo(0, path[++i]);
234                                 break;
235                             case "v":
236                                 p.relatively().lineTo(0, path[++i]);
237                                 break;
238                             case "Z":
239                             case "z":
240                                 p.andClose();
241                                 break;
242                         }
243                         i++;
244                     }
245                 }
246                 return p;
247             };
248             var setFillAndStroke = function (o, params) {
249                 o[0].attrs = o[0].attrs || {};
250                 for (var par in params) {
251                     o[0].attrs[par] = params[par];
252                 }
253                 params["font-family"] && (o[0].style.fontFamily = params["font-family"]);
254                 params["font-size"] && (o[0].style.fontSize = params["font-size"]);
255                 params["font"] && (o[0].style.font = params["font"]);
256                 params["font-weight"] && (o[0].style.fontWeight = params["font-weight"]);
257                 if (typeof params.opacity != "undefined" || typeof params["stroke-width"] != "undefined" || typeof params.fill != "undefined" || typeof params.stroke != "undefined") {
258                     o = o.shape || o[0];
259                     var fill = (o.getElementsByTagName("fill") && o.getElementsByTagName("fill")[0]) || document.createElement("rvml:fill");
260                     if ("fill-opacity" in params || "opacity" in params) {
261                         fill.opacity = ((params["fill-opacity"] + 1 || 2) - 1) * ((params.opacity + 1 || 2) - 1);
262                     }
263                     params.fill && (fill.on = true);
264                     if (fill.on && params.fill) {
265                         fill.color = params.fill;
266                     }
267                     if (params.fill == "none") {
268                         fill.on = false;
269                     }
270                     o.appendChild(fill);
271                     var stroke = (o.getElementsByTagName("stroke") && o.getElementsByTagName("stroke")[0]) || document.createElement("rvml:stroke");
272                     if ((params.stroke && params.stroke != "none") || params["stroke-width"] || params["stroke-opacity"] || params["stroke-dasharray"]) {
273                         stroke.on = true;
274                     }
275                     if (params.stroke == "none" || typeof stroke.on == "undefined") {
276                         stroke.on = false;
277                     }
278                     if (stroke.on && params.stroke) {
279                         stroke.color = params.stroke;
280                     }
281                     stroke.opacity = ((params["stroke-opacity"] + 1 || 2) - 1) * ((params.opacity + 1 || 2) - 1);
282                     stroke.joinstyle = params["stroke-linejoin"] || "miter";
283                     stroke.miterlimit = params["stroke-miterlimit"] || 8;
284                     stroke.endcap = {butt: "flat", square: "square", round: "round"}[params["stroke-linecap"] || "miter"];
285                     stroke.weight = (parseFloat(params["stroke-width"], 10) || 1);
286                     if (params["stroke-dasharray"]) {
287                         var dashes = params["stroke-dasharray"].replace(" ", ",").split(","),
288                             dashesn = [],
289                             str = stroke.weight;
290                         for (var i = 0, ii = dashes.length; i < ii; i++) {
291                             var res = dashes[i] / str;
292                             if (!isNaN(res)) {
293                                 dashesn.push(res);
294                             }
295                         };
296                         stroke.dashstyle = dashesn.join(" ");
297                     }
298                     o.appendChild(stroke);
299                 }
300             };
301             var addGrdientFill = function (o, gradient) {
302                 o[0].attrs = o[0].attrs || {};
303                 o[0].attrs.gradient = gradient;
304                 o = o.shape || o[0];
305                 var fill = o.getElementsByTagName("fill");
306                 if (fill.length) {
307                     fill = fill[0];
308                 } else {
309                     fill = document.createElement("rvml:fill");
310                 }
311                 if (gradient.dots.length) {
312                     fill.on = true;
313                     fill.type = (gradient.type.toLowerCase() == "linear") ? "gradient" : "gradientradial";
314                     if (typeof gradient.dots[0].color != "undefined") {
315                         fill.color = gradient.dots[0].color || "#000";
316                     }
317                     if (typeof gradient.dots[0].opacity != "undefined") {
318                         fill.opacity = gradient.dots[0].opacity;
319                     }
320                     if (typeof gradient.dots[gradient.dots.length - 1].opacity != "undefined") {
321                         fill.opacity2 = gradient.dots[gradient.dots.length - 1].opacity;
322                     }
323                     if (typeof gradient.dots[gradient.dots.length - 1].color != "undefined") {
324                         fill.color2 = gradient.dots[gradient.dots.length - 1].color || "#000";
325                     }
326                     var colors = "";
327                     for (var i = 1, ii = gradient.dots.length - 1; i < ii; i++) {
328                         colors += gradient.dots[i].offset + " " + gradient.dots[i].color;
329                         if (i != ii - 1) {
330                             colors += ",";
331                         }
332                     };
333                     if (colors) {
334                         fill.colors = colors;
335                     }
336                     if (gradient.vector) {
337                         var angle = Math.round(Math.atan((parseInt(gradient.vector[3], 10) - parseInt(gradient.vector[1], 10)) / (parseInt(gradient.vector[2], 10) - parseInt(gradient.vector[0], 10))) * 57.29) + 180;
338                         fill.angle = angle + 90;
339                     }
340                     if (gradient.type.toLowerCase() == "radial") {
341                         fill.focusposition = "0.5, 0.5";
342                         fill.focussize = "0, 0";
343                         fill.method = "none";
344                     }
345                 }
346             };
347             var Element = function (node, group, vml) {
348                 var Rotation = 0,
349                     RotX = 0,
350                     RotY = 0,
351                     Scale = 1;
352                 this[0] = node;
353                 this.X = 0;
354                 this.Y = 0;
355                 arguments.callee.name = "Element";
356                 this[0].attrs = {};
357                 this.Group = group;
358                 this.setBox = function (params) {
359                     var gs = this.Group.style,
360                         os = this[0].style;
361                     for (var i in params) {
362                         this[0].attrs[i] = params[i];
363                     }
364                     var attr = this[0].attrs, x, y, w, h;
365                     switch (this.type) {
366                         case "circle": 
367                             x = attr.cx - attr.r;
368                             y = attr.cy - attr.r;
369                             w = h = attr.r * 2;
370                             break;
371                         case "ellipse":
372                             x = attr.cx - attr.rx;
373                             y = attr.cy - attr.ry;
374                             w = attr.rx * 2;
375                             h = attr.ry * 2;
376                             break;
377                         case "rect":
378                         case "image":
379                             x = attr.x;
380                             y = attr.y;
381                             w = attr.w;
382                             h = attr.h;
383                             break;
384                         case "text":
385                             this.textpath.v = ["m", Math.round(attr.x), ", ", Math.round(attr.y - 2), "l", Math.round(attr.x) + 1, ", ", Math.round(attr.y - 2)].join("");
386                             return;
387                         default:
388                             return;
389                     }
390                     var left = vml.width / 2 - w / 2,
391                         top = vml.height / 2 - h / 2;
392                     gs.position = "absolute";
393                     gs.left = x - left + "px";
394                     gs.top = y - top + "px";
395                     this.X = x - left;
396                     this.Y = y - top;
397                     this.W = w;
398                     this.H = h;
399                     gs.width = vml.width + "px";
400                     gs.height = vml.height + "px";
401                     os.position = "absolute";
402                     os.top = top + "px";
403                     os.left = left + "px";
404                     os.width = w + "px";
405                     os.height = h + "px";
406                 };
407                 this.hide = function () {
408                     this.Group.style.display = "none";
409                     return this;
410                 };
411                 this.show = function () {
412                     this.Group.style.display = "block";
413                     return this;
414                 };
415                 this.rotate = function (deg) {
416                     Rotation += deg;
417                     this.Group.style.rotation = Rotation;
418                     return this;
419                 };
420                 this.translate = function (x, y) {
421                     this.X += x;
422                     this.Y += y;
423                     this.Group.style.left = this.X + "px";
424                     this.Group.style.top = this.Y + "px";
425                     return this;
426                 };
427                 // depricated
428                 this.matrix = function (xx, xy, yx, yy, dx, dy) {
429                     tMatrix = new Matrix(xx, xy, yx, yy, dx, dy);
430                     this.Group.style.filter = tMatrix;
431                     return this;
432                 };
433                 this.scale = function (x, y) {
434                     y = y || x;
435                     if (x != 0 && !(x == 1 && y == 1)) {
436                         var dirx = Math.round(x / Math.abs(x)),
437                             diry = Math.round(y / Math.abs(y));
438                         if (dirx != 1 || diry != 1) {
439                             this[0].style.filter = new Matrix(dirx, 0, 0, diry, 0, 0);
440                         }
441                         var width = parseInt(this[0].style.width, 10) * x * dirx;
442                         var height = parseInt(this[0].style.height, 10) * y * diry;
443                         var left = parseInt(this[0].style.left, 10);
444                         var top = parseInt(this[0].style.top, 10);
445                         this[0].style.left = this.X = left + this.W / 2 - width / 2;
446                         this[0].style.top = this.Y = top + this.H / 2 - height / 2;
447                         this[0].style.width = this.W = width;
448                         this[0].style.height = this.H = height;
449                     }
450                     return this;
451                 };
452                 this.getBBox = function () {
453                     return {
454                         x: this.Group.offsetLeft,
455                         y: this.Group.offsetTop,
456                         width: this.Group.offsetWidth,
457                         height: this.Group.offsetHeight
458                     };
459                 };
460                 this.remove = function () {
461                     this[0].parentNode.removeChild(this[0]);
462                     this.Group.parentNode.removeChild(this.Group);
463                     this.shape && this.shape.parentNode.removeChild(this.shape);
464                 };
465                 this.attr = function () {
466                     if (arguments.length == 1 && typeof arguments[0] == "string") {
467                         return this[0].attrs[arguments[0]];
468                     }
469                     if (this[0].attrs && arguments.length == 1 && arguments[0] instanceof Array) {
470                         var values = {};
471                         for (var i = 0, ii = arguments[0].length; i < ii; i++) {
472                             values[arguments[0][i]] = this[0].attrs[arguments[0][i]];
473                         };
474                         return values;
475                     }
476                     if (this[0].tagName.toLowerCase() == "group") {
477                         var children = this[0].childNodes;
478                         this[0].attrs = this[0].attrs || {};
479                         if (arguments.length == 2) {
480                             this[0].attrs[arguments[0]] = arguments[1];
481                         } else if (arguments.length == 1 || typeof arguments[0] == "object") {
482                             for (var j in arguments[0]) {
483                                 this[0].attrs[j] = arguments[0][j];
484                             }
485                         }
486                         for (var i = 0, ii = children.length; i < ii; i++) {
487                             this.attr.apply(new item(children[i], this[0], vml), arguments);
488                         }
489                     } else {
490                         var params;
491                         if (arguments.length == 2) {
492                             params = {};
493                             params[arguments[0]] = arguments[1];
494                         }
495                         if (arguments.length == 1 && typeof arguments[0] == "object") {
496                             params = arguments[0];
497                         }
498                         if (params) {
499                             setFillAndStroke(this, params);
500                             this.setBox(params);
501                             if (params.gradient) {
502                                 addGrdientFill(this, params.gradient);
503                             }
504                             if (params.text && this.type == "text") {
505                                 this[0].string = params.text;
506                             }
507                             if (params.id) {
508                                 this[0].id = params.id;
509                             }
510                         }
511                     }
512                     return this;
513                 };
514                 this.toFront = function () {
515                     this.Group.parentNode.appendChild(this.Group);
516                     return this;
517                 };
518                 this.toBack = function () {
519                     if (this.Group.parentNode.firstChild != this.Group) {
520                         this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
521                     }
522                     return this;
523                 };
524             };
525             var theCircle = function (vml, x, y, r) {
526                 var g = document.createElement("rvml:group");
527                 var o = document.createElement("rvml:oval");
528                 g.appendChild(o);
529                 vml.canvas.appendChild(g);
530                 var res = new Element(o, g, vml);
531                 setFillAndStroke(res, {stroke: "#000", fill: "none"});
532                 res.setBox({x: x - r, y: y - r, w: r * 2, h: r * 2});
533                 o.attrs.cx = x;
534                 o.attrs.cy = y;
535                 o.attrs.r = r;
536                 res.type = "circle";
537                 return res;
538             };
539             var theRect = function (vml, x, y, w, h, r) {
540                 var g = document.createElement("rvml:group");
541                 var o = document.createElement(r ? "rvml:roundrect" : "rvml:rect");
542                 if (r) {
543                     o.arcsize = r / (Math.min(w, h));
544                 }
545                 g.appendChild(o);
546                 vml.canvas.appendChild(g);
547                 var res = new Element(o, g, vml);
548                 setFillAndStroke(res, {stroke: "#000"});
549                 res.setBox({x: x, y: y, w: w, h: h});
550                 o.attrs.x = x;
551                 o.attrs.y = y;
552                 o.attrs.w = w;
553                 o.attrs.h = h;
554                 o.attrs.r = r;
555                 res.type = "rect";
556                 return res;
557             };
558             var theEllipse = function (vml, x, y, rx, ry) {
559                 var g = document.createElement("rvml:group");
560                 var o = document.createElement("rvml:oval");
561                 g.appendChild(o);
562                 vml.canvas.appendChild(g);
563                 var res = new Element(o, g, vml);
564                 setFillAndStroke(res, {stroke: "#000"});
565                 res.setBox({x: x - rx, y: y - ry, w: rx * 2, h: ry * 2});
566                 o.attrs.cx = x;
567                 o.attrs.cy = y;
568                 o.attrs.rx = rx;
569                 o.attrs.ry = ry;
570                 res.type = "ellipse";
571                 return res;
572             };
573             var theImage = function (vml, src, x, y, w, h) {
574                 var g = document.createElement("rvml:group");
575                 var o = document.createElement("rvml:image");
576                 o.src = src;
577                 g.appendChild(o);
578                 vml.canvas.appendChild(g);
579                 var res = new Element(o, g, vml);
580                 res.type = "image";
581                 res.setBox({x: x, y: y, w: w, h: h});
582                 o.attrs.x = x;
583                 o.attrs.y = y;
584                 o.attrs.w = w;
585                 o.attrs.h = h;
586                 return res;
587             };
588             var theText = function (vml, x, y, text) {
589                 // @TODO: setTheBox
590                 var g = document.createElement("rvml:group"), gs = g.style;
591                 var el = document.createElement("rvml:shape"), ol = el.style;
592                 var path = document.createElement("rvml:path"), ps = path.style;
593                 path.v = ["m", Math.round(x), ", ", Math.round(y - 2), "l", Math.round(x) + 1, ", ", Math.round(y - 2)].join("");
594                 path.textpathok = true;
595                 ol.width = vml.width;
596                 ol.height = vml.height;
597                 gs.position = "absolute";
598                 gs.left = 0;
599                 gs.top = 0;
600                 gs.width = vml.width;
601                 gs.height = vml.height;
602                 var o = document.createElement("rvml:textpath");
603                 o.string = text;
604                 o.on = true;
605                 o.coordsize = vml.coordsize;
606                 o.coordorigin = vml.coordorigin;
607                 el.appendChild(o);
608                 el.appendChild(path);
609                 g.appendChild(el);
610                 vml.canvas.appendChild(g);
611                 var res = new Element(o, g, vml);
612                 res.shape = el;
613                 res.textpath = path;
614                 res.type = "text";
615                 o.attrs.x = x;
616                 o.attrs.y = y;
617                 o.attrs.w = 1;
618                 o.attrs.h = 1;
619                 return res;
620             };
621             var theGroup = function (vml) {
622                 var el = document.createElement("rvml:group"), els = el.style;
623                 els.position = "absolute";
624                 els.left = 0;
625                 els.top = 0;
626                 els.width = vml.width;
627                 els.height = vml.height;
628                 if (vml.canvas) {
629                     vml.canvas.appendChild(el);
630                 }
631                 var res = new Element(el, el, vml);
632                 for (var f in vml) {
633                     if (f.charAt(0) != "_" && typeof vml[f] == "function") {
634                         res[f] = (function (f) {
635                             return function () {
636                                 var e = vml[f].apply(vml, arguments);
637                                 el.appendChild(e[0].parentNode);
638                                 return e;
639                             };
640                         })(f);
641                     }
642                 }
643                 res.type = "group";
644                 return res;
645             };
646             r._create = function () {
647                 // container, width, height
648                 // x, y, width, height
649                 var container, width, height;
650                 if (typeof arguments[0] == "string") {
651                     container = document.getElementById(arguments[0]);
652                     width = arguments[1];
653                     height = arguments[2];
654                 }
655                 if (typeof arguments[0] == "object") {
656                     container = arguments[0];
657                     width = arguments[1];
658                     height = arguments[2];
659                 }
660                 if (typeof arguments[0] == "number") {
661                     container = 1;
662                     x = arguments[0];
663                     y = arguments[1];
664                     width = arguments[2];
665                     height = arguments[3];
666                 }
667                 if (!container) {
668                     throw new Error("VML container not found.");
669                 }
670                 if (!document.namespaces["rvml"]) {
671                     document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
672                     document.createStyleSheet().addRule("rvml\\:*", "behavior:url(#default#VML)");
673                 }
674                 var c = document.createElement("div"),
675                     r = C.canvas = document.createElement("rvml:group"),
676                     cs = c.style, rs = r.style;
677                 C.width = width;
678                 C.height = height;
679                 width = width || "320px";
680                 height = height || "200px";
681                 cs.clip = "rect(0 " + width + " " + height + " 0)";
682                 cs.position = "absolute";
683                 rs.width  = width;
684                 rs.height = height;
685                 r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " + (height == "100%" ? height : parseFloat(height));
686                 r.coordorigin = "0 0";
687
688                 var b = document.createElement("rvml:rect"), bs = b.style;
689                 bs.left = bs.top = 0;
690                 bs.width  = rs.width;
691                 bs.height = rs.height;
692                 b.filled = b.stroked = "f";
693
694                 r.appendChild(b);
695                 c.appendChild(r);
696                 if (container == 1) {
697                     document.body.appendChild(c);
698                     cs.position = "absolute";
699                     cs.left = x + "px";
700                     cs.top = y + "px";
701                     cs.width = width;
702                     cs.height = height;
703                     container = {
704                         style: {
705                             width: width,
706                             height: height
707                         }
708                     };
709                 } else {
710                     cs.width = container.style.width = width;
711                     cs.height = container.style.height = height;
712                     if (container.firstChild) {
713                         container.insertBefore(c, container.firstChild);
714                     } else {
715                         container.appendChild(c);
716                     }
717                 }
718                 for (var prop in C) {
719                     container[prop] = C[prop];
720                 }
721                 container.clear = function () {
722                     var todel = [];
723                     for (var i = 0, ii = r.childNodes.length; i < ii; i++) {
724                         if (r.childNodes[i] != b) {
725                             todel.push(r.childNodes[i]);
726                         }
727                     }
728                     for (i = 0, ii = todel.length; i < ii; i++) {
729                         r.removeChild(todel[i]);
730                     }
731                 };
732                 return container;
733             };
734         }
735         if (type == "SVG") {
736             Matrix.prototype.toString = function () {
737                 return "matrix(" + this.m[0][0] +
738                     ", " + this.m[1][0] + ", " + this.m[0][1] + ", " + this.m[1][1] +
739                     ", " + this.m[2][0] + ", " + this.m[2][1] + ")";
740             };
741             var thePath = function (params, pathString, SVG) {
742                 var el = document.createElementNS(SVG.svgns, "path");
743                 el.setAttribute("fill", "none");
744                 if (params) {
745                     for (var attr in params) {
746                         if (params.gradient) {
747                             addGrdientFill(el, params.gradient, SVG);
748                         } else {
749                             el.setAttribute(attr, params[attr]);
750                         }
751                     }
752                 }
753                 if (SVG.canvas) {
754                     SVG.canvas.appendChild(el);
755                 }
756                 var p = new Element(el, SVG);
757                 for (var attr in params) {
758                     p.attrs[attr] = params[attr];
759                 }
760                 p.isAbsolute = true;
761                 p.path = [];
762                 p.last = {x: 0, y: 0, bx: 0, by: 0};
763                 p.absolutely = function () {
764                     this.isAbsolute = true;
765                     return this;
766                 };
767                 p.relatively = function () {
768                     this.isAbsolute = false;
769                     return this;
770                 };
771                 p.redraw = function () {
772                     this[0].setAttribute("d", "M0 0");
773                     var oldPath = this.path;
774                     this.path = [];
775                     for (var i = 0, ii = oldPath.length; i < ii; i++) {
776                         if (oldPath[i].type != "end") {
777                             this[oldPath[i].type + "To"].apply(this, oldPath[i].arg);
778                         } else {
779                             this.andClose();
780                         }
781                     };
782                 };
783                 p.moveTo = function (x, y) {
784                     var d = this.isAbsolute?"M":"m";
785                     var _getX = this.isAbsolute ? SVG._getX : SVG._getW;
786                     var _getY = this.isAbsolute ? SVG._getY : SVG._getH;
787                     d += _getX(parseFloat(x, 10)) + " " + _getY(parseFloat(y, 10)) + " ";
788                     var oldD = this[0].getAttribute("d") || "";
789                     this[0].setAttribute("d", oldD + d);
790                     this.last.x = SVG._getX(parseFloat(x, 10));
791                     this.last.y = SVG._getY(parseFloat(y, 10));
792                     this.path.push({type: "move", arg: arguments, pos: this.isAbsolute});
793                     return this;
794                 };
795                 p.lineTo = function (x, y) {
796                     var d = this.isAbsolute?"L":"l";
797                     var _getX = this.isAbsolute ? SVG._getX : SVG._getW;
798                     var _getY = this.isAbsolute ? SVG._getY : SVG._getH;
799                     d += _getX(parseFloat(x, 10)) + " " + _getY(parseFloat(y, 10)) + " ";
800                     var oldD = this[0].getAttribute("d") || "";
801                     this[0].setAttribute("d", oldD + d);
802                     this.last.x = SVG._getX(parseFloat(x, 10));
803                     this.last.y = SVG._getY(parseFloat(y, 10));
804                     this.path.push({type: "line", arg: arguments, pos: this.isAbsolute});
805                     return this;
806                 };
807                 p.cplineTo = function (x1, y1, w1) {
808                     if (!w1) {
809                         return this.lineTo(x1, y1);
810                     } else {
811                         var p = {};
812                         p._getX = this.isAbsolute ? SVG._getX : SVG._getW;
813                         p._getY = this.isAbsolute ? SVG._getY : SVG._getH;
814                         var x = p._getX(Math.round(parseFloat(x1, 10) * 100) / 100);
815                         var y = p._getY(Math.round(parseFloat(y1, 10) * 100) / 100);
816                         var w = SVG._getW(Math.round(parseFloat(w1, 10) * 100) / 100);
817                         var d = this.isAbsolute?"C":"c";
818                         var attr = [this.last.x + w, this.last.y, x - w, y, x, y];
819                         for (var i = 0, ii = attr.length; i < ii; i++) {
820                             d += attr[i] + " ";
821                         }
822                         this.last.x = attr[4];
823                         this.last.y = attr[5];
824                         this.last.bx = attr[2];
825                         this.last.by = attr[3];
826                         var oldD = this[0].getAttribute("d") || "";
827                         this[0].setAttribute("d", oldD + d);
828                         this.path.push({type: "cpline", arg: arguments, pos: this.isAbsolute});
829                         return this;
830                     }
831                 };
832                 p.curveTo = function () {
833                     var p = {};
834                     p._getX = this.isAbsolute ? SVG._getX : SVG._getW;
835                     p._getY = this.isAbsolute ? SVG._getY : SVG._getH;
836                     if (arguments.length == 6) {
837                         var d = this.isAbsolute?"C":"c";
838                         for (var i = 0, ii = arguments.length; i < ii; i++) {
839                             d += p[(i % 2 == 0) ? "_getX" : "_getY"](Math.round(parseFloat(arguments[i], 10) * 100) / 100) + " ";
840                         }
841                         this.last.x = p._getX((parseFloat(arguments[4], 10) * 100) / 100);
842                         this.last.y = p._getY((parseFloat(arguments[5], 10) * 100) / 100);
843                         this.last.bx = p._getX((parseFloat(arguments[2], 10) * 100) / 100);
844                         this.last.by = p._getY((parseFloat(arguments[3], 10) * 100) / 100);
845                     } else {
846                         if (arguments.length == 4) {
847                             var d = this.isAbsolute?"S":"s";
848                             for (var i = 0, ii = arguments.length; i < ii; i++) {
849                                 d += p[i % 2 == 0 ? "_getX" : "_getY"]((parseFloat(arguments[i], 10) * 100) / 100) + " ";
850                             }
851                         }
852                         this.last.x = p._getX((parseFloat(arguments[2], 10) * 100) / 100);
853                         this.last.y = p._getY((parseFloat(arguments[3], 10) * 100) / 100);
854                         this.last.bx = p._getX((parseFloat(arguments[0], 10) * 100) / 100);
855                         this.last.by = p._getY((parseFloat(arguments[1], 10) * 100) / 100);
856                     }
857                     var oldD = this[0].getAttribute("d") || "";
858                     this[0].setAttribute("d", oldD + d);
859                     this.path.push({type: "curve", arg: arguments, pos: this.isAbsolute});
860                     return this;
861                 };
862                 p.addRoundedCorner = function (r, dir) {
863                     var R = .5522 * r, rollback = this.isAbsolute, o = this;
864                     if (rollback) {
865                         this.relatively();
866                         rollback = function () {
867                             o.absolutely();
868                         };
869                     } else {
870                         rollback = function () {};
871                     }
872                     var actions = {
873                         l: function () {
874                             return {
875                                 u: function () {
876                                     o.curveTo(-R, 0, -r, -(r - R), -r, -r);
877                                 },
878                                 d: function () {
879                                     o.curveTo(-R, 0, -r, r - R, -r, r);
880                                 }
881                             };
882                         },
883                         r: function () {
884                             return {
885                                 u: function () {
886                                     o.curveTo(R, 0, r, -(r - R), r, -r);
887                                 },
888                                 d: function () {
889                                     o.curveTo(R, 0, r, r - R, r, r);
890                                 }
891                             };
892                         },
893                         u: function () {
894                             return {
895                                 r: function () {
896                                     o.curveTo(0, -R, -(R - r), -r, r, -r);
897                                 },
898                                 l: function () {
899                                     o.curveTo(0, -R, R - r, -r, -r, -r);
900                                 }
901                             };
902                         },
903                         d: function () {
904                             return {
905                                 r: function () {
906                                     o.curveTo(0, R, -(R - r), r, r, r);
907                                 },
908                                 l: function () {
909                                     o.curveTo(0, R, R - r, r, -r, r);
910                                 }
911                             };
912                         }
913                     };
914                     actions[dir[0]]()[dir[1]]();
915                     rollback();
916                     return o;
917                 };
918                 p.andClose = function () {
919                     var oldD = this[0].getAttribute("d") || "";
920                     this[0].setAttribute("d", oldD + "Z ");
921                     this.path.push({type: "end"});
922                     return this;
923                 };
924                 if (typeof pathString == "string") {
925                     pathString = pathString.replace(/([mzlhvcsqta])/ig, ",$1,").replace(/([^,])\-/ig, "$1,-");
926                     path = pathString.split(",");
927                     var i = 1, ii = path.length;
928                     while (i < ii) {
929                         switch (path[i]) {
930                             case "M":
931                                 p.absolutely().moveTo(path[++i], path[++i]);
932                                 break;
933                             case "m":
934                                 p.relatively().moveTo(path[++i], path[++i]);
935                                 break;
936                             case "C":
937                                 p.absolutely().curveTo(path[++i], path[++i], path[++i], path[++i], path[++i], path[++i]);
938                                 break;
939                             case "c":
940                                 p.relatively().curveTo(path[++i], path[++i], path[++i], path[++i], path[++i], path[++i]);
941                                 break;
942                             case "s":
943                                 p.relatively().curveTo(path[++i], path[++i], path[++i], path[++i]);
944                                 break;
945                             case "S":
946                                 p.absolutely().curveTo(path[++i], path[++i], path[++i], path[++i]);
947                                 break;
948                             case "L":
949                                 p.absolutely().lineTo(path[++i], path[++i]);
950                                 break;
951                             case "l":
952                                 p.relatively().lineTo(path[++i], path[++i]);
953                                 break;
954                             case "H":
955                                 p.absolutely().lineTo(path[++i], 0);
956                                 break;
957                             case "h":
958                                 p.relatively().lineTo(path[++i], 0);
959                                 break;
960                             case "V":
961                                 p.absolutely().lineTo(0, path[++i]);
962                                 break;
963                             case "v":
964                                 p.relatively().lineTo(0, path[++i]);
965                                 break;
966                             case "z":
967                                 p.andClose();
968                                 break;
969                         }
970                         i++;
971                     }
972                 }
973                 return p;
974             };
975             var addGrdientFill = function (o, gradient, SVG) {
976                 var el = document.createElementNS(SVG.svgns, gradient.type + "Gradient");
977                 el.id = "raphael-gradient-" + SVG.gradients++;
978                 if (gradient.vector && gradient.vector.length) {
979                     el.setAttribute("x1", gradient.vector[0]);
980                     el.setAttribute("y1", gradient.vector[1]);
981                     el.setAttribute("x2", gradient.vector[2]);
982                     el.setAttribute("y2", gradient.vector[3]);
983                 }
984                 SVG.defs.appendChild(el);
985                 for (var i = 0, ii = gradient.dots.length; i < ii; i++) {
986                     var stop = document.createElementNS(SVG.svgns, "stop");
987                     stop.setAttribute("offset", gradient.dots[i].offset ? gradient.dots[i].offset : (i == 0) ? "0%" : "100%");
988                     stop.setAttribute("stop-color", gradient.dots[i].color || "#fff");
989                     if (typeof gradient.dots[i].opacity != "undefined") {
990                         stop.setAttribute("stop-opacity", gradient.dots[i].opacity);
991                     }
992                     el.appendChild(stop);
993                 };
994                 o.setAttribute("fill", "url(#" + el.id + ")");
995             };
996             var Element = function (node, svg) {
997                 var X = 0,
998                     Y = 0,
999                     Rotation = {deg: 0, x: 0, y: 0},
1000                     ScaleX = 1,
1001                     ScaleY = 1,
1002                     tMatrix = null;
1003                 this[0] = node;
1004                 this.attrs = this.attrs || {};
1005                 this.transformations = []; // rotate, translate, scale, matrix
1006                 this.hide = function () {
1007                     this[0].style.display = "none";
1008                     return this;
1009                 };
1010                 this.show = function () {
1011                     this[0].style.display = "block";
1012                     return this;
1013                 };
1014                 this.rotate = function (deg) {
1015                     var bbox = this.getBBox();
1016                     Rotation.deg += deg;
1017                     if (Rotation.deg) {
1018                         this.transformations[0] = ("rotate(" + Rotation.deg + " " + (bbox.x + bbox.width / 2) + " " + (bbox.y + bbox.height / 2) + ")");
1019                     } else {
1020                         this.transformations[0] = "";
1021                     }
1022                     this[0].setAttribute("transform", this.transformations.join(" "));
1023                     return this;
1024                 };
1025                 this.translate = function (x, y) {
1026                     X += x;
1027                     Y += y;
1028                     if (X && Y) {
1029                         this.transformations[1] = "translate(" + X + "," + Y + ")";
1030                     } else {
1031                         this.transformations[1] = "";
1032                     }
1033                     this[0].setAttribute("transform", this.transformations.join(" "));
1034                     return this;
1035                 };
1036                 this.scale = function (x, y) {
1037                     y = y || x;
1038                     if (x != 0 && !(x == 1 && y == 1)) {
1039                         ScaleX *= x;
1040                         ScaleY *= y;
1041                         if (!(ScaleX == 1 && ScaleY == 1)) {
1042                             var bbox = this.getBBox(),
1043                                 dx = bbox.x * (1 - ScaleX) + (bbox.width / 2 - bbox.width * ScaleX / 2),
1044                                 dy = bbox.y * (1 - ScaleY) + (bbox.height / 2 - bbox.height * ScaleY / 2);
1045                             this.transformations[2] = new Matrix(ScaleX, 0, 0, ScaleY, dx, dy);
1046                         } else {
1047                             this.transformations[2] = "";
1048                         }
1049                         this[0].setAttribute("transform", this.transformations.join(" "));
1050                     }
1051                     return this;
1052                 };
1053                 // depricated
1054                 this.matrix = function (xx, xy, yx, yy, dx, dy) {
1055                     this.transformations[3] = new Matrix(xx, xy, yx, yy, dx, dy);
1056                     this[0].setAttribute("transform", this.transformations.join(" "));
1057                     return this;
1058                 };
1059                 this.remove = function () {
1060                     this[0].parentNode.removeChild(this[0]);
1061                 };
1062                 this.getBBox = function () {
1063                     return this[0].getBBox();
1064                 };
1065                 this.attr = function () {
1066                     if (arguments.length == 1 && typeof arguments[0] == "string") {
1067                         return this[0].getAttribute(arguments[0]);
1068                     }
1069                     if (arguments.length == 1 && arguments[0] instanceof Array) {
1070                         var values = {};
1071                         for (var j in arguments[0]) {
1072                             values[arguments[0][j]] = this.attrs[arguments[0][j]];
1073                         }
1074                         return values;
1075                     }
1076                     if (arguments.length == 2) {
1077                         var att = arguments[0],
1078                             value = arguments[1];
1079                         this[att] = value;
1080                         this.attrs[att] = value;
1081                         switch (att) {
1082                             case "rx":
1083                             case "cx":
1084                             case "x":
1085                                 this[0].setAttribute(att, svg._getX(value));
1086                                 break;
1087                             case "ry":
1088                             case "cy":
1089                             case "y":
1090                                 this[0].setAttribute(att, svg._getY(value));
1091                                 break;
1092                             case "width":
1093                                 this[0].setAttribute(att, svg._getW(value));
1094                                 break;
1095                             case "height":
1096                                 this[0].setAttribute(att, svg._getH(value));
1097                                 break;
1098                             case "gradient":
1099                                 addGrdientFill(this[0], params.gradient, svg);
1100                                 break;
1101                             case "stroke-dasharray":
1102                                 this[0].setAttribute(att, value.replace(" ", ","));
1103                                 break;
1104                             case "text":
1105                                 if (this.type == "text") {
1106                                     this[0].removeChild(this[0].firstChild);
1107                                     this[0].appendChild(document.createTextNode(value));
1108                                 }
1109                                 break;
1110                             default :
1111                                 var cssrule = att.replace(/(\-.)/g, function (w) {
1112                                     return w.substring(1).toUpperCase();
1113                                 });
1114                                 this[0].style[cssrule] = value;
1115                                 // Need following line for Firefox
1116                                 this[0].setAttribute(att, value);
1117                                 break;
1118                         }
1119                     } else if (arguments.length == 1 && typeof arguments[0] == "object") {
1120                         var params = arguments[0];
1121                         for (var attr in params) {
1122                             this.attrs[attr] = params[attr];
1123                             if (attr == "stroke-dasharray") {
1124                                 this[0].setAttribute(attr, params[attr].replace(" ", ","));
1125                             } else if (attr == "text" && this.type == "text") {
1126                                 this[0].removeChild(this[0].firstChild);
1127                                 this[0].appendChild(document.createTextNode(params[attr]));
1128                             } else {
1129                                 var cssrule = attr.replace(/(\-.)/g, function (w) {
1130                                     return w.substring(1).toUpperCase();
1131                                 });
1132                                 this[0].style[cssrule] = params[attr];
1133                                 // Need following line for Firefox
1134                                 this[0].setAttribute(attr, params[attr]);
1135                             }
1136                         }
1137                         if (params.gradient) {
1138                             this[0].attrs.gradient = params.gradient;
1139                             addGrdientFill(this[0], params.gradient, svg);
1140                         }
1141                     }
1142                     return this;
1143                 };
1144                 this.toFront = function () {
1145                     this[0].parentNode.appendChild(this[0]);
1146                 };
1147                 this.toBack = function () {
1148                     if (this[0].parentNode.firstChild != this[0]) {
1149                         this[0].parentNode.insertBefore(this[0], this[0].parentNode.firstChild);
1150                     }
1151                 };
1152             };
1153             var theCircle = function (svg, x, y, r) {
1154                 var el = document.createElementNS(svg.svgns, "circle");
1155                 el.setAttribute("cx", svg._getX(x));
1156                 el.setAttribute("cy", svg._getY(y));
1157                 el.setAttribute("r", r);
1158                 el.setAttribute("fill", "none");
1159                 el.setAttribute("stroke", "#000");
1160                 el.attrs = el.attrs || {};
1161                 el.attrs.cx = x;
1162                 el.attrs.cy = y;
1163                 el.attrs.r = r;
1164                 el.attrs.stroke = "#000";
1165                 if (svg.canvas) {
1166                     svg.canvas.appendChild(el);
1167                 }
1168                 var res = new Element(el, svg);
1169                 res.type = "circle";
1170                 return res;
1171             };
1172             var theRect = function (svg, x, y, w, h, r) {
1173                 var el = document.createElementNS(svg.svgns, "rect");
1174                 el.setAttribute("x", svg._getX(x));
1175                 el.setAttribute("y", svg._getY(y));
1176                 el.setAttribute("width", svg._getW(w));
1177                 el.setAttribute("height", svg._getH(h));
1178                 el.attrs = el.attrs || {};
1179                 el.attrs.x = x;
1180                 el.attrs.y = y;
1181                 el.attrs.width = w;
1182                 el.attrs.height = h;
1183                 if (r) {
1184                     el.setAttribute("rx", r);
1185                     el.setAttribute("ry", r);
1186                     el.attrs.rx = el.attrs.ry = r;
1187                 }
1188                 el.setAttribute("fill", "none");
1189                 el.setAttribute("stroke", "#000");
1190                 el.attrs.stroke = "#000";
1191                 if (svg.canvas) {
1192                     svg.canvas.appendChild(el);
1193                 }
1194                 var res = new Element(el, svg);
1195                 res.type = "rect";
1196                 return res;
1197             };
1198             var theEllipse = function (svg, x, y, rx, ry) {
1199                 var el = document.createElementNS(svg.svgns, "ellipse");
1200                 el.setAttribute("cx", svg._getX(x));
1201                 el.setAttribute("cy", svg._getY(y));
1202                 el.setAttribute("rx", svg._getW(rx));
1203                 el.setAttribute("ry", svg._getH(ry));
1204                 el.setAttribute("fill", "none");
1205                 el.setAttribute("stroke", "#000");
1206                 el.attrs = el.attrs || {};
1207                 el.attrs.cx = x;
1208                 el.attrs.cy = y;
1209                 el.attrs.rx = rx;
1210                 el.attrs.ry = ry;
1211                 el.attrs.stroke = "#000";
1212                 if (svg.canvas) {
1213                     svg.canvas.appendChild(el);
1214                 }
1215                 var res = new Element(el, svg);
1216                 res.type = "ellipse";
1217                 return res;
1218             };
1219             var theImage = function (svg, src, x, y, w, h) {
1220                 var el = document.createElementNS(svg.svgns, "image");
1221                 el.setAttribute("x", svg._getX(x));
1222                 el.setAttribute("y", svg._getY(y));
1223                 el.setAttribute("width", svg._getW(w));
1224                 el.setAttribute("height", svg._getH(h));
1225                 el.setAttributeNS(svg.xlink, "href", src);
1226                 if (svg.canvas) {
1227                     svg.canvas.appendChild(el);
1228                 }
1229                 var res = new Element(el, svg);
1230                 res.type = "image";
1231                 return res;
1232             };
1233             var theText = function (svg, x, y, text) {
1234                 var el = document.createElementNS(svg.svgns, "text");
1235                 el.setAttribute("x", x);
1236                 el.setAttribute("y", y);
1237                 el.setAttribute("text-anchor", "middle");
1238                 el.setAttribute("fill", "#000");
1239                 el.attrs = el.attrs || {};
1240                 el.attrs.x = x;
1241                 el.attrs.y = y;
1242                 el.attrs.fill = "#000";
1243                 if (text) {
1244                     el.appendChild(document.createTextNode(text));
1245                 }
1246                 if (svg.canvas) {
1247                     svg.canvas.appendChild(el);
1248                 }
1249                 var res = new Element(el, svg);
1250                 res.type = "text";
1251                 return res;
1252             };
1253             var theGroup = function (svg) {
1254                 var el = document.createElementNS(svg.svgns, "g");
1255                 if (svg.canvas) {
1256                     svg.canvas.appendChild(el);
1257                 }
1258                 var i = new Element(el, svg);
1259                 for (var f in svg) {
1260                     if (f[0] != "_" && typeof svg[f] == "function") {
1261                         i[f] = (function (f) {
1262                             return function () {
1263                                 var e = svg[f].apply(svg, arguments);
1264                                 el.appendChild(e[0]);
1265                                 return e;
1266                             };
1267                         })(f);
1268                     }
1269                 }
1270                 i.type = "group";
1271                 return i;
1272             };
1273             r._create = function () {
1274                 // container, width, height
1275                 // x, y, width, height
1276                 if (typeof arguments[0] == "string") {
1277                     var container = document.getElementById(arguments[0]);
1278                     var width = arguments[1];
1279                     var height = arguments[2];
1280                 }
1281                 if (typeof arguments[0] == "object") {
1282                     var container = arguments[0];
1283                     var width = arguments[1];
1284                     var height = arguments[2];
1285                 }
1286                 if (typeof arguments[0] == "number") {
1287                     var container = 1,
1288                         x = arguments[0],
1289                         y = arguments[1],
1290                         width = arguments[2],
1291                         height = arguments[3];
1292                 }
1293                 if (!container) {
1294                     throw new Error("SVG container not found.");
1295                 }
1296                 C.canvas = document.createElementNS(C.svgns, "svg");
1297                 C.canvas.setAttribute("width", width || 320);
1298                 C.width = width || 320;
1299                 C.canvas.setAttribute("height", height || 200);
1300                 C.height = height || 200;
1301                 if (container == 1) {
1302                     document.body.appendChild(C.canvas);
1303                     C.canvas.style.position = "absolute";
1304                     C.canvas.style.left = x + "px";
1305                     C.canvas.style.top = y + "px";
1306                 } else {
1307                     if (container.firstChild) {
1308                         container.insertBefore(C.canvas, container.firstChild);
1309                     } else {
1310                         container.appendChild(C.canvas);
1311                     }
1312                 }
1313                 container = {
1314                     canvas: C.canvas,
1315                     clear: function () {
1316                         while (this.canvas.firstChild) {
1317                             this.canvas.removeChild(this.canvas.firstChild);
1318                         }
1319                         this.defs = document.createElementNS(C.svgns, "defs");
1320                         this.gradients = 0;
1321                         this.canvas.appendChild(this.defs);
1322                     }
1323                 };
1324                 for (var prop in C) {
1325                     if (prop != "create") {
1326                         container[prop] = C[prop];
1327                     }
1328                 }
1329                 container.clear();
1330                 return container;
1331             };
1332             C.svgns = "http://www.w3.org/2000/svg";
1333             C.xlink = "http://www.w3.org/1999/xlink";
1334         }
1335         if (type == "VML" || type == "SVG") {
1336             C.circle = function (x, y, r) {
1337                 return theCircle(this, x, y, r);
1338             };
1339             C.rect = function (x, y, w, h, r) {
1340                 return theRect(this, x, y, w, h, r);
1341             };
1342             C.ellipse = function (x, y, rx, ry) {
1343                 return theEllipse(this, x, y, rx, ry);
1344             };
1345             C.path = function (params, pathString) {
1346                 return thePath(params, pathString, this);
1347             };
1348             C.image = function (src, x, y, w, h) {
1349                 return theImage(this, src, x, y, w, h);
1350             };
1351             C.text = function (x, y, text) {
1352                 return theText(this, x, y, text);
1353             };
1354             C.group = function () {
1355                 return theGroup(this);
1356             };
1357             C.linerect = function (x, y, w, h, r) {
1358                 if (r && parseInt(r, 10)) {
1359                     return this.path({stroke: "#000"}).moveTo(x + r, y).lineTo(x + w - r, y).addRoundedCorner(r, "rd").lineTo(x + w, y + h - r).addRoundedCorner(r, "dl").lineTo(x + r, y + h).addRoundedCorner(r, "lu").lineTo(x, y + r).addRoundedCorner(r, "ur").andClose();
1360                 }
1361                 return this.path({stroke: "#000"}).moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).andClose();
1362             };
1363             C.drawGrid = function (x, y, w, h, wv, hv, color) {
1364                 color = color || "#000";
1365                 var p = this.path({stroke: color, "stroke-width": 1})
1366                         .moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).lineTo(x, y);
1367                 for (var i = 1; i < hv; i++) {
1368                     p.moveTo(x, y + i * Math.round(h / hv)).lineTo(x + w, y + i * Math.round(h / hv));
1369                 }
1370                 for (var i = 1; i < wv; i++) {
1371                     p.moveTo(x + i * Math.round(w / wv), y).lineTo(x + i * Math.round(w / wv), y + h);
1372                 }
1373                 return p;
1374             };
1375             C.setGrid = function (xmin, ymin, xmax, ymax, w, h) {
1376                 var xc = (xmax - xmin) / w;
1377                 var yc = (ymax - ymin) / h;
1378                 this._getX = function (x) {
1379                     return xmin + x * xc;
1380                 };
1381                 this._getY = function (y) {
1382                     return ymin + y * yc;
1383                 };
1384                 this._getW = function (w) {
1385                     return w * xc;
1386                 };
1387                 this._getH = function (h) {
1388                     return h * yc;
1389                 };
1390             };
1391             C.clearGrid = function () {
1392                 this._getX = this._getY = this._getW = this._getH = function (x) { return x; };
1393             };
1394             C.safari = function () {
1395                 if (r.type == "SVG") {
1396                     var rect = C.rect(-C.width, -C.height, C.width * 3, C.height * 3).attr({stroke: "none"});
1397                     setTimeout(function () {rect.remove();}, 0);
1398                 }
1399             };
1400             
1401             return r;
1402         } else {
1403             return function () {};
1404         }
1405     })((!(window.SVGPreserveAspectRatio && window.SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN == 2)) ? "VML" : "SVG");
1406
1407
1408 Raphael.vml = !(Raphael.svg = (Raphael.type == "SVG"));
1409 if (Raphael.vml && window.CanvasRenderingContext2D) {
1410     Raphael.type = "Canvas only";
1411     Raphael.vml = Raphael.svg = false;
1412 }
1413 Raphael.toString = function () {
1414     return "Your browser supports " + this.type;
1415 };
1416 Raphael.hsb2rgb = function (hue, saturation, brightness) {
1417     if (typeof hue == "object" && "h" in hue && "s" in hue && "b" in hue) {
1418         brightness = hue.b;
1419         saturation = hue.s;
1420         hue = hue.h;
1421     }
1422     var red,
1423         green,
1424         blue;
1425     if (brightness == 0) {
1426         return {r: 0, g: 0, b: 0, hex: "#000"};
1427     } else {
1428         var i = Math.floor(hue * 6),
1429             f = (hue * 6) - i,
1430             p = brightness * (1 - saturation),
1431             q = brightness * (1 - (saturation * f)),
1432             t = brightness * (1 - (saturation * (1 - f)));
1433         [
1434             function () {red = brightness; green = t; blue = p;},
1435             function () {red = q; green = brightness; blue = p;},
1436             function () {red = p; green = brightness; blue = t;},
1437             function () {red = p; green = q; blue = brightness;},
1438             function () {red = t; green = p; blue = brightness;},
1439             function () {red = brightness; green = p; blue = q;},
1440             function () {red = brightness; green = t; blue = p;},
1441         ][i]();
1442     }
1443     var rgb = {r: red, g: green, b: blue};
1444     red *= 255;
1445     green *= 255;
1446     blue *= 255;
1447     var r = Math.round(red).toString(16);
1448     if (r.length == 1) {
1449         r = "0" + r;
1450     }
1451     var g = Math.round(green).toString(16);
1452     if (g.length == 1) {
1453         g = "0" + g;
1454     }
1455     var b = Math.round(blue).toString(16);
1456     if (b.length == 1) {
1457         b = "0" + b;
1458     }
1459     rgb.hex = "#" + r + g + b;
1460     return rgb;
1461 };
1462 Raphael.rgb2hsb = function (red, green, blue) {
1463     if (typeof red == "object" && "r" in red && "h" in red && "b" in red) {
1464         blue = red.b;
1465         green = red.g;
1466         red = red.r;
1467     }
1468     if (red.charAt(0) == "#") {
1469         if (red.length == 4) {
1470             blue = parseInt(red.substring(3), 16);
1471             green = parseInt(red.substring(2, 3), 16);
1472             red = parseInt(red.substring(1, 2), 16);
1473         } else {
1474             blue = parseInt(red.substring(5), 16);
1475             green = parseInt(red.substring(3, 5), 16);
1476             red = parseInt(red.substring(1, 3), 16);
1477         }
1478     }
1479     if (red > 1 || green > 1 || blue > 1) {
1480         red /= 255;
1481         green /= 255;
1482         blue /= 255;
1483     }
1484     var max = Math.max(red, green, blue),
1485         min = Math.min(red, green, blue),
1486         hue,
1487         saturation,
1488         brightness = max;
1489     if (min == max) {
1490         return {h: 0, s: 0, b: max};
1491     } else {
1492         var delta = (max - min);
1493         saturation = delta / max;
1494         if (red == max) {
1495             hue = (green - blue) / delta;
1496         } else if (green == max) {
1497             hue = 2 + ((blue - red) / delta);
1498         } else {
1499             hue = 4 + ((red - green) / delta);
1500         }
1501         hue /= 6;
1502         if (hue < 0) {
1503             hue += 1;
1504         }
1505         if (hue > 1) {
1506             hue -= 1;
1507         }
1508     }
1509     return {h: hue, s: saturation, b: brightness};
1510 };
1511 Raphael.getColor = function (value) {
1512     var start = arguments.callee.start = arguments.callee.start || {h: 0, s: 1, b: value || .75};
1513     var rgb = this.hsb2rgb(start.h, start.s, start.b);
1514     start.h += .1;
1515     if (start.h > 1) {
1516         start.h = 0;
1517         start.s -= .2;
1518         if (start.s <= 0) {
1519             start = {h: 0, s: 1, b: start.b};
1520         }
1521     }
1522     return rgb.hex;
1523 };
1524 Raphael.getColor.reset = function () {
1525     this.start = undefined;
1526 };