1.1
[raphael] / raphael.js
1 /*
2  * Raphael 1.1 - 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 window.Raphael = (function () {
10     var separator = /[, ]+/,
11         doc = document,
12         win = window,
13         oldRaphael = {
14             was: "Raphael" in win,
15             is: win.Raphael
16         },
17         R = function () {
18             if (R.is(arguments[0], "array")) {
19                 var a = arguments[0],
20                     cnv = create[apply](R, a.splice(0, 3 + R.is(a[0], nu))),
21                     res = cnv.set();
22                 for (var i = 0, ii = a[length]; i < ii; i++) {
23                     var j = a[i] || {};
24                     ({circle:1, rect:1, path:1, ellipse:1, text:1, image:1}[has](j.type)) && res[push](cnv[j.type]().attr(j));
25                 }
26                 return res;
27             }
28             return create[apply](R, arguments);
29         },
30         paper = {},
31         events = ["click", "dblclick", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup"],
32         E = "",
33         has = "hasOwnProperty",
34         proto = "prototype",
35         setAttribute = "setAttribute",
36         appendChild = "appendChild",
37         apply = "apply",
38         length = "length",
39         join = "join",
40         split = "split",
41         concat = "concat",
42         push = "push",
43         toFloat = parseFloat,
44         toInt = parseInt,
45         pow = Math.pow,
46         mmin = Math.min,
47         mmax = Math.max,
48         round = Math.round,
49         rg = /^(?=[\da-f]$)/,
50         nu = "number",
51         toString = "toString",
52         availableAttrs = {"clip-rect": "0 0 10e9 10e9", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", 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, target: "_blank", "text-anchor": "middle", title: "Raphael", translation: "0 0", width: 0, x: 0, y: 0},
53         availableAnimAttrs = {"clip-rect": "csv", cx: nu, cy: nu, fill: "colour", "fill-opacity": nu, "font-size": nu, height: nu, opacity: nu, path: "path", r: nu, rotation: "csv", rx: nu, ry: nu, scale: "csv", stroke: "colour", "stroke-opacity": nu, "stroke-width": nu, translation: "csv", width: nu, x: nu, y: nu},
54         rp = "replace";
55     R.version = "1.1";
56     R.type = (win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
57     R.svg = !(R.vml = R.type == "VML");
58     R.idGenerator = 0;
59     R.fn = {};
60     R.is = function (o, type) {
61         type = (type + E).toLowerCase();
62         if ((type == "object" || type == "undefined") && typeof o == type) {
63             return true;
64         }
65         if (o == null && type == "null") {
66             return true;
67         }
68         return Object[proto][toString].call(o)[rp](/^\[object\s+|\]$/gi, E).toLowerCase() == type;
69     };
70     R.setWindow = function (newwin) {
71         win = newwin;
72         doc = win.document;
73     };
74     // colour utilities
75     R.hsb2rgb = cacher(function (hue, saturation, brightness) {
76         if (R.is(hue, "object") && "h" in hue && "s" in hue && "b" in hue) {
77             brightness = hue.b;
78             saturation = hue.s;
79             hue = hue.h;
80         }
81         var red,
82             green,
83             blue;
84         if (brightness == 0) {
85             return {r: 0, g: 0, b: 0, hex: "#000"};
86         }
87         if (hue > 1 || saturation > 1 || brightness > 1) {
88             hue /= 255;
89             saturation /= 255;
90             brightness /= 255;
91         }
92         var i = ~~(hue * 6),
93             f = (hue * 6) - i,
94             p = brightness * (1 - saturation),
95             q = brightness * (1 - (saturation * f)),
96             t = brightness * (1 - (saturation * (1 - f)));
97         red = [brightness, q, p, p, t, brightness, brightness][i];
98         green = [t, brightness, brightness, q, p, p, t][i];
99         blue = [p, p, t, brightness, brightness, q, p][i];
100         red *= 255;
101         green *= 255;
102         blue *= 255;
103         var rgb = {r: red, g: green, b: blue},
104             r = (~~red)[toString](16),
105             g = (~~green)[toString](16),
106             b = (~~blue)[toString](16);
107         r = r[rp](rg, "0");
108         g = g[rp](rg, "0");
109         b = b[rp](rg, "0");
110         rgb.hex = "#" + r + g + b;
111         return rgb;
112     }, R);
113     R.rgb2hsb = cacher(function (red, green, blue) {
114         if (R.is(red, "object") && "r" in red && "g" in red && "b" in red) {
115             blue = red.b;
116             green = red.g;
117             red = red.r;
118         }
119         if (R.is(red, "string")) {
120             var clr = R.getRGB(red);
121             red = clr.r;
122             green = clr.g;
123             blue = clr.b;
124         }
125         if (red > 1 || green > 1 || blue > 1) {
126             red /= 255;
127             green /= 255;
128             blue /= 255;
129         }
130         var max = mmax(red, green, blue),
131             min = mmin(red, green, blue),
132             hue,
133             saturation,
134             brightness = max;
135         if (min == max) {
136             return {h: 0, s: 0, b: max};
137         } else {
138             var delta = (max - min);
139             saturation = delta / max;
140             if (red == max) {
141                 hue = (green - blue) / delta;
142             } else if (green == max) {
143                 hue = 2 + ((blue - red) / delta);
144             } else {
145                 hue = 4 + ((red - green) / delta);
146             }
147             hue /= 6;
148             if (hue < 0) {
149                 hue += 1;
150             }
151             if (hue > 1) {
152                 hue -= 1;
153             }
154         }
155         return {h: hue, s: saturation, b: brightness};
156     }, R);
157     R._path2string = function () {
158         var res = E,
159             item;
160         for (var i = 0, ii = this[length]; i < ii; i++) {
161             for (var j = 0, jj = this[i][length]; j < jj; j++) {
162                 res += this[i][j];
163                 j && j != jj - 1 && (res += ",");
164             }
165             i != ii - 1 && (res += " ");
166         }
167         return res[rp](/,(?=-)/g, E);
168     };
169     function cacher(f, scope, postprocessor) {
170         function newf() {
171             var arg = Array[proto].splice.call(arguments, 0, arguments[length]),
172                 args = arg[join]("\u25ba");
173             newf.cache = newf.cache || {};
174             newf.count = newf.count || [];
175             if (args in newf.cache) {
176                 return postprocessor ? postprocessor(newf.cache[args]) : newf.cache[args];
177             }
178             if (newf.count[length] >= 1e3) {
179                 delete newf.cache[newf.count.shift()];
180             }
181             newf.count[push](args);
182             newf.cache[args] = f[apply](scope, arg);
183             return postprocessor ? postprocessor(newf.cache[args]) : newf.cache[args];
184         }
185         return newf;
186     }
187
188     R.getRGB = cacher(function (colour) {
189         var htmlcolors = {none: "none", 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"},
190             res;
191         colour = htmlcolors[(colour + E).toLowerCase()] || colour;
192         if (!colour) {
193             return {r: -1, g: -1, b: -1, hex: "none", error: 1};
194         }
195         if (colour == "none") {
196             return {r: -1, g: -1, b: -1, hex: "none"};
197         }
198         var red,
199             green,
200             blue,
201             rgb = (colour + E).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*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i);
202         if (rgb) {
203             if (rgb[2]) {
204                 blue = toInt(rgb[2].substring(5), 16);
205                 green = toInt(rgb[2].substring(3, 5), 16);
206                 red = toInt(rgb[2].substring(1, 3), 16);
207             }
208             if (rgb[3]) {
209                 blue = toInt(rgb[3].substring(3) + rgb[3].substring(3), 16);
210                 green = toInt(rgb[3].substring(2, 3) + rgb[3].substring(2, 3), 16);
211                 red = toInt(rgb[3].substring(1, 2) + rgb[3].substring(1, 2), 16);
212             }
213             if (rgb[4]) {
214                 rgb = rgb[4][split](/\s*,\s*/);
215                 red = toFloat(rgb[0]);
216                 green = toFloat(rgb[1]);
217                 blue = toFloat(rgb[2]);
218             }
219             if (rgb[5]) {
220                 rgb = rgb[5][split](/\s*,\s*/);
221                 red = toFloat(rgb[0]) * 2.55;
222                 green = toFloat(rgb[1]) * 2.55;
223                 blue = toFloat(rgb[2]) * 2.55;
224             }
225             if (rgb[6]) {
226                 rgb = rgb[6][split](/\s*,\s*/);
227                 red = toFloat(rgb[0]);
228                 green = toFloat(rgb[1]);
229                 blue = toFloat(rgb[2]);
230                 return R.hsb2rgb(red, green, blue);
231             }
232             if (rgb[7]) {
233                 rgb = rgb[7][split](/\s*,\s*/);
234                 red = toFloat(rgb[0]) * 2.55;
235                 green = toFloat(rgb[1]) * 2.55;
236                 blue = toFloat(rgb[2]) * 2.55;
237                 return R.hsb2rgb(red, green, blue);
238             }
239             rgb = {r: red, g: green, b: blue};
240             var r = (~~red)[toString](16),
241                 g = (~~green)[toString](16),
242                 b = (~~blue)[toString](16);
243             r = r[rp](rg, "0");
244             g = g[rp](rg, "0");
245             b = b[rp](rg, "0");
246             rgb.hex = "#" + r + g + b;
247             res = rgb;
248         } else {
249             res = {r: -1, g: -1, b: -1, hex: "none", error: 1};
250         }
251         return res;
252     }, R);
253     R.getColor = function (value) {
254         var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
255             rgb = this.hsb2rgb(start.h, start.s, start.b);
256         start.h += .075;
257         if (start.h > 1) {
258             start.h = 0;
259             start.s -= .2;
260             if (start.s <= 0) {
261                 this.getColor.start = {h: 0, s: 1, b: start.b};
262             }
263         }
264         return rgb.hex;
265     };
266     R.getColor.reset = function () {
267         delete this.start;
268     };
269     // path utilities
270     R.parsePathString = cacher(function (pathString) {
271         if (!pathString) {
272             return null;
273         }
274         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
275             data = [];
276         if (R.is(pathString, "array") && R.is(pathString[0], "array")) { // rough assumption
277             data = pathClone(pathString);
278         }
279         if (!data[length]) {
280             (pathString + E)[rp](/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, function (a, b, c) {
281                 var params = [],
282                     name = b.toLowerCase();
283                 c[rp](/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig, function (a, b) {
284                     b && params[push](+b);
285                 });
286                 while (params[length] >= paramCounts[name]) {
287                     data[push]([b][concat](params.splice(0, paramCounts[name])));
288                     if (!paramCounts[name]) {
289                         break;
290                     };
291                 }
292             });
293         }
294         data[toString] = R._path2string;
295         return data;
296     });
297     var pathDimensions = cacher(function (path) {
298         if (!path) {
299             return {x: 0, y: 0, width: 0, height: 0};
300         }
301         path = path2curve(path);
302         var x = 0, 
303             y = 0,
304             X = [],
305             Y = [];
306         for (var i = 0, ii = path[length]; i < ii; i++) {
307             if (path[i][0] == "M") {
308                 x = path[i][1];
309                 y = path[i][2];
310                 X[push](x);
311                 Y[push](y);
312             } else {
313                 var dim = curveDim(x, y, path[i][1], path[i][2], path[i][3], path[i][4], path[i][5], path[i][6]);
314                 X = X[concat](dim.min.x, dim.max.x);
315                 Y = Y[concat](dim.min.y, dim.max.y);
316             }
317         }
318         var xmin = mmin[apply](0, X),
319             ymin = mmin[apply](0, Y);
320         return {
321             x: xmin,
322             y: ymin,
323             width: mmax[apply](0, X) - xmin,
324             height: mmax[apply](0, Y) - ymin
325         };
326     }),
327         pathClone = function (pathArray) {
328             var res = [];
329             if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption
330                 pathArray = R.parsePathString(pathArray);
331             }
332             for (var i = 0, ii = pathArray[length]; i < ii; i++) {
333                 res[i] = [];
334                 for (var j = 0, jj = pathArray[i][length]; j < jj; j++) {
335                     res[i][j] = pathArray[i][j];
336                 }
337             }
338             res[toString] = R._path2string;
339             return res;
340         },
341         pathToRelative = cacher(function (pathArray) {
342             if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption
343                 pathArray = R.parsePathString(pathArray);
344             }
345             var res = [],
346                 x = 0,
347                 y = 0,
348                 mx = 0,
349                 my = 0,
350                 start = 0;
351             if (pathArray[0][0] == "M") {
352                 x = pathArray[0][1];
353                 y = pathArray[0][2];
354                 mx = x;
355                 my = y;
356                 start++;
357                 res[push](["M", x, y]);
358             }
359             for (var i = start, ii = pathArray[length]; i < ii; i++) {
360                 var r = res[i] = [],
361                     pa = pathArray[i];
362                 if (pa[0] != pa[0].toLowerCase()) {
363                     r[0] = pa[0].toLowerCase();
364                     switch (r[0]) {
365                         case "a":
366                             r[1] = pa[1];
367                             r[2] = pa[2];
368                             r[3] = pa[3];
369                             r[4] = pa[4];
370                             r[5] = pa[5];
371                             r[6] = +(pa[6] - x).toFixed(3);
372                             r[7] = +(pa[7] - y).toFixed(3);
373                             break;
374                         case "v":
375                             r[1] = +(pa[1] - y).toFixed(3);
376                             break;
377                         case "m":
378                             mx = pa[1];
379                             my = pa[2];
380                         default:
381                             for (var j = 1, jj = pa[length]; j < jj; j++) {
382                                 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
383                             }
384                     }
385                 } else {
386                     r = res[i] = [];
387                     if (pa[0] == "m") {
388                         mx = pa[1] + x;
389                         my = pa[2] + y;
390                     }
391                     for (var k = 0, kk = pa[length]; k < kk; k++) {
392                         res[i][k] = pa[k];
393                     }
394                 }
395                 var len = res[i][length];
396                 switch (res[i][0]) {
397                     case "z":
398                         x = mx;
399                         y = my;
400                         break;
401                     case "h":
402                         x += +res[i][len - 1];
403                         break;
404                     case "v":
405                         y += +res[i][len - 1];
406                         break;
407                     default:
408                         x += +res[i][len - 2];
409                         y += +res[i][len - 1];
410                 }
411             }
412             res[toString] = R._path2string;
413             return res;
414         }, 0, pathClone),
415         pathToAbsolute = cacher(function (pathArray) {
416             if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption
417                 pathArray = R.parsePathString(pathArray);
418             }
419             var res = [],
420                 x = 0,
421                 y = 0,
422                 mx = 0,
423                 my = 0,
424                 start = 0;
425             if (pathArray[0][0] == "M") {
426                 x = +pathArray[0][1];
427                 y = +pathArray[0][2];
428                 mx = x;
429                 my = y;
430                 start++;
431                 res[0] = ["M", x, y];
432             }
433             for (var i = start, ii = pathArray[length]; i < ii; i++) {
434                 var r = res[i] = [],
435                     pa = pathArray[i];
436                 if (pa[0] != (pa[0] + E).toUpperCase()) {
437                     r[0] = (pa[0] + E).toUpperCase();
438                     switch (r[0]) {
439                         case "A":
440                             r[1] = pa[1];
441                             r[2] = pa[2];
442                             r[3] = pa[3];
443                             r[4] = pa[4];
444                             r[5] = pa[5];
445                             r[6] = +(pa[6] + x);
446                             r[7] = +(pa[7] + y);
447                             break;
448                         case "V":
449                             r[1] = +pa[1] + y;
450                             break;
451                         case "H":
452                             r[1] = +pa[1] + x;
453                             break;
454                         case "M":
455                             mx = +pa[1] + x;
456                             my = +pa[2] + y;
457                         default:
458                             for (var j = 1, jj = pa[length]; j < jj; j++) {
459                                 r[j] = +pa[j] + ((j % 2) ? x : y);
460                             }
461                     }
462                 } else {
463                     for (var k = 0, kk = pa[length]; k < kk; k++) {
464                         res[i][k] = pa[k];
465                     }
466                 }
467                 switch (r[0]) {
468                     case "Z":
469                         x = mx;
470                         y = my;
471                         break;
472                     case "H":
473                         x = r[1];
474                         break;
475                     case "V":
476                         y = r[1];
477                         break;
478                     default:
479                         x = res[i][res[i][length] - 2];
480                         y = res[i][res[i][length] - 1];
481                 }
482             }
483             res[toString] = R._path2string;
484             return res;
485         }, null, pathClone),
486         l2c = function (x1, y1, x2, y2) {
487             return [x1, y1, x2, y2, x2, y2];
488         },
489         q2c = function (x1, y1, ax, ay, x2, y2) {
490             var _13 = 1 / 3,
491                 _23 = 2 / 3;
492             return [
493                     _13 * x1 + _23 * ax,
494                     _13 * y1 + _23 * ay,
495                     _13 * x2 + _23 * ax,
496                     _13 * y2 + _23 * ay,
497                     x2,
498                     y2
499                 ];
500         },
501         a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
502             // for more information of where this math came from visit:
503             // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
504             var PI = Math.PI,
505                 _120 = PI * 120 / 180,
506                 rad = PI / 180 * (+angle || 0),
507                 res = [],
508                 xy,
509                 rotate = cacher(function (x, y, rad) {
510                     var X = x * Math.cos(rad) - y * Math.sin(rad),
511                         Y = x * Math.sin(rad) + y * Math.cos(rad);
512                     return {x: X, y: Y};
513                 });
514             if (!recursive) {
515                 xy = rotate(x1, y1, -rad);
516                 x1 = xy.x;
517                 y1 = xy.y;
518                 xy = rotate(x2, y2, -rad);
519                 x2 = xy.x;
520                 y2 = xy.y;
521                 var cos = Math.cos(PI / 180 * angle),
522                     sin = Math.sin(PI / 180 * angle),
523                     x = (x1 - x2) / 2,
524                     y = (y1 - y2) / 2;
525                 rx = mmax(rx, Math.abs(x));
526                 ry = mmax(ry, Math.abs(y));
527                 var rx2 = rx * rx,
528                     ry2 = ry * ry,
529                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
530                         Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
531                     cx = k * rx * y / ry + (x1 + x2) / 2,
532                     cy = k * -ry * x / rx + (y1 + y2) / 2,
533                     f1 = Math.asin((y1 - cy) / ry),
534                     f2 = Math.asin((y2 - cy) / ry);
535
536                 f1 = x1 < cx ? PI - f1 : f1;
537                 f2 = x2 < cx ? PI - f2 : f2;
538                 f1 < 0 && (f1 = PI * 2 + f1);
539                 f2 < 0 && (f2 = PI * 2 + f2);
540                 if (sweep_flag && f1 > f2) {
541                     f1 = f1 - PI * 2;
542                 }
543                 if (!sweep_flag && f2 > f1) {
544                     f2 = f2 - PI * 2;
545                 }
546             } else {
547                 f1 = recursive[0];
548                 f2 = recursive[1];
549                 cx = recursive[2];
550                 cy = recursive[3];
551             }
552             var df = f2 - f1;
553             if (Math.abs(df) > _120) {
554                 var f2old = f2,
555                     x2old = x2,
556                     y2old = y2;
557                 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
558                 x2 = cx + rx * Math.cos(f2);
559                 y2 = cy + ry * Math.sin(f2);
560                 res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
561             }
562             df = f2 - f1;
563             var c1 = Math.cos(f1),
564                 s1 = Math.sin(f1),
565                 c2 = Math.cos(f2),
566                 s2 = Math.sin(f2),
567                 t = Math.tan(df / 4),
568                 hx = 4 / 3 * rx * t,
569                 hy = 4 / 3 * ry * t,
570                 m1 = [x1, y1],
571                 m2 = [x1 + hx * s1, y1 - hy * c1],
572                 m3 = [x2 + hx * s2, y2 - hy * c2],
573                 m4 = [x2, y2];
574             m2[0] = 2 * m1[0] - m2[0];
575             m2[1] = 2 * m1[1] - m2[1];
576             if (recursive) {
577                 return [m2, m3, m4][concat](res);
578             } else {
579                 res = [m2, m3, m4][concat](res)[join](",")[split](",");
580                 var newres = [];
581                 for (var i = 0, ii = res[length]; i < ii; i++) {
582                     newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
583                 }
584                 return newres;
585             }
586         },
587         findDotAtSegment = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
588             var x = pow(1 - t, 3) * p1x + pow(1 - t, 2) * 3 * t * c1x + (1 - t) * 3 * t * t * c2x + pow(t, 3) * p2x,
589                 y = pow(1 - t, 3) * p1y + pow(1 - t, 2) * 3 * t * c1y + (1 - t) * 3 * t * t * c2y + pow(t, 3) * p2y,
590                 mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x),
591                 my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y),
592                 nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x),
593                 ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y),
594                 ax = (1 - t) * p1x + t * c1x,
595                 ay = (1 - t) * p1y + t * c1y,
596                 cx = (1 - t) * c2x + t * p2x,
597                 cy = (1 - t) * c2y + t * p2y;
598             return {x: x, y: y, m: {x: mx, y: my}, n: {x: nx, y: ny}, start: {x: ax, y: ay}, end: {x: cx, y: cy}};
599         }),
600         curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
601             var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
602                 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
603                 c = p1x - c1x,
604                 t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
605                 t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
606                 y = [p1y, p2y],
607                 x = [p1x, p2x],
608                 dot1 = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1 > 0 && t1 < 1 ? t1 : 0),
609                 dot2 = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2 > 0 && t2 < 1 ? t2 : 0);
610             x = x[concat](dot1.x, dot2.x);
611             y = y[concat](dot1.y, dot2.y);
612             a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
613             b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
614             c = p1y - c1y;
615             t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
616             t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
617             dot1 = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1 > 0 && t1 < 1 ? t1 : 0);
618             dot2 = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2 > 0 && t2 < 1 ? t2 : 0);
619             x = x[concat](dot1.x, dot2.x);
620             y = y[concat](dot1.y, dot2.y);
621             return {
622                 min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
623                 max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
624             };
625         }),
626         path2curve = cacher(function (path, path2) {
627             var p = pathToAbsolute(path),
628                 p2 = path2 && pathToAbsolute(path2),
629                 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
630                 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
631                 processPath = function (path, d) {
632                     var nx, ny;
633                     if (!path) {
634                         return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
635                     }
636                     !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
637                     switch (path[0]) {
638                         case "M":
639                             d.X = path[1];
640                             d.Y = path[2];
641                             break;
642                         case "A":
643                             path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
644                             break;
645                         case "S":
646                             nx = d.x + (d.x - (d.bx || d.x));
647                             ny = d.y + (d.y - (d.by || d.y));
648                             path = ["C", nx, ny][concat](path.slice(1));
649                             break;
650                         case "T":
651                             d.qx = d.x + (d.x - (d.qx || d.x));
652                             d.qy = d.y + (d.y - (d.qy || d.y));
653                             path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
654                             break;
655                         case "Q":
656                             d.qx = path[1];
657                             d.qy = path[2];
658                             path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
659                             break;
660                         case "L":
661                             path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
662                             break;
663                         case "H":
664                             path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
665                             break;
666                         case "V":
667                             path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
668                             break;
669                         case "Z":
670                             path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
671                             break;
672                     }
673                     return path;
674                 },
675                 fixArc = function (pp, i) {
676                     if (pp[i][length] > 7) {
677                         pp[i].shift();
678                         var pi = pp[i];
679                         while (pi[length]) {
680                             pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
681                         }
682                         pp.splice(i, 1);
683                         ii = mmax(p[length], p2 && p2[length] || 0);
684                     }
685                 },
686                 fixM = function (path1, path2, a1, a2, i) {
687                     if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
688                         path2.splice(i, 0, ["M", a2.x, a2.y]);
689                         a1.bx = 0;
690                         a1.by = 0;
691                         a1.x = path1[i][1];
692                         a1.y = path1[i][2];
693                         ii = mmax(p[length], p2 && p2[length] || 0);
694                     }
695                 };
696             for (var i = 0, ii = mmax(p[length], p2 && p2[length] || 0); i < ii; i++) {
697                 p[i] = processPath(p[i], attrs);
698                 fixArc(p, i);
699                 p2 && (p2[i] = processPath(p2[i], attrs2));
700                 p2 && fixArc(p2, i);
701                 fixM(p, p2, attrs, attrs2, i);
702                 fixM(p2, p, attrs2, attrs, i);
703                 var seg = p[i],
704                     seg2 = p2 && p2[i],
705                     seglen = seg[length],
706                     seg2len = p2 && seg2[length];
707                 attrs.x = seg[seglen - 2];
708                 attrs.y = seg[seglen - 1];
709                 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
710                 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
711                 attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
712                 attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
713                 attrs2.x = p2 && seg2[seg2len - 2];
714                 attrs2.y = p2 && seg2[seg2len - 1];
715             }
716             return p2 ? [p, p2] : p;
717         }, null, pathClone),
718         parseDots = cacher(function (gradient) {
719             var dots = [];
720             for (var i = 0, ii = gradient[length]; i < ii; i++) {
721                 var dot = {},
722                     par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
723                 dot.color = R.getRGB(par[1]);
724                 if (dot.color.error) {
725                     return null;
726                 }
727                 dot.color = dot.color.hex;
728                 par[2] && (dot.offset = par[2] + "%");
729                 dots[push](dot);
730             }
731             for (var i = 1, ii = dots[length] - 1; i < ii; i++) {
732                 if (!dots[i].offset) {
733                     var start = toFloat(dots[i - 1].offset || 0),
734                         end = 0;
735                     for (var j = i + 1; j < ii; j++) {
736                         if (dots[j].offset) {
737                             end = dots[j].offset;
738                             break;
739                         }
740                     }
741                     if (!end) {
742                         end = 100;
743                         j = ii;
744                     }
745                     end = toFloat(end);
746                     var d = (end - start) / (j - i + 1);
747                     for (; i < j; i++) {
748                         start += d;
749                         dots[i].offset = start + "%";
750                     }
751                 }
752             }
753             return dots;
754         }),
755         getContainer = function () {
756             var container,
757                 x,
758                 y,
759                 width,
760                 height;
761             if (R.is(arguments[0], "string") || R.is(arguments[0], "object")) {
762                 if (R.is(arguments[0], "string")) {
763                     container = doc.getElementById(arguments[0]);
764                 } else {
765                     container = arguments[0];
766                 }
767                 if (container.tagName) {
768                     if (arguments[1] == null) {
769                         return {
770                             container: container,
771                             width: container.style.pixelWidth || container.offsetWidth,
772                             height: container.style.pixelHeight || container.offsetHeight
773                         };
774                     } else {
775                         return {container: container, width: arguments[1], height: arguments[2]};
776                     }
777                 }
778             } else if (R.is(arguments[0], nu) && arguments[length] > 3) {
779                 return {container: 1, x: arguments[0], y: arguments[1], width: arguments[2], height: arguments[3]};
780             }
781         },
782         plugins = function (con, add) {
783             var that = this;
784             for (var prop in add) if (add[has](prop) && !(prop in con)) {
785                 switch (typeof add[prop]) {
786                     case "function":
787                         (function (f) {
788                             con[prop] = con === that ? f : function () { return f[apply](that, arguments); };
789                         })(add[prop]);
790                     break;
791                     case "object":
792                         con[prop] = con[prop] || {};
793                         plugins.call(this, con[prop], add[prop]);
794                     break;
795                     default:
796                         con[prop] = add[prop];
797                     break;
798                 }
799             }
800         };
801
802     // SVG
803     if (R.svg) {
804         paper.svgns = "http://www.w3.org/2000/svg";
805         paper.xlink = "http://www.w3.org/1999/xlink";
806         var round = function (num) {
807             return +num + (~~num === num) * .5;
808         };
809         var roundPath = function (path) {
810             for (var i = 0, ii = path[length]; i < ii; i++) {
811                 if (path[i][0].toLowerCase() != "a") {
812                     for (var j = 1, jj = path[i][length]; j < jj; j++) {
813                         path[i][j] = round(path[i][j]);
814                     }
815                 } else {
816                     path[i][6] = round(path[i][6]);
817                     path[i][7] = round(path[i][7]);
818                 }
819             }
820             return path;
821         };
822         var $ = function (el, attr) {
823             if (attr) {
824                 for (var key in attr) if (attr[has](key)) {
825                     el[setAttribute](key, attr[key]);
826                 }
827             } else {
828                 return doc.createElementNS(paper.svgns, el);
829             }
830         };
831         R[toString] = function () {
832             return  "Your browser supports SVG.\nYou are running Rapha\u00ebl " + this.version;
833         };
834         var thePath = function (pathString, SVG) {
835             var el = $("path");
836             SVG.canvas && SVG.canvas[appendChild](el);
837             var p = new Element(el, SVG);
838             p.type = "path";
839             setFillAndStroke(p, {fill: "none", stroke: "#000", path: pathString});
840             return p;
841         };
842         var addGradientFill = function (o, gradient, SVG) {
843             var type = "linear",
844                 fx = .5, fy = .5,
845                 s = o.style;
846             gradient = (gradient + E)[rp](/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/, function (all, _fx, _fy) {
847                 type = "radial";
848                 if (_fx && _fy) {
849                     fx = toFloat(_fx);
850                     fy = toFloat(_fy);
851                     if (pow(fx - .5, 2) + pow(fy - .5, 2) > .25) {
852                         fy = Math.sqrt(.25 - pow(fx - .5, 2)) + .5;
853                     }
854                 }
855                 return E;
856             });
857             gradient = gradient[split](/\s*\-\s*/);
858             if (type == "linear") {
859                 var angle = gradient.shift();
860                 angle = -toFloat(angle);
861                 if (isNaN(angle)) {
862                     return null;
863                 }
864                 var vector = [0, 0, Math.cos(angle * Math.PI / 180), Math.sin(angle * Math.PI / 180)],
865                     max = 1 / (mmax(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
866                 vector[2] *= max;
867                 vector[3] *= max;
868                 if (vector[2] < 0) {
869                     vector[0] = -vector[2];
870                     vector[2] = 0;
871                 }
872                 if (vector[3] < 0) {
873                     vector[1] = -vector[3];
874                     vector[3] = 0;
875                 }
876             }
877             var dots = parseDots(gradient);
878             if (!dots) {
879                 return null;
880             }
881             var el = $(type + "Gradient");
882             el.id = "r" + (R.idGenerator++)[toString](36);
883             type == "radial" ? $(el, {fx: fx, fy: fy}) : $(el, {x1: vector[0], y1: vector[1], x2: vector[2], y2: vector[3]});
884             SVG.defs[appendChild](el);
885             for (var i = 0, ii = dots[length]; i < ii; i++) {
886                 var stop = $("stop");
887                 $(stop, {
888                     offset: dots[i].offset ? dots[i].offset : !i ? "0%" : "100%",
889                     "stop-color": dots[i].color || "#fff"
890                 });
891                 el[appendChild](stop);
892             };
893             $(o, {
894                 fill: "url(#" + el.id + ")",
895                 opacity: 1,
896                 "fill-opacity": 1
897             });
898             s.fill = E;
899             s.opacity = 1;
900             s.fillOpacity = 1;
901             return 1;
902         };
903         var updatePosition = function (o) {
904             var bbox = o.getBBox();
905             $(o.pattern, {patternTransform: R.format("translate({0},{1})", bbox.x, bbox.y)});
906         };
907         var setFillAndStroke = function (o, params) {
908             var dasharray = {
909                     "": [0],
910                     "none": [0],
911                     "-": [3, 1],
912                     ".": [1, 1],
913                     "-.": [3, 1, 1, 1],
914                     "-..": [3, 1, 1, 1, 1, 1],
915                     ". ": [1, 3],
916                     "- ": [4, 3],
917                     "--": [8, 3],
918                     "- .": [4, 3, 1, 3],
919                     "--.": [8, 3, 1, 3],
920                     "--..": [8, 3, 1, 3, 1, 3]
921                 },
922                 node = o.node,
923                 attrs = o.attrs,
924                 rot = o.attr("rotation"),
925                 addDashes = function (o, value) {
926                     value = dasharray[(value + E).toLowerCase()];
927                     if (value) {
928                         var width = o.attrs["stroke-width"] || "1",
929                             butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
930                             dashes = [];
931                         var i = value[length];
932                         while (i--) {
933                             dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
934                         }
935                         $(node, {"stroke-dasharray": dashes[join](",")});
936                     }
937                 };
938             toFloat(rot) && o.rotate(0, true);
939             for (var att in params) if (params[has](att)) {
940                 if (!(att in availableAttrs)) {
941                     continue;
942                 }
943                 var value = params[att];
944                 attrs[att] = value;
945                 switch (att) {
946                     // Hyperlink
947                     case "href":
948                     case "title":
949                     case "target":
950                         var pn = node.parentNode;
951                         if (pn.tagName.toLowerCase() != "a") {
952                             var hl = $("a");
953                             pn.insertBefore(hl, node);
954                             hl[appendChild](node);
955                             pn = hl;
956                         }
957                         pn.setAttributeNS(o.paper.xlink, att, value);
958                       break;
959                     case "clip-rect":
960                         var rect = (value + E)[split](separator);
961                         if (rect[length] == 4) {
962                             o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
963                             var el = $("clipPath"),
964                                 rc = $("rect");
965                             el.id = "r" + (R.idGenerator++)[toString](36);
966                             $(rc, {
967                                 x: rect[0],
968                                 y: rect[1],
969                                 width: rect[2],
970                                 height: rect[3]
971                             });
972                             el[appendChild](rc);
973                             o.paper.defs[appendChild](el);
974                             $(node, {"clip-path": "url(#" + el.id + ")"});
975                             o.clip = rc;
976                         }
977                         if (!value) {
978                             var clip = doc.getElementById(node.getAttribute("clip-path")[rp](/(^url\(#|\)$)/g, E));
979                             clip && clip.parentNode.removeChild(clip);
980                             $(node, {"clip-path": E});
981                             delete o.clip;
982                         }
983                     break;
984                     case "path":
985                         if (value && o.type == "path") {
986                             attrs.path = roundPath(pathToAbsolute(value));
987                             $(node, {d: attrs.path});
988                         }
989                         break;
990                     case "width":
991                         node[setAttribute](att, value);
992                         if (attrs.fx) {
993                             att = "x";
994                             value = attrs.x;
995                         } else {
996                             break;
997                         }
998                     case "x":
999                         if (attrs.fx) {
1000                             value = -attrs.x - (attrs.width || 0);
1001                         }
1002                     case "rx":
1003                         if (att == "rx" && o.type == "rect") {
1004                             break;
1005                         }
1006                     case "cx":
1007                         node[setAttribute](att, value);
1008                         o.pattern && updatePosition(o);
1009                         break;
1010                     case "height":
1011                         node[setAttribute](att, value);
1012                         if (attrs.fy) {
1013                             att = "y";
1014                             value = attrs.y;
1015                         } else {
1016                             break;
1017                         }
1018                     case "y":
1019                         if (attrs.fy) {
1020                             value = -attrs.y - (attrs.height || 0);
1021                         }
1022                     case "ry":
1023                         if (att == "ry" && o.type == "rect") {
1024                             break;
1025                         }
1026                     case "cy":
1027                         node[setAttribute](att, value);
1028                         o.pattern && updatePosition(o);
1029                         break;
1030                     case "r":
1031                         if (o.type == "rect") {
1032                             $(node, {rx: value, ry: value});
1033                         } else {
1034                             node[setAttribute](att, value);
1035                         }
1036                         break;
1037                     case "src":
1038                         if (o.type == "image") {
1039                             node.setAttributeNS(o.paper.xlink, "href", value);
1040                         }
1041                         break;
1042                     case "stroke-width":
1043                         node.style.strokeWidth = value;
1044                         // Need following line for Firefox
1045                         node[setAttribute](att, value);
1046                         if (attrs["stroke-dasharray"]) {
1047                             addDashes(o, attrs["stroke-dasharray"]);
1048                         }
1049                         break;
1050                     case "stroke-dasharray":
1051                         addDashes(o, value);
1052                         break;
1053                     case "rotation":
1054                         rot = value;
1055                         o.rotate(value, true);
1056                         break;
1057                     case "translation":
1058                         var xy = (value + E)[split](separator);
1059                         o.translate((+xy[0] + 1 || 2) - 1, (+xy[1] + 1 || 2) - 1);
1060                         break;
1061                     case "scale":
1062                         var xy = (value + E)[split](separator);
1063                         o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null);
1064                         break;
1065                     case "fill":
1066                         var isURL = (value + E).match(/^url\(['"]?([^\)]+)['"]?\)$/i);
1067                         if (isURL) {
1068                             var el = $("pattern"),
1069                                 ig = $("image");
1070                             el.id = "r" + (R.idGenerator++)[toString](36);
1071                             $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse"});
1072                             $(ig, {x: 0, y:0});
1073                             ig.setAttributeNS(o.paper.xlink, "href", isURL[1]);
1074                             el[appendChild](ig);
1075
1076                             var img = doc.createElement("img");
1077                             img.style.cssText = "position:absolute;left:-9999em;top-9999em";
1078                             img.onload = function () {
1079                                 $(el, {width: this.offsetWidth, height: this.offsetHeight});
1080                                 $(ig, {width: this.offsetWidth, height: this.offsetHeight});
1081                                 doc.body.removeChild(this);
1082                                 paper.safari();
1083                             };
1084                             doc.body[appendChild](img);
1085                             img.src = isURL[1];
1086                             o.paper.defs[appendChild](el);
1087                             node.style.fill = "url(#" + el.id + ")";
1088                             $(node, {fill: "url(#" + el.id + ")"});
1089                             o.pattern = el;
1090                             o.pattern && updatePosition(o);
1091                             break;
1092                         }
1093                         if (!R.getRGB(value).error) {
1094                             delete params.gradient;
1095                             delete attrs.gradient;
1096                             if (!R.is(attrs.opacity, "undefined") && R.is(params.opacity, "undefined") ) {
1097                                 node.style.opacity = attrs.opacity;
1098                                 // Need following line for Firefox
1099                                 $(node, {opacity: attrs.opacity});
1100                             }
1101                             if (!R.is(attrs["fill-opacity"], "undefined") && R.is(params["fill-opacity"], "undefined") ) {
1102                                 node.style.fillOpacity = attrs["fill-opacity"];
1103                                 // Need following line for Firefox
1104                                 $(node, {"fill-opacity": attrs["fill-opacity"]});
1105                             }
1106                         } else if ((o.type in {circle: 1, ellipse: 1} || (value + E).charAt(0) != "r") && addGradientFill(node, value, o.paper)) {
1107                             attrs.gradient = value;
1108                             attrs.fill = "none";
1109                             break;
1110                         }
1111                     case "stroke":
1112                         node.style[att] = R.getRGB(value).hex;
1113                         // Need following line for Firefox
1114                         node[setAttribute](att, R.getRGB(value).hex);
1115                         break;
1116                     case "gradient":
1117                         (o.type in {circle: 1, ellipse: 1} || (value + E).charAt(0) != "r") && addGradientFill(node, value, o.paper);
1118                         break;
1119                     case "opacity":
1120                     case "fill-opacity":
1121                         if (attrs.gradient) {
1122                             var gradient = doc.getElementById(node.getAttribute("fill")[rp](/^url\(#|\)$/g, E));
1123                             if (gradient) {
1124                                 var stops = gradient.getElementsByTagName("stop");
1125                                 stops[stops[length] - 1][setAttribute]("stop-opacity", value);
1126                             }
1127                             break;
1128                         }
1129                     default:
1130                         att == "font-size" && (value = toInt(value, 10) + "px");
1131                         var cssrule = att[rp](/(\-.)/g, function (w) {
1132                             return w.substring(1).toUpperCase();
1133                         });
1134                         node.style[cssrule] = value;
1135                         // Need following line for Firefox
1136                         node[setAttribute](att, value);
1137                         break;
1138                 }
1139             }
1140             
1141             tuneText(o, params);
1142             toInt(rot, 10) && o.rotate(rot, true);
1143         };
1144         var leading = 1.2;
1145         var tuneText = function (el, params) {
1146             if (el.type != "text" || !("text" in params || "font" in params || "font-size" in params || "x" in params || "y" in params)) {
1147                 return;
1148             }
1149             var a = el.attrs,
1150                 node = el.node,
1151                 fontSize = node.firstChild ? toInt(doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
1152
1153             if ("text" in params) {
1154                 while (node.firstChild) {
1155                     node.removeChild(node.firstChild);
1156                 }
1157                 var texts = (params.text + E)[split]("\n");
1158                 for (var i = 0, ii = texts[length]; i < ii; i++) {
1159                     var tspan = $("tspan");
1160                     i && $(tspan, {dy: fontSize * leading, x: a.x});
1161                     tspan[appendChild](doc.createTextNode(texts[i]));
1162                     node[appendChild](tspan);
1163                 }
1164             } else {
1165                 var texts = node.getElementsByTagName("tspan");
1166                 for (var i = 0, ii = texts[length]; i < ii; i++) {
1167                     i && $(texts[i], {dy: fontSize * leading, x: a.x});
1168                 }
1169             }
1170             $(node, {y: a.y});
1171             var bb = el.getBBox(),
1172                 dif = a.y - (bb.y + bb.height / 2);
1173             dif && $(node, {y: a.y + dif});
1174         };
1175         var Element = function (node, svg) {
1176             var X = 0,
1177                 Y = 0;
1178             this[0] = node;
1179             this.node = node;
1180             node.raphael = this;
1181             this.paper = svg;
1182             this.attrs = this.attrs || {};
1183             this.transformations = []; // rotate, translate, scale
1184             this._ = {
1185                 tx: 0,
1186                 ty: 0,
1187                 rt: {deg: 0, cx: 0, cy: 0},
1188                 sx: 1,
1189                 sy: 1
1190             };
1191         };
1192         Element[proto].rotate = function (deg, cx, cy) {
1193             if (deg == null) {
1194                 if (this._.rt.cx) {
1195                     return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](" ");
1196                 }
1197                 return this._.rt.deg;
1198             }
1199             var bbox = this.getBBox();
1200             deg = (deg + E)[split](separator);
1201             if (deg[length] - 1) {
1202                 cx = toFloat(deg[1]);
1203                 cy = toFloat(deg[2]);
1204             }
1205             deg = toFloat(deg[0]);
1206             if (cx != null) {
1207                 this._.rt.deg = deg;
1208             } else {
1209                 this._.rt.deg += deg;
1210             }
1211             (cy == null) && (cx = null);
1212             this._.rt.cx = cx;
1213             this._.rt.cy = cy;
1214             cx = cx == null ? bbox.x + bbox.width / 2 : cx;
1215             cy = cy == null ? bbox.y + bbox.height / 2 : cy;
1216             if (this._.rt.deg) {
1217                 this.transformations[0] = R.format("rotate({0} {1} {2})", this._.rt.deg, cx, cy);
1218                 this.clip && $(this.clip, {transform: R.format("rotate({0} {1} {2})", -this._.rt.deg, cx, cy)});
1219             } else {
1220                 this.transformations[0] = E;
1221                 this.clip && $(this.clip, {transform: E});
1222             }
1223             $(this.node, {transform: this.transformations[join](" ")});
1224             return this;
1225         };
1226         Element[proto].hide = function () {
1227             this.node.style.display = "none";
1228             return this;
1229         };
1230         Element[proto].show = function () {
1231             this.node.style.display = "block";
1232             return this;
1233         };
1234         Element[proto].remove = function () {
1235             this.node.parentNode.removeChild(this.node);
1236             for (var i in this) {
1237                 delete this[i];
1238             }
1239         };
1240         Element[proto].getBBox = function () {
1241             if (this.type == "path") {
1242                 return pathDimensions(this.attrs.path);
1243             }
1244             if (this.node.style.display == "none") {
1245                 this.show();
1246                 var hide = true;
1247             }
1248             var bbox = {};
1249             try {
1250                 bbox = this.node.getBBox();
1251             } catch(e) {
1252                 // Firefox 3.0.x plays badly here
1253             } finally {
1254                 bbox = bbox || {};
1255             }
1256             if (this.type == "text") {
1257                 bbox = {x: bbox.x, y: Infinity, width: 0, height: 0};
1258                 for (var i = 0, ii = this.node.getNumberOfChars(); i < ii; i++) {
1259                     var bb = this.node.getExtentOfChar(i);
1260                     (bb.y < bbox.y) && (bbox.y = bb.y);
1261                     (bb.y + bb.height - bbox.y > bbox.height) && (bbox.height = bb.y + bb.height - bbox.y);
1262                     (bb.x + bb.width - bbox.x > bbox.width) && (bbox.width = bb.x + bb.width - bbox.x);
1263                 }
1264             }
1265             hide && this.hide();
1266             return bbox;
1267         };
1268         Element[proto].attr = function () {
1269             if (arguments[length] == 1 && R.is(arguments[0], "string")) {
1270                 if (arguments[0] == "translation") {
1271                     return this.translate();
1272                 }
1273                 if (arguments[0] == "rotation") {
1274                     return this.rotate();
1275                 }
1276                 if (arguments[0] == "scale") {
1277                     return this.scale();
1278                 }
1279                 return this.attrs[arguments[0]];
1280             }
1281             if (arguments[length] == 1 && R.is(arguments[0], "array")) {
1282                 var values = {};
1283                 for (var j in arguments[0]) if (arguments[0][has](j)) {
1284                     values[arguments[0][j]] = this.attrs[arguments[0][j]];
1285                 }
1286                 return values;
1287             }
1288             if (arguments[length] == 2) {
1289                 var params = {};
1290                 params[arguments[0]] = arguments[1];
1291                 setFillAndStroke(this, params);
1292             } else if (arguments[length] == 1 && R.is(arguments[0], "object")) {
1293                 setFillAndStroke(this, arguments[0]);
1294             }
1295             return this;
1296         };
1297         Element[proto].toFront = function () {
1298             this.node.parentNode[appendChild](this.node);
1299             return this;
1300         };
1301         Element[proto].toBack = function () {
1302             if (this.node.parentNode.firstChild != this.node) {
1303                 this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
1304             }
1305             return this;
1306         };
1307         Element[proto].insertAfter = function (element) {
1308             if (element.node.nextSibling) {
1309                 element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
1310             } else {
1311                 element.node.parentNode[appendChild](this.node);
1312             }
1313             return this;
1314         };
1315         Element[proto].insertBefore = function (element) {
1316             var node = element.node;
1317             node.parentNode.insertBefore(this.node, node);
1318             return this;
1319         };
1320         
1321         var theCircle = function (svg, x, y, r) {
1322             x = round(x);
1323             y = round(y);
1324             var el = $("circle");
1325             svg.canvas && svg.canvas[appendChild](el);
1326             var res = new Element(el, svg);
1327             res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
1328             res.type = "circle";
1329             $(el, res.attrs);
1330             return res;
1331         };
1332         var theRect = function (svg, x, y, w, h, r) {
1333             x = round(x);
1334             y = round(y);
1335             var el = $("rect");
1336             svg.canvas && svg.canvas[appendChild](el);
1337             var res = new Element(el, svg);
1338             res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
1339             res.type = "rect";
1340             $(el, res.attrs);
1341             return res;
1342         };
1343         var theEllipse = function (svg, x, y, rx, ry) {
1344             x = round(x);
1345             y = round(y);
1346             var el = $("ellipse");
1347             svg.canvas && svg.canvas[appendChild](el);
1348             var res = new Element(el, svg);
1349             res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
1350             res.type = "ellipse";
1351             $(el, res.attrs);
1352             return res;
1353         };
1354         var theImage = function (svg, src, x, y, w, h) {
1355             var el = $("image");
1356             $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
1357             el.setAttributeNS(svg.xlink, "href", src);
1358             svg.canvas && svg.canvas[appendChild](el);
1359             var res = new Element(el, svg);
1360             res.attrs = {x: x, y: y, width: w, height: h, src: src};
1361             res.type = "image";
1362             return res;
1363         };
1364         var theText = function (svg, x, y, text) {
1365             var el = $("text");
1366             $(el, {x: x, y: y, "text-anchor": "middle"});
1367             svg.canvas && svg.canvas[appendChild](el);
1368             var res = new Element(el, svg);
1369             res.attrs = {x: x, y: y, "text-anchor": "middle", text: text, font: availableAttrs.font, stroke: "none", fill: "#000"};
1370             res.type = "text";
1371             setFillAndStroke(res, res.attrs);
1372             return res;
1373         };
1374         var setSize = function (width, height) {
1375             this.width = width || this.width;
1376             this.height = height || this.height;
1377             this.canvas[setAttribute]("width", this.width);
1378             this.canvas[setAttribute]("height", this.height);
1379             return this;
1380         };
1381         var create = function () {
1382             var con = getContainer[apply](null, arguments),
1383                 container = con && con.container,
1384                 x = con.x,
1385                 y = con.y,
1386                 width = con.width,
1387                 height = con.height;
1388             if (!container) {
1389                 throw new Error("SVG container not found.");
1390             }
1391             paper.canvas = $("svg");
1392             var cnvs = paper.canvas;
1393             paper.width = width || 512;
1394             paper.height = height || 342;
1395             cnvs[setAttribute]("width", paper.width);
1396             cnvs[setAttribute]("height", paper.height);
1397             if (container == 1) {
1398                 cnvs.style.cssText = "position:absolute;left:" + x + "px;top:" + y + "px";
1399                 doc.body[appendChild](cnvs);
1400             } else {
1401                 if (container.firstChild) {
1402                     container.insertBefore(cnvs, container.firstChild);
1403                 } else {
1404                     container[appendChild](cnvs);
1405                 }
1406             }
1407             container = { canvas: cnvs };
1408             for (var prop in paper) if (paper[has](prop)) {
1409                 container[prop] = paper[prop];
1410             }
1411             plugins.call(container, container, R.fn);
1412             container.clear();
1413             container.raphael = R;
1414             return container;
1415         };
1416         paper.clear = function () {
1417             var c = this.canvas;
1418             while (c.firstChild) {
1419                 c.removeChild(c.firstChild);
1420             }
1421             (this.desc = $("desc"))[appendChild](doc.createTextNode("Created with Rapha\u00ebl"));
1422             c[appendChild](this.desc);
1423             c[appendChild](this.defs = $("defs"));
1424         };
1425         paper.remove = function () {
1426             this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
1427             for (var i in this) {
1428                 delete this[i];
1429             }
1430         };
1431     }
1432
1433     // VML
1434     if (R.vml) {
1435         var path2vml = function (path) {
1436             var total =  /[ahqtv]/ig,
1437                 command = pathToAbsolute;
1438             (path + E).match(total) && (command = path2curve);
1439             total =  /[clmz]/g;
1440             if (command == pathToAbsolute && !(path + E).match(total)) {
1441                 var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
1442                     bites = /([clmz]),?([^clmz]*)/gi,
1443                     val = /-?[^,\s-]+/g;
1444                 var res = (path + E)[rp](bites, function (all, command, args) {
1445                     var vals = [];
1446                     args[rp](val, function (value) {
1447                         vals[push](round(value));
1448                     });
1449                     return map[command] + vals;
1450                 });
1451                 return res;
1452             }
1453             var pa = command(path), p, res = [], r;
1454             for (var i = 0, ii = pa[length]; i < ii; i++) {
1455                 p = pa[i];
1456                 r = (pa[i][0] + E).toLowerCase();
1457                 r == "z" && (r = "x");
1458                 for (var j = 1, jj = p[length]; j < jj; j++) {
1459                     r += round(p[j]) + (j != jj - 1 ? "," : E);
1460                 }
1461                 res[push](r);
1462             }
1463             return res[join](" ");
1464         };
1465         
1466         R[toString] = function () {
1467             return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\u00ebl " + this.version;
1468         };
1469         var thePath = function (pathString, VML) {
1470             var g = createNode("group");
1471             g.style.cssText = "position:absolute;left:0;top:0;width:" + VML.width + "px;height:" + VML.height + "px";
1472             g.coordsize = VML.coordsize;
1473             g.coordorigin = VML.coordorigin;
1474             var el = createNode("shape"), ol = el.style;
1475             ol.width = VML.width + "px";
1476             ol.height = VML.height + "px";
1477             el.coordsize = this.coordsize;
1478             el.coordorigin = this.coordorigin;
1479             g[appendChild](el);
1480             var p = new Element(el, g, VML);
1481             p.isAbsolute = true;
1482             p.type = "path";
1483             p.path = [];
1484             p.Path = E;
1485             pathString && setFillAndStroke(p, {fill: "none", stroke: "#000", path: pathString});
1486             VML.canvas[appendChild](g);
1487             return p;
1488         };
1489         var setFillAndStroke = function (o, params) {
1490             o.attrs = o.attrs || {};
1491             var node = o.node,
1492                 a = o.attrs,
1493                 s = node.style,
1494                 xy,
1495                 res = o;
1496             for (var par in params) if (params[has](par)) {
1497                 a[par] = params[par];
1498             }
1499             params.href && (node.href = params.href);
1500             params.title && (node.title = params.title);
1501             params.target && (node.target = params.target);
1502             if (params.path && o.type == "path") {
1503                 // a.path = R.parsePathString(params.path);
1504                 a.path = params.path;
1505                 node.path = path2vml(a.path);
1506             }
1507             if (params.rotation != null) {
1508                 o.rotate(params.rotation, true);
1509             }
1510             if (params.translation) {
1511                 xy = (params.translation + E)[split](separator);
1512                 o.translate(xy[0], xy[1]);
1513             }
1514             if (params.scale) {
1515                 xy = (params.scale + E)[split](separator);
1516                 o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null);
1517             }
1518             if ("clip-rect" in params) {
1519                 var rect = (params["clip-rect"] + E)[split](separator);
1520                 if (rect[length] == 4) {
1521                     rect[2] = +rect[2] + (+rect[0]);
1522                     rect[3] = +rect[3] + (+rect[1]);
1523                     var div = node.clipRect || doc.createElement("div"),
1524                         dstyle = div.style,
1525                         group = node.parentNode;
1526                     dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
1527                     if (!node.clipRect) {
1528                         dstyle.position = "absolute";
1529                         dstyle.top = 0;
1530                         dstyle.left = 0;
1531                         dstyle.width = o.paper.width + "px";
1532                         dstyle.height = o.paper.height + "px";
1533                         group.parentNode.insertBefore(div, group);
1534                         div[appendChild](group);
1535                         node.clipRect = div;
1536                     }
1537                 }
1538                 if (!params["clip-rect"]) {
1539                     node.clipRect && (node.clipRect.style.clip = E);
1540                 }
1541             }
1542             if (o.type == "image" && params.src) {
1543                 node.src = params.src;
1544             }
1545             if (o.type == "image" && params.opacity) {
1546                 node.filterOpacity = " progid:DXImageTransform.Microsoft.Alpha(opacity=" + (params.opacity * 100) + ")";
1547                 s.filter = (node.filterMatrix || E) + (node.filterOpacity || E);
1548             }
1549             params.font && (s.font = params.font);
1550             params["font-family"] && (s.fontFamily = '"' + params["font-family"][split](",")[0][rp](/^['"]+|['"]+$/g, E) + '"');
1551             params["font-size"] && (s.fontSize = params["font-size"]);
1552             params["font-weight"] && (s.fontWeight = params["font-weight"]);
1553             params["font-style"] && (s.fontStyle = params["font-style"]);
1554             if (params.opacity != null || 
1555                 params["stroke-width"] != null ||
1556                 params.fill != null ||
1557                 params.stroke != null ||
1558                 params["stroke-width"] != null ||
1559                 params["stroke-opacity"] != null ||
1560                 params["fill-opacity"] != null ||
1561                 params["stroke-dasharray"] != null ||
1562                 params["stroke-miterlimit"] != null ||
1563                 params["stroke-linejoin"] != null ||
1564                 params["stroke-linecap"] != null) {
1565                 node = o.shape || node;
1566                 var fill = (node.getElementsByTagName("fill") && node.getElementsByTagName("fill")[0]),
1567                     newfill = false;
1568                 !fill && (newfill = fill = createNode("fill"));
1569                 if ("fill-opacity" in params || "opacity" in params) {
1570                     var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1);
1571                     opacity < 0 && (opacity = 0);
1572                     opacity > 1 && (opacity = 1);
1573                     fill.opacity = opacity;
1574                 }
1575                 params.fill && (fill.on = true);
1576                 if (fill.on == null || params.fill == "none") {
1577                     fill.on = false;
1578                 }
1579                 if (fill.on && params.fill) {
1580                     var isURL = params.fill.match(/^url\(([^\)]+)\)$/i);
1581                     if (isURL) {
1582                         fill.src = isURL[1];
1583                         fill.type = "tile";
1584                     } else {
1585                         fill.color = R.getRGB(params.fill).hex;
1586                         fill.src = E;
1587                         fill.type = "solid";
1588                         if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || (params.fill + E).charAt(0) != "r") && addGradientFill(res, params.fill)) {
1589                             a.fill = "none";
1590                             a.gradient = params.fill;
1591                         }
1592                     }
1593                 }
1594                 newfill && node[appendChild](fill);
1595                 var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
1596                 newstroke = false;
1597                 !stroke && (newstroke = stroke = createNode("stroke"));
1598                 if ((params.stroke && params.stroke != "none") ||
1599                     params["stroke-width"] ||
1600                     params["stroke-opacity"] != null ||
1601                     params["stroke-dasharray"] ||
1602                     params["stroke-miterlimit"] ||
1603                     params["stroke-linejoin"] ||
1604                     params["stroke-linecap"]) {
1605                     stroke.on = true;
1606                 }
1607                 (params.stroke == "none" || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
1608                 stroke.on && params.stroke && (stroke.color = R.getRGB(params.stroke).hex);
1609                 var opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1);
1610                 opacity < 0 && (opacity = 0);
1611                 opacity > 1 && (opacity = 1);
1612                 stroke.opacity = opacity;
1613                 params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
1614                 stroke.miterlimit = params["stroke-miterlimit"] || 8;
1615                 params["stroke-linecap"] && (stroke.endcap = {butt: "flat", square: "square", round: "round"}[params["stroke-linecap"]] || "miter");
1616                 params["stroke-width"] && (stroke.weight = (toFloat(params["stroke-width"]) || 1) * 12 / 16);
1617                 if (params["stroke-dasharray"]) {
1618                     var dasharray = {
1619                         "-": "shortdash",
1620                         ".": "shortdot",
1621                         "-.": "shortdashdot",
1622                         "-..": "shortdashdotdot",
1623                         ". ": "dot",
1624                         "- ": "dash",
1625                         "--": "longdash",
1626                         "- .": "dashdot",
1627                         "--.": "longdashdot",
1628                         "--..": "longdashdotdot"
1629                     };
1630                     stroke.dashstyle = dasharray[params["stroke-dasharray"]] || E;
1631                 }
1632                 newstroke && node[appendChild](stroke);
1633             }
1634             if (res.type == "text") {
1635                 var s = res.paper.span.style;
1636                 a.font && (s.font = a.font);
1637                 a["font-family"] && (s.fontFamily = a["font-family"]);
1638                 a["font-size"] && (s.fontSize = a["font-size"]);
1639                 a["font-weight"] && (s.fontWeight = a["font-weight"]);
1640                 a["font-style"] && (s.fontStyle = a["font-style"]);
1641                 res.node.string && (res.paper.span.innerHTML = (res.node.string + E)[rp](/</g, "&#60;")[rp](/&/g, "&#38;")[rp](/\n/g, "<br>"));
1642                 res.W = a.w = res.paper.span.offsetWidth;
1643                 res.H = a.h = res.paper.span.offsetHeight;
1644                 res.X = a.x;
1645                 res.Y = a.y + round(res.H / 2);
1646
1647                 // text-anchor emulationm
1648                 switch (a["text-anchor"]) {
1649                     case "start":
1650                         res.node.style["v-text-align"] = "left";
1651                         res.bbx = round(res.W / 2);
1652                     break;
1653                     case "end":
1654                         res.node.style["v-text-align"] = "right";
1655                         res.bbx = -round(res.W / 2);
1656                     break;
1657                     default:
1658                         res.node.style["v-text-align"] = "center";
1659                     break;
1660                 }
1661             }
1662         };
1663         var addGradientFill = function (o, gradient) {
1664             o.attrs = o.attrs || {};
1665             var attrs = o.attrs,
1666                 fill = o.node.getElementsByTagName("fill"),
1667                 type = "linear",
1668                 fxfy = ".5 .5";
1669             o.attrs.gradient = gradient;
1670             gradient = (gradient + E)[rp](/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/, function (all, fx, fy) {
1671                 type = "radial";
1672                 if (fx && fy) {
1673                     fx = toFloat(fx);
1674                     fy = toFloat(fy);
1675                     if (pow(fx - .5, 2) + pow(fy - .5, 2) > .25) {
1676                         fy = Math.sqrt(.25 - pow(fx - .5, 2)) + .5;
1677                     }
1678                     fxfy = fx + " " + fy;
1679                 }
1680                 return E;
1681             });
1682             gradient = gradient[split](/\s*\-\s*/);
1683             if (type == "linear") {
1684                 var angle = gradient.shift();
1685                 angle = -toFloat(angle);
1686                 if (isNaN(angle)) {
1687                     return null;
1688                 }
1689             }
1690             var dots = parseDots(gradient);
1691             if (!dots) {
1692                 return null;
1693             }
1694             o = o.shape || o.node;
1695             fill = fill[0] || createNode("fill");
1696             if (dots[length]) {
1697                 fill.on = true;
1698                 fill.method = "none";
1699                 fill.type = (type == "radial") ? "gradientradial" : "gradient";
1700                 fill.color = dots[0].color;
1701                 fill.color2 = dots[dots[length] - 1].color;
1702                 var clrs = [];
1703                 for (var i = 0, ii = dots[length]; i < ii; i++) {
1704                     dots[i].offset && clrs[push](dots[i].offset + " " + dots[i].color);
1705                 }
1706                 if (clrs[length] && fill.colors) {
1707                     fill.colors.value = clrs[join](",");
1708                 } else {
1709                     fill.colors.value = "0% " + fill.color;
1710                 }
1711                 if (type == "radial") {
1712                     fill.focus = "100%";
1713                     fill.focussize = fxfy;
1714                     fill.focusposition = fxfy;
1715                 } else {
1716                     fill.angle = (270 - angle) % 360;
1717                 }
1718             }
1719             return 1;
1720         };
1721         var Element = function (node, group, vml) {
1722             var Rotation = 0,
1723                 RotX = 0,
1724                 RotY = 0,
1725                 Scale = 1;
1726             this[0] = node;
1727             this.node = node;
1728             node.raphael = this;
1729             this.X = 0;
1730             this.Y = 0;
1731             this.attrs = {};
1732             this.Group = group;
1733             this.paper = vml;
1734             this._ = {
1735                 tx: 0,
1736                 ty: 0,
1737                 rt: {deg:0},
1738                 sx: 1,
1739                 sy: 1
1740             };
1741         };
1742         Element[proto].rotate = function (deg, cx, cy) {
1743             if (deg == null) {
1744                 if (this._.rt.cx) {
1745                     return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](" ");
1746                 }
1747                 return this._.rt.deg;
1748             }
1749             deg = (deg + E)[split](separator);
1750             if (deg[length] - 1) {
1751                 cx = toFloat(deg[1]);
1752                 cy = toFloat(deg[2]);
1753             }
1754             deg = toFloat(deg[0]);
1755             if (cx != null) {
1756                 this._.rt.deg = deg;
1757             } else {
1758                 this._.rt.deg += deg;
1759             }
1760             cy == null && (cx = null);
1761             this._.rt.cx = cx;
1762             this._.rt.cy = cy;
1763             this.setBox(this.attrs, cx, cy);
1764             this.Group.style.rotation = this._.rt.deg;
1765             // gradient fix for rotation. TODO
1766             // var fill = (this.shape || this.node).getElementsByTagName("fill");
1767             // fill = fill[0] || {};
1768             // var b = ((360 - this._.rt.deg) - 270) % 360;
1769             // !R.is(fill.angle, "undefined") && (fill.angle = b);
1770             return this;
1771         };
1772         Element[proto].setBox = function (params, cx, cy) {
1773             var gs = this.Group.style,
1774                 os = (this.shape && this.shape.style) || this.node.style;
1775             params = params || {};
1776             for (var i in params) if (params[has](i)) {
1777                 this.attrs[i] = params[i];
1778             }
1779             cx = cx || this._.rt.cx;
1780             cy = cy || this._.rt.cy;
1781             var attr = this.attrs,
1782                 x,
1783                 y,
1784                 w,
1785                 h;
1786             switch (this.type) {
1787                 case "circle":
1788                     x = attr.cx - attr.r;
1789                     y = attr.cy - attr.r;
1790                     w = h = attr.r * 2;
1791                     break;
1792                 case "ellipse":
1793                     x = attr.cx - attr.rx;
1794                     y = attr.cy - attr.ry;
1795                     w = attr.rx * 2;
1796                     h = attr.ry * 2;
1797                     break;
1798                 case "rect":
1799                 case "image":
1800                     x = +attr.x;
1801                     y = +attr.y;
1802                     w = attr.width || 0;
1803                     h = attr.height || 0;
1804                     break;
1805                 case "text":
1806                     this.textpath.v = ["m", round(attr.x), ", ", round(attr.y - 2), "l", round(attr.x) + 1, ", ", round(attr.y - 2)][join](E);
1807                     x = attr.x - round(this.W / 2);
1808                     y = attr.y - this.H / 2;
1809                     w = this.W;
1810                     h = this.H;
1811                     break;
1812                 case "path":
1813                     if (!this.attrs.path) {
1814                         x = 0;
1815                         y = 0;
1816                         w = this.paper.width;
1817                         h = this.paper.height;
1818                     } else {
1819                         var dim = pathDimensions(this.attrs.path);
1820                         x = dim.x;
1821                         y = dim.y;
1822                         w = dim.width;
1823                         h = dim.height;
1824                     }
1825                     break;
1826                 default:
1827                     x = 0;
1828                     y = 0;
1829                     w = this.paper.width;
1830                     h = this.paper.height;
1831                     break;
1832             }
1833             cx = (cx == null) ? x + w / 2 : cx;
1834             cy = (cy == null) ? y + h / 2 : cy;
1835             var left = cx - this.paper.width / 2,
1836                 top = cy - this.paper.height / 2;
1837             if (this.type == "path" || this.type == "text") {
1838                 (gs.left != left + "px") && (gs.left = left + "px");
1839                 (gs.top != top + "px") && (gs.top = top + "px");
1840                 this.X = this.type == "text" ? x : -left;
1841                 this.Y = this.type == "text" ? y : -top;
1842                 this.W = w;
1843                 this.H = h;
1844                 (os.left != -left + "px") && (os.left = -left + "px");
1845                 (os.top != -top + "px") && (os.top = -top + "px");
1846             } else {
1847                 (gs.left != left + "px") && (gs.left = left + "px");
1848                 (gs.top != top + "px") && (gs.top = top + "px");
1849                 this.X = x;
1850                 this.Y = y;
1851                 this.W = w;
1852                 this.H = h;
1853                 (gs.width != this.paper.width + "px") && (gs.width = this.paper.width + "px");
1854                 (gs.height != this.paper.height + "px") && (gs.height = this.paper.height + "px");
1855                 (os.left != x - left + "px") && (os.left = x - left + "px");
1856                 (os.top != y - top + "px") && (os.top = y - top + "px");
1857                 (os.width != w + "px") && (os.width = w + "px");
1858                 (os.height != h + "px") && (os.height = h + "px");
1859                 var arcsize = (+params.r || 0) / (mmin(w, h));
1860                 if (this.type == "rect" && this.arcsize != arcsize && (arcsize || this.arcsize)) {
1861                     // We should replace element with the new one
1862                     var o = createNode(arcsize ? "roundrect" : "rect");
1863                     o.arcsize = arcsize;
1864                     this.Group[appendChild](o);
1865                     this.node.parentNode.removeChild(this.node);
1866                     this.node = o;
1867                     this.arcsize = arcsize;
1868                     setFillAndStroke(this, this.attrs);
1869                     this.setBox(this.attrs);
1870                 }
1871             }
1872         };
1873         Element[proto].hide = function () {
1874             this.Group.style.display = "none";
1875             return this;
1876         };
1877         Element[proto].show = function () {
1878             this.Group.style.display = "block";
1879             return this;
1880         };
1881         Element[proto].getBBox = function () {
1882             if (this.type == "path") {
1883                 return pathDimensions(this.attrs.path);
1884             }
1885             return {
1886                 x: this.X + (this.bbx || 0),
1887                 y: this.Y,
1888                 width: this.W,
1889                 height: this.H
1890             };
1891         };
1892         Element[proto].remove = function () {
1893             this.node.parentNode.removeChild(this[0]);
1894             this.Group.parentNode.removeChild(this.Group);
1895             this.shape && this.shape.parentNode.removeChild(this.shape);
1896             for (var i in this) {
1897                 delete this[i];
1898             }
1899         };
1900         Element[proto].attr = function () {
1901             if (arguments[length] == 1 && R.is(arguments[0], "string")) {
1902                 if (arguments[0] == "translation") {
1903                     return this.translate();
1904                 }
1905                 if (arguments[0] == "rotation") {
1906                     return this.rotate();
1907                 }
1908                 if (arguments[0] == "scale") {
1909                     return this.scale();
1910                 }
1911                 return this.attrs[arguments[0]];
1912             }
1913             if (this.attrs && arguments[length] == 1 && R.is(arguments[0], "array")) {
1914                 var values = {};
1915                 for (var i = 0, ii = arguments[0][length]; i < ii; i++) {
1916                     values[arguments[0][i]] = this.attrs[arguments[0][i]];
1917                 };
1918                 return values;
1919             }
1920             var params;
1921             if (arguments[length] == 2) {
1922                 params = {};
1923                 params[arguments[0]] = arguments[1];
1924             }
1925             if (arguments[length] == 1 && R.is(arguments[0], "object")) {
1926                 params = arguments[0];
1927             }
1928             if (params) {
1929                 if (params.gradient && (this.type in {circle: 1, ellipse: 1} || (params.gradient + E).charAt(0) != "r")) {
1930                     addGradientFill(this, params.gradient);
1931                 }
1932                 if (params.text && this.type == "text") {
1933                     this.node.string = params.text;
1934                 }
1935                 setFillAndStroke(this, params);
1936                 this.setBox(this.attrs);
1937             }
1938             return this;
1939         };
1940         Element[proto].toFront = function () {
1941             this.Group.parentNode[appendChild](this.Group);
1942             return this;
1943         };
1944         Element[proto].toBack = function () {
1945             if (this.Group.parentNode.firstChild != this.Group) {
1946                 this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
1947             }
1948             return this;
1949         };
1950         Element[proto].insertAfter = function (element) {
1951             if (element.Group.nextSibling) {
1952                 element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling);
1953             } else {
1954                 element.Group.parentNode[appendChild](this.Group);
1955             }
1956             return this;
1957         };
1958         Element[proto].insertBefore = function (element) {
1959             element.Group.parentNode.insertBefore(this.Group, element.Group);
1960             return this;
1961         };
1962
1963         var theCircle = function (vml, x, y, r) {
1964             var g = createNode("group"),
1965                 o = createNode("oval"),
1966                 ol = o.style;
1967             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
1968             g.coordsize = vml.coordsize;
1969             g.coordorigin = vml.coordorigin;
1970             g[appendChild](o);
1971             var res = new Element(o, g, vml);
1972             res.type = "circle";
1973             setFillAndStroke(res, {stroke: "#000", fill: "none"});
1974             res.attrs.cx = x;
1975             res.attrs.cy = y;
1976             res.attrs.r = r;
1977             res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
1978             vml.canvas[appendChild](g);
1979             return res;
1980         };
1981         var theRect = function (vml, x, y, w, h, r) {
1982             var g = createNode("group"),
1983                 o = createNode(r ? "roundrect" : "rect"),
1984                 arcsize = (+r || 0) / (mmin(w, h));
1985             o.arcsize = arcsize;
1986             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
1987             g.coordsize = vml.coordsize;
1988             g.coordorigin = vml.coordorigin;
1989             g[appendChild](o);
1990             var res = new Element(o, g, vml);
1991             res.type = "rect";
1992             setFillAndStroke(res, {stroke: "#000"});
1993             res.arcsize = arcsize;
1994             res.setBox({x: x, y: y, width: w, height: h, r: +r});
1995             vml.canvas[appendChild](g);
1996             return res;
1997         };
1998         var theEllipse = function (vml, x, y, rx, ry) {
1999             var g = createNode("group"),
2000                 o = createNode("oval"),
2001                 ol = o.style;
2002             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2003             g.coordsize = vml.coordsize;
2004             g.coordorigin = vml.coordorigin;
2005             g[appendChild](o);
2006             var res = new Element(o, g, vml);
2007             res.type = "ellipse";
2008             setFillAndStroke(res, {stroke: "#000"});
2009             res.attrs.cx = x;
2010             res.attrs.cy = y;
2011             res.attrs.rx = rx;
2012             res.attrs.ry = ry;
2013             res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
2014             vml.canvas[appendChild](g);
2015             return res;
2016         };
2017         var theImage = function (vml, src, x, y, w, h) {
2018             var g = createNode("group"),
2019                 o = createNode("image"),
2020                 ol = o.style;
2021             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2022             g.coordsize = vml.coordsize;
2023             g.coordorigin = vml.coordorigin;
2024             o.src = src;
2025             g[appendChild](o);
2026             var res = new Element(o, g, vml);
2027             res.type = "image";
2028             res.attrs.src = src;
2029             res.attrs.x = x;
2030             res.attrs.y = y;
2031             res.attrs.w = w;
2032             res.attrs.h = h;
2033             res.setBox({x: x, y: y, width: w, height: h});
2034             vml.canvas[appendChild](g);
2035             return res;
2036         };
2037         var theText = function (vml, x, y, text) {
2038             var g = createNode("group"),
2039                 el = createNode("shape"),
2040                 ol = el.style,
2041                 path = createNode("path"),
2042                 ps = path.style,
2043                 o = createNode("textpath");
2044             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2045             g.coordsize = vml.coordsize;
2046             g.coordorigin = vml.coordorigin;
2047             path.v = R.format("m{0},{1}l{2},{1}", round(x), round(y), round(x) + 1);
2048             path.textpathok = true;
2049             ol.width = vml.width;
2050             ol.height = vml.height;
2051             o.string = text;
2052             o.on = true;
2053             el[appendChild](o);
2054             el[appendChild](path);
2055             g[appendChild](el);
2056             var res = new Element(o, g, vml);
2057             res.shape = el;
2058             res.textpath = path;
2059             res.type = "text";
2060             res.attrs.text = text;
2061             res.attrs.x = x;
2062             res.attrs.y = y;
2063             res.attrs.w = 1;
2064             res.attrs.h = 1;
2065             setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000"});
2066             res.setBox();
2067             vml.canvas[appendChild](g);
2068             return res;
2069         };
2070         var setSize = function (width, height) {
2071             var cs = this.canvas.style;
2072             this.width = toFloat(width || this.width);
2073             this.height = toFloat(height || this.height);
2074             cs.width = this.width + "px";
2075             cs.height = this.height + "px";
2076             cs.clip = "rect(0 " + this.width + "px " + this.height + "px 0)";
2077             this.coordsize = this.width + " " + this.height;
2078             return this;
2079         };
2080         doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
2081         try {
2082             !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
2083             var createNode = function (tagName) {
2084                 return doc.createElement('<rvml:' + tagName + ' class="rvml">');
2085             };
2086         } catch (e) {
2087             var createNode = function (tagName) {
2088                 return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
2089             };
2090         }
2091         var create = function () {
2092             var con = getContainer[apply](null, arguments),
2093                 container = con.container,
2094                 height = con.height,
2095                 s,
2096                 width = con.width,
2097                 x = con.x,
2098                 y = con.y;
2099             if (!container) {
2100                 throw new Error("VML container not found.");
2101             }
2102             var res = {},
2103                 c = res.canvas = doc.createElement("div"),
2104                 cs = c.style;
2105             width = toFloat(width) || 512;
2106             height = toFloat(height) || 342;
2107             res.width = width;
2108             res.height = height;
2109             res.coordsize = width + " " + height;
2110             res.coordorigin = "0 0";
2111             res.span = doc.createElement("span");
2112             res.span.style.cssText = "position:absolute;left:-9999px;top:-9999px;padding:0;margin:0;line-height:1;display:inline;";
2113             c[appendChild](res.span);
2114             cs.cssText = R.format("width:{0}px;height:{1}px;position:absolute;clip:rect(0 {0}px {1}px 0)", width, height);
2115             if (container == 1) {
2116                 doc.body[appendChild](c);
2117                 cs.left = x + "px";
2118                 cs.top = y + "px";
2119                 container = {
2120                     style: {
2121                         width: width,
2122                         height: height
2123                     }
2124                 };
2125             } else {
2126                 container.style.width = width;
2127                 container.style.height = height;
2128                 if (container.firstChild) {
2129                     container.insertBefore(c, container.firstChild);
2130                 } else {
2131                     container[appendChild](c);
2132                 }
2133             }
2134             for (var prop in paper) if (paper[has](prop)) {
2135                 res[prop] = paper[prop];
2136             }
2137             plugins.call(res, res, R.fn);
2138             res.clear = function () {
2139                 while (c.firstChild) {
2140                     c.removeChild(c.firstChild);
2141                 }
2142             };
2143             res.raphael = R;
2144             return res;
2145         };
2146         paper.remove = function () {
2147             this.canvas.parentNode.removeChild(this.canvas);
2148             for (var i in this) {
2149                 delete this[i];
2150             }
2151         };
2152     }
2153
2154     // rest
2155     // Safari or Chrome (WebKit) rendering bug workaround method
2156     if ({"Apple Computer, Inc.": 1, "Google Inc.": 1}[navigator.vendor]) {
2157         paper.safari = function () {
2158             var rect = this.rect(-99, -99, this.width + 99, this.height + 99);
2159             setTimeout(function () {rect.remove();});
2160         };
2161     } else {
2162         paper.safari = function () {};
2163     }
2164
2165     // Events
2166     var addEvent = (function () {
2167         if (doc.addEventListener) {
2168             return function (obj, type, fn, element) {
2169                 var f = function (e) {
2170                     return fn.call(element, e);
2171                 };
2172                 obj.addEventListener(type, f, false);
2173                 return function () {
2174                     obj.removeEventListener(type, f, false);
2175                     return true;
2176                 };
2177             };
2178         } else if (doc.attachEvent) {
2179             return function (obj, type, fn, element) {
2180                 var f = function (e) {
2181                     return fn.call(element, e || win.event);
2182                 };
2183                 obj.attachEvent("on" + type, f);
2184                 var detacher = function () {
2185                     obj.detachEvent("on" + type, f);
2186                     return true;
2187                 };
2188                 if (type == "mouseover") {
2189                     obj.attachEvent("onmouseenter", f);
2190                     return function () {
2191                         obj.detachEvent("onmouseenter", f);
2192                         return detacher();
2193                     };
2194                 } else if (type == "mouseout") {
2195                     obj.attachEvent("onmouseleave", f);
2196                     return function () {
2197                         obj.detachEvent("onmouseleave", f);
2198                         return detacher();
2199                     };
2200                 }
2201                 return detacher;
2202             };
2203         }
2204     })();
2205     for (var i = events[length]; i--;) {
2206         (function (eventName) {
2207             Element[proto][eventName] = function (fn) {
2208                 if (R.is(fn, "function")) {
2209                     this.events = this.events || {};
2210                     this.events[eventName] = this.events[eventName] || {};
2211                     this.events[eventName][fn] = this.events[eventName][fn] || [];
2212                     this.events[eventName][fn][push](addEvent(this.shape || this.node, eventName, fn, this));
2213                 }
2214                 return this;
2215             };
2216             Element[proto]["un" + eventName] = function (fn) {
2217                 this.events &&
2218                 this.events[eventName] &&
2219                 this.events[eventName][fn] &&
2220                 this.events[eventName][fn][length] &&
2221                 this.events[eventName][fn].shift()() &&
2222                 !this.events[eventName][fn][length] &&
2223                 delete this.events[eventName][fn];
2224             };
2225
2226         })(events[i]);
2227     }
2228     paper.circle = function (x, y, r) {
2229         return theCircle(this, x || 0, y || 0, r || 0);
2230     };
2231     paper.rect = function (x, y, w, h, r) {
2232         return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
2233     };
2234     paper.ellipse = function (x, y, rx, ry) {
2235         return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0);
2236     };
2237     paper.path = function (pathString) {
2238         pathString && !R.is(pathString, "string") && !R.is(pathString[0], "array") && (pathString += E);
2239         return thePath(R.format[apply](R, arguments), this);
2240     };
2241     paper.image = function (src, x, y, w, h) {
2242         return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
2243     };
2244     paper.text = function (x, y, text) {
2245         return theText(this, x || 0, y || 0, text || E);
2246     };
2247     paper.set = function (itemsArray) {
2248         arguments[length] > 1 && (itemsArray = Array[proto].splice.call(arguments, 0, arguments[length]));
2249         return new Set(itemsArray);
2250     };
2251     paper.setSize = setSize;
2252     Element[proto].stop = function () {
2253         clearTimeout(this.animation_in_progress);
2254         return this;
2255     };
2256     Element[proto].scale = function (x, y, cx, cy) {
2257         if (x == null && y == null) {
2258             return {x: this._.sx, y: this._.sy, toString: function () { return this.x + " " + this.y; }};
2259         }
2260         y = y || x;
2261         !+y && (y = x);
2262         var dx,
2263             dy,
2264             dcx,
2265             dcy,
2266             a = this.attrs;
2267         if (x != 0) {
2268             var bb = this.getBBox(),
2269                 rcx = bb.x + bb.width / 2,
2270                 rcy = bb.y + bb.height / 2,
2271                 kx = x / this._.sx,
2272                 ky = y / this._.sy;
2273             cx = (+cx || cx == 0) ? cx : rcx;
2274             cy = (+cy || cy == 0) ? cy : rcy;
2275             var dirx = ~~(x / Math.abs(x)),
2276                 diry = ~~(y / Math.abs(y)),
2277                 s = this.node.style,
2278                 ncx = cx + (rcx - cx) * dirx * kx,
2279                 ncy = cy + (rcy - cy) * diry * ky;
2280             switch (this.type) {
2281                 case "rect":
2282                 case "image":
2283                     var neww = a.width * dirx * kx,
2284                         newh = a.height * diry * ky,
2285                         newr = a.r * mmin(kx, ky),
2286                         newx = ncx - neww / 2,
2287                         newy = ncy - newh / 2;
2288                     this.attr({
2289                         width: neww,
2290                         height: newh,
2291                         x: newx,
2292                         y: newy,
2293                         r: newr
2294                     });
2295                     break;
2296                 case "circle":
2297                 case "ellipse":
2298                     this.attr({
2299                         rx: a.rx * kx,
2300                         ry: a.ry * ky,
2301                         r: a.r * mmin(kx, ky),
2302                         cx: ncx,
2303                         cy: ncy
2304                     });
2305                     break;
2306                 case "path":
2307                     var path = pathToRelative(a.path),
2308                         skip = true;
2309                     for (var i = 0, ii = path[length]; i < ii; i++) {
2310                         var p = path[i];
2311                         if (p[0].toUpperCase() == "M" && skip) {
2312                             continue;
2313                         } else {
2314                             skip = false;
2315                         }
2316                         if (R.svg && p[0].toUpperCase() == "A") {
2317                             p[path[i][length] - 2] *= kx;
2318                             p[path[i][length] - 1] *= ky;
2319                             p[1] *= kx;
2320                             p[2] *= ky;
2321                             p[5] = +(dirx + diry ? !!+p[5] : !+p[5]);
2322                         } else {
2323                             for (var j = 1, jj = p[length]; j < jj; j++) {
2324                                 p[j] *= (j % 2) ? kx : ky;
2325                             }
2326                         }
2327                     }
2328                     var dim2 = pathDimensions(path),
2329                         dx = ncx - dim2.x - dim2.width / 2,
2330                         dy = ncy - dim2.y - dim2.height / 2;
2331                     path[0][1] += dx;
2332                     path[0][2] += dy;
2333
2334                     this.attr({path: path});
2335                 break;
2336             }
2337             if (this.type in {text: 1, image:1} && (dirx != 1 || diry != 1)) {
2338                 if (this.transformations) {
2339                     this.transformations[2] = "scale("[concat](dirx, ",", diry, ")");
2340                     this.node[setAttribute]("transform", this.transformations[join](" "));
2341                     dx = (dirx == -1) ? -a.x - (neww || 0) : a.x;
2342                     dy = (diry == -1) ? -a.y - (newh || 0) : a.y;
2343                     this.attr({x: dx, y: dy});
2344                     a.fx = dirx - 1;
2345                     a.fy = diry - 1;
2346                 } else {
2347                     this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11="[concat](dirx,
2348                         ", M12=0, M21=0, M22=", diry,
2349                         ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')");
2350                     s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E);
2351                 }
2352             } else {
2353                 if (this.transformations) {
2354                     this.transformations[2] = E;
2355                     this.node[setAttribute]("transform", this.transformations[join](" "));
2356                     a.fx = 0;
2357                     a.fy = 0;
2358                 } else {
2359                     this.node.filterMatrix = E;
2360                     s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E);
2361                 }
2362             }
2363             a.scale = [x, y, cx, cy][join](" ");
2364             this._.sx = x;
2365             this._.sy = y;
2366         }
2367         return this;
2368     };
2369
2370     // animation easing formulas
2371     R.easing_formulas = {
2372         linear: function (n) {
2373             return n;
2374         },
2375         "<": function (n) {
2376             return pow(n, 3);
2377         },
2378         ">": function (n) {
2379             return pow(n - 1, 3) + 1;
2380         },
2381         "<>": function (n) {
2382             n = n * 2;
2383             if (n < 1) {
2384                 return pow(n, 3) / 2;
2385             }
2386             n -= 2;
2387             return (pow(n, 3) + 2) / 2;
2388         },
2389         backIn: function (n) {
2390             var s = 1.70158;
2391             return n * n * ((s + 1) * n - s);
2392         },
2393         backOut: function (n) {
2394             n = n - 1;
2395             var s = 1.70158;
2396             return n * n * ((s + 1) * n + s) + 1;
2397         },
2398         elastic: function (n) {
2399             if (n == 0 || n == 1) {
2400                 return n;
2401             }
2402             var p = .3,
2403                 s = p / 4;
2404             return pow(2, -10 * n) * Math.sin((n - s) * (2 * Math.PI) / p) + 1;
2405         },
2406         bounce: function (n) {
2407             var s = 7.5625,
2408                 p = 2.75,
2409                 l;
2410             if (n < (1 / p)) {
2411                 l = s * n * n;
2412             } else {
2413                 if (n < (2 / p)) {
2414                     n -= (1.5 / p);
2415                     l = s * n * n + .75;
2416                 } else {
2417                     if (n < (2.5 / p)) {
2418                         n -= (2.25 / p);
2419                         l = s * n * n + .9375;
2420                     } else {
2421                         n -= (2.625 / p);
2422                         l = s * n * n + .984375;
2423                     }
2424                 }
2425             }
2426             return l;
2427         }
2428     };
2429
2430     Element[proto].animate = function (params, ms, easing, callback) {
2431         clearTimeout(this.animation_in_progress);
2432         if (R.is(easing, "function") || !easing) {
2433             callback = easing || null;
2434         }
2435         var from = {},
2436             to = {},
2437             diff = {},
2438             t = {x: 0, y: 0};
2439         for (var attr in params) if (params[has](attr)) {
2440             if (attr in availableAnimAttrs) {
2441                 from[attr] = this.attr(attr);
2442                 (from[attr] == null) && (from[attr] = availableAttrs[attr]);
2443                 to[attr] = params[attr];
2444                 switch (availableAnimAttrs[attr]) {
2445                     case "number":
2446                         diff[attr] = (to[attr] - from[attr]) / ms;
2447                         break;
2448                     case "colour":
2449                         from[attr] = R.getRGB(from[attr]);
2450                         var toColour = R.getRGB(to[attr]);
2451                         diff[attr] = {
2452                             r: (toColour.r - from[attr].r) / ms,
2453                             g: (toColour.g - from[attr].g) / ms,
2454                             b: (toColour.b - from[attr].b) / ms
2455                         };
2456                         break;
2457                     case "path":
2458                         var pathes = path2curve(from[attr], to[attr]);
2459                         from[attr] = pathes[0];
2460                         to[attr] = pathes[1];
2461                         diff[attr] = [];
2462                         for (var i = 0, ii = from[attr][length]; i < ii; i++) {
2463                             diff[attr][i] = [0];
2464                             for (var j = 1, jj = from[attr][i][length]; j < jj; j++) {
2465                                 diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
2466                             }
2467                         }
2468                         break;
2469                     case "csv":
2470                         var values = (params[attr] + E)[split](separator),
2471                             from2 = (from[attr] + E)[split](separator);
2472                         switch (attr) {
2473                             case "translation":
2474                                 from[attr] = [0, 0];
2475                                 diff[attr] = [values[0] / ms, values[1] / ms];
2476                             break;
2477                             case "rotation":
2478                                 from[attr] = (from2[1] == values[1] && from2[2] == values[2]) ? from2 : [0, values[1], values[2]];
2479                                 diff[attr] = [(values[0] - from[attr][0]) / ms, 0, 0];
2480                             break;
2481                             case "scale":
2482                                 params[attr] = values;
2483                                 from[attr] = (from[attr] + E)[split](separator);
2484                                 diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][1]) / ms, 0, 0];
2485                             break;
2486                             case "clip-rect":
2487                                 from[attr] = (from[attr] + E)[split](separator);
2488                                 diff[attr] = [];
2489                                 var i = 4;
2490                                 while (i--) {
2491                                     diff[attr][i] = (values[i] - from[attr][i]) / ms;
2492                                 }
2493                             break;
2494                         }
2495                         to[attr] = values;
2496                 }
2497             }
2498         }
2499         var start = +new Date,
2500             prev = 0,
2501             upto255 = function (color) {
2502                 return color > 255 ? 255 : color;
2503             },
2504             that = this;
2505         (function tick() {
2506             var time = new Date - start,
2507                 set = {},
2508                 now;
2509             if (time < ms) {
2510                 var pos = R.easing_formulas[easing] ? R.easing_formulas[easing](time / ms) : time / ms;
2511                 for (var attr in from) if (from[has](attr)) {
2512                     switch (availableAnimAttrs[attr]) {
2513                         case "number":
2514                             now = +from[attr] + pos * ms * diff[attr];
2515                             break;
2516                         case "colour":
2517                             now = "rgb(" + [
2518                                 upto255(round(from[attr].r + pos * ms * diff[attr].r)),
2519                                 upto255(round(from[attr].g + pos * ms * diff[attr].g)),
2520                                 upto255(round(from[attr].b + pos * ms * diff[attr].b))
2521                             ][join](",") + ")";
2522                             break;
2523                         case "path":
2524                             now = [];
2525                             for (var i = 0, ii = from[attr][length]; i < ii; i++) {
2526                                 now[i] = [from[attr][i][0]];
2527                                 for (var j = 1, jj = from[attr][i][length]; j < jj; j++) {
2528                                     now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
2529                                 }
2530                                 now[i] = now[i][join](" ");
2531                             }
2532                             now = now[join](" ");
2533                             break;
2534                         case "csv":
2535                             switch (attr) {
2536                                 case "translation":
2537                                     var x = diff[attr][0] * (time - prev),
2538                                         y = diff[attr][1] * (time - prev);
2539                                     t.x += x;
2540                                     t.y += y;
2541                                     now = x + " " + y;
2542                                 break;
2543                                 case "rotation":
2544                                     now = +from[attr][0] + pos * ms * diff[attr][0];
2545                                     from[attr][1] && (now += "," + from[attr][1] + "," + from[attr][2]);
2546                                 break;
2547                                 case "scale":
2548                                     now = [+from[attr][0] + pos * ms * diff[attr][0], +from[attr][1] + pos * ms * diff[attr][1], (2 in params[attr] ? params[attr][2] : E), (3 in params[attr] ? params[attr][3] : E)][join](" ");
2549                                 break;
2550                                 case "clip-rect":
2551                                     now = [];
2552                                     var i = 4;
2553                                     while (i--) {
2554                                         now[i] = +from[attr][i] + pos * ms * diff[attr][i];
2555                                     }
2556                                 break;
2557                             }
2558                             break;
2559                     }
2560                     set[attr] = now + E;
2561                 }
2562                 that.attr(set);
2563                 that.animation_in_progress = setTimeout(tick);
2564                 R.svg && paper.safari();
2565             } else {
2566                 (t.x || t.y) && that.translate(-t.x, -t.y);
2567                 params.scale && (params.scale = params.scale + E);
2568                 that.attr(params);
2569                 clearTimeout(that.animation_in_progress);
2570                 R.svg && paper.safari();
2571                 (R.is(callback, "function")) && callback.call(that);
2572             }
2573             prev = time;
2574         })();
2575         return this;
2576     };
2577     Element[proto].translate = function (x, y) {
2578         if (x == null) {
2579             return {x: this._.tx, y: this._.ty};
2580         }
2581         this._.tx += +x;
2582         this._.ty += +y;
2583         switch (this.type) {
2584             case "circle":
2585             case "ellipse":
2586                 this.attr({cx: +x + this.attrs.cx, cy: +y + this.attrs.cy});
2587                 break;
2588             case "rect":
2589             case "image":
2590             case "text":
2591                 this.attr({x: +x + this.attrs.x, y: +y + this.attrs.y});
2592                 break;
2593             case "path":
2594                 var path = pathToRelative(this.attrs.path);
2595                 path[0][1] += +x;
2596                 path[0][2] += +y;
2597                 this.attr({path: path});
2598             break;
2599         }
2600         return this;
2601     };
2602     Element[proto][toString] = function () {
2603         return "Rapha\u00ebl\u2019s object";
2604     };
2605     
2606
2607     // Set
2608     var Set = function (items) {
2609         this.items = [];
2610         this[length] = 0;
2611         if (items) {
2612             for (var i = 0, ii = items[length]; i < ii; i++) {
2613                 if (items[i] && (items[i].constructor == Element || items[i].constructor == Set)) {
2614                     this[this.items[length]] = this.items[this.items[length]] = items[i];
2615                     this[length]++;
2616                 }
2617             }
2618         }
2619     };
2620     Set[proto][push] = function () {
2621         var item,
2622             len;
2623         for (var i = 0, ii = arguments[length]; i < ii; i++) {
2624             item = arguments[i];
2625             if (item && (item.constructor == Element || item.constructor == Set)) {
2626                 len = this.items[length];
2627                 this[len] = this.items[len] = item;
2628                 this[length]++;
2629             }
2630         }
2631         return this;
2632     };
2633     Set[proto].pop = function () {
2634         delete this[this[length]--];
2635         return this.items.pop();
2636     };
2637     for (var method in Element[proto]) if (Element[proto][has](method)) {
2638         Set[proto][method] = (function (methodname) {
2639             return function () {
2640                 for (var i = 0, ii = this.items[length]; i < ii; i++) {
2641                     this.items[i][methodname][apply](this.items[i], arguments);
2642                 }
2643                 return this;
2644             };
2645         })(method);
2646     }
2647     Set[proto].attr = function (name, value) {
2648         if (name && R.is(name, "array") && R.is(name[0], "object")) {
2649             for (var j = 0, jj = name[length]; j < jj; j++) {
2650                 this.items[j].attr(name[j]);
2651             }
2652         } else {
2653             for (var i = 0, ii = this.items[length]; i < ii; i++) {
2654                 this.items[i].attr[apply](this.items[i], arguments);
2655             }
2656         }
2657         return this;
2658     };
2659     Set[proto].animate = function (params, ms, easing, callback) {
2660         if (R.is(easing, "function") || !easing) {
2661             callback = easing || null;
2662         }
2663         var len = this.items[length],
2664             i = len,
2665             set = this;
2666         if (callback) {
2667             var collector = function () {
2668                 !--len && callback.call(set);
2669             };
2670             while (i--) {
2671                 this.items[i].animate(params, ms, easing || collector, collector);
2672             }
2673         } else {
2674             while (i--) {
2675                 this.items[i].animate(params, ms, easing);
2676             }
2677         }
2678         return this;
2679     };
2680     
2681     Set[proto].getBBox = function () {
2682         var x = [],
2683             y = [],
2684             w = [],
2685             h = [];
2686         for (var i = this.items[length]; i--;) {
2687             var box = this.items[i].getBBox();
2688             x[push](box.x);
2689             y[push](box.y);
2690             w[push](box.x + box.width);
2691             h[push](box.y + box.height);
2692         }
2693         x = mmin[apply](0, x);
2694         y = mmin[apply](0, y);
2695         return {
2696             x: x,
2697             y: y,
2698             width: mmax[apply](0, w) - x,
2699             height: mmax[apply](0, h) - y
2700         };
2701     };
2702
2703     R.registerFont = function (font) {
2704         if (!font.face) {
2705             return font;
2706         }
2707         this.fonts = this.fonts || {};
2708         var fontcopy = {
2709                 w: font.w,
2710                 face: {},
2711                 glyphs: {}
2712             },
2713             family = font.face["font-family"];
2714         for (var prop in font.face) if (font.face[has](prop)) {
2715             fontcopy.face[prop] = font.face[prop];
2716         }
2717         if (this.fonts[family]) {
2718             this.fonts[family][push](fontcopy);
2719         } else {
2720             this.fonts[family] = [fontcopy];
2721         }
2722         if (!font.svg) {
2723             fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
2724             for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
2725                 var path = font.glyphs[glyph];
2726                 fontcopy.glyphs[glyph] = {
2727                     w: path.w,
2728                     k: {},
2729                     d: path.d && "M" + path.d[rp](/[mlcxtrv]/g, function (command) {
2730                             return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
2731                         }) + "z"
2732                 };
2733                 if (path.k) {
2734                     for (var k in path.k) if (path[has](k)) {
2735                         fontcopy.glyphs[glyph].k[k] = path.k[k];
2736                     }
2737                 }
2738             }
2739         }
2740         return font;
2741     };
2742     paper.getFont = function (family, weight, style, stretch) {
2743         stretch = stretch || "normal";
2744         style = style || "normal";
2745         weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
2746         var font = R.fonts[family];
2747         if (!font) {
2748             var name = new RegExp("(^|\\s)" + family[rp](/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
2749             for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
2750                 if (name.test(fontName)) {
2751                     font = R.fonts[fontName];
2752                     break;
2753                 }
2754             }
2755         }
2756         var thefont;
2757         if (font) {
2758             for (var i = 0, ii = font[length]; i < ii; i++) {
2759                 thefont = font[i];
2760                 if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
2761                     break;
2762                 }
2763             }
2764         }
2765         return thefont;
2766     };
2767     paper.print = function (x, y, string, font, size) {
2768         var out = this.set(),
2769             letters = (string + E)[split](E),
2770             shift = 0,
2771             path = E,
2772             scale;
2773         R.is(font, "string") && (font = this.getFont(font));
2774         if (font) {
2775             scale = (size || 16) / font.face["units-per-em"];
2776             for (var i = 0, ii = letters[length]; i < ii; i++) {
2777                 var prev = i && font.glyphs[letters[i - 1]] || {},
2778                     curr = font.glyphs[letters[i]];
2779                 shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) : 0;
2780                 curr && curr.d && out[push](this.path(curr.d).attr({fill: "#000", stroke: "none", translation: [shift, 0]}));
2781             }
2782             out.scale(scale, scale, 0, y).translate(x, (size || 16) / 2);
2783         }
2784         return out;
2785     };
2786
2787     R.format = function (token) {
2788         var args = R.is(arguments[1], "array") ? [0][concat](arguments[1]) : arguments;
2789         token && R.is(token, "string") && args[length] - 1 && (token = token[rp](/\{(\d+)\}/g, function (str, i) {
2790             return args[++i] == null ? E : args[i];
2791         }));
2792         return token || E;
2793     };
2794     R.ninja = function () {
2795         var r = win.Raphael, u;
2796         if (oldRaphael.was) {
2797             win.Raphael = oldRaphael.is;
2798         } else {
2799             try {
2800                 delete win.Raphael;
2801             } catch (e) {
2802                 win.Raphael = u;
2803             }
2804         }
2805         return r;
2806     };
2807     R.el = Element[proto];
2808     return R;
2809 })();