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