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