Rolling back splitting. It happens that there no win: two requests is still longer...
[raphael] / raphael.js
1 /*
2  * Raphael 0.7 - JavaScript Vector Library
3  *
4  * Copyright (c) 2008 – 2009 Dmitry Baranovskiy (http://raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 function Raphael() {
8     return Raphael._create.apply(Raphael, arguments);
9 };
10
11
12 (function (R) {
13     R.version = "0.7";
14     R.type = (window.SVGAngle ? "SVG" : "VML");
15     R.svg = !(R.vml = R.type == "VML");
16     R.idGenerator = 0;
17     var paper = {};
18     R.fn = {};
19     var availableAttrs = {cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '16px "Arial"', "font-family": '"Arial"', "font-size": "16", gradient: 0, height: 0, opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, translation: "0 0", width: 0, x: 0, y: 0},
20         availableAnimAttrs = {cx: "number", cy: "number", fill: "colour", "fill-opacity": "number", "font-size": "number", height: "number", opacity: "number", path: "path", r: "number", rotation: "number", rx: "number", ry: "number", scale: "csv", stroke: "colour", "stroke-opacity": "number", "stroke-width": "number", translation: "csv", width: "number", x: "number", y: "number"};
21
22     R.toString = function () {
23         return  "Your browser " + (this.vml ? "doesn't ": "") + "support" + (this.svg ? "s": "") +
24                 " SVG.\nYou are running " + unescape("Rapha%EBl%20") + this.version;
25     };
26     // generic utilities
27     R.hsb2rgb = function (hue, saturation, brightness) {
28         if (typeof hue == "object" && "h" in hue && "s" in hue && "b" in hue) {
29             brightness = hue.b;
30             saturation = hue.s;
31             hue = hue.h;
32         }
33         var red,
34             green,
35             blue;
36         if (brightness == 0) {
37             return {r: 0, g: 0, b: 0, hex: "#000"};
38         } else {
39             var i = Math.floor(hue * 6),
40                 f = (hue * 6) - i,
41                 p = brightness * (1 - saturation),
42                 q = brightness * (1 - (saturation * f)),
43                 t = brightness * (1 - (saturation * (1 - f)));
44             [
45                 function () {red = brightness; green = t; blue = p;},
46                 function () {red = q; green = brightness; blue = p;},
47                 function () {red = p; green = brightness; blue = t;},
48                 function () {red = p; green = q; blue = brightness;},
49                 function () {red = t; green = p; blue = brightness;},
50                 function () {red = brightness; green = p; blue = q;},
51                 function () {red = brightness; green = t; blue = p;}
52             ][i]();
53         }
54         var rgb = {r: red, g: green, b: blue};
55         red *= 255;
56         green *= 255;
57         blue *= 255;
58         var r = Math.round(red).toString(16);
59         if (r.length == 1) {
60             r = "0" + r;
61         }
62         var g = Math.round(green).toString(16);
63         if (g.length == 1) {
64             g = "0" + g;
65         }
66         var b = Math.round(blue).toString(16);
67         if (b.length == 1) {
68             b = "0" + b;
69         }
70         rgb.hex = "#" + r + g + b;
71         return rgb;
72     };
73     R.rgb2hsb = function (red, green, blue) {
74         if (typeof red == "object" && "r" in red && "g" in red && "b" in red) {
75             blue = red.b;
76             green = red.g;
77             red = red.r;
78         }
79         if (typeof red == "string" && red.charAt(0) == "#") {
80             if (red.length == 4) {
81                 blue = parseInt(red.substring(3), 16);
82                 green = parseInt(red.substring(2, 3), 16);
83                 red = parseInt(red.substring(1, 2), 16);
84             } else {
85                 blue = parseInt(red.substring(5), 16);
86                 green = parseInt(red.substring(3, 5), 16);
87                 red = parseInt(red.substring(1, 3), 16);
88             }
89         }
90         if (red > 1 || green > 1 || blue > 1) {
91             red /= 255;
92             green /= 255;
93             blue /= 255;
94         }
95         var max = Math.max(red, green, blue),
96             min = Math.min(red, green, blue),
97             hue,
98             saturation,
99             brightness = max;
100         if (min == max) {
101             return {h: 0, s: 0, b: max};
102         } else {
103             var delta = (max - min);
104             saturation = delta / max;
105             if (red == max) {
106                 hue = (green - blue) / delta;
107             } else if (green == max) {
108                 hue = 2 + ((blue - red) / delta);
109             } else {
110                 hue = 4 + ((red - green) / delta);
111             }
112             hue /= 6;
113             if (hue < 0) {
114                 hue += 1;
115             }
116             if (hue > 1) {
117                 hue -= 1;
118             }
119         }
120         return {h: hue, s: saturation, b: brightness};
121     };
122     R.getRGB = function (colour) {
123         var red, green, blue,
124             rgb = colour.match(/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*(\d+,\s*\d+,\s*\d+)\s*\)|rgb\(\s*(\d+%,\s*\d+%,\s*\d+%)\s*\)|hsb\(\s*(\d+,\s*\d+,\s*\d+)\s*\)|hsb\(\s*(\d+%,\s*\d+%,\s*\d+%)\s*\))\s*$/i);
125         if (rgb) {
126             if (rgb[2]) {
127                 blue = parseInt(rgb[2].substring(5), 16);
128                 green = parseInt(rgb[2].substring(3, 5), 16);
129                 red = parseInt(rgb[2].substring(1, 3), 16);
130             }
131             if (rgb[3]) {
132                 blue = parseInt(rgb[3].substring(3) + rgb[3].substring(3), 16);
133                 green = parseInt(rgb[3].substring(2, 3) + rgb[3].substring(2, 3), 16);
134                 red = parseInt(rgb[3].substring(1, 2) + rgb[3].substring(1, 2), 16);
135             }
136             if (rgb[4]) {
137                 rgb = rgb[4].split(/\s*,\s*/);
138                 red = parseInt(rgb[0], 10);
139                 green = parseInt(rgb[1], 10);
140                 blue = parseInt(rgb[2], 10);
141             }
142             if (rgb[5]) {
143                 rgb = rgb[5].split(/\s*,\s*/);
144                 red = parseInt(rgb[0], 10) * 2.55;
145                 green = parseInt(rgb[1], 10) * 2.55;
146                 blue = parseInt(rgb[2], 10) * 2.55;
147             }
148             if (rgb[6]) {
149                 rgb = rgb[6].split(/\s*,\s*/);
150                 red = parseInt(rgb[0], 10);
151                 green = parseInt(rgb[1], 10);
152                 blue = parseInt(rgb[2], 10);
153                 return this.hsb2rgb(red, green, blue);
154             }
155             if (rgb[7]) {
156                 rgb = rgb[7].split(/\s*,\s*/);
157                 red = parseInt(rgb[0], 10) * 2.55;
158                 green = parseInt(rgb[1], 10) * 2.55;
159                 blue = parseInt(rgb[2], 10) * 2.55;
160                 return this.hsb2rgb(red, green, blue);
161             }
162             var rgb = {r: red, g: green, b: blue};
163             var r = Math.round(red).toString(16);
164             (r.length == 1) && (r = "0" + r);
165             var g = Math.round(green).toString(16);
166             (g.length == 1) && (g = "0" + g);
167             var b = Math.round(blue).toString(16);
168             (b.length == 1) && (b = "0" + b);
169             rgb.hex = "#" + r + g + b;
170             return rgb;
171         }
172     };
173     R.getColor = function (value) {
174         var start = arguments.callee.start = arguments.callee.start || {h: 0, s: 1, b: value || .75};
175         var rgb = this.hsb2rgb(start.h, start.s, start.b);
176         start.h += .075;
177         if (start.h > 1) {
178             start.h = 0;
179             start.s -= .2;
180             if (start.s <= 0) {
181                 arguments.callee.start = {h: 0, s: 1, b: start.b};
182             }
183         }
184         return rgb.hex;
185     };
186     R.getColor.reset = function () {
187         this.start = undefined;
188     };
189     R.parsePathString = function (pathString) {
190         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
191             data = [],
192             toString = function () {
193                 var res = "";
194                 for (var i = 0, ii = this.length; i < ii; i++) {
195                     res += this[i][0] + this[i].join(",").substring(2);
196                 }
197                 return res;
198             };
199         if (pathString.toString.toString() == toString.toString()) {
200             return pathString;
201         }
202         pathString.replace(/([achlmqstvz])[\s,]*((-?\d*\.?\d*\s*,?\s*)+)/ig, function (a, b, c) {
203             var params = [], name = b.toLowerCase();
204             c.replace(/(-?\d*\.?\d*)\s*,?\s*/ig, function (a, b) {
205                 b && params.push(+b);
206             });
207             while (params.length >= paramCounts[name]) {
208                 data.push([b].concat(params.splice(0, paramCounts[name])));
209                 if (!paramCounts[name]) {
210                     break;
211                 };
212             }
213         });
214         data.toString = toString;
215         return data;
216     };
217     R.pathDimensions = function (path) {
218         var pathArray = path;
219         if (typeof path == "string") {
220             pathArray = this.parsePathString(path);
221         }
222         pathArray = this.pathToAbsolute(pathArray);
223         var x = [], y = [], length = 0;
224         for (var i = 0, ii = pathArray.length; i < ii; i++) {
225             switch (pathArray[i][0]) {
226                 case "Z":
227                     break;
228                 case "A":
229                     x.push(pathArray[i][pathArray[i].length - 2]);
230                     y.push(pathArray[i][pathArray[i].length - 1]);
231                     break;
232                 default:
233                     for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
234                         if (j % 2) {
235                             x.push(pathArray[i][j]);
236                         } else {
237                             y.push(pathArray[i][j]);
238                         }
239                     }
240             }
241         }
242         var minx = Math.min.apply(Math, x),
243             miny = Math.min.apply(Math, y);
244         return {
245             x: minx,
246             y: miny,
247             width: Math.max.apply(Math, x) - minx,
248             height: Math.max.apply(Math, y) - miny,
249             X: x,
250             Y: y
251         };
252     };
253     R.pathToRelative = function (pathArray) {
254         var res = [];
255         if (typeof pathArray == "string") {
256             pathArray = this.parsePathString(pathArray);
257         }
258         var x = 0, y = 0, start = 0;
259         if (pathArray[0][0] == "M") {
260             x = pathArray[0][1];
261             y = pathArray[0][2];
262             start++;
263             res.push(pathArray[0]);
264         }
265         for (var i = start, ii = pathArray.length; i < ii; i++) {
266             res[i] = [];
267             if (pathArray[i][0] != pathArray[i][0].toLowerCase()) {
268                 res[i][0] = pathArray[i][0].toLowerCase();
269                 switch (res[i][0]) {
270                     case "a":
271                         res[i][1] = pathArray[i][1];
272                         res[i][2] = pathArray[i][2];
273                         res[i][3] = 0;
274                         res[i][4] = pathArray[i][4];
275                         res[i][5] = pathArray[i][5];
276                         res[i][6] = +(pathArray[i][6] - x).toFixed(3);
277                         res[i][7] = +(pathArray[i][7] - y).toFixed(3);
278                         break;
279                     case "v":
280                         res[i][1] = +(pathArray[i][1] - y).toFixed(3);
281                         break;
282                     default:
283                         for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
284                             res[i][j] = +(pathArray[i][j] - ((j % 2) ? x : y)).toFixed(3);
285                         }
286                 }
287             } else {
288                 res[i] = pathArray[i];
289             }
290             switch (res[i][0]) {
291                 case "z":
292                     break;
293                 case "h": 
294                     x += res[i][res[i].length - 1];
295                     break;
296                 case "v":
297                     y += res[i][res[i].length - 1];
298                     break;
299                 default:
300                     x += res[i][res[i].length - 2];
301                     y += res[i][res[i].length - 1];
302             }
303         }
304         res.toString = pathArray.toString;
305         return res;
306     };
307     R.pathToAbsolute = function (pathArray) {
308         var res = [];
309         if (typeof pathArray == "string") {
310             pathArray = this.parsePathString(pathArray);
311         }
312         var x = 0, y = 0, start = 0;
313         if (pathArray[0][0] == "M") {
314             x = +pathArray[0][1];
315             y = +pathArray[0][2];
316             start++;
317             res[0] = pathArray[0];
318         }
319         for (var i = start, ii = pathArray.length; i < ii; i++) {
320             res[i] = [];
321             if (pathArray[i][0] != pathArray[i][0].toUpperCase()) {
322                 res[i][0] = pathArray[i][0].toUpperCase();
323                 switch (res[i][0]) {
324                     case "A":
325                         res[i][1] = pathArray[i][1];
326                         res[i][2] = pathArray[i][2];
327                         res[i][3] = 0;
328                         res[i][4] = pathArray[i][4];
329                         res[i][5] = pathArray[i][5];
330                         res[i][6] = +(pathArray[i][6] + x).toFixed(3);
331                         res[i][7] = +(pathArray[i][7] + y).toFixed(3);
332                         break;
333                     case "V":
334                         res[i][1] = +pathArray[i][1] + y;
335                         break;
336                     default:
337                         for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
338                             res[i][j] = +pathArray[i][j] + ((j % 2) ? x : y);
339                         }
340                 }
341             } else {
342                 res[i] = pathArray[i];
343             }
344             switch (res[i][0]) {
345                 case "Z":
346                     break;
347                 case "H": 
348                     x = res[i][1];
349                     break;
350                 case "V":
351                     y = res[i][1];
352                     break;
353                 default:
354                     x = res[i][res[i].length - 2];
355                     y = res[i][res[i].length - 1];
356             }
357         }
358         res.toString = pathArray.toString;
359         return res;
360     };
361     R.pathEqualiser = function (path1, path2) {
362         var data = [this.pathToAbsolute(this.parsePathString(path1)), this.pathToAbsolute(this.parsePathString(path2))],
363             attrs = [{x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}, {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}],
364             processPath = function (path, d) {
365                 if (!path) {
366                     return ["U"];
367                 }
368                 switch (path[0]) {
369                     case "M":
370                         d.X = path[1];
371                         d.Y = path[2];
372                         break;
373                     case "S":
374                         var nx = d.x + (d.x - (d.bx || d.x));
375                         var ny = d.y + (d.y - (d.by || d.y));
376                         path = ["C", nx, ny, path[1], path[2], path[3], path[4]];
377                         break;
378                     case "T":
379                         var nx = d.x + (d.x - (d.bx || d.x));
380                         var ny = d.y + (d.y - (d.by || d.y));
381                         path = ["Q", nx, ny, path[1], path[2]];
382                         break;
383                     case "H":
384                         path = ["L", path[1], d.y];
385                         break;
386                     case "V":
387                         path = ["L", d.x, path[1]];
388                         break;
389                     case "Z":
390                         path = ["L", d.X, d.Y];
391                         break;
392                 }
393                 return path;
394             },
395             edgeCases = function (a, b, i) {
396                 if (data[a][i][0] == "M" && data[b][i][0] != "M") {
397                     data[b].splice(i, 0, ["M", attrs[b].x, attrs[b].y]);
398                     attrs[a].bx = data[a][i][data[a][i].length - 4] || 0;
399                     attrs[a].by = data[a][i][data[a][i].length - 3] || 0;
400                     attrs[a].x = data[a][i][data[a][i].length - 2];
401                     attrs[a].y = data[a][i][data[a][i].length - 1];
402                     return true;
403                 } else if (data[a][i][0] == "L" && data[b][i][0] == "C") {
404                     data[a][i] = ["C", attrs[a].x, attrs[a].y, data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]];
405                 } else if (data[a][i][0] == "L" && data[b][i][0] == "Q") {
406                     data[a][i] = ["Q", data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]];
407                 } else if (data[a][i][0] == "Q" && data[b][i][0] == "C") {
408                     var x = data[b][i][data[b][i].length - 2];
409                     var y = data[b][i][data[b][i].length - 1];
410                     data[b].splice(i + 1, 0, ["Q", x, y, x, y]);
411                     data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]);
412                     i++;
413                     attrs[b].bx = data[b][i][data[b][i].length - 4] || 0;
414                     attrs[b].by = data[b][i][data[b][i].length - 3] || 0;
415                     attrs[b].x = data[b][i][data[b][i].length - 2];
416                     attrs[b].y = data[b][i][data[b][i].length - 1];
417                     return true;
418                 } else if (data[a][i][0] == "A" && data[b][i][0] == "C") {
419                     var x = data[b][i][data[b][i].length - 2];
420                     var y = data[b][i][data[b][i].length - 1];
421                     data[b].splice(i + 1, 0, ["A", 0, 0, data[a][i][3], data[a][i][4], data[a][i][5], x, y]);
422                     data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]);
423                     i++;
424                     attrs[b].bx = data[b][i][data[b][i].length - 4] || 0;
425                     attrs[b].by = data[b][i][data[b][i].length - 3] || 0;
426                     attrs[b].x = data[b][i][data[b][i].length - 2];
427                     attrs[b].y = data[b][i][data[b][i].length - 1];
428                     return true;
429                 } else if (data[a][i][0] == "U") {
430                     data[a][i][0] = data[b][i][0];
431                     for (var j = 1, jj = data[b][i].length; j < jj; j++) {
432                         data[a][i][j] = (j % 2) ? attrs[a].x : attrs[a].y;
433                     }
434                 }
435                 return false;
436             };
437         for (var i = 0; i < Math.max(data[0].length, data[1].length); i++) {
438             data[0][i] = processPath(data[0][i], attrs[0]);
439             data[1][i] = processPath(data[1][i], attrs[1]);
440             if (data[0][i][0] != data[1][i][0] && (edgeCases(0, 1, i) || edgeCases(1, 0, i))) {
441                 continue;
442             }
443             attrs[0].bx = data[0][i][data[0][i].length - 4] || 0;
444             attrs[0].by = data[0][i][data[0][i].length - 3] || 0;
445             attrs[0].x = data[0][i][data[0][i].length - 2];
446             attrs[0].y = data[0][i][data[0][i].length - 1];
447             attrs[1].bx = data[1][i][data[1][i].length - 4] || 0;
448             attrs[1].by = data[1][i][data[1][i].length - 3] || 0;
449             attrs[1].x = data[1][i][data[1][i].length - 2];
450             attrs[1].y = data[1][i][data[1][i].length - 1];
451         }
452         return data;
453     };
454
455     // SVG
456     if (R.svg) {
457         thePath = function (params, pathString, SVG) {
458             var el = document.createElementNS(SVG.svgns, "path");
459             el.setAttribute("fill", "none");
460             if (SVG.canvas) {
461                 SVG.canvas.appendChild(el);
462             }
463             var p = new Element(el, SVG);
464             p.isAbsolute = true;
465             p.type = "path";
466             p.last = {x: 0, y: 0, bx: 0, by: 0};
467             p.absolutely = function () {
468                 this.isAbsolute = true;
469                 return this;
470             };
471             p.relatively = function () {
472                 this.isAbsolute = false;
473                 return this;
474             };
475             p.moveTo = function (x, y) {
476                 var d = this.isAbsolute?"M":"m";
477                 d += parseFloat(x, 10).toFixed(3) + " " + parseFloat(y, 10).toFixed(3) + " ";
478                 var oldD = this[0].getAttribute("d") || "";
479                 (oldD == "M0,0") && (oldD = "");
480                 this[0].setAttribute("d", oldD + d);
481                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
482                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
483                 this.attrs.path = oldD + d;
484                 return this;
485             };
486             p.lineTo = function (x, y) {
487                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
488                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
489                 var d = this.isAbsolute?"L":"l";
490                 d += parseFloat(x, 10).toFixed(3) + " " + parseFloat(y, 10).toFixed(3) + " ";
491                 var oldD = this[0].getAttribute("d") || "";
492                 this[0].setAttribute("d", oldD + d);
493                 this.attrs.path = oldD + d;
494                 return this;
495             };
496             p.arcTo = function (rx, ry, large_arc_flag, sweep_flag, x, y) {
497                 var d = this.isAbsolute ? "A" : "a";
498                 d += [parseFloat(rx, 10).toFixed(3), parseFloat(ry, 10).toFixed(3), 0, large_arc_flag, sweep_flag, parseFloat(x, 10).toFixed(3), parseFloat(y, 10).toFixed(3)].join(" ");
499                 var oldD = this[0].getAttribute("d") || "";
500                 this[0].setAttribute("d", oldD + d);
501                 this.last.x = parseFloat(x, 10);
502                 this.last.y = parseFloat(y, 10);
503                 this.attrs.path = oldD + d;
504                 return this;
505             };
506             p.cplineTo = function (x1, y1, w1) {
507                 if (!w1) {
508                     return this.lineTo(x1, y1);
509                 } else {
510                     var p = {};
511                     var x = parseFloat(x1, 10);
512                     var y = parseFloat(y1, 10);
513                     var w = parseFloat(w1, 10);
514                     var d = this.isAbsolute?"C":"c";
515                     var attr = [+this.last.x + w, +this.last.y, x - w, y, x, y];
516                     for (var i = 0, ii = attr.length; i < ii; i++) {
517                         d += attr[i].toFixed(3) + " ";
518                     }
519                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + attr[4];
520                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + attr[5];
521                     this.last.bx = attr[2];
522                     this.last.by = attr[3];
523                     var oldD = this[0].getAttribute("d") || "";
524                     this[0].setAttribute("d", oldD + d);
525                     this.attrs.path = oldD + d;
526                     return this;
527                 }
528             };
529             p.curveTo = function () {
530                 var p = {},
531                     command = [0, 1, 2, 3, "s", 5, "c"];
532
533                 var d = command[arguments.length];
534                 if (this.isAbsolute) {
535                     d = d.toUpperCase();
536                 }
537                 for (var i = 0, ii = arguments.length; i < ii; i++) {
538                     d += parseFloat(arguments[i], 10).toFixed(3) + " ";
539                 }
540                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[arguments.length - 2], 10);
541                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[arguments.length - 1], 10);
542                 this.last.bx = parseFloat(arguments[arguments.length - 4], 10);
543                 this.last.by = parseFloat(arguments[arguments.length - 3], 10);
544                 var oldD = this.node.getAttribute("d") || "";
545                 this.node.setAttribute("d", oldD + d);
546                 this.attrs.path = oldD + d;
547                 return this;
548             };
549             p.qcurveTo = function () {
550                 var p = {},
551                     command = [0, 1, "t", 3, "q"];
552
553                 var d = command[arguments.length];
554                 if (this.isAbsolute) {
555                     d = d.toUpperCase();
556                 }
557                 for (var i = 0, ii = arguments.length; i < ii; i++) {
558                     d += parseFloat(arguments[i], 10).toFixed(3) + " ";
559                 }
560                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[arguments.length - 2], 10);
561                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[arguments.length - 1], 10);
562                 if (arguments.length != 2) {
563                     this.last.qx = parseFloat(arguments[arguments.length - 4], 10);
564                     this.last.qy = parseFloat(arguments[arguments.length - 3], 10);
565                 }
566                 var oldD = this.node.getAttribute("d") || "";
567                 this.node.setAttribute("d", oldD + d);
568                 this.attrs.path = oldD + d;
569                 return this;
570             };
571             p.addRoundedCorner = function (r, dir) {
572                 var R = .5522 * r, rollback = this.isAbsolute, o = this;
573                 if (rollback) {
574                     this.relatively();
575                     rollback = function () {
576                         o.absolutely();
577                     };
578                 } else {
579                     rollback = function () {};
580                 }
581                 var actions = {
582                     l: function () {
583                         return {
584                             u: function () {
585                                 o.curveTo(-R, 0, -r, -(r - R), -r, -r);
586                             },
587                             d: function () {
588                                 o.curveTo(-R, 0, -r, r - R, -r, r);
589                             }
590                         };
591                     },
592                     r: function () {
593                         return {
594                             u: function () {
595                                 o.curveTo(R, 0, r, -(r - R), r, -r);
596                             },
597                             d: function () {
598                                 o.curveTo(R, 0, r, r - R, r, r);
599                             }
600                         };
601                     },
602                     u: function () {
603                         return {
604                             r: function () {
605                                 o.curveTo(0, -R, -(R - r), -r, r, -r);
606                             },
607                             l: function () {
608                                 o.curveTo(0, -R, R - r, -r, -r, -r);
609                             }
610                         };
611                     },
612                     d: function () {
613                         return {
614                             r: function () {
615                                 o.curveTo(0, R, -(R - r), r, r, r);
616                             },
617                             l: function () {
618                                 o.curveTo(0, R, R - r, r, -r, r);
619                             }
620                         };
621                     }
622                 };
623                 actions[dir[0]]()[dir[1]]();
624                 rollback();
625                 return o;
626             };
627             p.andClose = function () {
628                 var oldD = this[0].getAttribute("d") || "";
629                 this[0].setAttribute("d", oldD + "Z ");
630                 this.attrs.path = oldD + "Z ";
631                 return this;
632             };
633             if (pathString) {
634                 p.attrs.path = "" + pathString;
635                 p.absolutely();
636                 paper.pathfinder(p, p.attrs.path);
637             }
638             if (params) {
639                 setFillAndStroke(p, params);
640             }
641             return p;
642         };
643         addGrdientFill = function (o, gradient, SVG) {
644             var el = document.createElementNS(SVG.svgns, gradient.type + "Gradient");
645             el.id = "raphael-gradient-" + Raphael.idGenerator++;
646             if (gradient.vector && gradient.vector.length) {
647                 el.setAttribute("x1", gradient.vector[0]);
648                 el.setAttribute("y1", gradient.vector[1]);
649                 el.setAttribute("x2", gradient.vector[2]);
650                 el.setAttribute("y2", gradient.vector[3]);
651             }
652             SVG.defs.appendChild(el);
653             for (var i = 0, ii = gradient.dots.length; i < ii; i++) {
654                 var stop = document.createElementNS(SVG.svgns, "stop");
655                 stop.setAttribute("offset", gradient.dots[i].offset ? gradient.dots[i].offset : (i == 0) ? "0%" : "100%");
656                 stop.setAttribute("stop-color", gradient.dots[i].color || "#fff");
657                 if (typeof gradient.dots[i].opacity != "undefined") {
658                     stop.setAttribute("stop-opacity", gradient.dots[i].opacity);
659                 }
660                 el.appendChild(stop);
661             };
662             o.setAttribute("fill", "url(#" + el.id + ")");
663         };
664         updatePosition = function (o) {
665             if (o.pattern) {
666                 var bbox = o.node.getBBox();
667                 o.pattern.setAttribute("patternTransform", "translate(" + [bbox.x, bbox.y].join(",") + ")");
668             }
669         };
670         setFillAndStroke = function (o, params) {
671             var dasharray = {
672                 "-": [3, 1],
673                 ".": [1, 1],
674                 "-.": [3, 1, 1, 1],
675                 "-..": [3, 1, 1, 1, 1, 1],
676                 ". ": [1, 3],
677                 "- ": [4, 3],
678                 "--": [8, 3],
679                 "- .": [4, 3, 1, 3],
680                 "--.": [8, 3, 1, 3],
681                 "--..": [8, 3, 1, 3, 1, 3]
682             },
683             addDashes = function (o, value) {
684                 value = dasharray[value.toString().toLowerCase()];
685                 if (value) {
686                     var width = o.attrs["stroke-width"] || "1",
687                         butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
688                         dashes = [];
689                     for (var i = 0, ii = value.length; i < ii; i++) {
690                         dashes.push(value[i] * width + ((i % 2) ? 1 : -1) * butt);
691                     }
692                     value = dashes.join(",");
693                     o.node.setAttribute("stroke-dasharray", value);
694                 }
695             };
696             for (var att in params) {
697                 var value = params[att];
698                 o.attrs[att] = value;
699                 switch (att) {
700                     case "path":
701                         if (o.type == "path") {
702                             o.node.setAttribute("d", "M0,0");
703                             paper.pathfinder(o, value);
704                         }
705                     case "rx":
706                     case "cx":
707                     case "x":
708                         o.node.setAttribute(att, value);
709                         updatePosition(o);
710                         break;
711                     case "ry":
712                     case "cy":
713                     case "y":
714                         o.node.setAttribute(att, value);
715                         updatePosition(o);
716                         break;
717                     case "width":
718                         o.node.setAttribute(att, value);
719                         break;
720                     case "height":
721                         o.node.setAttribute(att, value);
722                         break;
723                     case "gradient":
724                         addGrdientFill(o.node, value, o.svg);
725                         break;
726                     case "stroke-width":
727                         o.node.style.strokeWidth = value;
728                         // Need following line for Firefox
729                         o.node.setAttribute(att, value);
730                         if (o.attrs["stroke-dasharray"]) {
731                             addDashes(o, o.attrs["stroke-dasharray"]);
732                         }
733                         break;
734                     case "stroke-dasharray":
735                         addDashes(o, value);
736                         break;
737                     case "text":
738                         if (o.type == "text") {
739                             o.node.childNodes.length && o.node.removeChild(o.node.firstChild);
740                             o.node.appendChild(document.createTextNode(value));
741                         }
742                         break;
743                     case "rotation":
744                         o.rotate(value, true);
745                         break;
746                     case "translation":
747                         var xy = value.split(/[, ]+/);
748                         o.translate(xy[0], xy[1]);
749                         break;
750                     case "scale":
751                         var xy = value.split(/[, ]+/);
752                         o.scale(xy[0], xy[1]);
753                         break;
754                     case "fill":
755                         var isURL = value.match(/^url\(([^\)]+)\)$/i);
756                         if (isURL) {
757                             var el = document.createElementNS(o.svg.svgns, "pattern");
758                             var ig = document.createElementNS(o.svg.svgns, "image");
759                             el.id = "raphael-pattern-" + Raphael.idGenerator++;
760                             el.setAttribute("x", 0);
761                             el.setAttribute("y", 0);
762                             el.setAttribute("patternUnits", "userSpaceOnUse");
763                             ig.setAttribute("x", 0);
764                             ig.setAttribute("y", 0);
765                             ig.setAttributeNS(o.svg.xlink, "href", isURL[1]);
766                             el.appendChild(ig);
767
768                             var img = document.createElement("img");
769                             img.style.position = "absolute";
770                             img.style.top = "-9999em";
771                             img.style.left = "-9999em";
772                             img.onload = function () {
773                                 el.setAttribute("width", this.offsetWidth);
774                                 el.setAttribute("height", this.offsetHeight);
775                                 ig.setAttribute("width", this.offsetWidth);
776                                 ig.setAttribute("height", this.offsetHeight);
777                                 document.body.removeChild(this);
778                                 paper.safari();
779                             };
780                             document.body.appendChild(img);
781                             img.src = isURL[1];
782                             o.svg.defs.appendChild(el);
783                             o.node.style.fill = "url(#" + el.id + ")";
784                             o.node.setAttribute("fill", "url(#" + el.id + ")");
785                             o.pattern = el;
786                             updatePosition(o);
787                             break;
788                         }
789                     default :
790                         var cssrule = att.replace(/(\-.)/g, function (w) {
791                             return w.substring(1).toUpperCase();
792                         });
793                         o.node.style[cssrule] = value;
794                         // Need following line for Firefox
795                         o.node.setAttribute(att, value);
796                         break;
797                 }
798             }
799         };
800         var Element = function (node, svg) {
801             var X = 0,
802                 Y = 0;
803             this[0] = node;
804             this.node = node;
805             this.svg = svg;
806             this.attrs = this.attrs || {};
807             this.transformations = []; // rotate, translate, scale
808             this._ = {
809                 tx: 0,
810                 ty: 0,
811                 rt: {deg: 0, x: 0, y: 0},
812                 sx: 1,
813                 sy: 1
814             };
815         };
816         Element.prototype.translate = function (x, y) {
817             if (x == undefined && y == undefined) {
818                 return {x: this._.tx, y: this._.ty};
819             }
820             this._.tx += +x;
821             this._.ty += +y;
822             switch (this.type) {
823                 case "circle":
824                 case "ellipse":
825                     this.attr({cx: this.attrs.cx + x, cy: this.attrs.cy + y});
826                     break;
827                 case "rect":
828                 case "image":
829                 case "text":
830                     this.attr({x: this.attrs.x + x, y: this.attrs.y + y});
831                     break;
832                 case "path":
833                     var path = Raphael.pathToRelative(this.attrs.path);
834                     path[0][1] += +x;
835                     path[0][2] += +y;
836                     this.attr({path: path.join(" ")});
837                 break;
838             }
839             return this;
840         };
841         Element.prototype.rotate = function (deg, isAbsolute) {
842             if (deg == undefined) {
843                 return this._.rt.deg;
844             }
845             var bbox = this.getBBox();
846             if (isAbsolute) {
847                 this._.rt.deg = deg;
848             } else {
849                 this._.rt.deg += deg;
850             }
851
852             if (this._.rt.deg) {
853                 this.transformations[0] = ("rotate(" + this._.rt.deg + " " + (bbox.x + bbox.width / 2) + " " + (bbox.y + bbox.height / 2) + ")");
854             } else {
855                 this.transformations[0] = "";
856             }
857             this.node.setAttribute("transform", this.transformations.join(" "));
858             return this;
859         };
860         Element.prototype.hide = function () {
861             this.node.style.display = "none";
862             return this;
863         };
864         Element.prototype.show = function () {
865             this.node.style.display = "block";
866             return this;
867         };
868         Element.prototype.remove = function () {
869             this.node.parentNode.removeChild(this.node);
870         };
871         Element.prototype.getBBox = function () {
872             return this.node.getBBox();
873         };
874         Element.prototype.attr = function () {
875             if (arguments.length == 1 && typeof arguments[0] == "string") {
876                 if (arguments[0] == "translation") {
877                     return this.translate();
878                 }
879                 return this.attrs[arguments[0]];
880             }
881             if (arguments.length == 1 && arguments[0] instanceof Array) {
882                 var values = {};
883                 for (var j in arguments[0]) {
884                     values[arguments[0][j]] = this.attrs[arguments[0][j]];
885                 }
886                 return values;
887             }
888             if (arguments.length == 2) {
889                 var params = {};
890                 params[arguments[0]] = arguments[1];
891                 setFillAndStroke(this, params);
892             } else if (arguments.length == 1 && typeof arguments[0] == "object") {
893                 setFillAndStroke(this, arguments[0]);
894             }
895             return this;
896         };
897         Element.prototype.toFront = function () {
898             this.node.parentNode.appendChild(this.node);
899             return this;
900         };
901         Element.prototype.toBack = function () {
902             if (this.node.parentNode.firstChild != this.node) {
903                 this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
904             }
905             return this;
906         };
907         Element.prototype.insertAfter = function (element) {
908             if (element.node.nextSibling) {
909                 element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
910             } else {
911                 element.node.parentNode.appendChild(this.node);
912             }
913             return this;
914         };
915         Element.prototype.insertBefore = function (element) {
916             element.node.parentNode.insertBefore(this.node, element.node);
917             return this;
918         };
919         theCircle = function (svg, x, y, r) {
920             var el = document.createElementNS(svg.svgns, "circle");
921             el.setAttribute("cx", x);
922             el.setAttribute("cy", y);
923             el.setAttribute("r", r);
924             el.setAttribute("fill", "none");
925             el.setAttribute("stroke", "#000");
926             if (svg.canvas) {
927                 svg.canvas.appendChild(el);
928             }
929             var res = new Element(el, svg);
930             res.attrs = res.attrs || {};
931             res.attrs.cx = x;
932             res.attrs.cy = y;
933             res.attrs.r = r;
934             res.attrs.stroke = "#000";
935             res.type = "circle";
936             return res;
937         };
938         theRect = function (svg, x, y, w, h, r) {
939             var el = document.createElementNS(svg.svgns, "rect");
940             el.setAttribute("x", x);
941             el.setAttribute("y", y);
942             el.setAttribute("width", w);
943             el.setAttribute("height", h);
944             if (r) {
945                 el.setAttribute("rx", r);
946                 el.setAttribute("ry", r);
947             }
948             el.setAttribute("fill", "none");
949             el.setAttribute("stroke", "#000");
950             if (svg.canvas) {
951                 svg.canvas.appendChild(el);
952             }
953             var res = new Element(el, svg);
954             res.attrs = res.attrs || {};
955             res.attrs.x = x;
956             res.attrs.y = y;
957             res.attrs.width = w;
958             res.attrs.height = h;
959             res.attrs.stroke = "#000";
960             if (r) {
961                 res.attrs.rx = res.attrs.ry = r;
962             }
963             res.type = "rect";
964             return res;
965         };
966         theEllipse = function (svg, x, y, rx, ry) {
967             var el = document.createElementNS(svg.svgns, "ellipse");
968             el.setAttribute("cx", x);
969             el.setAttribute("cy", y);
970             el.setAttribute("rx", rx);
971             el.setAttribute("ry", ry);
972             el.setAttribute("fill", "none");
973             el.setAttribute("stroke", "#000");
974             if (svg.canvas) {
975                 svg.canvas.appendChild(el);
976             }
977             var res = new Element(el, svg);
978             res.attrs = res.attrs || {};
979             res.attrs.cx = x;
980             res.attrs.cy = y;
981             res.attrs.rx = rx;
982             res.attrs.ry = ry;
983             res.attrs.stroke = "#000";
984             res.type = "ellipse";
985             return res;
986         };
987         theImage = function (svg, src, x, y, w, h) {
988             var el = document.createElementNS(svg.svgns, "image");
989             el.setAttribute("x", x);
990             el.setAttribute("y", y);
991             el.setAttribute("width", w);
992             el.setAttribute("height", h);
993             el.setAttribute("preserveAspectRatio", "none");
994             el.setAttributeNS(svg.xlink, "href", src);
995             if (svg.canvas) {
996                 svg.canvas.appendChild(el);
997             }
998             var res = new Element(el, svg);
999             res.attrs = res.attrs || {};
1000             res.attrs.x = x;
1001             res.attrs.y = y;
1002             res.attrs.width = w;
1003             res.attrs.height = h;
1004             res.type = "image";
1005             return res;
1006         };
1007         theText = function (svg, x, y, text) {
1008             var el = document.createElementNS(svg.svgns, "text");
1009             el.setAttribute("x", x);
1010             el.setAttribute("y", y);
1011             el.setAttribute("text-anchor", "middle");
1012             el.setAttribute("fill", "#000");
1013             if (text) {
1014                 el.appendChild(document.createTextNode(text));
1015             }
1016             if (svg.canvas) {
1017                 svg.canvas.appendChild(el);
1018             }
1019             var res = new Element(el, svg);
1020             res.attrs = res.attrs || {};
1021             res.attrs.x = x;
1022             res.attrs.y = y;
1023             res.attrs.fill = "#000";
1024             res.type = "text";
1025             return res;
1026         };
1027         theGroup = function (svg) {
1028             var el = document.createElementNS(svg.svgns, "g");
1029             if (svg.canvas) {
1030                 svg.canvas.appendChild(el);
1031             }
1032             var i = new Element(el, svg);
1033             for (var f in svg) {
1034                 if (f[0] != "_" && typeof svg[f] == "function") {
1035                     i[f] = (function (f) {
1036                         return function () {
1037                             var e = svg[f].apply(svg, arguments);
1038                             el.appendChild(e[0]);
1039                             return e;
1040                         };
1041                     })(f);
1042                 }
1043             }
1044             i.type = "group";
1045             return i;
1046         };
1047         R._create = function () {
1048             // container, width, height
1049             // x, y, width, height
1050             if (typeof arguments[0] == "string") {
1051                 var container = document.getElementById(arguments[0]);
1052                 var width = arguments[1];
1053                 var height = arguments[2];
1054             }
1055             if (typeof arguments[0] == "object") {
1056                 var container = arguments[0];
1057                 var width = arguments[1];
1058                 var height = arguments[2];
1059             }
1060             if (typeof arguments[0] == "number") {
1061                 var container = 1,
1062                     x = arguments[0],
1063                     y = arguments[1],
1064                     width = arguments[2],
1065                     height = arguments[3];
1066             }
1067             if (!container) {
1068                 throw new Error("SVG container not found.");
1069             }
1070             paper.canvas = document.createElementNS(paper.svgns, "svg");
1071             paper.canvas.setAttribute("width", width || 320);
1072             paper.width = width || 320;
1073             paper.canvas.setAttribute("height", height || 200);
1074             paper.height = height || 200;
1075             if (container == 1) {
1076                 document.body.appendChild(paper.canvas);
1077                 paper.canvas.style.position = "absolute";
1078                 paper.canvas.style.left = x + "px";
1079                 paper.canvas.style.top = y + "px";
1080             } else {
1081                 if (container.firstChild) {
1082                     container.insertBefore(paper.canvas, container.firstChild);
1083                 } else {
1084                     container.appendChild(paper.canvas);
1085                 }
1086             }
1087             container = {
1088                 canvas: paper.canvas,
1089                 clear: function () {
1090                     while (this.canvas.firstChild) {
1091                         this.canvas.removeChild(this.canvas.firstChild);
1092                     }
1093                     this.defs = document.createElementNS(paper.svgns, "defs");
1094                     this.canvas.appendChild(this.defs);
1095                 }
1096             };
1097             for (var prop in paper) {
1098                 if (prop != "create") {
1099                     container[prop] = paper[prop];
1100                 }
1101             }
1102             for (var prop in R.fn) {
1103                 if (!container[prop]) {
1104                     container[prop] = R.fn[prop];
1105                 }
1106             }
1107             container.clear();
1108             return container;
1109         };
1110         paper.remove = function () {
1111             this.canvas.parentNode.removeChild(this.canvas);
1112         };
1113         paper.svgns = "http://www.w3.org/2000/svg";
1114         paper.xlink = "http://www.w3.org/1999/xlink";
1115         paper.safari = function () {
1116             if (navigator.vendor == "Apple Computer, Inc.") {
1117                 var rect = this.rect(-this.width, -this.height, this.width * 3, this.height * 3).attr({stroke: "none"});
1118                 setTimeout(function () {rect.remove();}, 0);
1119             }
1120         };
1121     }
1122
1123     // VML
1124     if (R.vml) {
1125         thePath = function (params, pathString, VML) {
1126             var g = document.createElement("rvml:group"), gl = g.style;
1127             gl.position = "absolute";
1128             gl.left = 0;
1129             gl.top = 0;
1130             gl.width = VML.width + "px";
1131             gl.height = VML.height + "px";
1132             var el = document.createElement("rvml:shape"), ol = el.style;
1133             ol.width = VML.width + "px";
1134             ol.height = VML.height + "px";
1135             el.path = "";
1136             if (params["class"]) {
1137                 el.className = params["class"];
1138             }
1139             el.coordsize = this.coordsize;
1140             el.coordorigin = this.coordorigin;
1141             g.appendChild(el);
1142             VML.canvas.appendChild(g);
1143             var p = new Element(el, g, VML);
1144             p.isAbsolute = true;
1145             p.type = "path";
1146             p.path = [];
1147             p.last = {x: 0, y: 0, bx: 0, by: 0, isAbsolute: true};
1148             p.Path = "";
1149             p.absolutely = function () {
1150                 this.isAbsolute = true;
1151                 return this;
1152             };
1153             p.relatively = function () {
1154                 this.isAbsolute = false;
1155                 return this;
1156             };
1157             p.moveTo = function (x, y) {
1158                 var d = this.isAbsolute?"m":"t";
1159                 d += Math.round(parseFloat(x, 10)) + " " + Math.round(parseFloat(y, 10));
1160                 this.node.path = this.Path += d;
1161                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
1162                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
1163                 this.last.isAbsolute = this.isAbsolute;
1164                 this.attrs.path += (this.isAbsolute ? "M" : "m") + [x, y];
1165                 return this;
1166             };
1167             p.lineTo = function (x, y) {
1168                 var d = this.isAbsolute?"l":"r";
1169                 d += Math.round(parseFloat(x, 10)) + " " + Math.round(parseFloat(y, 10));
1170                 this[0].path = this.Path += d;
1171                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
1172                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
1173                 this.last.isAbsolute = this.isAbsolute;
1174                 this.attrs.path += (this.isAbsolute ? "L" : "l") + [x, y];
1175                 return this;
1176             };
1177             p.arcTo = function (rx, ry, large_arc_flag, sweep_flag, x2, y2) {
1178                 // for more information of where this math came from visit:
1179                 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1180                 x2 = (this.isAbsolute ? 0 : this.last.x) + x2;
1181                 y2 = (this.isAbsolute ? 0 : this.last.y) + y2;
1182                 var x1 = this.last.x,
1183                     y1 = this.last.y,
1184                     x = (x1 - x2) / 2,
1185                     y = (y1 - y2) / 2,
1186                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
1187                         Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * y * y - ry * ry * x * x) / (rx * rx * y * y + ry * ry * x * x)),
1188                     cx = k * rx * y / ry + (x1 + x2) / 2,
1189                     cy = k * -ry * x / rx + (y1 + y2) / 2,
1190                     d = sweep_flag ? (this.isAbsolute ? "wa" : "wr") : (this.isAbsolute ? "at" : "ar"),
1191                     left = Math.round(cx - rx),
1192                     top = Math.round(cy - ry);
1193                 d += [left, top, Math.round(left + rx * 2), Math.round(top + ry * 2), Math.round(x1), Math.round(y1), Math.round(parseFloat(x2, 10)), Math.round(parseFloat(y2, 10))].join(", ");
1194                 this.node.path = this.Path += d;
1195                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x2, 10);
1196                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y2, 10);
1197                 this.last.isAbsolute = this.isAbsolute;
1198                 this.attrs.path += (this.isAbsolute ? "A" : "a") + [rx, ry, 0, large_arc_flag, sweep_flag, x2, y2];
1199                 return this;
1200             };
1201             p.cplineTo = function (x1, y1, w1) {
1202                 if (!w1) {
1203                     return this.lineTo(x1, y1);
1204                 } else {
1205                     var x = Math.round(Math.round(parseFloat(x1, 10) * 100) / 100),
1206                         y = Math.round(Math.round(parseFloat(y1, 10) * 100) / 100),
1207                         w = Math.round(Math.round(parseFloat(w1, 10) * 100) / 100),
1208                         d = this.isAbsolute ? "c" : "v",
1209                         attr = [Math.round(this.last.x) + w, Math.round(this.last.y), x - w, y, x, y],
1210                         svgattr = [this.last.x + w1, this.last.y, x1 - w1, y1, x1, y1];
1211                     d += attr.join(" ") + " ";
1212                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + attr[4];
1213                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + attr[5];
1214                     this.last.bx = attr[2];
1215                     this.last.by = attr[3];
1216                     this.node.path = this.Path += d;
1217                     this.attrs.path += (this.isAbsolute ? "C" : "c") + svgattr;
1218                     return this;
1219                 }
1220             };
1221             p.curveTo = function () {
1222                 var d = this.isAbsolute ? "c" : "v";
1223                 if (arguments.length == 6) {
1224                     this.last.bx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1225                     this.last.by = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1226                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[4], 10);
1227                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[5], 10);
1228                     d += [Math.round(parseFloat(arguments[0], 10)),
1229                          Math.round(parseFloat(arguments[1], 10)),
1230                          Math.round(parseFloat(arguments[2], 10)),
1231                          Math.round(parseFloat(arguments[3], 10)),
1232                          Math.round(parseFloat(arguments[4], 10)),
1233                          Math.round(parseFloat(arguments[5], 10))].join(" ") + " ";
1234                     this.last.isAbsolute = this.isAbsolute;
1235                     this.attrs.path += (this.isAbsolute ? "C" : "c") + Array.prototype.splice.call(arguments, 0, arguments.length);
1236                 }
1237                 if (arguments.length == 4) {
1238                     var bx = this.last.x * 2 - this.last.bx;
1239                     var by = this.last.y * 2 - this.last.by;
1240                     this.last.bx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[0], 10);
1241                     this.last.by = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[1], 10);
1242                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1243                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1244                     d += [Math.round(bx), Math.round(by),
1245                          Math.round(parseFloat(arguments[0], 10)),
1246                          Math.round(parseFloat(arguments[1], 10)),
1247                          Math.round(parseFloat(arguments[2], 10)),
1248                          Math.round(parseFloat(arguments[3], 10))].join(" ") + " ";
1249                      this.attrs.path += (this.isAbsolute ? "S" : "s") + Array.prototype.splice.call(arguments, 0, arguments.length);
1250                 }
1251                 this.node.path = this.Path += d;
1252                 return this;
1253             };
1254             p.qcurveTo = function () {
1255                 var d = "qb";
1256                 if (arguments.length == 4) {
1257                     this.last.qx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[0], 10);
1258                     this.last.qy = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[1], 10);
1259                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1260                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1261                     d += [Math.round(this.last.qx),
1262                          Math.round(this.last.qy),
1263                          Math.round(this.last.x),
1264                          Math.round(this.last.y)].join(" ") + " ";
1265                     this.last.isAbsolute = this.isAbsolute;
1266                     this.attrs.path += (this.isAbsolute ? "Q" : "q") + Array.prototype.splice.call(arguments, 0, arguments.length);
1267                 }
1268                 if (arguments.length == 2) {
1269                     this.last.qx = this.last.x * 2 - this.last.qx;
1270                     this.last.qy = this.last.y * 2 - this.last.qy;
1271                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1272                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1273                     d += [Math.round(this.last.qx),
1274                          Math.round(this.last.qy),
1275                          Math.round(this.last.x),
1276                          Math.round(this.last.y)].join(" ") + " ";
1277                      this.attrs.path += (this.isAbsolute ? "T" : "t") + Array.prototype.splice.call(arguments, 0, arguments.length);
1278                 }
1279                 this.node.path = this.Path += d;
1280                 this.path.push({type: "qcurve", arg: [].slice.call(arguments, 0), pos: this.isAbsolute});
1281                 return this;
1282             };
1283             p.addRoundedCorner = function (r, dir) {
1284                 var R = .5522 * r, rollback = this.isAbsolute, o = this;
1285                 if (rollback) {
1286                     this.relatively();
1287                     rollback = function () {
1288                         o.absolutely();
1289                     };
1290                 } else {
1291                     rollback = function () {};
1292                 }
1293                 var actions = {
1294                     l: function () {
1295                         return {
1296                             u: function () {
1297                                 o.curveTo(-R, 0, -r, -(r - R), -r, -r);
1298                             },
1299                             d: function () {
1300                                 o.curveTo(-R, 0, -r, r - R, -r, r);
1301                             }
1302                         };
1303                     },
1304                     r: function () {
1305                         return {
1306                             u: function () {
1307                                 o.curveTo(R, 0, r, -(r - R), r, -r);
1308                             },
1309                             d: function () {
1310                                 o.curveTo(R, 0, r, r - R, r, r);
1311                             }
1312                         };
1313                     },
1314                     u: function () {
1315                         return {
1316                             r: function () {
1317                                 o.curveTo(0, -R, -(R - r), -r, r, -r);
1318                             },
1319                             l: function () {
1320                                 o.curveTo(0, -R, R - r, -r, -r, -r);
1321                             }
1322                         };
1323                     },
1324                     d: function () {
1325                         return {
1326                             r: function () {
1327                                 o.curveTo(0, R, -(R - r), r, r, r);
1328                             },
1329                             l: function () {
1330                                 o.curveTo(0, R, R - r, r, -r, r);
1331                             }
1332                         };
1333                     }
1334                 };
1335                 actions[dir.charAt(0)]()[dir.charAt(1)]();
1336                 rollback();
1337                 return o;
1338             };
1339             p.andClose = function () {
1340                 this.node.path = (this.Path += "x e");
1341                 this.attrs.path += "z";
1342                 return this;
1343             };
1344             if (pathString) {
1345                 p.absolutely();
1346                 p.attrs.path = "";
1347                 paper.pathfinder(p, "" + pathString);
1348             }
1349             p.setBox();
1350             setFillAndStroke(p, params);
1351             if (params.gradient) {
1352                 addGrdientFill(p, params.gradient);
1353             }
1354             return p;
1355         };
1356         setFillAndStroke = function (o, params) {
1357             var s = o[0].style;
1358             o.attrs = o.attrs || {};
1359             for (var par in params) {
1360                 o.attrs[par] = params[par];
1361             }
1362             if (params.path && o.type == "path") {
1363                 o.Path = "";
1364                 o.path = [];
1365                 paper.pathfinder(o, params.path);
1366             }
1367             if (params.rotation != null) {
1368                 o.Group.style.rotation = params.rotation;
1369             }
1370             if (params.translation) {
1371                 var xy = params.translation.split(/[, ]+/);
1372                 o.translate(xy[0], xy[1]);
1373             }
1374             if (params.scale) {
1375                 var xy = params.scale.split(/[, ]+/);
1376                 o.scale(xy[0], xy[1]);
1377             }
1378             if (o.type == "image" && params.opacity) {
1379                 o.node.filterOpacity = " progid:DXImageTransform.Microsoft.Alpha(opacity=" + (params.opacity * 100) + ")";
1380                 o.node.style.filter = (o.node.filterMatrix || "") + (o.node.filterOpacity || "");
1381             }
1382             params["font-family"] && (s.fontFamily = params["font-family"]);
1383             params["font-size"] && (s.fontSize = params["font-size"]);
1384             params["font"] && (s.font = params["font"]);
1385             params["font-weight"] && (s.fontWeight = params["font-weight"]);
1386             if (typeof params.opacity != "undefined" || typeof params["stroke-width"] != "undefined" || typeof params.fill != "undefined" || typeof params.stroke != "undefined") {
1387                 o = o.shape || o.node;
1388                 var fill = (o.getElementsByTagName("fill") && o.getElementsByTagName("fill")[0]) || document.createElement("rvml:fill");
1389                 if ("fill-opacity" in params || "opacity" in params) {
1390                     fill.opacity = ((params["fill-opacity"] + 1 || 2) - 1) * ((params.opacity + 1 || 2) - 1);
1391                 }
1392                 if (params.fill) {
1393                     fill.on = true;
1394                 }
1395                 if (fill.on == undefined || params.fill == "none") {
1396                     fill.on = false;
1397                 }
1398                 if (fill.on && params.fill) {
1399                     var isURL = params.fill.match(/^url\(([^\)]+)\)$/i);
1400                     if (isURL) {
1401                         fill.src = isURL[1];
1402                         fill.type = "tile";
1403                     } else {
1404                         fill.color = params.fill;
1405                         fill.src = "";
1406                         fill.type = "solid";
1407                     }
1408                 }
1409                 o.appendChild(fill);
1410                 var stroke = (o.getElementsByTagName("stroke") && o.getElementsByTagName("stroke")[0]) || document.createElement("rvml:stroke");
1411                 if ((params.stroke && params.stroke != "none") || params["stroke-width"] || params["stroke-opacity"] || params["stroke-dasharray"]) {
1412                     stroke.on = true;
1413                 }
1414                 if (params.stroke == "none" || typeof stroke.on == "undefined") {
1415                     stroke.on = false;
1416                 }
1417                 if (stroke.on && params.stroke) {
1418                     stroke.color = params.stroke;
1419                 }
1420                 stroke.opacity = ((params["stroke-opacity"] + 1 || 2) - 1) * ((params.opacity + 1 || 2) - 1);
1421                 params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
1422                 stroke.miterlimit = params["stroke-miterlimit"] || 8;
1423                 params["stroke-linecap"] && (stroke.endcap = {butt: "flat", square: "square", round: "round"}[params["stroke-linecap"]] || "miter");
1424                 params["stroke-width"] && (stroke.weight = (parseFloat(params["stroke-width"], 10) || 1) * 12 / 16);
1425                 if (params["stroke-dasharray"]) {
1426                     var dasharray = {
1427                         "-": "shortdash",
1428                         ".": "shortdot",
1429                         "-.": "shortdashdot",
1430                         "-..": "shortdashdotdot",
1431                         ". ": "dot",
1432                         "- ": "dash",
1433                         "--": "longdash",
1434                         "- .": "dashdot",
1435                         "--.": "longdashdot",
1436                         "--..": "longdashdotdot"
1437                     };
1438                     stroke.dashstyle = dasharray[params["stroke-dasharray"]] || "";
1439                 }
1440                 o.appendChild(stroke);
1441             }
1442         };
1443         addGrdientFill = function (o, gradient) {
1444             o.attrs = o.attrs || {};
1445             o.attrs.gradient = gradient;
1446             o = o.shape || o[0];
1447             var fill = o.getElementsByTagName("fill");
1448             if (fill.length) {
1449                 fill = fill[0];
1450             } else {
1451                 fill = document.createElement("rvml:fill");
1452             }
1453             if (gradient.dots.length) {
1454                 fill.on = true;
1455                 fill.method = "none";
1456                 fill.type = (gradient.type.toLowerCase() == "linear") ? "gradient" : "gradientTitle";
1457                 if (typeof gradient.dots[0].color != "undefined") {
1458                     fill.color = gradient.dots[0].color || "#000";
1459                 }
1460                 if (typeof gradient.dots[gradient.dots.length - 1].color != "undefined") {
1461                     fill.color2 = gradient.dots[gradient.dots.length - 1].color || "#000";
1462                 }
1463                 var colors = [];
1464                 for (var i = 0, ii = gradient.dots.length; i < ii; i++) {
1465                     if (gradient.dots[i].offset) {
1466                         colors.push(gradient.dots[i].offset + " " + gradient.dots[i].color);
1467                     }
1468                 };
1469                 var fillOpacity = gradient.dots[0].opacity || 1;
1470                 var fillOpacity2 = gradient.dots[gradient.dots.length - 1].opacity || 1;
1471                 if (colors) {
1472                     fill.colors.value = colors.join(",");
1473                     fillOpacity2 += fillOpacity;
1474                     fillOpacity = fillOpacity2 - fillOpacity;
1475                     fillOpacity2 -= fillOpacity;
1476                 }
1477                 fill.setAttribute("opacity", fillOpacity);
1478                 fill.setAttribute("opacity2", fillOpacity2);
1479                 if (gradient.vector) {
1480                     var angle = Math.round(Math.atan((parseFloat(gradient.vector[3], 10) - parseFloat(gradient.vector[1], 10)) / (parseFloat(gradient.vector[2], 10) - parseFloat(gradient.vector[0], 10))) * 57.29) || 0;
1481                     fill.angle = 270 - angle;
1482                 }
1483                 if (gradient.type.toLowerCase() == "radial") {
1484                     fill.focus = "100%";
1485                     fill.focusposition = "0.5 0.5";
1486                 }
1487             }
1488         };
1489         var Element = function (node, group, vml) {
1490             var Rotation = 0,
1491                 RotX = 0,
1492                 RotY = 0,
1493                 Scale = 1;
1494             this[0] = node;
1495             this.node = node;
1496             this.X = 0;
1497             this.Y = 0;
1498             this.attrs = {};
1499             this.Group = group;
1500             this.vml = vml;
1501             this._ = {
1502                 tx: 0,
1503                 ty: 0,
1504                 rt: 0,
1505                 sx: 1,
1506                 sy: 1
1507             };
1508         };
1509         Element.prototype.rotate = function (deg, isAbsolute) {
1510             if (deg == undefined) {
1511                 return this._.rt;
1512             }
1513             if (isAbsolute) {
1514                 this._.rt = deg;
1515             } else {
1516                 this._.rt += deg;
1517             }
1518             this.Group.style.rotation = this._.rt;
1519             return this;
1520         };
1521         Element.prototype.setBox = function (params) {
1522             var gs = this.Group.style,
1523                 os = this[0].style;
1524             for (var i in params) {
1525                 this.attrs[i] = params[i];
1526             }
1527             var attr = this.attrs, x, y, w, h;
1528             switch (this.type) {
1529                 case "circle": 
1530                     x = attr.cx - attr.r;
1531                     y = attr.cy - attr.r;
1532                     w = h = attr.r * 2;
1533                     break;
1534                 case "ellipse":
1535                     x = attr.cx - attr.rx;
1536                     y = attr.cy - attr.ry;
1537                     w = attr.rx * 2;
1538                     h = attr.ry * 2;
1539                     break;
1540                 case "rect":
1541                 case "image":
1542                     x = attr.x;
1543                     y = attr.y;
1544                     w = attr.width || 0;
1545                     h = attr.height || 0;
1546                     break;
1547                 case "text":
1548                     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("");
1549                     return;
1550                 case "path":
1551                     if (!this.attrs.path) {
1552                         x = 0;
1553                         y = 0;
1554                         w = this.vml.width;
1555                         h = this.vml.height;
1556                     } else {
1557                         var dim = Raphael.pathDimensions(this.attrs.path),
1558                         x = dim.x;
1559                         y = dim.y;
1560                         w = dim.width;
1561                         h = dim.height;
1562                     }
1563                     break;
1564                 default:
1565                     x = 0;
1566                     y = 0;
1567                     w = this.vml.width;
1568                     h = this.vml.height;
1569                     break;
1570             }
1571             if (this.type == "path") {
1572                 var left = Math.round(this.vml.width / 2 - w / 2 - x),
1573                     top = Math.round(this.vml.height / 2 - h / 2 - y);
1574                 gs.left = - left + "px";
1575                 gs.top = - top + "px";
1576                 this.X = left;
1577                 this.Y = top;
1578                 this.W = w;
1579                 this.H = h;
1580                 os.top = top + "px";
1581                 os.left = left + "px";
1582             } else {
1583                 var left = this.vml.width / 2 - w / 2,
1584                     top = this.vml.height / 2 - h / 2;
1585                 gs.position = "absolute";
1586                 gs.left = x - left + "px";
1587                 gs.top = y - top + "px";
1588                 this.X = x - left;
1589                 this.Y = y - top;
1590                 this.W = w;
1591                 this.H = h;
1592                 gs.width = this.vml.width + "px";
1593                 gs.height = this.vml.height + "px";
1594                 os.position = "absolute";
1595                 os.top = top + "px";
1596                 os.left = left + "px";
1597                 os.width = w + "px";
1598                 os.height = h + "px";
1599             }
1600         };
1601         Element.prototype.hide = function () {
1602             this.Group.style.display = "none";
1603             return this;
1604         };
1605         Element.prototype.show = function () {
1606             this.Group.style.display = "block";
1607             return this;
1608         };
1609         Element.prototype.translate = function (x, y) {
1610             if (x == undefined && y == undefined) {
1611                 return {x: this._.tx, y: this._.ty};
1612             }
1613             this._.tx += +x;
1614             this._.ty += +y;
1615             if (this.type == "path") {
1616                 var path = this.attrs.path;
1617                 path = Raphael.pathToRelative(path);
1618                 path[0][1] += +x;
1619                 path[0][2] += +y;
1620                 this.attr({path: path.join(" ")});
1621             }
1622             this.setBox({x: this._.tx, y: this._.ty});
1623             return this;
1624         };
1625         Element.prototype.getBBox = function () {
1626             return {
1627                 x: this.X,
1628                 y: this.Y,
1629                 width: this.W,
1630                 height: this.H
1631             };
1632         };
1633         Element.prototype.remove = function () {
1634             this[0].parentNode.removeChild(this[0]);
1635             this.Group.parentNode.removeChild(this.Group);
1636             this.shape && this.shape.parentNode.removeChild(this.shape);
1637         };
1638         Element.prototype.attr = function () {
1639             if (arguments.length == 1 && typeof arguments[0] == "string") {
1640                 if (arguments[0] == "translation") {
1641                     return this.translate();
1642                 }
1643                 return this.attrs[arguments[0]];
1644             }
1645             if (this.attrs && arguments.length == 1 && arguments[0] instanceof Array) {
1646                 var values = {};
1647                 for (var i = 0, ii = arguments[0].length; i < ii; i++) {
1648                     values[arguments[0][i]] = this.attrs[arguments[0][i]];
1649                 };
1650                 return values;
1651             }
1652             if (this[0].tagName.toLowerCase() == "group") {
1653                 var children = this[0].childNodes;
1654                 this.attrs = this.attrs || {};
1655                 if (arguments.length == 2) {
1656                     this.attrs[arguments[0]] = arguments[1];
1657                 } else if (arguments.length == 1 || typeof arguments[0] == "object") {
1658                     for (var j in arguments[0]) {
1659                         this.attrs[j] = arguments[0][j];
1660                     }
1661                 }
1662                 for (var i = 0, ii = children.length; i < ii; i++) {
1663                     this.attr.apply(new item(children[i], this[0], this.vml), arguments);
1664                 }
1665             } else {
1666                 var params;
1667                 if (arguments.length == 2) {
1668                     params = {};
1669                     params[arguments[0]] = arguments[1];
1670                 }
1671                 if (arguments.length == 1 && typeof arguments[0] == "object") {
1672                     params = arguments[0];
1673                 }
1674                 if (params) {
1675                     setFillAndStroke(this, params);
1676                     this.setBox(params);
1677                     if (params.gradient) {
1678                         addGrdientFill(this, params.gradient);
1679                     }
1680                     if (params.text && this.type == "text") {
1681                         this[0].string = params.text;
1682                     }
1683                     if (params.id) {
1684                         this[0].id = params.id;
1685                     }
1686                 }
1687             }
1688             return this;
1689         };
1690         Element.prototype.toFront = function () {
1691             this.Group.parentNode.appendChild(this.Group);
1692             return this;
1693         };
1694         Element.prototype.toBack = function () {
1695             if (this.Group.parentNode.firstChild != this.Group) {
1696                 this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
1697             }
1698             return this;
1699         };
1700         Element.prototype.insertAfter = function (element) {
1701             if (element.Group.nextSibling) {
1702                 element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling);
1703             } else {
1704                 element.Group.parentNode.appendChild(this.Group);
1705             }
1706             return this;
1707         };
1708         Element.prototype.insertBefore = function (element) {
1709             element.Group.parentNode.insertBefore(this.Group, element.Group);
1710             return this;
1711         };
1712         theCircle = function (vml, x, y, r) {
1713             var g = document.createElement("rvml:group");
1714             var o = document.createElement("rvml:oval");
1715             g.appendChild(o);
1716             vml.canvas.appendChild(g);
1717             var res = new Element(o, g, vml);
1718             setFillAndStroke(res, {stroke: "#000", fill: "none"});
1719             res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
1720             res.attrs.cx = x;
1721             res.attrs.cy = y;
1722             res.attrs.r = r;
1723             res.type = "circle";
1724             return res;
1725         };
1726         theRect = function (vml, x, y, w, h, r) {
1727             var g = document.createElement("rvml:group");
1728             var o = document.createElement(r ? "rvml:roundrect" : "rvml:rect");
1729             if (r) {
1730                 o.arcsize = r / (Math.min(w, h));
1731             }
1732             g.appendChild(o);
1733             vml.canvas.appendChild(g);
1734             var res = new Element(o, g, vml);
1735             setFillAndStroke(res, {stroke: "#000"});
1736             res.setBox({x: x, y: y, width: w, height: h});
1737             res.attrs.x = x;
1738             res.attrs.y = y;
1739             res.attrs.w = w;
1740             res.attrs.h = h;
1741             res.attrs.r = r;
1742             res.type = "rect";
1743             return res;
1744         };
1745         theEllipse = function (vml, x, y, rx, ry) {
1746             var g = document.createElement("rvml:group");
1747             var o = document.createElement("rvml:oval");
1748             g.appendChild(o);
1749             vml.canvas.appendChild(g);
1750             var res = new Element(o, g, vml);
1751             setFillAndStroke(res, {stroke: "#000"});
1752             res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
1753             res.attrs.cx = x;
1754             res.attrs.cy = y;
1755             res.attrs.rx = rx;
1756             res.attrs.ry = ry;
1757             res.type = "ellipse";
1758             return res;
1759         };
1760         theImage = function (vml, src, x, y, w, h) {
1761             var g = document.createElement("rvml:group");
1762             var o = document.createElement("rvml:image");
1763             o.src = src;
1764             g.appendChild(o);
1765             vml.canvas.appendChild(g);
1766             var res = new Element(o, g, vml);
1767             res.type = "image";
1768             res.setBox({x: x, y: y, width: w, height: h});
1769             res.attrs.x = x;
1770             res.attrs.y = y;
1771             res.attrs.w = w;
1772             res.attrs.h = h;
1773             return res;
1774         };
1775         theText = function (vml, x, y, text) {
1776             // @TODO: setTheBox
1777             var g = document.createElement("rvml:group"), gs = g.style;
1778             var el = document.createElement("rvml:shape"), ol = el.style;
1779             var path = document.createElement("rvml:path"), ps = path.style;
1780             path.v = ["m", Math.round(x), ", ", Math.round(y - 2), "l", Math.round(x) + 1, ", ", Math.round(y - 2)].join("");
1781             path.textpathok = true;
1782             ol.width = vml.width;
1783             ol.height = vml.height;
1784             gs.position = "absolute";
1785             gs.left = 0;
1786             gs.top = 0;
1787             gs.width = vml.width;
1788             gs.height = vml.height;
1789             var o = document.createElement("rvml:textpath");
1790             o.string = text;
1791             o.on = true;
1792             o.coordsize = vml.coordsize;
1793             o.coordorigin = vml.coordorigin;
1794             el.appendChild(o);
1795             el.appendChild(path);
1796             g.appendChild(el);
1797             vml.canvas.appendChild(g);
1798             var res = new Element(o, g, vml);
1799             res.shape = el;
1800             res.textpath = path;
1801             res.type = "text";
1802             res.attrs.x = x;
1803             res.attrs.y = y;
1804             res.attrs.w = 1;
1805             res.attrs.h = 1;
1806             setFillAndStroke(res, {stroke: "none", fill: "#000"});
1807             return res;
1808         };
1809         theGroup = function (vml) {
1810             return this;
1811         };
1812         R._create = function () {
1813             // container, width, height
1814             // x, y, width, height
1815             var container, width, height;
1816             if (typeof arguments[0] == "string") {
1817                 container = document.getElementById(arguments[0]);
1818                 width = arguments[1];
1819                 height = arguments[2];
1820             }
1821             if (typeof arguments[0] == "object") {
1822                 container = arguments[0];
1823                 width = arguments[1];
1824                 height = arguments[2];
1825             }
1826             if (typeof arguments[0] == "number") {
1827                 container = 1;
1828                 x = arguments[0];
1829                 y = arguments[1];
1830                 width = arguments[2];
1831                 height = arguments[3];
1832             }
1833             if (!container) {
1834                 throw new Error("VML container not found.");
1835             }
1836             if (!document.namespaces["rvml"]) {
1837                 document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
1838                 document.createStyleSheet().addRule("rvml\\:*", "behavior:url(#default#VML)");
1839             }
1840             var c = document.createElement("div"),
1841                 d = document.createElement("div"),
1842                 r = paper.canvas = document.createElement("rvml:group"),
1843                 cs = c.style, rs = r.style;
1844             paper.width = width;
1845             paper.height = height;
1846             width = width || "320px";
1847             height = height || "200px";
1848             cs.clip = "rect(0 " + width + " " + height + " 0)";
1849             cs.top = "-2px";
1850             cs.left = "-2px";
1851             cs.position = "absolute";
1852             rs.position = "absolute";
1853             d.style.position = "relative";
1854             rs.width  = width;
1855             rs.height = height;
1856             r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " + (height == "100%" ? height : parseFloat(height));
1857             r.coordorigin = "0 0";
1858
1859             var b = document.createElement("rvml:rect"), bs = b.style;
1860             bs.left = bs.top = 0;
1861             bs.width  = rs.width;
1862             bs.height = rs.height;
1863             b.filled = b.stroked = "f";
1864
1865             r.appendChild(b);
1866             c.appendChild(r);
1867             d.appendChild(c);
1868             if (container == 1) {
1869                 document.body.appendChild(d);
1870                 cs.position = "absolute";
1871                 cs.left = x + "px";
1872                 cs.top = y + "px";
1873                 cs.width = width;
1874                 cs.height = height;
1875                 container = {
1876                     style: {
1877                         width: width,
1878                         height: height
1879                     }
1880                 };
1881             } else {
1882                 cs.width = container.style.width = width;
1883                 cs.height = container.style.height = height;
1884                 if (container.firstChild) {
1885                     container.insertBefore(d, container.firstChild);
1886                 } else {
1887                     container.appendChild(d);
1888                 }
1889             }
1890             for (var prop in paper) {
1891                 container[prop] = paper[prop];
1892             }
1893             for (var prop in R.fn) {
1894                 if (!container[prop]) {
1895                     container[prop] = R.fn[prop];
1896                 }
1897             }
1898             container.clear = function () {
1899                 var todel = [];
1900                 for (var i = 0, ii = r.childNodes.length; i < ii; i++) {
1901                     if (r.childNodes[i] != b) {
1902                         todel.push(r.childNodes[i]);
1903                     }
1904                 }
1905                 for (i = 0, ii = todel.length; i < ii; i++) {
1906                     r.removeChild(todel[i]);
1907                 }
1908             };
1909             return container;
1910         };
1911         paper.remove = function () {
1912             this.canvas.parentNode.parentNode.parentNode.removeChild(this.canvas.parentNode.parentNode);
1913         };
1914         paper.safari = function () {};
1915     }
1916
1917     // rest
1918
1919     // Events
1920     var addEvent = (function () {
1921         if (document.addEventListener) {
1922             return function (obj, type, fn) {
1923                 obj.addEventListener(type, fn, false);
1924             };
1925         } else if (document.attachEvent) {
1926             return function (obj, type, fn) {
1927                 var f = function (e) {
1928                     fn.call(this, e || window.event);
1929                 };
1930                 obj.attachEvent("on" + type, f);
1931             };
1932         }
1933     })();
1934     var events = ["click", "dblclick", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup"];
1935     for (var i = events.length; i--;) {
1936         (function (eventName) {
1937             Element.prototype[eventName] = function (fn) {
1938                 addEvent(this.node, eventName, fn);
1939                 return this;
1940             };
1941         })(events[i]);
1942     }
1943
1944     // Set
1945     var Set = function (itemsArray) {
1946         this.items = [];
1947         if (itemsArray && itemsArray.constructor == Array) {
1948             for (var i = itemsArray.length; i--;) {
1949                 if (itemsArray[i].constructor == Element) {
1950                     this.items[this.items.length] = itemsArray[i];
1951                 }
1952             }
1953         }
1954     };
1955     Set.prototype.add = function (item) {
1956         if (item && item.constructor == Element) {
1957             this.items[this.items.length] = item;
1958         }
1959         return this;
1960     };
1961     Set.prototype.remove = function (item) {
1962         if (item && item.constructor == Element) {
1963             for (var i = this.items.length; i--;) {
1964                 if (this.items[i] == item) {
1965                     this.items.splice(i, 1);
1966                     return this;
1967                 }
1968             }
1969         }
1970         return this;
1971     };
1972     for (var method in Element.prototype) {
1973         Set.prototype[method] = (function (methodname) {
1974             return function () {
1975                 for (var i = this.items.length; i--;) {
1976                     this.items[i][methodname].apply(this.items[i], arguments);
1977                 }
1978                 return this;
1979             };
1980         })(method);
1981     }
1982     Set.prototype.getBBox = function () {
1983         var x = [], y = [], w = [], h = [];
1984         for (var i = this.items.length; i--;) {
1985             var box = this.items[i].getBBox();
1986             x.push(box.x);
1987             y.push(box.y);
1988             w.push(box.x + box.width);
1989             h.push(box.y + box.height);
1990         }
1991         x = Math.min(x);
1992         y = Math.min(y);
1993         return {
1994             x: x,
1995             y: y,
1996             w: Math.max(w) - x,
1997             h: Math.max(h) - y
1998         };
1999     };
2000
2001     paper.circle = function (x, y, r) {
2002         return theCircle(this, x, y, r);
2003     };
2004     paper.rect = function (x, y, w, h, r) {
2005         return theRect(this, x, y, w, h, r);
2006     };
2007     paper.ellipse = function (x, y, rx, ry) {
2008         return theEllipse(this, x, y, rx, ry);
2009     };
2010     paper.path = function (params, pathString) {
2011         return thePath(params, pathString, this);
2012     };
2013     paper.image = function (src, x, y, w, h) {
2014         return theImage(this, src, x, y, w, h);
2015     };
2016     paper.text = function (x, y, text) {
2017         return theText(this, x, y, text);
2018     };
2019     paper.group = function () {
2020         return this;
2021     };
2022     paper.drawGrid = function (x, y, w, h, wv, hv, color) {
2023         color = color || "#000";
2024         var p = this.path({stroke: color, "stroke-width": 1})
2025                 .moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).lineTo(x, y),
2026             rowHeight = h / hv,
2027             columnWidth = w / wv;
2028         for (var i = 1; i < hv; i++) {
2029             p.moveTo(x, y + i * rowHeight).lineTo(x + w, y + i * rowHeight);
2030         }
2031         for (var i = 1; i < wv; i++) {
2032             p.moveTo(x + i * columnWidth, y).lineTo(x + i * columnWidth, y + h);
2033         }
2034         return p;
2035     };
2036     paper.pathfinder = function (p, path) {
2037         var commands = {
2038             M: function (x, y) {
2039                 this.moveTo(x, y);
2040             },
2041             C: function (x1, y1, x2, y2, x3, y3) {
2042                 this.curveTo(x1, y1, x2, y2, x3, y3);
2043             },
2044             Q: function (x1, y1, x2, y2) {
2045                 this.qcurveTo(x1, y1, x2, y2);
2046             },
2047             T: function (x, y) {
2048                 this.qcurveTo(x, y);
2049             },
2050             S: function (x1, y1, x2, y2) {
2051                 p.curveTo(x1, y1, x2, y2);
2052             },
2053             L: function (x, y) {
2054                 p.lineTo(x, y);
2055             },
2056             H: function (x) {
2057                 this.lineTo(x, this.last.y);
2058             },
2059             V: function (y) {
2060                 this.lineTo(this.last.x, y);
2061             },
2062             A: function (rx, ry, xaxisrotation, largearcflag, sweepflag, x, y) {
2063                 this.arcTo(rx, ry, largearcflag, sweepflag, x, y);
2064             },
2065             Z: function () {
2066                 this.andClose();
2067             }
2068         };
2069
2070         path = Raphael.pathToAbsolute(path);
2071         for (var i = 0, ii = path.length; i < ii; i++) {
2072             var b = path[i].shift();
2073             commands[b].apply(p, path[i]);
2074         }
2075     };
2076     paper.set = function (itemsArray) {
2077         return new Set(itemsArray);
2078     };
2079     Element.prototype.stop = function () {
2080         clearTimeout(this.animation_in_progress);
2081     };
2082     Element.prototype.scale = function (x, y) {
2083         if (x == undefined && y == undefined) {
2084             return {x: this._.sx, y: this._.sy};
2085         }
2086         y = y || x;
2087         var dx, dy, cx, cy;
2088         if (x != 0 && !(x == 1 && y == 1)) {
2089             var dirx = Math.round(x / Math.abs(x)),
2090                 diry = Math.round(y / Math.abs(y)),
2091                 s = this.node.style;
2092             dx = this.attr("x");
2093             dy = this.attr("y");
2094             cx = this.attr("cx");
2095             cy = this.attr("cy");
2096             if (dirx != 1 || diry != 1) {
2097                 if (this.transformations) {
2098                     this.transformations[2] = "scale(" + [dirx, diry] + ")";
2099                     this.node.setAttribute("transform", this.transformations.join(" "));
2100                     dx = (dirx < 0) ? -this.attr("x") - this.attrs.width * x * dirx / this._.sx : this.attr("x");
2101                     dy = (diry < 0) ? -this.attr("y") - this.attrs.height * y * diry / this._.sy : this.attr("y");
2102                     cx = this.attr("cx") * dirx;
2103                     cy = this.attr("cy") * diry;
2104                 } else {
2105                     this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11=" + dirx +
2106                         ", M12=0, M21=0, M22=" + diry +
2107                         ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')";
2108                     s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
2109                 }
2110             } else {
2111                 if (this.transformations) {
2112                     this.transformations[2] = "";
2113                     this.node.setAttribute("transform", this.transformations.join(" "));
2114                 } else {
2115                     this.node.filterMatrix = "";
2116                     s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
2117                 }
2118             }
2119             switch (this.type) {
2120                 case "rect":
2121                 case "image":
2122                     this.attr({
2123                         width: this.attrs.width * x * dirx / this._.sx,
2124                         height: this.attrs.height * y * diry / this._.sy,
2125                         x: dx,
2126                         y: dy
2127                     });
2128                     break;
2129                 case "circle":
2130                 case "ellipse":
2131                     this.attr({
2132                         rx: this.attrs.rx * x * dirx / this._.sx,
2133                         ry: this.attrs.ry * y * diry / this._.sy,
2134                         r: this.attrs.r * x * diry / this._.sx,
2135                         cx: cx,
2136                         cy: cy
2137                     });
2138                     break;
2139                 case "path":
2140                     var path = Raphael.pathToRelative(Raphael.parsePathString(this.attr("path"))), 
2141                         skip = true,
2142                         dim = Raphael.pathDimensions(this.attrs.path),
2143                         dx = -dim.width * (x - 1) / 2,
2144                         dy = -dim.height * (y - 1) / 2;
2145                     for (var i = 0, ii = path.length; i < ii; i++) {
2146                         if (path[i][0].toUpperCase() == "M" && skip) {
2147                             continue;
2148                         } else {
2149                             skip = false;
2150                         }
2151                         if (path[i][0].toUpperCase() == "A") {
2152                             path[i][path[i].length - 2] *= x * dirx;
2153                             path[i][path[i].length - 1] *= y * diry;
2154                         } else {
2155                             for (var j = 1, jj = path[i].length; j < jj; j++) {
2156                                 path[i][j] *= (j % 2) ? x * dirx / this._.sx : y * diry / this._.sy;
2157                             }
2158                         }
2159                     }
2160                     var dim2 = Raphael.pathDimensions(path),
2161                         dx = dim.x + dim.width / 2 - dim2.x - dim2.width / 2,
2162                         dy = dim.y + dim.height / 2 - dim2.y - dim2.height / 2;
2163                     path = Raphael.pathToRelative(path);
2164                     path[0][1] += dx;
2165                     path[0][2] += dy;
2166                     
2167                     this.attr({path: path.join(" ")});
2168             }
2169         }
2170         this._.sx = x;
2171         this._.sy = y;
2172         return this;
2173     };
2174     Element.prototype.animate = function (params, ms, callback) {
2175         clearTimeout(this.animation_in_progress);
2176         var from = {}, to = {}, diff = {}, t = {x: 0, y: 0};
2177         for (var attr in params) {
2178             if (attr in availableAnimAttrs) {
2179                 from[attr] = this.attr(attr);
2180                 if (typeof from[attr] == "undefined") {
2181                     from[attr] = availableAttrs[attr];
2182                 }
2183                 to[attr] = params[attr];
2184                 switch (availableAnimAttrs[attr]) {
2185                     case "number":
2186                         diff[attr] = (to[attr] - from[attr]) / ms;
2187                         break;
2188                     case "colour":
2189                         from[attr] = Raphael.getRGB(from[attr]);
2190                         var toColour = Raphael.getRGB(to[attr]);
2191                         diff[attr] = {
2192                             r: (toColour.r - from[attr].r) / ms,
2193                             g: (toColour.g - from[attr].g) / ms,
2194                             b: (toColour.b - from[attr].b) / ms
2195                         };
2196                         break;
2197                     case "path":
2198                         var pathes = Raphael.pathEqualiser(from[attr], to[attr]);
2199                         from[attr] = pathes[0];
2200                         to[attr] = pathes[1];
2201                         diff[attr] = [];
2202                         for (var i = 0, ii = from[attr].length; i < ii; i++) {
2203                             diff[attr][i] = [0];
2204                             for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
2205                                 diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
2206                             }
2207                         }
2208                         break;
2209                     case "csv":
2210                         var values = params[attr].split(/[, ]+/);
2211                         if (attr == "translation") {
2212                             from[attr] = [0, 0];
2213                             diff[attr] = [values[0] / ms, values[1] / ms];
2214                         } else {
2215                             from[attr] = from[attr].split(/[, ]+/);
2216                             diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][0]) / ms];
2217                         }
2218                         to[attr] = values;
2219                 }
2220             }
2221         }
2222         var start = new Date(),
2223             prev = 0,
2224             that = this;
2225         (function () {
2226             var time = (new Date()).getTime() - start.getTime(),
2227                 set = {},
2228                 now;
2229             if (time < ms) {
2230                 for (var attr in from) {
2231                     switch (availableAnimAttrs[attr]) {
2232                         case "number":
2233                             now = +from[attr] + time * diff[attr];
2234                             break;
2235                         case "colour":
2236                             now = "rgb(" + [
2237                                 Math.round(from[attr].r + time * diff[attr].r),
2238                                 Math.round(from[attr].g + time * diff[attr].g),
2239                                 Math.round(from[attr].b + time * diff[attr].b)
2240                             ].join(",") + ")";
2241                             break;
2242                         case "path":
2243                             now = [];
2244                             for (var i = 0, ii = from[attr].length; i < ii; i++) {
2245                                 now[i] = [from[attr][i][0]];
2246                                 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
2247                                     now[i][j] = from[attr][i][j] + time * diff[attr][i][j];
2248                                 }
2249                                 now[i] = now[i].join(" ");
2250                             }
2251                             now = now.join(" ");
2252                             break;
2253                         case "csv":
2254                             if (attr == "translation") {
2255                                 var x = diff[attr][0] * (time - prev),
2256                                     y = diff[attr][1] * (time - prev);
2257                                 t.x += x;
2258                                 t.y += y;
2259                                 now = [x, y].join(" ");
2260                             } else {
2261                                 now = [+from[attr][0] + time * diff[attr][0], +from[attr][1] + time * diff[attr][1]].join(" ");
2262                             }
2263                             break;
2264                     }
2265                     if (attr == "font-size") {
2266                         set[attr] = now + "px";
2267                     } else {
2268                         set[attr] = now;
2269                     }
2270                 }
2271                 that.attr(set);
2272                 that.animation_in_progress = setTimeout(arguments.callee, 0);
2273                 paper.safari();
2274             } else {
2275                 if (t.x || t.y) {
2276                     that.translate(-t.x, -t.y);
2277                 }
2278                 that.attr(params);
2279                 clearTimeout(that.animation_in_progress);
2280                 paper.safari();
2281                 (typeof callback == "function") && callback.call(that);
2282             }
2283             prev = time;
2284         })();
2285         return this;
2286     };
2287
2288
2289
2290 })(Raphael);