Fixed the bug with the image in IE.
[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                 if (typeof arguments[0] == "string") {
650                     var container = document.getElementById(arguments[0]);
651                     var width = arguments[1];
652                     var height = arguments[2];
653                 }
654                 if (typeof arguments[0] == "object") {
655                     var container = arguments[0];
656                     var width = arguments[1];
657                     var height = arguments[2];
658                 }
659                 if (typeof arguments[0] == "number") {
660                     var container = 1,
661                         x = arguments[0],
662                         y = arguments[1],
663                         width = arguments[2],
664                         height = arguments[3];
665                 }
666                 if (!container) {
667                     throw new Error("VML container not found.");
668                 }
669                 if (!document.namespaces["rvml"]) {
670                     document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
671                     document.createStyleSheet().addRule("rvml\\:*", "behavior:url(#default#VML)");
672                 }
673                 var c = document.createElement("div"),
674                     r = C.canvas = document.createElement("rvml:group"),
675                     cs = c.style, rs = r.style;
676                 C.width = width;
677                 C.height = height;
678                 width = width || "320px";
679                 height = height || "200px";
680                 cs.clip = "rect(0 " + width + " " + height + " 0)";
681                 cs.position = "absolute";
682                 rs.width  = width;
683                 rs.height = height;
684                 r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " + (height == "100%" ? height : parseFloat(height));
685                 r.coordorigin = "0 0";
686
687                 var b = document.createElement("rvml:rect"), bs = b.style;
688                 bs.left = bs.top = 0;
689                 bs.width  = rs.width;
690                 bs.height = rs.height;
691                 b.filled = b.stroked = "f";
692
693                 r.appendChild(b);
694                 c.appendChild(r);
695                 if (container == 1) {
696                     document.body.appendChild(c);
697                     cs.position = "absolute";
698                     cs.left = x + "px";
699                     cs.top = y + "px";
700                     cs.width = width;
701                     cs.height = height;
702                     container = {
703                         style: {
704                             width: width,
705                             height: height
706                         }
707                     };
708                 } else {
709                     cs.width = container.style.width = width;
710                     cs.height = container.style.height = height;
711                     if (container.firstChild) {
712                         container.insertBefore(c, container.firstChild);
713                     } else {
714                         container.appendChild(c);
715                     }
716                 }
717                 for (var prop in C) {
718                     container[prop] = C[prop];
719                 }
720                 container.clear = function () {
721                     var todel = [];
722                     for (var i = 0, ii = r.childNodes.length; i < ii; i++) {
723                         if (r.childNodes[i] != b) {
724                             todel.push(r.childNodes[i]);
725                         }
726                     }
727                     for (i = 0, ii = todel.length; i < ii; i++) {
728                         r.removeChild(todel[i]);
729                     }
730                 };
731                 return container;
732             };
733         }
734         if (type == "SVG") {
735             Matrix.prototype.toString = function () {
736                 return "matrix(" + this.m[0][0] +
737                     ", " + this.m[1][0] + ", " + this.m[0][1] + ", " + this.m[1][1] +
738                     ", " + this.m[2][0] + ", " + this.m[2][1] + ")";
739             };
740             var thePath = function (params, pathString, SVG) {
741                 var el = document.createElementNS(SVG.svgns, "path");
742                 el.setAttribute("fill", "none");
743                 if (params) {
744                     for (var attr in params) {
745                         if (params.gradient) {
746                             addGrdientFill(el, params.gradient, SVG);
747                         } else {
748                             el.setAttribute(attr, params[attr]);
749                         }
750                     }
751                 }
752                 if (SVG.canvas) {
753                     SVG.canvas.appendChild(el);
754                 }
755                 var p = new Element(el, SVG);
756                 for (var attr in params) {
757                     p.attrs[attr] = params[attr];
758                 }
759                 p.isAbsolute = true;
760                 p.path = [];
761                 p.last = {x: 0, y: 0, bx: 0, by: 0};
762                 p.absolutely = function () {
763                     this.isAbsolute = true;
764                     return this;
765                 };
766                 p.relatively = function () {
767                     this.isAbsolute = false;
768                     return this;
769                 };
770                 p.redraw = function () {
771                     this[0].setAttribute("d", "M0 0");
772                     var oldPath = this.path;
773                     this.path = [];
774                     for (var i = 0, ii = oldPath.length; i < ii; i++) {
775                         if (oldPath[i].type != "end") {
776                             this[oldPath[i].type + "To"].apply(this, oldPath[i].arg);
777                         } else {
778                             this.andClose();
779                         }
780                     };
781                 };
782                 p.moveTo = function (x, y) {
783                     var d = this.isAbsolute?"M":"m";
784                     var _getX = this.isAbsolute ? SVG._getX : SVG._getW;
785                     var _getY = this.isAbsolute ? SVG._getY : SVG._getH;
786                     d += _getX(parseFloat(x, 10)) + " " + _getY(parseFloat(y, 10)) + " ";
787                     var oldD = this[0].getAttribute("d") || "";
788                     this[0].setAttribute("d", oldD + d);
789                     this.last.x = SVG._getX(parseFloat(x, 10));
790                     this.last.y = SVG._getY(parseFloat(y, 10));
791                     this.path.push({type: "move", arg: arguments, pos: this.isAbsolute});
792                     return this;
793                 };
794                 p.lineTo = function (x, y) {
795                     var d = this.isAbsolute?"L":"l";
796                     var _getX = this.isAbsolute ? SVG._getX : SVG._getW;
797                     var _getY = this.isAbsolute ? SVG._getY : SVG._getH;
798                     d += _getX(parseFloat(x, 10)) + " " + _getY(parseFloat(y, 10)) + " ";
799                     var oldD = this[0].getAttribute("d") || "";
800                     this[0].setAttribute("d", oldD + d);
801                     this.last.x = SVG._getX(parseFloat(x, 10));
802                     this.last.y = SVG._getY(parseFloat(y, 10));
803                     this.path.push({type: "line", arg: arguments, pos: this.isAbsolute});
804                     return this;
805                 };
806                 p.cplineTo = function (x1, y1, w1) {
807                     if (!w1) {
808                         return this.lineTo(x1, y1);
809                     } else {
810                         var p = {};
811                         p._getX = this.isAbsolute ? SVG._getX : SVG._getW;
812                         p._getY = this.isAbsolute ? SVG._getY : SVG._getH;
813                         var x = p._getX(Math.round(parseFloat(x1, 10) * 100) / 100);
814                         var y = p._getY(Math.round(parseFloat(y1, 10) * 100) / 100);
815                         var w = SVG._getW(Math.round(parseFloat(w1, 10) * 100) / 100);
816                         var d = this.isAbsolute?"C":"c";
817                         var attr = [this.last.x + w, this.last.y, x - w, y, x, y];
818                         for (var i = 0, ii = attr.length; i < ii; i++) {
819                             d += attr[i] + " ";
820                         }
821                         this.last.x = attr[4];
822                         this.last.y = attr[5];
823                         this.last.bx = attr[2];
824                         this.last.by = attr[3];
825                         var oldD = this[0].getAttribute("d") || "";
826                         this[0].setAttribute("d", oldD + d);
827                         this.path.push({type: "cpline", arg: arguments, pos: this.isAbsolute});
828                         return this;
829                     }
830                 };
831                 p.curveTo = function () {
832                     var p = {};
833                     p._getX = this.isAbsolute ? SVG._getX : SVG._getW;
834                     p._getY = this.isAbsolute ? SVG._getY : SVG._getH;
835                     if (arguments.length == 6) {
836                         var d = this.isAbsolute?"C":"c";
837                         for (var i = 0, ii = arguments.length; i < ii; i++) {
838                             d += p[(i % 2 == 0) ? "_getX" : "_getY"](Math.round(parseFloat(arguments[i], 10) * 100) / 100) + " ";
839                         }
840                         this.last.x = p._getX((parseFloat(arguments[4], 10) * 100) / 100);
841                         this.last.y = p._getY((parseFloat(arguments[5], 10) * 100) / 100);
842                         this.last.bx = p._getX((parseFloat(arguments[2], 10) * 100) / 100);
843                         this.last.by = p._getY((parseFloat(arguments[3], 10) * 100) / 100);
844                     } else {
845                         if (arguments.length == 4) {
846                             var d = this.isAbsolute?"S":"s";
847                             for (var i = 0, ii = arguments.length; i < ii; i++) {
848                                 d += p[i % 2 == 0 ? "_getX" : "_getY"]((parseFloat(arguments[i], 10) * 100) / 100) + " ";
849                             }
850                         }
851                         this.last.x = p._getX((parseFloat(arguments[2], 10) * 100) / 100);
852                         this.last.y = p._getY((parseFloat(arguments[3], 10) * 100) / 100);
853                         this.last.bx = p._getX((parseFloat(arguments[0], 10) * 100) / 100);
854                         this.last.by = p._getY((parseFloat(arguments[1], 10) * 100) / 100);
855                     }
856                     var oldD = this[0].getAttribute("d") || "";
857                     this[0].setAttribute("d", oldD + d);
858                     this.path.push({type: "curve", arg: arguments, pos: this.isAbsolute});
859                     return this;
860                 };
861                 p.addRoundedCorner = function (r, dir) {
862                     var R = .5522 * r, rollback = this.isAbsolute, o = this;
863                     if (rollback) {
864                         this.relatively();
865                         rollback = function () {
866                             o.absolutely();
867                         };
868                     } else {
869                         rollback = function () {};
870                     }
871                     var actions = {
872                         l: function () {
873                             return {
874                                 u: function () {
875                                     o.curveTo(-R, 0, -r, -(r - R), -r, -r);
876                                 },
877                                 d: function () {
878                                     o.curveTo(-R, 0, -r, r - R, -r, r);
879                                 }
880                             };
881                         },
882                         r: function () {
883                             return {
884                                 u: function () {
885                                     o.curveTo(R, 0, r, -(r - R), r, -r);
886                                 },
887                                 d: function () {
888                                     o.curveTo(R, 0, r, r - R, r, r);
889                                 }
890                             };
891                         },
892                         u: function () {
893                             return {
894                                 r: function () {
895                                     o.curveTo(0, -R, -(R - r), -r, r, -r);
896                                 },
897                                 l: function () {
898                                     o.curveTo(0, -R, R - r, -r, -r, -r);
899                                 }
900                             };
901                         },
902                         d: function () {
903                             return {
904                                 r: function () {
905                                     o.curveTo(0, R, -(R - r), r, r, r);
906                                 },
907                                 l: function () {
908                                     o.curveTo(0, R, R - r, r, -r, r);
909                                 }
910                             };
911                         }
912                     };
913                     actions[dir[0]]()[dir[1]]();
914                     rollback();
915                     return o;
916                 };
917                 p.andClose = function () {
918                     var oldD = this[0].getAttribute("d") || "";
919                     this[0].setAttribute("d", oldD + "Z ");
920                     this.path.push({type: "end"});
921                     return this;
922                 };
923                 if (typeof pathString == "string") {
924                     pathString = pathString.replace(/([mzlhvcsqta])/ig, ",$1,").replace(/([^,])\-/ig, "$1,-");
925                     path = pathString.split(",");
926                     var i = 1, ii = path.length;
927                     while (i < ii) {
928                         switch (path[i]) {
929                             case "M":
930                                 p.absolutely().moveTo(path[++i], path[++i]);
931                                 break;
932                             case "m":
933                                 p.relatively().moveTo(path[++i], path[++i]);
934                                 break;
935                             case "C":
936                                 p.absolutely().curveTo(path[++i], path[++i], path[++i], path[++i], path[++i], path[++i]);
937                                 break;
938                             case "c":
939                                 p.relatively().curveTo(path[++i], path[++i], path[++i], path[++i], path[++i], path[++i]);
940                                 break;
941                             case "s":
942                                 p.relatively().curveTo(path[++i], path[++i], path[++i], path[++i]);
943                                 break;
944                             case "S":
945                                 p.absolutely().curveTo(path[++i], path[++i], path[++i], path[++i]);
946                                 break;
947                             case "L":
948                                 p.absolutely().lineTo(path[++i], path[++i]);
949                                 break;
950                             case "l":
951                                 p.relatively().lineTo(path[++i], path[++i]);
952                                 break;
953                             case "H":
954                                 p.absolutely().lineTo(path[++i], 0);
955                                 break;
956                             case "h":
957                                 p.relatively().lineTo(path[++i], 0);
958                                 break;
959                             case "V":
960                                 p.absolutely().lineTo(0, path[++i]);
961                                 break;
962                             case "v":
963                                 p.relatively().lineTo(0, path[++i]);
964                                 break;
965                             case "z":
966                                 p.andClose();
967                                 break;
968                         }
969                         i++;
970                     }
971                 }
972                 return p;
973             };
974             var addGrdientFill = function (o, gradient, SVG) {
975                 var el = document.createElementNS(SVG.svgns, gradient.type + "Gradient");
976                 el.id = "raphael-gradient-" + SVG.gradients++;
977                 if (gradient.vector && gradient.vector.length) {
978                     el.setAttribute("x1", gradient.vector[0]);
979                     el.setAttribute("y1", gradient.vector[1]);
980                     el.setAttribute("x2", gradient.vector[2]);
981                     el.setAttribute("y2", gradient.vector[3]);
982                 }
983                 SVG.defs.appendChild(el);
984                 for (var i = 0, ii = gradient.dots.length; i < ii; i++) {
985                     var stop = document.createElementNS(SVG.svgns, "stop");
986                     stop.setAttribute("offset", gradient.dots[i].offset ? gradient.dots[i].offset : (i == 0) ? "0%" : "100%");
987                     stop.setAttribute("stop-color", gradient.dots[i].color || "#fff");
988                     if (typeof gradient.dots[i].opacity != "undefined") {
989                         stop.setAttribute("stop-opacity", gradient.dots[i].opacity);
990                     }
991                     el.appendChild(stop);
992                 };
993                 o.setAttribute("fill", "url(#" + el.id + ")");
994             };
995             var Element = function (node, svg) {
996                 var X = 0,
997                     Y = 0,
998                     Rotation = {deg: 0, x: 0, y: 0},
999                     Scale = 1,
1000                     tMatrix = null;
1001                 this[0] = node;
1002                 this.attrs = this.attrs || {};
1003                 this.transformations = [];
1004                 this.hide = function () {
1005                     this[0].style.display = "none";
1006                     return this;
1007                 };
1008                 this.show = function () {
1009                     this[0].style.display = "block";
1010                     return this;
1011                 };
1012                 this.rotate = function (deg) {
1013                     var bbox = this.getBBox();
1014                     this.transformations.push("rotate(" + deg + " " + (bbox.x + bbox.width / 2) + " " + (bbox.y + bbox.height / 2) + ")");
1015                     this[0].setAttribute("transform", this.transformations.join(" "));
1016                     return this;
1017                 };
1018                 this.translate = function (x, y) {
1019                     this.transformations.push("translate(" + x + "," + y + ")");
1020                     this[0].setAttribute("transform", this.transformations.join(" "));
1021                     return this;
1022                 };
1023                 this.scale = function (x, y) {
1024                     y = y || x;
1025                     if (x != 0 && !(x == 1 && y == 1)) {
1026                         var bbox = this.getBBox(),
1027                             dx = bbox.x * (1 - x) + (bbox.width / 2 - bbox.width * x / 2),
1028                             dy = bbox.y * (1 - y) + (bbox.height / 2 - bbox.height * y / 2);
1029                         this.transformations.push(new Matrix(x, 0, 0, y, dx, dy));
1030                         this[0].setAttribute("transform", this.transformations.join(" "));
1031                     }
1032                     return this;
1033                 };
1034                 this.matrix = function (xx, xy, yx, yy, dx, dy) {
1035                     this.transformations.push(new Matrix(xx, xy, yx, yy, dx, dy));
1036                     this[0].setAttribute("transform", this.transformations.join(" "));
1037                     return this;
1038                 };
1039                 this.remove = function () {
1040                     this[0].parentNode.removeChild(this[0]);
1041                 };
1042                 this.getBBox = function () {
1043                     return this[0].getBBox();
1044                 };
1045                 this.attr = function () {
1046                     if (arguments.length == 1 && typeof arguments[0] == "string") {
1047                         return this[0].getAttribute(arguments[0]);
1048                     }
1049                     if (arguments.length == 1 && arguments[0] instanceof Array) {
1050                         var values = {};
1051                         for (var j in arguments[0]) {
1052                             values[arguments[0][j]] = this.attrs[arguments[0][j]];
1053                         }
1054                         return values;
1055                     }
1056                     if (arguments.length == 2) {
1057                         var att = arguments[0],
1058                             value = arguments[1];
1059                         this[att] = value;
1060                         this.attrs[att] = value;
1061                         switch (att) {
1062                             case "rx":
1063                             case "cx":
1064                             case "x":
1065                                 this[0].setAttribute(att, svg._getX(value));
1066                                 break;
1067                             case "ry":
1068                             case "cy":
1069                             case "y":
1070                                 this[0].setAttribute(att, svg._getY(value));
1071                                 break;
1072                             case "width":
1073                                 this[0].setAttribute(att, svg._getW(value));
1074                                 break;
1075                             case "height":
1076                                 this[0].setAttribute(att, svg._getH(value));
1077                                 break;
1078                             case "gradient":
1079                                 addGrdientFill(this[0], params.gradient, svg);
1080                                 break;
1081                             case "stroke-dasharray":
1082                                 this[0].setAttribute(att, value.replace(" ", ","));
1083                                 break;
1084                             case "text":
1085                                 if (this.type == "text") {
1086                                     this[0].removeChild(this[0].firstChild);
1087                                     this[0].appendChild(document.createTextNode(value));
1088                                 }
1089                                 break;
1090                             default :
1091                                 var cssrule = att.replace(/(\-.)/g, function (w) {
1092                                     return w.substring(1).toUpperCase();
1093                                 });
1094                                 this[0].style[cssrule] = value;
1095                                 // Need following line for Firefox
1096                                 this[0].setAttribute(att, value);
1097                         }
1098                     } else if (arguments.length = 1 && typeof arguments[0] == "object") {
1099                         var params = arguments[0];
1100                         for (var attr in params) {
1101                             this.attrs[attr] = params[attr];
1102                             if (attr == "stroke-dasharray") {
1103                                 this[0].setAttribute(attr, params[attr].replace(" ", ","));
1104                             } else if (attr == "text" && this.type == "text") {
1105                                 this[0].removeChild(this[0].firstChild);
1106                                 this[0].appendChild(document.createTextNode(params[attr]));
1107                             } else {
1108                                 var cssrule = attr.replace(/(\-.)/g, function (w) {
1109                                     return w.substring(1).toUpperCase();
1110                                 });
1111                                 this[0].style[cssrule] = params[attr];
1112                                 // Need following line for Firefox
1113                                 this[0].setAttribute(attr, params[attr]);
1114                             }
1115                         }
1116                         if (params.gradient) {
1117                             this[0].attrs.gradient = params.gradient;
1118                             addGrdientFill(this[0], params.gradient, svg);
1119                         }
1120                     }
1121                     return this;
1122                 };
1123                 this.toFront = function () {
1124                     this[0].parentNode.appendChild(this[0]);
1125                 };
1126                 this.toBack = function () {
1127                     if (this[0].parentNode.firstChild != this[0]) {
1128                         this[0].parentNode.insertBefore(this[0], this[0].parentNode.firstChild);
1129                     }
1130                 };
1131             };
1132             var theCircle = function (svg, x, y, r) {
1133                 var el = document.createElementNS(svg.svgns, "circle");
1134                 el.setAttribute("cx", svg._getX(x));
1135                 el.setAttribute("cy", svg._getY(y));
1136                 el.setAttribute("r", r);
1137                 el.setAttribute("fill", "none");
1138                 el.setAttribute("stroke", "#000");
1139                 el.attrs = el.attrs || {};
1140                 el.attrs.cx = x;
1141                 el.attrs.cy = y;
1142                 el.attrs.r = r;
1143                 el.attrs.stroke = "#000";
1144                 if (svg.canvas) {
1145                     svg.canvas.appendChild(el);
1146                 }
1147                 var res = new Element(el, svg);
1148                 res.type = "circle";
1149                 return res;
1150             };
1151             var theRect = function (svg, x, y, w, h, r) {
1152                 var el = document.createElementNS(svg.svgns, "rect");
1153                 el.setAttribute("x", svg._getX(x));
1154                 el.setAttribute("y", svg._getY(y));
1155                 el.setAttribute("width", svg._getW(w));
1156                 el.setAttribute("height", svg._getH(h));
1157                 el.attrs = el.attrs || {};
1158                 el.attrs.x = x;
1159                 el.attrs.y = y;
1160                 el.attrs.width = w;
1161                 el.attrs.height = h;
1162                 if (r) {
1163                     el.setAttribute("rx", r);
1164                     el.setAttribute("ry", r);
1165                     el.attrs.rx = el.attrs.ry = r;
1166                 }
1167                 el.setAttribute("fill", "none");
1168                 el.setAttribute("stroke", "#000");
1169                 el.attrs.stroke = "#000";
1170                 if (svg.canvas) {
1171                     svg.canvas.appendChild(el);
1172                 }
1173                 var res = new Element(el, svg);
1174                 res.type = "rect";
1175                 return res;
1176             };
1177             var theEllipse = function (svg, x, y, rx, ry) {
1178                 var el = document.createElementNS(svg.svgns, "ellipse");
1179                 el.setAttribute("cx", svg._getX(x));
1180                 el.setAttribute("cy", svg._getY(y));
1181                 el.setAttribute("rx", svg._getW(rx));
1182                 el.setAttribute("ry", svg._getH(ry));
1183                 el.setAttribute("fill", "none");
1184                 el.setAttribute("stroke", "#000");
1185                 el.attrs = el.attrs || {};
1186                 el.attrs.cx = x;
1187                 el.attrs.cy = y;
1188                 el.attrs.rx = rx;
1189                 el.attrs.ry = ry;
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 = "ellipse";
1196                 return res;
1197             };
1198             var theImage = function (svg, src, x, y, w, h) {
1199                 var el = document.createElementNS(svg.svgns, "image");
1200                 el.setAttribute("x", svg._getX(x));
1201                 el.setAttribute("y", svg._getY(y));
1202                 el.setAttribute("width", svg._getW(w));
1203                 el.setAttribute("height", svg._getH(h));
1204                 el.setAttributeNS(svg.xlink, "href", src);
1205                 if (svg.canvas) {
1206                     svg.canvas.appendChild(el);
1207                 }
1208                 var res = new Element(el, svg);
1209                 res.type = "image";
1210                 return res;
1211             };
1212             var theText = function (svg, x, y, text) {
1213                 var el = document.createElementNS(svg.svgns, "text");
1214                 el.setAttribute("x", x);
1215                 el.setAttribute("y", y);
1216                 el.setAttribute("text-anchor", "middle");
1217                 el.setAttribute("fill", "#000");
1218                 el.attrs = el.attrs || {};
1219                 el.attrs.x = x;
1220                 el.attrs.y = y;
1221                 el.attrs.fill = "#000";
1222                 if (text) {
1223                     el.appendChild(document.createTextNode(text));
1224                 }
1225                 if (svg.canvas) {
1226                     svg.canvas.appendChild(el);
1227                 }
1228                 var res = new Element(el, svg);
1229                 res.type = "text";
1230                 return res;
1231             };
1232             var theGroup = function (svg) {
1233                 var el = document.createElementNS(svg.svgns, "g");
1234                 if (svg.canvas) {
1235                     svg.canvas.appendChild(el);
1236                 }
1237                 var i = new Element(el, svg);
1238                 for (var f in svg) {
1239                     if (f[0] != "_" && typeof svg[f] == "function") {
1240                         i[f] = (function (f) {
1241                             return function () {
1242                                 var e = svg[f].apply(svg, arguments);
1243                                 el.appendChild(e[0]);
1244                                 return e;
1245                             };
1246                         })(f);
1247                     }
1248                 }
1249                 i.type = "group";
1250                 return i;
1251             };
1252             r._create = function () {
1253                 // container, width, height
1254                 // x, y, width, height
1255                 if (typeof arguments[0] == "string") {
1256                     var container = document.getElementById(arguments[0]);
1257                     var width = arguments[1];
1258                     var height = arguments[2];
1259                 }
1260                 if (typeof arguments[0] == "object") {
1261                     var container = arguments[0];
1262                     var width = arguments[1];
1263                     var height = arguments[2];
1264                 }
1265                 if (typeof arguments[0] == "number") {
1266                     var container = 1,
1267                         x = arguments[0],
1268                         y = arguments[1],
1269                         width = arguments[2],
1270                         height = arguments[3];
1271                 }
1272                 if (!container) {
1273                     throw new Error("SVG container not found.");
1274                 }
1275                 C.canvas = document.createElementNS(C.svgns, "svg");
1276                 C.canvas.setAttribute("width", width || 320);
1277                 C.width = width || 320;
1278                 C.canvas.setAttribute("height", height || 200);
1279                 C.height = height || 200;
1280                 if (container == 1) {
1281                     document.body.appendChild(C.canvas);
1282                     C.canvas.style.position = "absolute";
1283                     C.canvas.style.left = x + "px";
1284                     C.canvas.style.top = y + "px";
1285                 } else {
1286                     if (container.firstChild) {
1287                         container.insertBefore(C.canvas, container.firstChild);
1288                     } else {
1289                         container.appendChild(C.canvas);
1290                     }
1291                 }
1292                 container = {
1293                     canvas: C.canvas,
1294                     clear: function () {
1295                         while (this.canvas.firstChild) {
1296                             this.canvas.removeChild(this.canvas.firstChild);
1297                         }
1298                         this.defs = document.createElementNS(C.svgns, "defs");
1299                         this.gradients = 0;
1300                         this.canvas.appendChild(this.defs);
1301                     }
1302                 };
1303                 for (var prop in C) {
1304                     if (prop != "create") {
1305                         container[prop] = C[prop];
1306                     }
1307                 }
1308                 container.clear();
1309                 return container;
1310             };
1311             C.svgns = "http://www.w3.org/2000/svg";
1312             C.xlink = "http://www.w3.org/1999/xlink";
1313         }
1314         if (type == "VML" || type == "SVG") {
1315             C.circle = function (x, y, r) {
1316                 return theCircle(this, x, y, r);
1317             };
1318             C.rect = function (x, y, w, h, r) {
1319                 return theRect(this, x, y, w, h, r);
1320             };
1321             C.ellipse = function (x, y, rx, ry) {
1322                 return theEllipse(this, x, y, rx, ry);
1323             };
1324             C.path = function (params, pathString) {
1325                 return thePath(params, pathString, this);
1326             };
1327             C.image = function (src, x, y, w, h) {
1328                 return theImage(this, src, x, y, w, h);
1329             };
1330             C.text = function (x, y, text) {
1331                 return theText(this, x, y, text);
1332             };
1333             C.group = function () {
1334                 return theGroup(this);
1335             };
1336             C.linerect = function (x, y, w, h, r) {
1337                 if (r && parseInt(r, 10)) {
1338                     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();
1339                 }
1340                 return this.path({stroke: "#000"}).moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).andClose();
1341             };
1342             C.drawGrid = function (x, y, w, h, wv, hv, color) {
1343                 color = color || "#000";
1344                 var res = this.group();
1345                 var params = {stroke: color, "stroke-width": "1px", "stroke-opacity": .3};
1346                 res.rect(x, y, w, h).attr(params);
1347                 for (var i = 1; i < hv; i++) {
1348                     var p = res.path(params);
1349                     p.moveTo(x, y + i * Math.round(h / hv)).lineTo(x + w, y + i * Math.round(h / hv));
1350                 }
1351                 for (var i = 1; i < wv; i++) {
1352                     res.path(params).moveTo(x + i * Math.round(w / wv), y).lineTo(x + i * Math.round(w / wv), y + h);
1353                 }
1354                 return res;
1355             };
1356             C.setGrid = function (xmin, ymin, xmax, ymax, w, h) {
1357                 var xc = (xmax - xmin) / w;
1358                 var yc = (ymax - ymin) / h;
1359                 this._getX = function (x) {
1360                     return xmin + x * xc;
1361                 };
1362                 this._getY = function (y) {
1363                     return ymin + y * yc;
1364                 };
1365                 this._getW = function (w) {
1366                     return w * xc;
1367                 };
1368                 this._getH = function (h) {
1369                     return h * yc;
1370                 };
1371             };
1372             C.clearGrid = function () {
1373                 this._getX = this._getY = this._getW = this._getH = function (x) { return x; };
1374             };
1375             C.safari = function () {
1376                 if (r.type == "SVG") {
1377                     var rect = C.rect(-C.width, -C.width, C.width * 3, C.height * 3).attr({stroke: "none"});
1378                     setTimeout(function () {rect.remove();}, 0);
1379                 }
1380             };
1381             
1382             return r;
1383         } else {
1384             return function () {};
1385         }
1386     })((!(window.SVGPreserveAspectRatio && window.SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN == 2) && !(window.CanvasRenderingContext2D)) ? "VML" : "SVG");
1387
1388
1389 Raphael.type = (!(window.SVGPreserveAspectRatio && window.SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN == 2) && !(window.CanvasRenderingContext2D)) ? "VML" : "SVG";
1390 Raphael.vml = !(Raphael.svg = (Raphael.type == "SVG"));
1391 if (!(window.SVGPreserveAspectRatio && window.SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN == 2) && window.CanvasRenderingContext2D) {
1392     Raphael.type = "Canvas only";
1393     Raphael.vml = Raphael.svg = false;
1394 }
1395 Raphael.toString = function () {
1396     return "You browser supports " + this.type;
1397 };
1398 Raphael.hsb2rgb = function (hue, saturation, brightness) {
1399     if (typeof hue == "object" && "h" in hue && "s" in hue && "b" in hue) {
1400         brightness = hue.b;
1401         saturation = hue.s;
1402         hue = hue.h;
1403     }
1404     var red,
1405         green,
1406         blue;
1407     if (brightness == 0.0) {
1408         return {r: 0, g: 0, b: 0};
1409     } else {
1410         var i = Math.floor(hue * 6),
1411             f = (hue * 6) - i,
1412             p = brightness * (1 - saturation),
1413             q = brightness * (1 - (saturation * f)),
1414             t = brightness * (1 - (saturation * (1 - f)));
1415         [
1416             function () {red = brightness; green = t; blue = p;},
1417             function () {red = q; green = brightness; blue = p;},
1418             function () {red = p; green = brightness; blue = t;},
1419             function () {red = p; green = q; blue = brightness;},
1420             function () {red = t; green = p; blue = brightness;},
1421             function () {red = brightness; green = p; blue = q;},
1422             function () {red = brightness; green = t; blue = p;},
1423         ][i]();
1424     }
1425     var rgb = {r: red, g: green, b: blue};
1426     var r = Math.round(rgb.r).toString(16);
1427     if (r.length == 1) {
1428         r = "0" + r;
1429     }
1430     var g = Math.round(rgb.g).toString(16);
1431     if (g.length == 1) {
1432         g = "0" + g;
1433     }
1434     var b = Math.round(rgb.b).toString(16);
1435     if (b.length == 1) {
1436         b = "0" + b;
1437     }
1438     rgb.hex = "#" + r + g + b;
1439     return rgb;
1440 };
1441 Raphael.rgb2hsb = function (red, green, blue) {
1442     if (typeof red == "object" && "r" in red && "h" in red && "b" in red) {
1443         blue = red.b;
1444         green = red.g;
1445         red = red.r;
1446     }
1447     if (red.charAt(0) == "#") {
1448         if (red.length == 4) {
1449             blue = parseInt(red.substring(3), 16);
1450             green = parseInt(red.substring(2, 3), 16);
1451             red = parseInt(red.substring(1, 2), 16);
1452         } else {
1453             blue = parseInt(red.substring(5), 16);
1454             green = parseInt(red.substring(3, 5), 16);
1455             red = parseInt(red.substring(1, 3), 16);
1456         }
1457     }
1458     var max = Math.max(red, green, blue),
1459         min = Math.min(red, green, blue),
1460         hue,
1461         saturation,
1462         brightness = max;
1463     if (min == max) {
1464         return {h: 0, s: 0, b: max};
1465     } else {
1466         var delta = (max - min);
1467         saturation = delta / max;
1468         if (red == max) {
1469             hue = (green - blue) / delta;
1470         } else if (green == max) {
1471             hue = 2 + ((blue - red) / delta);
1472         } else {
1473             hue = 4 + ((red - green) / delta);
1474         }
1475         hue /= 6;
1476         if (hue < 0) {
1477             hue += 1;
1478         }
1479         if (hue > 1) {
1480             hue -= 1;
1481         }
1482     }
1483     return {h: hue, s: saturation, b: brightness};
1484 };
1485 Raphael.getColor = function (value) {
1486     var start = arguments.callee.start = arguments.callee.start || {h: 0, s: 1, b: value || 200};
1487     var rgb = this.hsb2rgb(start.h, start.s, start.b);
1488     start.h += .1;
1489     if (start.h > 1) {
1490         start.h = 0;
1491         start.s -= .2;
1492         if (start.s <= 0) {
1493             start = {h: 0, s: 1, b: start.b};
1494         }
1495     }
1496     return rgb.hex;
1497 };
1498 Raphael.getColor.reset = function () {
1499     this.start = undefined;
1500 };