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