Important change so previous fix work with attr
[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             if (text) {
1013                 el.appendChild(document.createTextNode(text));
1014             }
1015             if (svg.canvas) {
1016                 svg.canvas.appendChild(el);
1017             }
1018             var res = new Element(el, svg);
1019             res.attrs = res.attrs || {};
1020             res.attrs.x = x;
1021             res.attrs.y = y;
1022             res.type = "text";
1023             setFillAndStroke(res, {font: '10px "Arial"', stroke: "none", fill: "#000"});
1024             return res;
1025         };
1026         theGroup = function (svg) {
1027             var el = document.createElementNS(svg.svgns, "g");
1028             if (svg.canvas) {
1029                 svg.canvas.appendChild(el);
1030             }
1031             var i = new Element(el, svg);
1032             for (var f in svg) {
1033                 if (f[0] != "_" && typeof svg[f] == "function") {
1034                     i[f] = (function (f) {
1035                         return function () {
1036                             var e = svg[f].apply(svg, arguments);
1037                             el.appendChild(e[0]);
1038                             return e;
1039                         };
1040                     })(f);
1041                 }
1042             }
1043             i.type = "group";
1044             return i;
1045         };
1046         R._create = function () {
1047             // container, width, height
1048             // x, y, width, height
1049             if (typeof arguments[0] == "string") {
1050                 var container = document.getElementById(arguments[0]);
1051                 var width = arguments[1];
1052                 var height = arguments[2];
1053             }
1054             if (typeof arguments[0] == "object") {
1055                 var container = arguments[0];
1056                 var width = arguments[1];
1057                 var height = arguments[2];
1058             }
1059             if (typeof arguments[0] == "number") {
1060                 var container = 1,
1061                     x = arguments[0],
1062                     y = arguments[1],
1063                     width = arguments[2],
1064                     height = arguments[3];
1065             }
1066             if (!container) {
1067                 throw new Error("SVG container not found.");
1068             }
1069             paper.canvas = document.createElementNS(paper.svgns, "svg");
1070             paper.canvas.setAttribute("width", width || 320);
1071             paper.width = width || 320;
1072             paper.canvas.setAttribute("height", height || 200);
1073             paper.height = height || 200;
1074             if (container == 1) {
1075                 document.body.appendChild(paper.canvas);
1076                 paper.canvas.style.position = "absolute";
1077                 paper.canvas.style.left = x + "px";
1078                 paper.canvas.style.top = y + "px";
1079             } else {
1080                 if (container.firstChild) {
1081                     container.insertBefore(paper.canvas, container.firstChild);
1082                 } else {
1083                     container.appendChild(paper.canvas);
1084                 }
1085             }
1086             container = {
1087                 canvas: paper.canvas,
1088                 clear: function () {
1089                     while (this.canvas.firstChild) {
1090                         this.canvas.removeChild(this.canvas.firstChild);
1091                     }
1092                     this.defs = document.createElementNS(paper.svgns, "defs");
1093                     this.canvas.appendChild(this.defs);
1094                 }
1095             };
1096             for (var prop in paper) {
1097                 if (prop != "create") {
1098                     container[prop] = paper[prop];
1099                 }
1100             }
1101             for (var prop in R.fn) {
1102                 if (!container[prop]) {
1103                     container[prop] = R.fn[prop];
1104                 }
1105             }
1106             container.clear();
1107             return container;
1108         };
1109         paper.remove = function () {
1110             this.canvas.parentNode.removeChild(this.canvas);
1111         };
1112         paper.svgns = "http://www.w3.org/2000/svg";
1113         paper.xlink = "http://www.w3.org/1999/xlink";
1114         paper.safari = function () {
1115             if (navigator.vendor == "Apple Computer, Inc.") {
1116                 var rect = this.rect(-this.width, -this.height, this.width * 3, this.height * 3).attr({stroke: "none"});
1117                 setTimeout(function () {rect.remove();}, 0);
1118             }
1119         };
1120     }
1121
1122     // VML
1123     if (R.vml) {
1124         thePath = function (params, pathString, VML) {
1125             var g = document.createElement("rvml:group"), gl = g.style;
1126             gl.position = "absolute";
1127             gl.left = 0;
1128             gl.top = 0;
1129             gl.width = VML.width + "px";
1130             gl.height = VML.height + "px";
1131             var el = document.createElement("rvml:shape"), ol = el.style;
1132             ol.width = VML.width + "px";
1133             ol.height = VML.height + "px";
1134             el.path = "";
1135             if (params["class"]) {
1136                 el.className = params["class"];
1137             }
1138             el.coordsize = this.coordsize;
1139             el.coordorigin = this.coordorigin;
1140             g.appendChild(el);
1141             VML.canvas.appendChild(g);
1142             var p = new Element(el, g, VML);
1143             p.isAbsolute = true;
1144             p.type = "path";
1145             p.path = [];
1146             p.last = {x: 0, y: 0, bx: 0, by: 0, isAbsolute: true};
1147             p.Path = "";
1148             p.absolutely = function () {
1149                 this.isAbsolute = true;
1150                 return this;
1151             };
1152             p.relatively = function () {
1153                 this.isAbsolute = false;
1154                 return this;
1155             };
1156             p.moveTo = function (x, y) {
1157                 var d = this.isAbsolute?"m":"t";
1158                 d += Math.round(parseFloat(x, 10)) + " " + Math.round(parseFloat(y, 10));
1159                 this.node.path = this.Path += d;
1160                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
1161                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
1162                 this.last.isAbsolute = this.isAbsolute;
1163                 this.attrs.path += (this.isAbsolute ? "M" : "m") + [x, y];
1164                 return this;
1165             };
1166             p.lineTo = function (x, y) {
1167                 var d = this.isAbsolute?"l":"r";
1168                 d += Math.round(parseFloat(x, 10)) + " " + Math.round(parseFloat(y, 10));
1169                 this[0].path = this.Path += d;
1170                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x, 10);
1171                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y, 10);
1172                 this.last.isAbsolute = this.isAbsolute;
1173                 this.attrs.path += (this.isAbsolute ? "L" : "l") + [x, y];
1174                 return this;
1175             };
1176             p.arcTo = function (rx, ry, large_arc_flag, sweep_flag, x2, y2) {
1177                 // for more information of where this math came from visit:
1178                 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1179                 x2 = (this.isAbsolute ? 0 : this.last.x) + x2;
1180                 y2 = (this.isAbsolute ? 0 : this.last.y) + y2;
1181                 var x1 = this.last.x,
1182                     y1 = this.last.y,
1183                     x = (x1 - x2) / 2,
1184                     y = (y1 - y2) / 2,
1185                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
1186                         Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * y * y - ry * ry * x * x) / (rx * rx * y * y + ry * ry * x * x)),
1187                     cx = k * rx * y / ry + (x1 + x2) / 2,
1188                     cy = k * -ry * x / rx + (y1 + y2) / 2,
1189                     d = sweep_flag ? (this.isAbsolute ? "wa" : "wr") : (this.isAbsolute ? "at" : "ar"),
1190                     left = Math.round(cx - rx),
1191                     top = Math.round(cy - ry);
1192                 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(", ");
1193                 this.node.path = this.Path += d;
1194                 this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(x2, 10);
1195                 this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(y2, 10);
1196                 this.last.isAbsolute = this.isAbsolute;
1197                 this.attrs.path += (this.isAbsolute ? "A" : "a") + [rx, ry, 0, large_arc_flag, sweep_flag, x2, y2];
1198                 return this;
1199             };
1200             p.cplineTo = function (x1, y1, w1) {
1201                 if (!w1) {
1202                     return this.lineTo(x1, y1);
1203                 } else {
1204                     var x = Math.round(Math.round(parseFloat(x1, 10) * 100) / 100),
1205                         y = Math.round(Math.round(parseFloat(y1, 10) * 100) / 100),
1206                         w = Math.round(Math.round(parseFloat(w1, 10) * 100) / 100),
1207                         d = this.isAbsolute ? "c" : "v",
1208                         attr = [Math.round(this.last.x) + w, Math.round(this.last.y), x - w, y, x, y],
1209                         svgattr = [this.last.x + w1, this.last.y, x1 - w1, y1, x1, y1];
1210                     d += attr.join(" ") + " ";
1211                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + attr[4];
1212                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + attr[5];
1213                     this.last.bx = attr[2];
1214                     this.last.by = attr[3];
1215                     this.node.path = this.Path += d;
1216                     this.attrs.path += (this.isAbsolute ? "C" : "c") + svgattr;
1217                     return this;
1218                 }
1219             };
1220             p.curveTo = function () {
1221                 var d = this.isAbsolute ? "c" : "v";
1222                 if (arguments.length == 6) {
1223                     this.last.bx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1224                     this.last.by = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1225                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[4], 10);
1226                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[5], 10);
1227                     d += [Math.round(parseFloat(arguments[0], 10)),
1228                          Math.round(parseFloat(arguments[1], 10)),
1229                          Math.round(parseFloat(arguments[2], 10)),
1230                          Math.round(parseFloat(arguments[3], 10)),
1231                          Math.round(parseFloat(arguments[4], 10)),
1232                          Math.round(parseFloat(arguments[5], 10))].join(" ") + " ";
1233                     this.last.isAbsolute = this.isAbsolute;
1234                     this.attrs.path += (this.isAbsolute ? "C" : "c") + Array.prototype.splice.call(arguments, 0, arguments.length);
1235                 }
1236                 if (arguments.length == 4) {
1237                     var bx = this.last.x * 2 - this.last.bx;
1238                     var by = this.last.y * 2 - this.last.by;
1239                     this.last.bx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[0], 10);
1240                     this.last.by = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[1], 10);
1241                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1242                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1243                     d += [Math.round(bx), Math.round(by),
1244                          Math.round(parseFloat(arguments[0], 10)),
1245                          Math.round(parseFloat(arguments[1], 10)),
1246                          Math.round(parseFloat(arguments[2], 10)),
1247                          Math.round(parseFloat(arguments[3], 10))].join(" ") + " ";
1248                      this.attrs.path += (this.isAbsolute ? "S" : "s") + Array.prototype.splice.call(arguments, 0, arguments.length);
1249                 }
1250                 this.node.path = this.Path += d;
1251                 return this;
1252             };
1253             p.qcurveTo = function () {
1254                 var d = "qb";
1255                 if (arguments.length == 4) {
1256                     this.last.qx = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[0], 10);
1257                     this.last.qy = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[1], 10);
1258                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1259                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1260                     d += [Math.round(this.last.qx),
1261                          Math.round(this.last.qy),
1262                          Math.round(this.last.x),
1263                          Math.round(this.last.y)].join(" ") + " ";
1264                     this.last.isAbsolute = this.isAbsolute;
1265                     this.attrs.path += (this.isAbsolute ? "Q" : "q") + Array.prototype.splice.call(arguments, 0, arguments.length);
1266                 }
1267                 if (arguments.length == 2) {
1268                     this.last.qx = this.last.x * 2 - this.last.qx;
1269                     this.last.qy = this.last.y * 2 - this.last.qy;
1270                     this.last.x = (this.isAbsolute ? 0 : this.last.x) + parseFloat(arguments[2], 10);
1271                     this.last.y = (this.isAbsolute ? 0 : this.last.y) + parseFloat(arguments[3], 10);
1272                     d += [Math.round(this.last.qx),
1273                          Math.round(this.last.qy),
1274                          Math.round(this.last.x),
1275                          Math.round(this.last.y)].join(" ") + " ";
1276                      this.attrs.path += (this.isAbsolute ? "T" : "t") + Array.prototype.splice.call(arguments, 0, arguments.length);
1277                 }
1278                 this.node.path = this.Path += d;
1279                 this.path.push({type: "qcurve", arg: [].slice.call(arguments, 0), pos: this.isAbsolute});
1280                 return this;
1281             };
1282             p.addRoundedCorner = function (r, dir) {
1283                 var R = .5522 * r, rollback = this.isAbsolute, o = this;
1284                 if (rollback) {
1285                     this.relatively();
1286                     rollback = function () {
1287                         o.absolutely();
1288                     };
1289                 } else {
1290                     rollback = function () {};
1291                 }
1292                 var actions = {
1293                     l: function () {
1294                         return {
1295                             u: function () {
1296                                 o.curveTo(-R, 0, -r, -(r - R), -r, -r);
1297                             },
1298                             d: function () {
1299                                 o.curveTo(-R, 0, -r, r - R, -r, r);
1300                             }
1301                         };
1302                     },
1303                     r: function () {
1304                         return {
1305                             u: function () {
1306                                 o.curveTo(R, 0, r, -(r - R), r, -r);
1307                             },
1308                             d: function () {
1309                                 o.curveTo(R, 0, r, r - R, r, r);
1310                             }
1311                         };
1312                     },
1313                     u: function () {
1314                         return {
1315                             r: function () {
1316                                 o.curveTo(0, -R, -(R - r), -r, r, -r);
1317                             },
1318                             l: function () {
1319                                 o.curveTo(0, -R, R - r, -r, -r, -r);
1320                             }
1321                         };
1322                     },
1323                     d: function () {
1324                         return {
1325                             r: function () {
1326                                 o.curveTo(0, R, -(R - r), r, r, r);
1327                             },
1328                             l: function () {
1329                                 o.curveTo(0, R, R - r, r, -r, r);
1330                             }
1331                         };
1332                     }
1333                 };
1334                 actions[dir.charAt(0)]()[dir.charAt(1)]();
1335                 rollback();
1336                 return o;
1337             };
1338             p.andClose = function () {
1339                 this.node.path = (this.Path += "x e");
1340                 this.attrs.path += "z";
1341                 return this;
1342             };
1343             if (pathString) {
1344                 p.absolutely();
1345                 p.attrs.path = "";
1346                 paper.pathfinder(p, "" + pathString);
1347             }
1348             p.setBox();
1349             setFillAndStroke(p, params);
1350             if (params.gradient) {
1351                 addGrdientFill(p, params.gradient);
1352             }
1353             return p;
1354         };
1355         setFillAndStroke = function (o, params) {
1356             var s = o.node.style,
1357                 res = o;
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 && (s.font = params.font);
1383             params["font-family"] && (s.fontFamily = params["font-family"]);
1384             params["font-size"] && (s.fontSize = params["font-size"]);
1385             params["font-weight"] && (s.fontWeight = params["font-weight"]);
1386             params["font-style"] && (s.fontStyle = params["font-style"]);
1387             if (typeof params.opacity != "undefined" || typeof params["stroke-width"] != "undefined" || typeof params.fill != "undefined" || typeof params.stroke != "undefined") {
1388                 o = o.shape || o.node;
1389                 var fill = (o.getElementsByTagName("fill") && o.getElementsByTagName("fill")[0]) || document.createElement("rvml:fill");
1390                 if ("fill-opacity" in params || "opacity" in params) {
1391                     fill.opacity = ((params["fill-opacity"] + 1 || 2) - 1) * ((params.opacity + 1 || 2) - 1);
1392                 }
1393                 if (params.fill) {
1394                     fill.on = true;
1395                 }
1396                 if (fill.on == undefined || params.fill == "none") {
1397                     fill.on = false;
1398                 }
1399                 if (fill.on && params.fill) {
1400                     var isURL = params.fill.match(/^url\(([^\)]+)\)$/i);
1401                     if (isURL) {
1402                         fill.src = isURL[1];
1403                         fill.type = "tile";
1404                     } else {
1405                         fill.color = params.fill;
1406                         fill.src = "";
1407                         fill.type = "solid";
1408                     }
1409                 }
1410                 o.appendChild(fill);
1411                 var stroke = (o.getElementsByTagName("stroke") && o.getElementsByTagName("stroke")[0]) || document.createElement("rvml:stroke");
1412                 if ((params.stroke && params.stroke != "none") || params["stroke-width"] || params["stroke-opacity"] || params["stroke-dasharray"]) {
1413                     stroke.on = true;
1414                 }
1415                 if (params.stroke == "none" || typeof stroke.on == "undefined") {
1416                     stroke.on = false;
1417                 }
1418                 if (stroke.on && params.stroke) {
1419                     stroke.color = params.stroke;
1420                 }
1421                 stroke.opacity = ((params["stroke-opacity"] + 1 || 2) - 1) * ((params.opacity + 1 || 2) - 1);
1422                 params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
1423                 stroke.miterlimit = params["stroke-miterlimit"] || 8;
1424                 params["stroke-linecap"] && (stroke.endcap = {butt: "flat", square: "square", round: "round"}[params["stroke-linecap"]] || "miter");
1425                 params["stroke-width"] && (stroke.weight = (parseFloat(params["stroke-width"], 10) || 1) * 12 / 16);
1426                 if (params["stroke-dasharray"]) {
1427                     var dasharray = {
1428                         "-": "shortdash",
1429                         ".": "shortdot",
1430                         "-.": "shortdashdot",
1431                         "-..": "shortdashdotdot",
1432                         ". ": "dot",
1433                         "- ": "dash",
1434                         "--": "longdash",
1435                         "- .": "dashdot",
1436                         "--.": "longdashdot",
1437                         "--..": "longdashdotdot"
1438                     };
1439                     stroke.dashstyle = dasharray[params["stroke-dasharray"]] || "";
1440                 }
1441                 o.appendChild(stroke);
1442             }
1443             if (res.type == "text") {
1444                 var span = document.createElement("span"),
1445                     s = span.style;
1446                 res.attrs.font && (s.font = res.attrs.font);
1447                 res.attrs["font-family"] && (s.fontFamily = res.attrs["font-family"]);
1448                 res.attrs["font-size"] && (s.fontSize = res.attrs["font-size"]);
1449                 res.attrs["font-weight"] && (s.fontWeight = res.attrs["font-weight"]);
1450                 res.attrs["font-style"] && (s.fontStyle = res.attrs["font-style"]);
1451                 res.node.parentNode.appendChild(span);
1452                 span.innerText = res.node.string;
1453                 res.W = res.attrs.w = span.offsetWidth;
1454                 res.H = res.attrs.h = span.offsetHeight;
1455                 res.X = res.attrs.x - Math.round(res.W / 2);
1456                 res.Y = res.attrs.y - Math.round(res.H / 2);
1457                 res.node.parentNode.removeChild(span);
1458             }
1459         };
1460         addGrdientFill = function (o, gradient) {
1461             o.attrs = o.attrs || {};
1462             o.attrs.gradient = gradient;
1463             o = o.shape || o[0];
1464             var fill = o.getElementsByTagName("fill");
1465             if (fill.length) {
1466                 fill = fill[0];
1467             } else {
1468                 fill = document.createElement("rvml:fill");
1469             }
1470             if (gradient.dots.length) {
1471                 fill.on = true;
1472                 fill.method = "none";
1473                 fill.type = (gradient.type.toLowerCase() == "linear") ? "gradient" : "gradientTitle";
1474                 if (typeof gradient.dots[0].color != "undefined") {
1475                     fill.color = gradient.dots[0].color || "#000";
1476                 }
1477                 if (typeof gradient.dots[gradient.dots.length - 1].color != "undefined") {
1478                     fill.color2 = gradient.dots[gradient.dots.length - 1].color || "#000";
1479                 }
1480                 var colors = [];
1481                 for (var i = 0, ii = gradient.dots.length; i < ii; i++) {
1482                     if (gradient.dots[i].offset) {
1483                         colors.push(gradient.dots[i].offset + " " + gradient.dots[i].color);
1484                     }
1485                 };
1486                 var fillOpacity = gradient.dots[0].opacity || 1;
1487                 var fillOpacity2 = gradient.dots[gradient.dots.length - 1].opacity || 1;
1488                 if (colors) {
1489                     fill.colors.value = colors.join(",");
1490                     fillOpacity2 += fillOpacity;
1491                     fillOpacity = fillOpacity2 - fillOpacity;
1492                     fillOpacity2 -= fillOpacity;
1493                 }
1494                 fill.setAttribute("opacity", fillOpacity);
1495                 fill.setAttribute("opacity2", fillOpacity2);
1496                 if (gradient.vector) {
1497                     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;
1498                     fill.angle = 270 - angle;
1499                 }
1500                 if (gradient.type.toLowerCase() == "radial") {
1501                     fill.focus = "100%";
1502                     fill.focusposition = "0.5 0.5";
1503                 }
1504             }
1505         };
1506         var Element = function (node, group, vml) {
1507             var Rotation = 0,
1508                 RotX = 0,
1509                 RotY = 0,
1510                 Scale = 1;
1511             this[0] = node;
1512             this.node = node;
1513             this.X = 0;
1514             this.Y = 0;
1515             this.attrs = {};
1516             this.Group = group;
1517             this.vml = vml;
1518             this._ = {
1519                 tx: 0,
1520                 ty: 0,
1521                 rt: 0,
1522                 sx: 1,
1523                 sy: 1
1524             };
1525         };
1526         Element.prototype.rotate = function (deg, isAbsolute) {
1527             if (deg == undefined) {
1528                 return this._.rt;
1529             }
1530             if (isAbsolute) {
1531                 this._.rt = deg;
1532             } else {
1533                 this._.rt += deg;
1534             }
1535             this.Group.style.rotation = this._.rt;
1536             return this;
1537         };
1538         Element.prototype.setBox = function (params) {
1539             var gs = this.Group.style,
1540                 os = (this.shape && this.shape.style) || this.node.style;
1541             for (var i in params) {
1542                 this.attrs[i] = params[i];
1543             }
1544             var attr = this.attrs, x, y, w, h;
1545             switch (this.type) {
1546                 case "circle": 
1547                     x = attr.cx - attr.r;
1548                     y = attr.cy - attr.r;
1549                     w = h = attr.r * 2;
1550                     break;
1551                 case "ellipse":
1552                     x = attr.cx - attr.rx;
1553                     y = attr.cy - attr.ry;
1554                     w = attr.rx * 2;
1555                     h = attr.ry * 2;
1556                     break;
1557                 case "rect":
1558                 case "image":
1559                     x = attr.x;
1560                     y = attr.y;
1561                     w = attr.width || 0;
1562                     h = attr.height || 0;
1563                     break;
1564                 case "text":
1565                     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("");
1566                     x = attr.x - Math.round(this.W / 2);
1567                     y = attr.y - this.H / 2;
1568                     w = this.W;
1569                     h = this.H;
1570                     break;
1571                 case "path":
1572                     if (!this.attrs.path) {
1573                         x = 0;
1574                         y = 0;
1575                         w = this.vml.width;
1576                         h = this.vml.height;
1577                     } else {
1578                         var dim = Raphael.pathDimensions(this.attrs.path),
1579                         x = dim.x;
1580                         y = dim.y;
1581                         w = dim.width;
1582                         h = dim.height;
1583                     }
1584                     break;
1585                 default:
1586                     x = 0;
1587                     y = 0;
1588                     w = this.vml.width;
1589                     h = this.vml.height;
1590                     break;
1591             }
1592             if (this.type == "path" || this.type == "text") {
1593                 var left = Math.round(this.vml.width / 2 - w / 2 - x),
1594                     top = Math.round(this.vml.height / 2 - h / 2 - y);
1595                 gs.left = - left + "px";
1596                 gs.top = - top + "px";
1597                 this.X = this.type == "text" ? x : left;
1598                 this.Y = this.type == "text" ? y : top;
1599                 this.W = w;
1600                 this.H = h;
1601                 os.top = top + "px";
1602                 os.left = left + "px";
1603             } else {
1604                 var left = this.vml.width / 2 - w / 2,
1605                     top = this.vml.height / 2 - h / 2;
1606                 gs.position = "absolute";
1607                 gs.left = x - left + "px";
1608                 gs.top = y - top + "px";
1609                 this.X = x - left;
1610                 this.Y = y - top;
1611                 this.W = w;
1612                 this.H = h;
1613                 gs.width = this.vml.width + "px";
1614                 gs.height = this.vml.height + "px";
1615                 os.position = "absolute";
1616                 os.top = top + "px";
1617                 os.left = left + "px";
1618                 os.width = w + "px";
1619                 os.height = h + "px";
1620             }
1621         };
1622         Element.prototype.hide = function () {
1623             this.Group.style.display = "none";
1624             return this;
1625         };
1626         Element.prototype.show = function () {
1627             this.Group.style.display = "block";
1628             return this;
1629         };
1630         Element.prototype.translate = function (x, y) {
1631             if (x == undefined && y == undefined) {
1632                 return {x: this._.tx, y: this._.ty};
1633             }
1634             this._.tx += +x;
1635             this._.ty += +y;
1636             if (this.type == "path") {
1637                 var path = this.attrs.path;
1638                 path = Raphael.pathToRelative(path);
1639                 path[0][1] += +x;
1640                 path[0][2] += +y;
1641                 this.attr({path: path.join(" ")});
1642             }
1643             this.setBox({x: this._.tx, y: this._.ty});
1644             return this;
1645         };
1646         Element.prototype.getBBox = function () {
1647             return {
1648                 x: this.X,
1649                 y: this.Y,
1650                 width: this.W,
1651                 height: this.H
1652             };
1653         };
1654         Element.prototype.remove = function () {
1655             this[0].parentNode.removeChild(this[0]);
1656             this.Group.parentNode.removeChild(this.Group);
1657             this.shape && this.shape.parentNode.removeChild(this.shape);
1658         };
1659         Element.prototype.attr = function () {
1660             if (arguments.length == 1 && typeof arguments[0] == "string") {
1661                 if (arguments[0] == "translation") {
1662                     return this.translate();
1663                 }
1664                 return this.attrs[arguments[0]];
1665             }
1666             if (this.attrs && arguments.length == 1 && arguments[0] instanceof Array) {
1667                 var values = {};
1668                 for (var i = 0, ii = arguments[0].length; i < ii; i++) {
1669                     values[arguments[0][i]] = this.attrs[arguments[0][i]];
1670                 };
1671                 return values;
1672             }
1673             if (this[0].tagName.toLowerCase() == "group") {
1674                 var children = this[0].childNodes;
1675                 this.attrs = this.attrs || {};
1676                 if (arguments.length == 2) {
1677                     this.attrs[arguments[0]] = arguments[1];
1678                 } else if (arguments.length == 1 || typeof arguments[0] == "object") {
1679                     for (var j in arguments[0]) {
1680                         this.attrs[j] = arguments[0][j];
1681                     }
1682                 }
1683                 for (var i = 0, ii = children.length; i < ii; i++) {
1684                     this.attr.apply(new item(children[i], this[0], this.vml), arguments);
1685                 }
1686             } else {
1687                 var params;
1688                 if (arguments.length == 2) {
1689                     params = {};
1690                     params[arguments[0]] = arguments[1];
1691                 }
1692                 if (arguments.length == 1 && typeof arguments[0] == "object") {
1693                     params = arguments[0];
1694                 }
1695                 if (params) {
1696                     if (params.gradient) {
1697                         addGrdientFill(this, params.gradient);
1698                     }
1699                     if (params.text && this.type == "text") {
1700                         this.node.string = params.text;
1701                     }
1702                     if (params.id) {
1703                         this.node.id = params.id;
1704                     }
1705                     setFillAndStroke(this, params);
1706                     this.setBox(params);
1707                 }
1708             }
1709             return this;
1710         };
1711         Element.prototype.toFront = function () {
1712             this.Group.parentNode.appendChild(this.Group);
1713             return this;
1714         };
1715         Element.prototype.toBack = function () {
1716             if (this.Group.parentNode.firstChild != this.Group) {
1717                 this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
1718             }
1719             return this;
1720         };
1721         Element.prototype.insertAfter = function (element) {
1722             if (element.Group.nextSibling) {
1723                 element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling);
1724             } else {
1725                 element.Group.parentNode.appendChild(this.Group);
1726             }
1727             return this;
1728         };
1729         Element.prototype.insertBefore = function (element) {
1730             element.Group.parentNode.insertBefore(this.Group, element.Group);
1731             return this;
1732         };
1733         theCircle = function (vml, x, y, r) {
1734             var g = document.createElement("rvml:group");
1735             var o = document.createElement("rvml:oval");
1736             g.appendChild(o);
1737             vml.canvas.appendChild(g);
1738             var res = new Element(o, g, vml);
1739             res.type = "circle";
1740             setFillAndStroke(res, {stroke: "#000", fill: "none"});
1741             res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
1742             res.attrs.cx = x;
1743             res.attrs.cy = y;
1744             res.attrs.r = r;
1745             return res;
1746         };
1747         theRect = function (vml, x, y, w, h, r) {
1748             var g = document.createElement("rvml:group");
1749             var o = document.createElement(r ? "rvml:roundrect" : "rvml:rect");
1750             if (r) {
1751                 o.arcsize = r / (Math.min(w, h));
1752             }
1753             g.appendChild(o);
1754             vml.canvas.appendChild(g);
1755             var res = new Element(o, g, vml);
1756             res.type = "rect";
1757             setFillAndStroke(res, {stroke: "#000"});
1758             res.setBox({x: x, y: y, width: w, height: h});
1759             res.attrs.x = x;
1760             res.attrs.y = y;
1761             res.attrs.w = w;
1762             res.attrs.h = h;
1763             res.attrs.r = r;
1764             return res;
1765         };
1766         theEllipse = function (vml, x, y, rx, ry) {
1767             var g = document.createElement("rvml:group");
1768             var o = document.createElement("rvml:oval");
1769             g.appendChild(o);
1770             vml.canvas.appendChild(g);
1771             var res = new Element(o, g, vml);
1772             res.type = "ellipse";
1773             setFillAndStroke(res, {stroke: "#000"});
1774             res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
1775             res.attrs.cx = x;
1776             res.attrs.cy = y;
1777             res.attrs.rx = rx;
1778             res.attrs.ry = ry;
1779             return res;
1780         };
1781         theImage = function (vml, src, x, y, w, h) {
1782             var g = document.createElement("rvml:group");
1783             var o = document.createElement("rvml:image");
1784             o.src = src;
1785             g.appendChild(o);
1786             vml.canvas.appendChild(g);
1787             var res = new Element(o, g, vml);
1788             res.type = "image";
1789             res.setBox({x: x, y: y, width: w, height: h});
1790             res.attrs.x = x;
1791             res.attrs.y = y;
1792             res.attrs.w = w;
1793             res.attrs.h = h;
1794             return res;
1795         };
1796         theText = function (vml, x, y, text) {
1797             var g = document.createElement("rvml:group"), gs = g.style;
1798             var el = document.createElement("rvml:shape"), ol = el.style;
1799             var path = document.createElement("rvml:path"), ps = path.style;
1800             path.v = ["m", Math.round(x), ", ", Math.round(y - 2), "l", Math.round(x) + 1, ", ", Math.round(y - 2)].join("");
1801             path.textpathok = true;
1802             ol.width = vml.width;
1803             ol.height = vml.height;
1804             gs.position = "absolute";
1805             gs.left = 0;
1806             gs.top = 0;
1807             gs.width = vml.width;
1808             gs.height = vml.height;
1809             var o = document.createElement("rvml:textpath");
1810             o.string = text;
1811             o.on = true;
1812             o.coordsize = vml.coordsize;
1813             o.coordorigin = vml.coordorigin;
1814             el.appendChild(o);
1815             el.appendChild(path);
1816             g.appendChild(el);
1817             vml.canvas.appendChild(g);
1818             var res = new Element(o, g, vml);
1819             res.shape = el;
1820             res.textpath = path;
1821             res.type = "text";
1822             res.attrs.x = x;
1823             res.attrs.y = y;
1824             res.attrs.w = 1;
1825             res.attrs.h = 1;
1826             setFillAndStroke(res, {font: '10px "Arial"', stroke: "none", fill: "#000"});
1827             return res;
1828         };
1829         theGroup = function (vml) {
1830             return this;
1831         };
1832         R._create = function () {
1833             // container, width, height
1834             // x, y, width, height
1835             var container, width, height;
1836             if (typeof arguments[0] == "string") {
1837                 container = document.getElementById(arguments[0]);
1838                 width = arguments[1];
1839                 height = arguments[2];
1840             }
1841             if (typeof arguments[0] == "object") {
1842                 container = arguments[0];
1843                 width = arguments[1];
1844                 height = arguments[2];
1845             }
1846             if (typeof arguments[0] == "number") {
1847                 container = 1;
1848                 x = arguments[0];
1849                 y = arguments[1];
1850                 width = arguments[2];
1851                 height = arguments[3];
1852             }
1853             if (!container) {
1854                 throw new Error("VML container not found.");
1855             }
1856             if (!document.namespaces["rvml"]) {
1857                 document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
1858                 document.createStyleSheet().addRule("rvml\\:*", "behavior:url(#default#VML)");
1859             }
1860             var c = document.createElement("div"),
1861                 d = document.createElement("div"),
1862                 r = paper.canvas = document.createElement("rvml:group"),
1863                 cs = c.style, rs = r.style;
1864             paper.width = width;
1865             paper.height = height;
1866             width = width || "320px";
1867             height = height || "200px";
1868             cs.clip = "rect(0 " + width + " " + height + " 0)";
1869             cs.top = "-2px";
1870             cs.left = "-2px";
1871             cs.position = "absolute";
1872             rs.position = "absolute";
1873             d.style.position = "relative";
1874             rs.width  = width;
1875             rs.height = height;
1876             r.coordsize = (width == "100%" ? width : parseFloat(width)) + " " + (height == "100%" ? height : parseFloat(height));
1877             r.coordorigin = "0 0";
1878
1879             var b = document.createElement("rvml:rect"), bs = b.style;
1880             bs.left = bs.top = 0;
1881             bs.width  = rs.width;
1882             bs.height = rs.height;
1883             b.filled = b.stroked = "f";
1884
1885             r.appendChild(b);
1886             c.appendChild(r);
1887             d.appendChild(c);
1888             if (container == 1) {
1889                 document.body.appendChild(d);
1890                 cs.position = "absolute";
1891                 cs.left = x + "px";
1892                 cs.top = y + "px";
1893                 cs.width = width;
1894                 cs.height = height;
1895                 container = {
1896                     style: {
1897                         width: width,
1898                         height: height
1899                     }
1900                 };
1901             } else {
1902                 cs.width = container.style.width = width;
1903                 cs.height = container.style.height = height;
1904                 if (container.firstChild) {
1905                     container.insertBefore(d, container.firstChild);
1906                 } else {
1907                     container.appendChild(d);
1908                 }
1909             }
1910             for (var prop in paper) {
1911                 container[prop] = paper[prop];
1912             }
1913             for (var prop in R.fn) {
1914                 if (!container[prop]) {
1915                     container[prop] = R.fn[prop];
1916                 }
1917             }
1918             container.clear = function () {
1919                 var todel = [];
1920                 for (var i = 0, ii = r.childNodes.length; i < ii; i++) {
1921                     if (r.childNodes[i] != b) {
1922                         todel.push(r.childNodes[i]);
1923                     }
1924                 }
1925                 for (i = 0, ii = todel.length; i < ii; i++) {
1926                     r.removeChild(todel[i]);
1927                 }
1928             };
1929             return container;
1930         };
1931         paper.remove = function () {
1932             this.canvas.parentNode.parentNode.parentNode.removeChild(this.canvas.parentNode.parentNode);
1933         };
1934         paper.safari = function () {};
1935     }
1936
1937     // rest
1938
1939     // Events
1940     var addEvent = (function () {
1941         if (document.addEventListener) {
1942             return function (obj, type, fn) {
1943                 obj.addEventListener(type, fn, false);
1944             };
1945         } else if (document.attachEvent) {
1946             return function (obj, type, fn) {
1947                 var f = function (e) {
1948                     fn.call(this, e || window.event);
1949                 };
1950                 obj.attachEvent("on" + type, f);
1951             };
1952         }
1953     })();
1954     var events = ["click", "dblclick", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup"];
1955     for (var i = events.length; i--;) {
1956         (function (eventName) {
1957             Element.prototype[eventName] = function (fn) {
1958                 addEvent(this.node, eventName, fn);
1959                 return this;
1960             };
1961         })(events[i]);
1962     }
1963
1964     // Set
1965     var Set = function (itemsArray) {
1966         this.items = [];
1967         if (itemsArray && itemsArray.constructor == Array) {
1968             for (var i = itemsArray.length; i--;) {
1969                 if (itemsArray[i].constructor == Element) {
1970                     this.items[this.items.length] = itemsArray[i];
1971                 }
1972             }
1973         }
1974     };
1975     Set.prototype.add = function (item) {
1976         if (item && item.constructor == Element) {
1977             this.items[this.items.length] = item;
1978         }
1979         return this;
1980     };
1981     Set.prototype.remove = function (item) {
1982         if (item && item.constructor == Element) {
1983             for (var i = this.items.length; i--;) {
1984                 if (this.items[i] == item) {
1985                     this.items.splice(i, 1);
1986                     return this;
1987                 }
1988             }
1989         }
1990         return this;
1991     };
1992     for (var method in Element.prototype) {
1993         Set.prototype[method] = (function (methodname) {
1994             return function () {
1995                 for (var i = this.items.length; i--;) {
1996                     this.items[i][methodname].apply(this.items[i], arguments);
1997                 }
1998                 return this;
1999             };
2000         })(method);
2001     }
2002     Set.prototype.getBBox = function () {
2003         var x = [], y = [], w = [], h = [];
2004         for (var i = this.items.length; i--;) {
2005             var box = this.items[i].getBBox();
2006             x.push(box.x);
2007             y.push(box.y);
2008             w.push(box.x + box.width);
2009             h.push(box.y + box.height);
2010         }
2011         x = Math.min(x);
2012         y = Math.min(y);
2013         return {
2014             x: x,
2015             y: y,
2016             w: Math.max(w) - x,
2017             h: Math.max(h) - y
2018         };
2019     };
2020
2021     paper.circle = function (x, y, r) {
2022         return theCircle(this, x, y, r);
2023     };
2024     paper.rect = function (x, y, w, h, r) {
2025         return theRect(this, x, y, w, h, r);
2026     };
2027     paper.ellipse = function (x, y, rx, ry) {
2028         return theEllipse(this, x, y, rx, ry);
2029     };
2030     paper.path = function (params, pathString) {
2031         return thePath(params, pathString, this);
2032     };
2033     paper.image = function (src, x, y, w, h) {
2034         return theImage(this, src, x, y, w, h);
2035     };
2036     paper.text = function (x, y, text) {
2037         return theText(this, x, y, text);
2038     };
2039     paper.group = function () {
2040         return this;
2041     };
2042     paper.drawGrid = function (x, y, w, h, wv, hv, color) {
2043         color = color || "#000";
2044         var p = this.path({stroke: color, "stroke-width": 1})
2045                 .moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).lineTo(x, y),
2046             rowHeight = h / hv,
2047             columnWidth = w / wv;
2048         for (var i = 1; i < hv; i++) {
2049             p.moveTo(x, y + i * rowHeight).lineTo(x + w, y + i * rowHeight);
2050         }
2051         for (var i = 1; i < wv; i++) {
2052             p.moveTo(x + i * columnWidth, y).lineTo(x + i * columnWidth, y + h);
2053         }
2054         return p;
2055     };
2056     paper.pathfinder = function (p, path) {
2057         var commands = {
2058             M: function (x, y) {
2059                 this.moveTo(x, y);
2060             },
2061             C: function (x1, y1, x2, y2, x3, y3) {
2062                 this.curveTo(x1, y1, x2, y2, x3, y3);
2063             },
2064             Q: function (x1, y1, x2, y2) {
2065                 this.qcurveTo(x1, y1, x2, y2);
2066             },
2067             T: function (x, y) {
2068                 this.qcurveTo(x, y);
2069             },
2070             S: function (x1, y1, x2, y2) {
2071                 p.curveTo(x1, y1, x2, y2);
2072             },
2073             L: function (x, y) {
2074                 p.lineTo(x, y);
2075             },
2076             H: function (x) {
2077                 this.lineTo(x, this.last.y);
2078             },
2079             V: function (y) {
2080                 this.lineTo(this.last.x, y);
2081             },
2082             A: function (rx, ry, xaxisrotation, largearcflag, sweepflag, x, y) {
2083                 this.arcTo(rx, ry, largearcflag, sweepflag, x, y);
2084             },
2085             Z: function () {
2086                 this.andClose();
2087             }
2088         };
2089
2090         path = Raphael.pathToAbsolute(path);
2091         for (var i = 0, ii = path.length; i < ii; i++) {
2092             var b = path[i].shift();
2093             commands[b].apply(p, path[i]);
2094         }
2095     };
2096     paper.set = function (itemsArray) {
2097         return new Set(itemsArray);
2098     };
2099     Element.prototype.stop = function () {
2100         clearTimeout(this.animation_in_progress);
2101     };
2102     Element.prototype.scale = function (x, y) {
2103         if (x == undefined && y == undefined) {
2104             return {x: this._.sx, y: this._.sy};
2105         }
2106         y = y || x;
2107         var dx, dy, cx, cy;
2108         if (x != 0 && !(x == 1 && y == 1)) {
2109             var dirx = Math.round(x / Math.abs(x)),
2110                 diry = Math.round(y / Math.abs(y)),
2111                 s = this.node.style;
2112             dx = this.attr("x");
2113             dy = this.attr("y");
2114             cx = this.attr("cx");
2115             cy = this.attr("cy");
2116             if (dirx != 1 || diry != 1) {
2117                 if (this.transformations) {
2118                     this.transformations[2] = "scale(" + [dirx, diry] + ")";
2119                     this.node.setAttribute("transform", this.transformations.join(" "));
2120                     dx = (dirx < 0) ? -this.attr("x") - this.attrs.width * x * dirx / this._.sx : this.attr("x");
2121                     dy = (diry < 0) ? -this.attr("y") - this.attrs.height * y * diry / this._.sy : this.attr("y");
2122                     cx = this.attr("cx") * dirx;
2123                     cy = this.attr("cy") * diry;
2124                 } else {
2125                     this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11=" + dirx +
2126                         ", M12=0, M21=0, M22=" + diry +
2127                         ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')";
2128                     s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
2129                 }
2130             } else {
2131                 if (this.transformations) {
2132                     this.transformations[2] = "";
2133                     this.node.setAttribute("transform", this.transformations.join(" "));
2134                 } else {
2135                     this.node.filterMatrix = "";
2136                     s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
2137                 }
2138             }
2139             switch (this.type) {
2140                 case "rect":
2141                 case "image":
2142                     this.attr({
2143                         width: this.attrs.width * x * dirx / this._.sx,
2144                         height: this.attrs.height * y * diry / this._.sy,
2145                         x: dx,
2146                         y: dy
2147                     });
2148                     break;
2149                 case "circle":
2150                 case "ellipse":
2151                     this.attr({
2152                         rx: this.attrs.rx * x * dirx / this._.sx,
2153                         ry: this.attrs.ry * y * diry / this._.sy,
2154                         r: this.attrs.r * x * diry / this._.sx,
2155                         cx: cx,
2156                         cy: cy
2157                     });
2158                     break;
2159                 case "path":
2160                     var path = Raphael.pathToRelative(Raphael.parsePathString(this.attr("path"))), 
2161                         skip = true,
2162                         dim = Raphael.pathDimensions(this.attrs.path),
2163                         dx = -dim.width * (x - 1) / 2,
2164                         dy = -dim.height * (y - 1) / 2;
2165                     for (var i = 0, ii = path.length; i < ii; i++) {
2166                         if (path[i][0].toUpperCase() == "M" && skip) {
2167                             continue;
2168                         } else {
2169                             skip = false;
2170                         }
2171                         if (path[i][0].toUpperCase() == "A") {
2172                             path[i][path[i].length - 2] *= x * dirx;
2173                             path[i][path[i].length - 1] *= y * diry;
2174                         } else {
2175                             for (var j = 1, jj = path[i].length; j < jj; j++) {
2176                                 path[i][j] *= (j % 2) ? x * dirx / this._.sx : y * diry / this._.sy;
2177                             }
2178                         }
2179                     }
2180                     var dim2 = Raphael.pathDimensions(path),
2181                         dx = dim.x + dim.width / 2 - dim2.x - dim2.width / 2,
2182                         dy = dim.y + dim.height / 2 - dim2.y - dim2.height / 2;
2183                     path = Raphael.pathToRelative(path);
2184                     path[0][1] += dx;
2185                     path[0][2] += dy;
2186                     
2187                     this.attr({path: path.join(" ")});
2188             }
2189         }
2190         this._.sx = x;
2191         this._.sy = y;
2192         return this;
2193     };
2194     Element.prototype.animate = function (params, ms, callback) {
2195         clearTimeout(this.animation_in_progress);
2196         var from = {}, to = {}, diff = {}, t = {x: 0, y: 0};
2197         for (var attr in params) {
2198             if (attr in availableAnimAttrs) {
2199                 from[attr] = this.attr(attr);
2200                 if (typeof from[attr] == "undefined") {
2201                     from[attr] = availableAttrs[attr];
2202                 }
2203                 to[attr] = params[attr];
2204                 switch (availableAnimAttrs[attr]) {
2205                     case "number":
2206                         diff[attr] = (to[attr] - from[attr]) / ms;
2207                         break;
2208                     case "colour":
2209                         from[attr] = Raphael.getRGB(from[attr]);
2210                         var toColour = Raphael.getRGB(to[attr]);
2211                         diff[attr] = {
2212                             r: (toColour.r - from[attr].r) / ms,
2213                             g: (toColour.g - from[attr].g) / ms,
2214                             b: (toColour.b - from[attr].b) / ms
2215                         };
2216                         break;
2217                     case "path":
2218                         var pathes = Raphael.pathEqualiser(from[attr], to[attr]);
2219                         from[attr] = pathes[0];
2220                         to[attr] = pathes[1];
2221                         diff[attr] = [];
2222                         for (var i = 0, ii = from[attr].length; i < ii; i++) {
2223                             diff[attr][i] = [0];
2224                             for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
2225                                 diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
2226                             }
2227                         }
2228                         break;
2229                     case "csv":
2230                         var values = params[attr].split(/[, ]+/);
2231                         if (attr == "translation") {
2232                             from[attr] = [0, 0];
2233                             diff[attr] = [values[0] / ms, values[1] / ms];
2234                         } else {
2235                             from[attr] = from[attr].split(/[, ]+/);
2236                             diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][0]) / ms];
2237                         }
2238                         to[attr] = values;
2239                 }
2240             }
2241         }
2242         var start = new Date(),
2243             prev = 0,
2244             that = this;
2245         (function () {
2246             var time = (new Date()).getTime() - start.getTime(),
2247                 set = {},
2248                 now;
2249             if (time < ms) {
2250                 for (var attr in from) {
2251                     switch (availableAnimAttrs[attr]) {
2252                         case "number":
2253                             now = +from[attr] + time * diff[attr];
2254                             break;
2255                         case "colour":
2256                             now = "rgb(" + [
2257                                 Math.round(from[attr].r + time * diff[attr].r),
2258                                 Math.round(from[attr].g + time * diff[attr].g),
2259                                 Math.round(from[attr].b + time * diff[attr].b)
2260                             ].join(",") + ")";
2261                             break;
2262                         case "path":
2263                             now = [];
2264                             for (var i = 0, ii = from[attr].length; i < ii; i++) {
2265                                 now[i] = [from[attr][i][0]];
2266                                 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
2267                                     now[i][j] = from[attr][i][j] + time * diff[attr][i][j];
2268                                 }
2269                                 now[i] = now[i].join(" ");
2270                             }
2271                             now = now.join(" ");
2272                             break;
2273                         case "csv":
2274                             if (attr == "translation") {
2275                                 var x = diff[attr][0] * (time - prev),
2276                                     y = diff[attr][1] * (time - prev);
2277                                 t.x += x;
2278                                 t.y += y;
2279                                 now = [x, y].join(" ");
2280                             } else {
2281                                 now = [+from[attr][0] + time * diff[attr][0], +from[attr][1] + time * diff[attr][1]].join(" ");
2282                             }
2283                             break;
2284                     }
2285                     if (attr == "font-size") {
2286                         set[attr] = now + "px";
2287                     } else {
2288                         set[attr] = now;
2289                     }
2290                 }
2291                 that.attr(set);
2292                 that.animation_in_progress = setTimeout(arguments.callee, 0);
2293                 paper.safari();
2294             } else {
2295                 if (t.x || t.y) {
2296                     that.translate(-t.x, -t.y);
2297                 }
2298                 that.attr(params);
2299                 clearTimeout(that.animation_in_progress);
2300                 paper.safari();
2301                 (typeof callback == "function") && callback.call(that);
2302             }
2303             prev = time;
2304         })();
2305         return this;
2306     };
2307
2308
2309
2310 })(Raphael);