Hot fix.
[raphael] / raphael.js
1 /*!
2  * Raphael 1.4.7 - JavaScript Vector Library
3  *
4  * Copyright (c) 2010 Dmitry Baranovskiy (http://raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7  
8 Raphael = (function () {
9     function R() {
10         if (R.is(arguments[0], array)) {
11             var a = arguments[0],
12                 cnv = create[apply](R, a.splice(0, 3 + R.is(a[0], nu))),
13                 res = cnv.set();
14             for (var i = 0, ii = a[length]; i < ii; i++) {
15                 var j = a[i] || {};
16                 elements.test(j.type) && res[push](cnv[j.type]().attr(j));
17             }
18             return res;
19         }
20         return create[apply](R, arguments);
21     }
22     R.version = "1.4.7";
23     var separator = /[, ]+/,
24         elements = /^(circle|rect|path|ellipse|text|image)$/,
25         proto = "prototype",
26         has = "hasOwnProperty",
27         doc = document,
28         win = window,
29         oldRaphael = {
30             was: Object[proto][has].call(win, "Raphael"),
31             is: win.Raphael
32         },
33         Paper = function () {},
34         appendChild = "appendChild",
35         apply = "apply",
36         concat = "concat",
37         supportsTouch = "createTouch" in doc,
38         E = "",
39         S = " ",
40         Str = String,
41         split = "split",
42         events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend orientationchange touchcancel gesturestart gesturechange gestureend"[split](S),
43         touchMap = {
44             mousedown: "touchstart",
45             mousemove: "touchmove",
46             mouseup: "touchend"
47         },
48         join = "join",
49         length = "length",
50         lowerCase = String[proto].toLowerCase,
51         math = Math,
52         mmax = math.max,
53         mmin = math.min,
54         nu = "number",
55         string = "string",
56         array = "array",
57         toString = "toString",
58         fillString = "fill",
59         objectToString = Object[proto][toString],
60         paper = {},
61         pow = math.pow,
62         push = "push",
63         rg = /^(?=[\da-f]$)/,
64         ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
65         colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+(?:\s*,\s*[\d\.]+)?)\s*\)|rgba?\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%(?:\s*,\s*[\d\.]+%)?)\s*\)|hsb\(\s*([\d\.]+(?:deg|\xb0)?\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hsb\(\s*([\d\.]+(?:deg|\xb0|%)\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hsl\(\s*([\d\.]+(?:deg|\xb0)?\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hsl\(\s*([\d\.]+(?:deg|\xb0|%)\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i,
66         round = math.round,
67         setAttribute = "setAttribute",
68         toFloat = parseFloat,
69         toInt = parseInt,
70         ms = " progid:DXImageTransform.Microsoft",
71         upperCase = String[proto].toUpperCase,
72         availableAttrs = {blur: 0, "clip-rect": "0 0 1e9 1e9", cursor: "default", 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},
73         availableAnimAttrs = {along: "along", blur: nu, "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},
74         rp = "replace";
75     R.type = (win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
76     if (R.type == "VML") {
77         var d = doc.createElement("div"),
78             b;
79         d.innerHTML = '<v:shape adj="1"/>';
80         b = d.firstChild;
81         b.style.behavior = "url(#default#VML)";
82         if (!(b && typeof b.adj == "object")) {
83             return R.type = null;
84         }
85         d = null;
86     }
87     R.svg = !(R.vml = R.type == "VML");
88     Paper[proto] = R[proto];
89     R._id = 0;
90     R._oid = 0;
91     R.fn = {};
92     R.is = function (o, type) {
93         type = lowerCase.call(type);
94         return  (type == "object" && o === Object(o)) ||
95                 (type == "undefined" && typeof o == type) ||
96                 (type == "null" && o == null) ||
97                 (type == "array" && Array.isArray && Array.isArray(o)) ||
98                 lowerCase.call(objectToString.call(o).slice(8, -1)) == type;
99     };
100
101     R.setWindow = function (newwin) {
102         win = newwin;
103         doc = win.document;
104     };
105     // colour utilities
106     var toHex = function (color) {
107         if (R.vml) {
108             // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
109             var trim = /^\s+|\s+$/g;
110             toHex = cacher(function (color) {
111                 var bod;
112                 color = Str(color)[rp](trim, E);
113                 try {
114                     var docum = new win.ActiveXObject("htmlfile");
115                     docum.write("<body>");
116                     docum.close();
117                     bod = docum.body;
118                 } catch(e) {
119                     bod = win.createPopup().document.body;
120                 }
121                 var range = bod.createTextRange();
122                 try {
123                     bod.style.color = color;
124                     var value = range.queryCommandValue("ForeColor");
125                     value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
126                     return "#" + ("000000" + value[toString](16)).slice(-6);
127                 } catch(e) {
128                     return "none";
129                 }
130             });
131         } else {
132             var i = doc.createElement("i");
133             i.title = "Rapha\xebl Colour Picker";
134             i.style.display = "none";
135             doc.body[appendChild](i);
136             toHex = cacher(function (color) {
137                 i.style.color = color;
138                 return doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
139             });
140         }
141         return toHex(color);
142     },
143     hsbtoString = function () {
144         return "hsb(" + [this.h, this.s, this.b] + ")";
145     },
146     hsltoString = function () {
147         return "hsl(" + [this.h, this.s, this.l] + ")";
148     },
149     rgbtoString = function () {
150         return this.hex;
151     };
152     R.hsb2rgb = function (h, s, b) {
153         if (R.is(h, "object") && "h" in h && "s" in h && "b" in h) {
154             b = h.b;
155             s = h.s;
156             h = h.h;
157         }
158         return R.hsl2rgb(h, s, b / 2);
159     };
160     R.hsl2rgb = function (h, s, l) {
161         if (R.is(h, "object") && "h" in h && "s" in h && "l" in h) {
162             l = h.l;
163             s = h.s;
164             h = h.h;
165         }
166         if (h > 1 || s > 1 || l > 1) {
167             h /= 255;
168             s /= 255;
169             l /= 255;
170         }
171         var rgb = {},
172             channels = ["r", "g", "b"],
173             t2, t1, t3, r, g, b;
174         if (!s) {
175             rgb = {
176                 r: l,
177                 g: l,
178                 b: l
179             };
180         } else {
181             if (l < .5) {
182                 t2 = l * (1 + s);
183             } else {
184                 t2 = l + s - l * s;
185             }
186             t1 = 2 * l - t2;
187             for (var i = 0, ii = channels.length; i < ii; i++) {
188                 t3 = h + 1 / 3 * -(i - 1);
189                 t3 < 0 && t3++;
190                 t3 > 1 && t3--;
191                 if (t3 * 6 < 1) {
192                     rgb[channels[i]] = t1 + (t2 - t1) * 6 * t3;
193                 } else if (t3 * 2 < 1) {
194                     rgb[channels[i]] = t2;
195                 } else if (t3 * 3 < 2) {
196                     rgb[channels[i]] = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
197                 } else {
198                     rgb[channels[i]] = t1;
199                 }
200             }
201         }
202         rgb.r *= 255;
203         rgb.g *= 255;
204         rgb.b *= 255;
205         r = (~~rgb.r)[toString](16);
206         g = (~~rgb.g)[toString](16);
207         b = (~~rgb.b)[toString](16);
208         r = r[rp](rg, "0");
209         g = g[rp](rg, "0");
210         b = b[rp](rg, "0");
211         rgb.hex = "#" + r + g + b;
212         rgb.toString = rgbtoString;
213         return rgb;
214     };
215     R.rgb2hsb = function (red, green, blue) {
216         if (green == null && R.is(red, "object") && "r" in red && "g" in red && "b" in red) {
217             blue = red.b;
218             green = red.g;
219             red = red.r;
220         }
221         if (green == null && R.is(red, string)) {
222             var clr = R.getRGB(red);
223             red = clr.r;
224             green = clr.g;
225             blue = clr.b;
226         }
227         if (red > 1 || green > 1 || blue > 1) {
228             red /= 255;
229             green /= 255;
230             blue /= 255;
231         }
232         var max = mmax(red, green, blue),
233             min = mmin(red, green, blue),
234             hue,
235             saturation,
236             brightness = max;
237         if (min == max) {
238             return {h: 0, s: 0, b: max, toString: hsbtoString};
239         } else {
240             var delta = (max - min);
241             saturation = delta / max;
242             if (red == max) {
243                 hue = (green - blue) / delta;
244             } else if (green == max) {
245                 hue = 2 + ((blue - red) / delta);
246             } else {
247                 hue = 4 + ((red - green) / delta);
248             }
249             hue /= 6;
250             hue < 0 && hue++;
251             hue > 1 && hue--;
252         }
253         return {h: hue, s: saturation, b: brightness, toString: hsbtoString};
254     };
255     R.rgb2hsl = function (red, green, blue) {
256         if (green == null && R.is(red, "object") && "r" in red && "g" in red && "b" in red) {
257             blue = red.b;
258             green = red.g;
259             red = red.r;
260         }
261         if (green == null && R.is(red, string)) {
262             var clr = R.getRGB(red);
263             red = clr.r;
264             green = clr.g;
265             blue = clr.b;
266         }
267         if (red > 1 || green > 1 || blue > 1) {
268             red /= 255;
269             green /= 255;
270             blue /= 255;
271         }
272         var max = mmax(red, green, blue),
273             min = mmin(red, green, blue),
274             h,
275             s,
276             l = (max + min) / 2,
277             hsl;
278         if (min == max) {
279             hsl =  {h: 0, s: 0, l: l};
280         } else {
281             var delta = max - min;
282             s = l < .5 ? delta / (max + min) : delta / (2 - max - min);
283             if (red == max) {
284                 h = (green - blue) / delta;
285             } else if (green == max) {
286                 h = 2 + (blue - red) / delta;
287             } else {
288                 h = 4 + (red - green) / delta;
289             }
290             h /= 6;
291             h < 0 && h++;
292             h > 1 && h--;
293             hsl = {h: h, s: s, l: l};
294         }
295         hsl.toString = hsltoString;
296         return hsl;
297     };
298     var p2s = /,?([achlmqrstvxz]),?/gi,
299         commaSpaces = /\s*,\s*/,
300         hsrg = {hs: 1, rg: 1};
301     R._path2string = function () {
302         return this.join(",")[rp](p2s, "$1");
303     };
304     function cacher(f, scope, postprocessor) {
305         function newf() {
306             var arg = Array[proto].slice.call(arguments, 0),
307                 args = arg[join]("\u25ba"),
308                 cache = newf.cache = newf.cache || {},
309                 count = newf.count = newf.count || [];
310             if (cache[has](args)) {
311                 return postprocessor ? postprocessor(cache[args]) : cache[args];
312             }
313             count[length] >= 1e3 && delete cache[count.shift()];
314             count[push](args);
315             cache[args] = f[apply](scope, arg);
316             return postprocessor ? postprocessor(cache[args]) : cache[args];
317         }
318         return newf;
319     }
320  
321     R.getRGB = cacher(function (colour) {
322         if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
323             return {r: -1, g: -1, b: -1, hex: "none", error: 1};
324         }
325         if (colour == "none") {
326             return {r: -1, g: -1, b: -1, hex: "none"};
327         }
328         !(hsrg[has](colour.substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
329         var res,
330             red,
331             green,
332             blue,
333             opacity,
334             t,
335             rgb = colour.match(colourRegExp);
336         if (rgb) {
337             if (rgb[2]) {
338                 blue = toInt(rgb[2].substring(5), 16);
339                 green = toInt(rgb[2].substring(3, 5), 16);
340                 red = toInt(rgb[2].substring(1, 3), 16);
341             }
342             if (rgb[3]) {
343                 blue = toInt((t = rgb[3].charAt(3)) + t, 16);
344                 green = toInt((t = rgb[3].charAt(2)) + t, 16);
345                 red = toInt((t = rgb[3].charAt(1)) + t, 16);
346             }
347             if (rgb[4]) {
348                 rgb = rgb[4][split](commaSpaces);
349                 red = toFloat(rgb[0]);
350                 green = toFloat(rgb[1]);
351                 blue = toFloat(rgb[2]);
352                 opacity = toFloat(rgb[3]);
353             }
354             if (rgb[5]) {
355                 rgb = rgb[5][split](commaSpaces);
356                 red = toFloat(rgb[0]) * 2.55;
357                 green = toFloat(rgb[1]) * 2.55;
358                 blue = toFloat(rgb[2]) * 2.55;
359                 opacity = toFloat(rgb[3]);
360             }
361             if (rgb[6]) {
362                 rgb = rgb[6][split](commaSpaces);
363                 red = toFloat(rgb[0]);
364                 green = toFloat(rgb[1]);
365                 blue = toFloat(rgb[2]);
366                 (rgb[0].slice(-3) == "deg" || rgb[0].slice(-1) == "\xb0") && (red /= 360);
367                 return R.hsb2rgb(red, green, blue);
368             }
369             if (rgb[7]) {
370                 rgb = rgb[7][split](commaSpaces);
371                 red = toFloat(rgb[0]) * 2.55;
372                 green = toFloat(rgb[1]) * 2.55;
373                 blue = toFloat(rgb[2]) * 2.55;
374                 (rgb[0].slice(-3) == "deg" || rgb[0].slice(-1) == "\xb0") && (red /= 360 * 2.55);
375                 return R.hsb2rgb(red, green, blue);
376             }
377             if (rgb[8]) {
378                 rgb = rgb[8][split](commaSpaces);
379                 red = toFloat(rgb[0]);
380                 green = toFloat(rgb[1]);
381                 blue = toFloat(rgb[2]);
382                 (rgb[0].slice(-3) == "deg" || rgb[0].slice(-1) == "\xb0") && (red /= 360);
383                 return R.hsl2rgb(red, green, blue);
384             }
385             if (rgb[9]) {
386                 rgb = rgb[9][split](commaSpaces);
387                 red = toFloat(rgb[0]) * 2.55;
388                 green = toFloat(rgb[1]) * 2.55;
389                 blue = toFloat(rgb[2]) * 2.55;
390                 (rgb[0].slice(-3) == "deg" || rgb[0].slice(-1) == "\xb0") && (red /= 360 * 2.55);
391                 return R.hsl2rgb(red, green, blue);
392             }
393             rgb = {r: red, g: green, b: blue};
394             var r = (~~red)[toString](16),
395                 g = (~~green)[toString](16),
396                 b = (~~blue)[toString](16);
397             r = r[rp](rg, "0");
398             g = g[rp](rg, "0");
399             b = b[rp](rg, "0");
400             rgb.hex = "#" + r + g + b;
401             isFinite(toFloat(opacity)) && (rgb.o = opacity);
402             return rgb;
403         }
404         return {r: -1, g: -1, b: -1, hex: "none", error: 1};
405     }, R);
406     R.getColor = function (value) {
407         var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
408             rgb = this.hsb2rgb(start.h, start.s, start.b);
409         start.h += .075;
410         if (start.h > 1) {
411             start.h = 0;
412             start.s -= .2;
413             start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
414         }
415         return rgb.hex;
416     };
417     R.getColor.reset = function () {
418         delete this.start;
419     };
420     // path utilities
421     var pathCommand = /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
422         pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig;
423     R.parsePathString = cacher(function (pathString) {
424         if (!pathString) {
425             return null;
426         }
427         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
428             data = [];
429         if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
430             data = pathClone(pathString);
431         }
432         if (!data[length]) {
433             Str(pathString)[rp](pathCommand, function (a, b, c) {
434                 var params = [],
435                     name = lowerCase.call(b);
436                 c[rp](pathValues, function (a, b) {
437                     b && params[push](+b);
438                 });
439                 if (name == "m" && params[length] > 2) {
440                     data[push]([b][concat](params.splice(0, 2)));
441                     name = "l";
442                     b = b == "m" ? "l" : "L";
443                 }
444                 while (params[length] >= paramCounts[name]) {
445                     data[push]([b][concat](params.splice(0, paramCounts[name])));
446                     if (!paramCounts[name]) {
447                         break;
448                     }
449                 }
450             });
451         }
452         data[toString] = R._path2string;
453         return data;
454     });
455     R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
456         var t1 = 1 - t,
457             x = pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
458             y = pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y,
459             mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x),
460             my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y),
461             nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x),
462             ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y),
463             ax = (1 - t) * p1x + t * c1x,
464             ay = (1 - t) * p1y + t * c1y,
465             cx = (1 - t) * c2x + t * p2x,
466             cy = (1 - t) * c2y + t * p2y,
467             alpha = (90 - math.atan((mx - nx) / (my - ny)) * 180 / math.PI);
468         (mx > nx || my < ny) && (alpha += 180);
469         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}, alpha: alpha};
470     };
471     var pathDimensions = cacher(function (path) {
472         if (!path) {
473             return {x: 0, y: 0, width: 0, height: 0};
474         }
475         path = path2curve(path);
476         var x = 0, 
477             y = 0,
478             X = [],
479             Y = [],
480             p;
481         for (var i = 0, ii = path[length]; i < ii; i++) {
482             p = path[i];
483             if (p[0] == "M") {
484                 x = p[1];
485                 y = p[2];
486                 X[push](x);
487                 Y[push](y);
488             } else {
489                 var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
490                 X = X[concat](dim.min.x, dim.max.x);
491                 Y = Y[concat](dim.min.y, dim.max.y);
492                 x = p[5];
493                 y = p[6];
494             }
495         }
496         var xmin = mmin[apply](0, X),
497             ymin = mmin[apply](0, Y);
498         return {
499             x: xmin,
500             y: ymin,
501             width: mmax[apply](0, X) - xmin,
502             height: mmax[apply](0, Y) - ymin
503         };
504     }),
505         pathClone = function (pathArray) {
506             var res = [];
507             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
508                 pathArray = R.parsePathString(pathArray);
509             }
510             for (var i = 0, ii = pathArray[length]; i < ii; i++) {
511                 res[i] = [];
512                 for (var j = 0, jj = pathArray[i][length]; j < jj; j++) {
513                     res[i][j] = pathArray[i][j];
514                 }
515             }
516             res[toString] = R._path2string;
517             return res;
518         },
519         pathToRelative = cacher(function (pathArray) {
520             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
521                 pathArray = R.parsePathString(pathArray);
522             }
523             var res = [],
524                 x = 0,
525                 y = 0,
526                 mx = 0,
527                 my = 0,
528                 start = 0;
529             if (pathArray[0][0] == "M") {
530                 x = pathArray[0][1];
531                 y = pathArray[0][2];
532                 mx = x;
533                 my = y;
534                 start++;
535                 res[push](["M", x, y]);
536             }
537             for (var i = start, ii = pathArray[length]; i < ii; i++) {
538                 var r = res[i] = [],
539                     pa = pathArray[i];
540                 if (pa[0] != lowerCase.call(pa[0])) {
541                     r[0] = lowerCase.call(pa[0]);
542                     switch (r[0]) {
543                         case "a":
544                             r[1] = pa[1];
545                             r[2] = pa[2];
546                             r[3] = pa[3];
547                             r[4] = pa[4];
548                             r[5] = pa[5];
549                             r[6] = +(pa[6] - x).toFixed(3);
550                             r[7] = +(pa[7] - y).toFixed(3);
551                             break;
552                         case "v":
553                             r[1] = +(pa[1] - y).toFixed(3);
554                             break;
555                         case "m":
556                             mx = pa[1];
557                             my = pa[2];
558                         default:
559                             for (var j = 1, jj = pa[length]; j < jj; j++) {
560                                 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
561                             }
562                     }
563                 } else {
564                     r = res[i] = [];
565                     if (pa[0] == "m") {
566                         mx = pa[1] + x;
567                         my = pa[2] + y;
568                     }
569                     for (var k = 0, kk = pa[length]; k < kk; k++) {
570                         res[i][k] = pa[k];
571                     }
572                 }
573                 var len = res[i][length];
574                 switch (res[i][0]) {
575                     case "z":
576                         x = mx;
577                         y = my;
578                         break;
579                     case "h":
580                         x += +res[i][len - 1];
581                         break;
582                     case "v":
583                         y += +res[i][len - 1];
584                         break;
585                     default:
586                         x += +res[i][len - 2];
587                         y += +res[i][len - 1];
588                 }
589             }
590             res[toString] = R._path2string;
591             return res;
592         }, 0, pathClone),
593         pathToAbsolute = cacher(function (pathArray) {
594             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
595                 pathArray = R.parsePathString(pathArray);
596             }
597             var res = [],
598                 x = 0,
599                 y = 0,
600                 mx = 0,
601                 my = 0,
602                 start = 0;
603             if (pathArray[0][0] == "M") {
604                 x = +pathArray[0][1];
605                 y = +pathArray[0][2];
606                 mx = x;
607                 my = y;
608                 start++;
609                 res[0] = ["M", x, y];
610             }
611             for (var i = start, ii = pathArray[length]; i < ii; i++) {
612                 var r = res[i] = [],
613                     pa = pathArray[i];
614                 if (pa[0] != upperCase.call(pa[0])) {
615                     r[0] = upperCase.call(pa[0]);
616                     switch (r[0]) {
617                         case "A":
618                             r[1] = pa[1];
619                             r[2] = pa[2];
620                             r[3] = pa[3];
621                             r[4] = pa[4];
622                             r[5] = pa[5];
623                             r[6] = +(pa[6] + x);
624                             r[7] = +(pa[7] + y);
625                             break;
626                         case "V":
627                             r[1] = +pa[1] + y;
628                             break;
629                         case "H":
630                             r[1] = +pa[1] + x;
631                             break;
632                         case "M":
633                             mx = +pa[1] + x;
634                             my = +pa[2] + y;
635                         default:
636                             for (var j = 1, jj = pa[length]; j < jj; j++) {
637                                 r[j] = +pa[j] + ((j % 2) ? x : y);
638                             }
639                     }
640                 } else {
641                     for (var k = 0, kk = pa[length]; k < kk; k++) {
642                         res[i][k] = pa[k];
643                     }
644                 }
645                 switch (r[0]) {
646                     case "Z":
647                         x = mx;
648                         y = my;
649                         break;
650                     case "H":
651                         x = r[1];
652                         break;
653                     case "V":
654                         y = r[1];
655                         break;
656                     case "M":
657                         mx = res[i][res[i][length] - 2];
658                         my = res[i][res[i][length] - 1];
659                     default:
660                         x = res[i][res[i][length] - 2];
661                         y = res[i][res[i][length] - 1];
662                 }
663             }
664             res[toString] = R._path2string;
665             return res;
666         }, null, pathClone),
667         l2c = function (x1, y1, x2, y2) {
668             return [x1, y1, x2, y2, x2, y2];
669         },
670         q2c = function (x1, y1, ax, ay, x2, y2) {
671             var _13 = 1 / 3,
672                 _23 = 2 / 3;
673             return [
674                     _13 * x1 + _23 * ax,
675                     _13 * y1 + _23 * ay,
676                     _13 * x2 + _23 * ax,
677                     _13 * y2 + _23 * ay,
678                     x2,
679                     y2
680                 ];
681         },
682         a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
683             // for more information of where this math came from visit:
684             // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
685             var PI = math.PI,
686                 _120 = PI * 120 / 180,
687                 rad = PI / 180 * (+angle || 0),
688                 res = [],
689                 xy,
690                 rotate = cacher(function (x, y, rad) {
691                     var X = x * math.cos(rad) - y * math.sin(rad),
692                         Y = x * math.sin(rad) + y * math.cos(rad);
693                     return {x: X, y: Y};
694                 });
695             if (!recursive) {
696                 xy = rotate(x1, y1, -rad);
697                 x1 = xy.x;
698                 y1 = xy.y;
699                 xy = rotate(x2, y2, -rad);
700                 x2 = xy.x;
701                 y2 = xy.y;
702                 var cos = math.cos(PI / 180 * angle),
703                     sin = math.sin(PI / 180 * angle),
704                     x = (x1 - x2) / 2,
705                     y = (y1 - y2) / 2;
706                 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
707                 if (h > 1) {
708                     h = math.sqrt(h);
709                     rx = h * rx;
710                     ry = h * ry;
711                 }
712                 var rx2 = rx * rx,
713                     ry2 = ry * ry,
714                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
715                         math.sqrt(math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
716                     cx = k * rx * y / ry + (x1 + x2) / 2,
717                     cy = k * -ry * x / rx + (y1 + y2) / 2,
718                     f1 = math.asin(((y1 - cy) / ry).toFixed(7)),
719                     f2 = math.asin(((y2 - cy) / ry).toFixed(7));
720
721                 f1 = x1 < cx ? PI - f1 : f1;
722                 f2 = x2 < cx ? PI - f2 : f2;
723                 f1 < 0 && (f1 = PI * 2 + f1);
724                 f2 < 0 && (f2 = PI * 2 + f2);
725                 if (sweep_flag && f1 > f2) {
726                     f1 = f1 - PI * 2;
727                 }
728                 if (!sweep_flag && f2 > f1) {
729                     f2 = f2 - PI * 2;
730                 }
731             } else {
732                 f1 = recursive[0];
733                 f2 = recursive[1];
734                 cx = recursive[2];
735                 cy = recursive[3];
736             }
737             var df = f2 - f1;
738             if (math.abs(df) > _120) {
739                 var f2old = f2,
740                     x2old = x2,
741                     y2old = y2;
742                 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
743                 x2 = cx + rx * math.cos(f2);
744                 y2 = cy + ry * math.sin(f2);
745                 res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
746             }
747             df = f2 - f1;
748             var c1 = math.cos(f1),
749                 s1 = math.sin(f1),
750                 c2 = math.cos(f2),
751                 s2 = math.sin(f2),
752                 t = math.tan(df / 4),
753                 hx = 4 / 3 * rx * t,
754                 hy = 4 / 3 * ry * t,
755                 m1 = [x1, y1],
756                 m2 = [x1 + hx * s1, y1 - hy * c1],
757                 m3 = [x2 + hx * s2, y2 - hy * c2],
758                 m4 = [x2, y2];
759             m2[0] = 2 * m1[0] - m2[0];
760             m2[1] = 2 * m1[1] - m2[1];
761             if (recursive) {
762                 return [m2, m3, m4][concat](res);
763             } else {
764                 res = [m2, m3, m4][concat](res)[join]()[split](",");
765                 var newres = [];
766                 for (var i = 0, ii = res[length]; i < ii; i++) {
767                     newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
768                 }
769                 return newres;
770             }
771         },
772         findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
773             var t1 = 1 - t;
774             return {
775                 x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
776                 y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
777             };
778         },
779         curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
780             var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
781                 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
782                 c = p1x - c1x,
783                 t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
784                 t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
785                 y = [p1y, p2y],
786                 x = [p1x, p2x],
787                 dot;
788             math.abs(t1) > 1e12 && (t1 = .5);
789             math.abs(t2) > 1e12 && (t2 = .5);
790             if (t1 > 0 && t1 < 1) {
791                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
792                 x[push](dot.x);
793                 y[push](dot.y);
794             }
795             if (t2 > 0 && t2 < 1) {
796                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
797                 x[push](dot.x);
798                 y[push](dot.y);
799             }
800             a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
801             b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
802             c = p1y - c1y;
803             t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
804             t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
805             math.abs(t1) > 1e12 && (t1 = .5);
806             math.abs(t2) > 1e12 && (t2 = .5);
807             if (t1 > 0 && t1 < 1) {
808                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
809                 x[push](dot.x);
810                 y[push](dot.y);
811             }
812             if (t2 > 0 && t2 < 1) {
813                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
814                 x[push](dot.x);
815                 y[push](dot.y);
816             }
817             return {
818                 min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
819                 max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
820             };
821         }),
822         path2curve = cacher(function (path, path2) {
823             var p = pathToAbsolute(path),
824                 p2 = path2 && pathToAbsolute(path2),
825                 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
826                 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
827                 processPath = function (path, d) {
828                     var nx, ny;
829                     if (!path) {
830                         return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
831                     }
832                     !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
833                     switch (path[0]) {
834                         case "M":
835                             d.X = path[1];
836                             d.Y = path[2];
837                             break;
838                         case "A":
839                             path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
840                             break;
841                         case "S":
842                             nx = d.x + (d.x - (d.bx || d.x));
843                             ny = d.y + (d.y - (d.by || d.y));
844                             path = ["C", nx, ny][concat](path.slice(1));
845                             break;
846                         case "T":
847                             d.qx = d.x + (d.x - (d.qx || d.x));
848                             d.qy = d.y + (d.y - (d.qy || d.y));
849                             path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
850                             break;
851                         case "Q":
852                             d.qx = path[1];
853                             d.qy = path[2];
854                             path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
855                             break;
856                         case "L":
857                             path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
858                             break;
859                         case "H":
860                             path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
861                             break;
862                         case "V":
863                             path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
864                             break;
865                         case "Z":
866                             path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
867                             break;
868                     }
869                     return path;
870                 },
871                 fixArc = function (pp, i) {
872                     if (pp[i][length] > 7) {
873                         pp[i].shift();
874                         var pi = pp[i];
875                         while (pi[length]) {
876                             pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
877                         }
878                         pp.splice(i, 1);
879                         ii = mmax(p[length], p2 && p2[length] || 0);
880                     }
881                 },
882                 fixM = function (path1, path2, a1, a2, i) {
883                     if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
884                         path2.splice(i, 0, ["M", a2.x, a2.y]);
885                         a1.bx = 0;
886                         a1.by = 0;
887                         a1.x = path1[i][1];
888                         a1.y = path1[i][2];
889                         ii = mmax(p[length], p2 && p2[length] || 0);
890                     }
891                 };
892             for (var i = 0, ii = mmax(p[length], p2 && p2[length] || 0); i < ii; i++) {
893                 p[i] = processPath(p[i], attrs);
894                 fixArc(p, i);
895                 p2 && (p2[i] = processPath(p2[i], attrs2));
896                 p2 && fixArc(p2, i);
897                 fixM(p, p2, attrs, attrs2, i);
898                 fixM(p2, p, attrs2, attrs, i);
899                 var seg = p[i],
900                     seg2 = p2 && p2[i],
901                     seglen = seg[length],
902                     seg2len = p2 && seg2[length];
903                 attrs.x = seg[seglen - 2];
904                 attrs.y = seg[seglen - 1];
905                 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
906                 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
907                 attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
908                 attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
909                 attrs2.x = p2 && seg2[seg2len - 2];
910                 attrs2.y = p2 && seg2[seg2len - 1];
911             }
912             return p2 ? [p, p2] : p;
913         }, null, pathClone),
914         parseDots = cacher(function (gradient) {
915             var dots = [];
916             for (var i = 0, ii = gradient[length]; i < ii; i++) {
917                 var dot = {},
918                     par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
919                 dot.color = R.getRGB(par[1]);
920                 if (dot.color.error) {
921                     return null;
922                 }
923                 dot.color = dot.color.hex;
924                 par[2] && (dot.offset = par[2] + "%");
925                 dots[push](dot);
926             }
927             for (i = 1, ii = dots[length] - 1; i < ii; i++) {
928                 if (!dots[i].offset) {
929                     var start = toFloat(dots[i - 1].offset || 0),
930                         end = 0;
931                     for (var j = i + 1; j < ii; j++) {
932                         if (dots[j].offset) {
933                             end = dots[j].offset;
934                             break;
935                         }
936                     }
937                     if (!end) {
938                         end = 100;
939                         j = ii;
940                     }
941                     end = toFloat(end);
942                     var d = (end - start) / (j - i + 1);
943                     for (; i < j; i++) {
944                         start += d;
945                         dots[i].offset = start + "%";
946                     }
947                 }
948             }
949             return dots;
950         }),
951         getContainer = function (x, y, w, h) {
952             var container;
953             if (R.is(x, string) || R.is(x, "object")) {
954                 container = R.is(x, string) ? doc.getElementById(x) : x;
955                 if (container.tagName) {
956                     if (y == null) {
957                         return {
958                             container: container,
959                             width: container.style.pixelWidth || container.offsetWidth,
960                             height: container.style.pixelHeight || container.offsetHeight
961                         };
962                     } else {
963                         return {container: container, width: y, height: w};
964                     }
965                 }
966             } else {
967                 return {container: 1, x: x, y: y, width: w, height: h};
968             }
969         },
970         plugins = function (con, add) {
971             var that = this;
972             for (var prop in add) {
973                 if (add[has](prop) && !(prop in con)) {
974                     switch (typeof add[prop]) {
975                         case "function":
976                             (function (f) {
977                                 con[prop] = con === that ? f : function () { return f[apply](that, arguments); };
978                             })(add[prop]);
979                         break;
980                         case "object":
981                             con[prop] = con[prop] || {};
982                             plugins.call(this, con[prop], add[prop]);
983                         break;
984                         default:
985                             con[prop] = add[prop];
986                         break;
987                     }
988                 }
989             }
990         },
991         tear = function (el, paper) {
992             el == paper.top && (paper.top = el.prev);
993             el == paper.bottom && (paper.bottom = el.next);
994             el.next && (el.next.prev = el.prev);
995             el.prev && (el.prev.next = el.next);
996         },
997         tofront = function (el, paper) {
998             if (paper.top === el) {
999                 return;
1000             }
1001             tear(el, paper);
1002             el.next = null;
1003             el.prev = paper.top;
1004             paper.top.next = el;
1005             paper.top = el;
1006         },
1007         toback = function (el, paper) {
1008             if (paper.bottom === el) {
1009                 return;
1010             }
1011             tear(el, paper);
1012             el.next = paper.bottom;
1013             el.prev = null;
1014             paper.bottom.prev = el;
1015             paper.bottom = el;
1016         },
1017         insertafter = function (el, el2, paper) {
1018             tear(el, paper);
1019             el2 == paper.top && (paper.top = el);
1020             el2.next && (el2.next.prev = el);
1021             el.next = el2.next;
1022             el.prev = el2;
1023             el2.next = el;
1024         },
1025         insertbefore = function (el, el2, paper) {
1026             tear(el, paper);
1027             el2 == paper.bottom && (paper.bottom = el);
1028             el2.prev && (el2.prev.next = el);
1029             el.prev = el2.prev;
1030             el2.prev = el;
1031             el.next = el2;
1032         },
1033         removed = function (methodname) {
1034             return function () {
1035                 throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
1036             };
1037         },
1038         radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/;
1039     R.pathToRelative = pathToRelative;
1040     // SVG
1041     if (R.svg) {
1042         Paper[proto].svgns = "http://www.w3.org/2000/svg";
1043         Paper[proto].xlink = "http://www.w3.org/1999/xlink";
1044         round = function (num) {
1045             return +num + (~~num === num) * .5;
1046         };
1047         var $ = function (el, attr) {
1048             if (attr) {
1049                 for (var key in attr) {
1050                     if (attr[has](key)) {
1051                         el[setAttribute](key, Str(attr[key]));
1052                     }
1053                 }
1054             } else {
1055                 el = doc.createElementNS(Paper[proto].svgns, el);
1056                 el.style.webkitTapHighlightColor = "rgba(0,0,0,0)";
1057                 return el;
1058             }
1059         };
1060         R[toString] = function () {
1061             return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
1062         };
1063         var thePath = function (pathString, SVG) {
1064             var el = $("path");
1065             SVG.canvas && SVG.canvas[appendChild](el);
1066             var p = new Element(el, SVG);
1067             p.type = "path";
1068             setFillAndStroke(p, {fill: "none", stroke: "#000", path: pathString});
1069             return p;
1070         };
1071         var addGradientFill = function (o, gradient, SVG) {
1072             var type = "linear",
1073                 fx = .5, fy = .5,
1074                 s = o.style;
1075             gradient = Str(gradient)[rp](radial_gradient, function (all, _fx, _fy) {
1076                 type = "radial";
1077                 if (_fx && _fy) {
1078                     fx = toFloat(_fx);
1079                     fy = toFloat(_fy);
1080                     var dir = ((fy > .5) * 2 - 1);
1081                     pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
1082                         (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
1083                         fy != .5 &&
1084                         (fy = fy.toFixed(5) - 1e-5 * dir);
1085                 }
1086                 return E;
1087             });
1088             gradient = gradient[split](/\s*\-\s*/);
1089             if (type == "linear") {
1090                 var angle = gradient.shift();
1091                 angle = -toFloat(angle);
1092                 if (isNaN(angle)) {
1093                     return null;
1094                 }
1095                 var vector = [0, 0, math.cos(angle * math.PI / 180), math.sin(angle * math.PI / 180)],
1096                     max = 1 / (mmax(math.abs(vector[2]), math.abs(vector[3])) || 1);
1097                 vector[2] *= max;
1098                 vector[3] *= max;
1099                 if (vector[2] < 0) {
1100                     vector[0] = -vector[2];
1101                     vector[2] = 0;
1102                 }
1103                 if (vector[3] < 0) {
1104                     vector[1] = -vector[3];
1105                     vector[3] = 0;
1106                 }
1107             }
1108             var dots = parseDots(gradient);
1109             if (!dots) {
1110                 return null;
1111             }
1112             var id = o.getAttribute(fillString);
1113             id = id.match(/^url\(#(.*)\)$/);
1114             id && SVG.defs.removeChild(doc.getElementById(id[1]));
1115             
1116             var el = $(type + "Gradient");
1117             el.id = "r" + (R._id++)[toString](36);
1118             $(el, type == "radial" ? {fx: fx, fy: fy} : {x1: vector[0], y1: vector[1], x2: vector[2], y2: vector[3]});
1119             SVG.defs[appendChild](el);
1120             for (var i = 0, ii = dots[length]; i < ii; i++) {
1121                 var stop = $("stop");
1122                 $(stop, {
1123                     offset: dots[i].offset ? dots[i].offset : !i ? "0%" : "100%",
1124                     "stop-color": dots[i].color || "#fff"
1125                 });
1126                 el[appendChild](stop);
1127             }
1128             $(o, {
1129                 fill: "url(#" + el.id + ")",
1130                 opacity: 1,
1131                 "fill-opacity": 1
1132             });
1133             s.fill = E;
1134             s.opacity = 1;
1135             s.fillOpacity = 1;
1136             return 1;
1137         };
1138         var updatePosition = function (o) {
1139             var bbox = o.getBBox();
1140             $(o.pattern, {patternTransform: R.format("translate({0},{1})", bbox.x, bbox.y)});
1141         };
1142         var setFillAndStroke = function (o, params) {
1143             var dasharray = {
1144                     "": [0],
1145                     "none": [0],
1146                     "-": [3, 1],
1147                     ".": [1, 1],
1148                     "-.": [3, 1, 1, 1],
1149                     "-..": [3, 1, 1, 1, 1, 1],
1150                     ". ": [1, 3],
1151                     "- ": [4, 3],
1152                     "--": [8, 3],
1153                     "- .": [4, 3, 1, 3],
1154                     "--.": [8, 3, 1, 3],
1155                     "--..": [8, 3, 1, 3, 1, 3]
1156                 },
1157                 node = o.node,
1158                 attrs = o.attrs,
1159                 rot = o.rotate(),
1160                 addDashes = function (o, value) {
1161                     value = dasharray[lowerCase.call(value)];
1162                     if (value) {
1163                         var width = o.attrs["stroke-width"] || "1",
1164                             butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
1165                             dashes = [];
1166                         var i = value[length];
1167                         while (i--) {
1168                             dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
1169                         }
1170                         $(node, {"stroke-dasharray": dashes[join](",")});
1171                     }
1172                 };
1173             params[has]("rotation") && (rot = params.rotation);
1174             var rotxy = Str(rot)[split](separator);
1175             if (!(rotxy.length - 1)) {
1176                 rotxy = null;
1177             } else {
1178                 rotxy[1] = +rotxy[1];
1179                 rotxy[2] = +rotxy[2];
1180             }
1181             toFloat(rot) && o.rotate(0, true);
1182             for (var att in params) {
1183                 if (params[has](att)) {
1184                     if (!availableAttrs[has](att)) {
1185                         continue;
1186                     }
1187                     var value = params[att];
1188                     attrs[att] = value;
1189                     switch (att) {
1190                         case "blur":
1191                             o.blur(value);
1192                             break;
1193                         case "rotation":
1194                             o.rotate(value, true);
1195                             break;
1196                         case "href":
1197                         case "title":
1198                         case "target":
1199                             var pn = node.parentNode;
1200                             if (lowerCase.call(pn.tagName) != "a") {
1201                                 var hl = $("a");
1202                                 pn.insertBefore(hl, node);
1203                                 hl[appendChild](node);
1204                                 pn = hl;
1205                             }
1206                             pn.setAttributeNS(o.paper.xlink, att, value);
1207                             break;
1208                         case "cursor":
1209                             node.style.cursor = value;
1210                             break;
1211                         case "clip-rect":
1212                             var rect = Str(value)[split](separator);
1213                             if (rect[length] == 4) {
1214                                 o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
1215                                 var el = $("clipPath"),
1216                                     rc = $("rect");
1217                                 el.id = "r" + (R._id++)[toString](36);
1218                                 $(rc, {
1219                                     x: rect[0],
1220                                     y: rect[1],
1221                                     width: rect[2],
1222                                     height: rect[3]
1223                                 });
1224                                 el[appendChild](rc);
1225                                 o.paper.defs[appendChild](el);
1226                                 $(node, {"clip-path": "url(#" + el.id + ")"});
1227                                 o.clip = rc;
1228                             }
1229                             if (!value) {
1230                                 var clip = doc.getElementById(node.getAttribute("clip-path")[rp](/(^url\(#|\)$)/g, E));
1231                                 clip && clip.parentNode.removeChild(clip);
1232                                 $(node, {"clip-path": E});
1233                                 delete o.clip;
1234                             }
1235                         break;
1236                         case "path":
1237                             if (o.type == "path") {
1238                                 $(node, {d: value ? attrs.path = pathToAbsolute(value) : "M0,0"});
1239                             }
1240                             break;
1241                         case "width":
1242                             node[setAttribute](att, value);
1243                             if (attrs.fx) {
1244                                 att = "x";
1245                                 value = attrs.x;
1246                             } else {
1247                                 break;
1248                             }
1249                         case "x":
1250                             if (attrs.fx) {
1251                                 value = -attrs.x - (attrs.width || 0);
1252                             }
1253                         case "rx":
1254                             if (att == "rx" && o.type == "rect") {
1255                                 break;
1256                             }
1257                         case "cx":
1258                             rotxy && (att == "x" || att == "cx") && (rotxy[1] += value - attrs[att]);
1259                             node[setAttribute](att, value);
1260                             o.pattern && updatePosition(o);
1261                             break;
1262                         case "height":
1263                             node[setAttribute](att, value);
1264                             if (attrs.fy) {
1265                                 att = "y";
1266                                 value = attrs.y;
1267                             } else {
1268                                 break;
1269                             }
1270                         case "y":
1271                             if (attrs.fy) {
1272                                 value = -attrs.y - (attrs.height || 0);
1273                             }
1274                         case "ry":
1275                             if (att == "ry" && o.type == "rect") {
1276                                 break;
1277                             }
1278                         case "cy":
1279                             rotxy && (att == "y" || att == "cy") && (rotxy[2] += value - attrs[att]);
1280                             node[setAttribute](att, value);
1281                             o.pattern && updatePosition(o);
1282                             break;
1283                         case "r":
1284                             if (o.type == "rect") {
1285                                 $(node, {rx: value, ry: value});
1286                             } else {
1287                                 node[setAttribute](att, value);
1288                             }
1289                             break;
1290                         case "src":
1291                             if (o.type == "image") {
1292                                 node.setAttributeNS(o.paper.xlink, "href", value);
1293                             }
1294                             break;
1295                         case "stroke-width":
1296                             node.style.strokeWidth = value;
1297                             // Need following line for Firefox
1298                             node[setAttribute](att, value);
1299                             if (attrs["stroke-dasharray"]) {
1300                                 addDashes(o, attrs["stroke-dasharray"]);
1301                             }
1302                             break;
1303                         case "stroke-dasharray":
1304                             addDashes(o, value);
1305                             break;
1306                         case "translation":
1307                             var xy = Str(value)[split](separator);
1308                             xy[0] = +xy[0] || 0;
1309                             xy[1] = +xy[1] || 0;
1310                             if (rotxy) {
1311                                 rotxy[1] += xy[0];
1312                                 rotxy[2] += xy[1];
1313                             }
1314                             translate.call(o, xy[0], xy[1]);
1315                             break;
1316                         case "scale":
1317                             xy = Str(value)[split](separator);
1318                             o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, isNaN(toFloat(xy[2])) ? null : +xy[2], isNaN(toFloat(xy[3])) ? null : +xy[3]);
1319                             break;
1320                         case fillString:
1321                             var isURL = Str(value).match(ISURL);
1322                             if (isURL) {
1323                                 el = $("pattern");
1324                                 var ig = $("image");
1325                                 el.id = "r" + (R._id++)[toString](36);
1326                                 $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
1327                                 $(ig, {x: 0, y: 0});
1328                                 ig.setAttributeNS(o.paper.xlink, "href", isURL[1]);
1329                                 el[appendChild](ig);
1330  
1331                                 var img = doc.createElement("img");
1332                                 img.style.cssText = "position:absolute;left:-9999em;top-9999em";
1333                                 img.onload = function () {
1334                                     $(el, {width: this.offsetWidth, height: this.offsetHeight});
1335                                     $(ig, {width: this.offsetWidth, height: this.offsetHeight});
1336                                     doc.body.removeChild(this);
1337                                     o.paper.safari();
1338                                 };
1339                                 doc.body[appendChild](img);
1340                                 img.src = isURL[1];
1341                                 o.paper.defs[appendChild](el);
1342                                 node.style.fill = "url(#" + el.id + ")";
1343                                 $(node, {fill: "url(#" + el.id + ")"});
1344                                 o.pattern = el;
1345                                 o.pattern && updatePosition(o);
1346                                 break;
1347                             }
1348                             var clr = R.getRGB(value);
1349                             if (!clr.error) {
1350                                 delete params.gradient;
1351                                 delete attrs.gradient;
1352                                 !R.is(attrs.opacity, "undefined") &&
1353                                     R.is(params.opacity, "undefined") &&
1354                                     $(node, {opacity: attrs.opacity});
1355                                 !R.is(attrs["fill-opacity"], "undefined") &&
1356                                     R.is(params["fill-opacity"], "undefined") &&
1357                                     $(node, {"fill-opacity": attrs["fill-opacity"]});
1358                             } else if ((({circle: 1, ellipse: 1})[has](o.type) || Str(value).charAt() != "r") && addGradientFill(node, value, o.paper)) {
1359                                 attrs.gradient = value;
1360                                 attrs.fill = "none";
1361                                 break;
1362                             }
1363                             clr[has]("o") && $(node, {"fill-opacity": clr.o / 100});
1364                         case "stroke":
1365                             clr = R.getRGB(value);
1366                             node[setAttribute](att, clr.hex);
1367                             att == "stroke" && clr[has]("o") && $(node, {"stroke-opacity": clr.o / 100});
1368                             break;
1369                         case "gradient":
1370                             (({circle: 1, ellipse: 1})[has](o.type) || Str(value).charAt() != "r") && addGradientFill(node, value, o.paper);
1371                             break;
1372                         case "opacity":
1373                         case "fill-opacity":
1374                             if (attrs.gradient) {
1375                                 var gradient = doc.getElementById(node.getAttribute(fillString)[rp](/^url\(#|\)$/g, E));
1376                                 if (gradient) {
1377                                     var stops = gradient.getElementsByTagName("stop");
1378                                     stops[stops[length] - 1][setAttribute]("stop-opacity", value);
1379                                 }
1380                                 break;
1381                             }
1382                         default:
1383                             att == "font-size" && (value = toInt(value, 10) + "px");
1384                             var cssrule = att[rp](/(\-.)/g, function (w) {
1385                                 return upperCase.call(w.substring(1));
1386                             });
1387                             node.style[cssrule] = value;
1388                             // Need following line for Firefox
1389                             node[setAttribute](att, value);
1390                             break;
1391                     }
1392                 }
1393             }
1394             
1395             tuneText(o, params);
1396             if (rotxy) {
1397                 o.rotate(rotxy.join(S));
1398             } else {
1399                 toFloat(rot) && o.rotate(rot, true);
1400             }
1401         };
1402         var leading = 1.2,
1403         tuneText = function (el, params) {
1404             if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
1405                 return;
1406             }
1407             var a = el.attrs,
1408                 node = el.node,
1409                 fontSize = node.firstChild ? toInt(doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
1410  
1411             if (params[has]("text")) {
1412                 a.text = params.text;
1413                 while (node.firstChild) {
1414                     node.removeChild(node.firstChild);
1415                 }
1416                 var texts = Str(params.text)[split]("\n");
1417                 for (var i = 0, ii = texts[length]; i < ii; i++) if (texts[i]) {
1418                     var tspan = $("tspan");
1419                     i && $(tspan, {dy: fontSize * leading, x: a.x});
1420                     tspan[appendChild](doc.createTextNode(texts[i]));
1421                     node[appendChild](tspan);
1422                 }
1423             } else {
1424                 texts = node.getElementsByTagName("tspan");
1425                 for (i = 0, ii = texts[length]; i < ii; i++) {
1426                     i && $(texts[i], {dy: fontSize * leading, x: a.x});
1427                 }
1428             }
1429             $(node, {y: a.y});
1430             var bb = el.getBBox(),
1431                 dif = a.y - (bb.y + bb.height / 2);
1432             dif && isFinite(dif) && $(node, {y: a.y + dif});
1433         },
1434         Element = function (node, svg) {
1435             var X = 0,
1436                 Y = 0;
1437             this[0] = node;
1438             this.id = R._oid++;
1439             this.node = node;
1440             node.raphael = this;
1441             this.paper = svg;
1442             this.attrs = this.attrs || {};
1443             this.transformations = []; // rotate, translate, scale
1444             this._ = {
1445                 tx: 0,
1446                 ty: 0,
1447                 rt: {deg: 0, cx: 0, cy: 0},
1448                 sx: 1,
1449                 sy: 1
1450             };
1451             !svg.bottom && (svg.bottom = this);
1452             this.prev = svg.top;
1453             svg.top && (svg.top.next = this);
1454             svg.top = this;
1455             this.next = null;
1456         };
1457         Element[proto].rotate = function (deg, cx, cy) {
1458             if (this.removed) {
1459                 return this;
1460             }
1461             if (deg == null) {
1462                 if (this._.rt.cx) {
1463                     return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S);
1464                 }
1465                 return this._.rt.deg;
1466             }
1467             var bbox = this.getBBox();
1468             deg = Str(deg)[split](separator);
1469             if (deg[length] - 1) {
1470                 cx = toFloat(deg[1]);
1471                 cy = toFloat(deg[2]);
1472             }
1473             deg = toFloat(deg[0]);
1474             if (cx != null) {
1475                 this._.rt.deg = deg;
1476             } else {
1477                 this._.rt.deg += deg;
1478             }
1479             (cy == null) && (cx = null);
1480             this._.rt.cx = cx;
1481             this._.rt.cy = cy;
1482             cx = cx == null ? bbox.x + bbox.width / 2 : cx;
1483             cy = cy == null ? bbox.y + bbox.height / 2 : cy;
1484             if (this._.rt.deg) {
1485                 this.transformations[0] = R.format("rotate({0} {1} {2})", this._.rt.deg, cx, cy);
1486                 this.clip && $(this.clip, {transform: R.format("rotate({0} {1} {2})", -this._.rt.deg, cx, cy)});
1487             } else {
1488                 this.transformations[0] = E;
1489                 this.clip && $(this.clip, {transform: E});
1490             }
1491             $(this.node, {transform: this.transformations[join](S)});
1492             return this;
1493         };
1494         Element[proto].hide = function () {
1495             !this.removed && (this.node.style.display = "none");
1496             return this;
1497         };
1498         Element[proto].show = function () {
1499             !this.removed && (this.node.style.display = "");
1500             return this;
1501         };
1502         Element[proto].remove = function () {
1503             if (this.removed) {
1504                 return;
1505             }
1506             tear(this, this.paper);
1507             this.node.parentNode.removeChild(this.node);
1508             for (var i in this) {
1509                 delete this[i];
1510             }
1511             this.removed = true;
1512         };
1513         Element[proto].getBBox = function () {
1514             if (this.removed) {
1515                 return this;
1516             }
1517             if (this.type == "path") {
1518                 return pathDimensions(this.attrs.path);
1519             }
1520             if (this.node.style.display == "none") {
1521                 this.show();
1522                 var hide = true;
1523             }
1524             var bbox = {};
1525             try {
1526                 bbox = this.node.getBBox();
1527             } catch(e) {
1528                 // Firefox 3.0.x plays badly here
1529             } finally {
1530                 bbox = bbox || {};
1531             }
1532             if (this.type == "text") {
1533                 bbox = {x: bbox.x, y: Infinity, width: 0, height: 0};
1534                 for (var i = 0, ii = this.node.getNumberOfChars(); i < ii; i++) {
1535                     var bb = this.node.getExtentOfChar(i);
1536                     (bb.y < bbox.y) && (bbox.y = bb.y);
1537                     (bb.y + bb.height - bbox.y > bbox.height) && (bbox.height = bb.y + bb.height - bbox.y);
1538                     (bb.x + bb.width - bbox.x > bbox.width) && (bbox.width = bb.x + bb.width - bbox.x);
1539                 }
1540             }
1541             hide && this.hide();
1542             return bbox;
1543         };
1544         Element[proto].attr = function (name, value) {
1545             if (this.removed) {
1546                 return this;
1547             }
1548             if (name == null) {
1549                 var res = {};
1550                 for (var i in this.attrs) if (this.attrs[has](i)) {
1551                     res[i] = this.attrs[i];
1552                 }
1553                 this._.rt.deg && (res.rotation = this.rotate());
1554                 (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale());
1555                 res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
1556                 return res;
1557             }
1558             if (value == null && R.is(name, string)) {
1559                 if (name == "translation") {
1560                     return translate.call(this);
1561                 }
1562                 if (name == "rotation") {
1563                     return this.rotate();
1564                 }
1565                 if (name == "scale") {
1566                     return this.scale();
1567                 }
1568                 if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
1569                     return this.attrs.gradient;
1570                 }
1571                 return this.attrs[name];
1572             }
1573             if (value == null && R.is(name, array)) {
1574                 var values = {};
1575                 for (var j = 0, jj = name.length; j < jj; j++) {
1576                     values[name[j]] = this.attr(name[j]);
1577                 }
1578                 return values;
1579             }
1580             if (value != null) {
1581                 var params = {};
1582                 params[name] = value;
1583                 setFillAndStroke(this, params);
1584             } else if (name != null && R.is(name, "object")) {
1585                 setFillAndStroke(this, name);
1586             }
1587             return this;
1588         };
1589         Element[proto].toFront = function () {
1590             if (this.removed) {
1591                 return this;
1592             }
1593             this.node.parentNode[appendChild](this.node);
1594             var svg = this.paper;
1595             svg.top != this && tofront(this, svg);
1596             return this;
1597         };
1598         Element[proto].toBack = function () {
1599             if (this.removed) {
1600                 return this;
1601             }
1602             if (this.node.parentNode.firstChild != this.node) {
1603                 this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
1604                 toback(this, this.paper);
1605                 var svg = this.paper;
1606             }
1607             return this;
1608         };
1609         Element[proto].insertAfter = function (element) {
1610             if (this.removed) {
1611                 return this;
1612             }
1613             var node = element.node || element[element.length].node;
1614             if (node.nextSibling) {
1615                 node.parentNode.insertBefore(this.node, node.nextSibling);
1616             } else {
1617                 node.parentNode[appendChild](this.node);
1618             }
1619             insertafter(this, element, this.paper);
1620             return this;
1621         };
1622         Element[proto].insertBefore = function (element) {
1623             if (this.removed) {
1624                 return this;
1625             }
1626             var node = element.node || element[0].node;
1627             node.parentNode.insertBefore(this.node, node);
1628             insertbefore(this, element, this.paper);
1629             return this;
1630         };
1631         Element[proto].blur = function (size) {
1632             // Experimental. No Safari support. Use it on your own risk.
1633             var t = this;
1634             if (+size !== 0) {
1635                 var fltr = $("filter"),
1636                     blur = $("feGaussianBlur");
1637                 t.attrs.blur = size;
1638                 fltr.id = "r" + (R._id++)[toString](36);
1639                 $(blur, {stdDeviation: +size || 1.5});
1640                 fltr.appendChild(blur);
1641                 t.paper.defs.appendChild(fltr);
1642                 t._blur = fltr;
1643                 $(t.node, {filter: "url(#" + fltr.id + ")"});
1644             } else {
1645                 if (t._blur) {
1646                     t._blur.parentNode.removeChild(t._blur);
1647                     delete t._blur;
1648                     delete t.attrs.blur;
1649                 }
1650                 t.node.removeAttribute("filter");
1651             }
1652         };
1653         var theCircle = function (svg, x, y, r) {
1654             var el = $("circle");
1655             svg.canvas && svg.canvas[appendChild](el);
1656             var res = new Element(el, svg);
1657             res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
1658             res.type = "circle";
1659             $(el, res.attrs);
1660             return res;
1661         };
1662         var theRect = function (svg, x, y, w, h, r) {
1663             var el = $("rect");
1664             svg.canvas && svg.canvas[appendChild](el);
1665             var res = new Element(el, svg);
1666             res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
1667             res.type = "rect";
1668             $(el, res.attrs);
1669             return res;
1670         };
1671         var theEllipse = function (svg, x, y, rx, ry) {
1672             var el = $("ellipse");
1673             svg.canvas && svg.canvas[appendChild](el);
1674             var res = new Element(el, svg);
1675             res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
1676             res.type = "ellipse";
1677             $(el, res.attrs);
1678             return res;
1679         };
1680         var theImage = function (svg, src, x, y, w, h) {
1681             var el = $("image");
1682             $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
1683             el.setAttributeNS(svg.xlink, "href", src);
1684             svg.canvas && svg.canvas[appendChild](el);
1685             var res = new Element(el, svg);
1686             res.attrs = {x: x, y: y, width: w, height: h, src: src};
1687             res.type = "image";
1688             return res;
1689         };
1690         var theText = function (svg, x, y, text) {
1691             var el = $("text");
1692             $(el, {x: x, y: y, "text-anchor": "middle"});
1693             svg.canvas && svg.canvas[appendChild](el);
1694             var res = new Element(el, svg);
1695             res.attrs = {x: x, y: y, "text-anchor": "middle", text: text, font: availableAttrs.font, stroke: "none", fill: "#000"};
1696             res.type = "text";
1697             setFillAndStroke(res, res.attrs);
1698             return res;
1699         };
1700         var setSize = function (width, height) {
1701             this.width = width || this.width;
1702             this.height = height || this.height;
1703             this.canvas[setAttribute]("width", this.width);
1704             this.canvas[setAttribute]("height", this.height);
1705             return this;
1706         };
1707         var create = function () {
1708             var con = getContainer[apply](0, arguments),
1709                 container = con && con.container,
1710                 x = con.x,
1711                 y = con.y,
1712                 width = con.width,
1713                 height = con.height;
1714             if (!container) {
1715                 throw new Error("SVG container not found.");
1716             }
1717             var cnvs = $("svg");
1718             x = x || 0;
1719             y = y || 0;
1720             width = width || 512;
1721             height = height || 342;
1722             $(cnvs, {
1723                 xmlns: "http://www.w3.org/2000/svg",
1724                 version: 1.1,
1725                 width: width,
1726                 height: height
1727             });
1728             if (container == 1) {
1729                 cnvs.style.cssText = "position:absolute;left:" + x + "px;top:" + y + "px";
1730                 doc.body[appendChild](cnvs);
1731             } else {
1732                 if (container.firstChild) {
1733                     container.insertBefore(cnvs, container.firstChild);
1734                 } else {
1735                     container[appendChild](cnvs);
1736                 }
1737             }
1738             container = new Paper;
1739             container.width = width;
1740             container.height = height;
1741             container.canvas = cnvs;
1742             plugins.call(container, container, R.fn);
1743             container.clear();
1744             return container;
1745         };
1746         Paper[proto].clear = function () {
1747             var c = this.canvas;
1748             while (c.firstChild) {
1749                 c.removeChild(c.firstChild);
1750             }
1751             this.bottom = this.top = null;
1752             (this.desc = $("desc"))[appendChild](doc.createTextNode("Created with Rapha\xebl"));
1753             c[appendChild](this.desc);
1754             c[appendChild](this.defs = $("defs"));
1755         };
1756         Paper[proto].remove = function () {
1757             this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
1758             for (var i in this) {
1759                 this[i] = removed(i);
1760             }
1761         };
1762     }
1763
1764     // VML
1765     if (R.vml) {
1766         var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
1767             bites = /([clmz]),?([^clmz]*)/gi,
1768             val = /-?[^,\s-]+/g,
1769             coordsize = 1e3 + S + 1e3,
1770             zoom = 10,
1771             pathlike = {path: 1, rect: 1},
1772             path2vml = function (path) {
1773                 var total =  /[ahqstv]/ig,
1774                     command = pathToAbsolute;
1775                 Str(path).match(total) && (command = path2curve);
1776                 total = /[clmz]/g;
1777                 if (command == pathToAbsolute && !Str(path).match(total)) {
1778                     var res = Str(path)[rp](bites, function (all, command, args) {
1779                         var vals = [],
1780                             isMove = lowerCase.call(command) == "m",
1781                             res = map[command];
1782                         args[rp](val, function (value) {
1783                             if (isMove && vals[length] == 2) {
1784                                 res += vals + map[command == "m" ? "l" : "L"];
1785                                 vals = [];
1786                             }
1787                             vals[push](round(value * zoom));
1788                         });
1789                         return res + vals;
1790                     });
1791                     return res;
1792                 }
1793                 var pa = command(path), p, r;
1794                 res = [];
1795                 for (var i = 0, ii = pa[length]; i < ii; i++) {
1796                     p = pa[i];
1797                     r = lowerCase.call(pa[i][0]);
1798                     r == "z" && (r = "x");
1799                     for (var j = 1, jj = p[length]; j < jj; j++) {
1800                         r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
1801                     }
1802                     res[push](r);
1803                 }
1804                 return res[join](S);
1805             };
1806         
1807         R[toString] = function () {
1808             return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
1809         };
1810         thePath = function (pathString, vml) {
1811             var g = createNode("group");
1812             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
1813             g.coordsize = vml.coordsize;
1814             g.coordorigin = vml.coordorigin;
1815             var el = createNode("shape"), ol = el.style;
1816             ol.width = vml.width + "px";
1817             ol.height = vml.height + "px";
1818             el.coordsize = coordsize;
1819             el.coordorigin = vml.coordorigin;
1820             g[appendChild](el);
1821             var p = new Element(el, g, vml),
1822                 attr = {fill: "none", stroke: "#000"};
1823             pathString && (attr.path = pathString);
1824             p.isAbsolute = true;
1825             p.type = "path";
1826             p.path = [];
1827             p.Path = E;
1828             setFillAndStroke(p, attr);
1829             vml.canvas[appendChild](g);
1830             return p;
1831         };
1832         setFillAndStroke = function (o, params) {
1833             o.attrs = o.attrs || {};
1834             var node = o.node,
1835                 a = o.attrs,
1836                 s = node.style,
1837                 xy,
1838                 newpath = (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.r != a.r) && o.type == "rect",
1839                 res = o;
1840
1841             for (var par in params) if (params[has](par)) {
1842                 a[par] = params[par];
1843             }
1844             if (newpath) {
1845                 a.path = rectPath(a.x, a.y, a.width, a.height, a.r);
1846                 o.X = a.x;
1847                 o.Y = a.y;
1848                 o.W = a.width;
1849                 o.H = a.height;
1850             }
1851             params.href && (node.href = params.href);
1852             params.title && (node.title = params.title);
1853             params.target && (node.target = params.target);
1854             params.cursor && (s.cursor = params.cursor);
1855             "blur" in params && o.blur(params.blur);
1856             if (params.path && o.type == "path" || newpath) {
1857                     node.path = path2vml(a.path);
1858             }
1859             if (params.rotation != null) {
1860                 o.rotate(params.rotation, true);
1861             }
1862             if (params.translation) {
1863                 xy = Str(params.translation)[split](separator);
1864                 translate.call(o, xy[0], xy[1]);
1865                 if (o._.rt.cx != null) {
1866                     o._.rt.cx +=+ xy[0];
1867                     o._.rt.cy +=+ xy[1];
1868                     o.setBox(o.attrs, xy[0], xy[1]);
1869                 }
1870             }
1871             if (params.scale) {
1872                 xy = Str(params.scale)[split](separator);
1873                 o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null);
1874             }
1875             if ("clip-rect" in params) {
1876                 var rect = Str(params["clip-rect"])[split](separator);
1877                 if (rect[length] == 4) {
1878                     rect[2] = +rect[2] + (+rect[0]);
1879                     rect[3] = +rect[3] + (+rect[1]);
1880                     var div = node.clipRect || doc.createElement("div"),
1881                         dstyle = div.style,
1882                         group = node.parentNode;
1883                     dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
1884                     if (!node.clipRect) {
1885                         dstyle.position = "absolute";
1886                         dstyle.top = 0;
1887                         dstyle.left = 0;
1888                         dstyle.width = o.paper.width + "px";
1889                         dstyle.height = o.paper.height + "px";
1890                         group.parentNode.insertBefore(div, group);
1891                         div[appendChild](group);
1892                         node.clipRect = div;
1893                     }
1894                 }
1895                 if (!params["clip-rect"]) {
1896                     node.clipRect && (node.clipRect.style.clip = E);
1897                 }
1898             }
1899             if (o.type == "image" && params.src) {
1900                 node.src = params.src;
1901             }
1902             if (o.type == "image" && params.opacity) {
1903                 node.filterOpacity = ms + ".Alpha(opacity=" + (params.opacity * 100) + ")";
1904                 s.filter = (node.filterMatrix || E) + (node.filterOpacity || E);
1905             }
1906             params.font && (s.font = params.font);
1907             params["font-family"] && (s.fontFamily = '"' + params["font-family"][split](",")[0][rp](/^['"]+|['"]+$/g, E) + '"');
1908             params["font-size"] && (s.fontSize = params["font-size"]);
1909             params["font-weight"] && (s.fontWeight = params["font-weight"]);
1910             params["font-style"] && (s.fontStyle = params["font-style"]);
1911             if (params.opacity != null || 
1912                 params["stroke-width"] != null ||
1913                 params.fill != null ||
1914                 params.stroke != null ||
1915                 params["stroke-width"] != null ||
1916                 params["stroke-opacity"] != null ||
1917                 params["fill-opacity"] != null ||
1918                 params["stroke-dasharray"] != null ||
1919                 params["stroke-miterlimit"] != null ||
1920                 params["stroke-linejoin"] != null ||
1921                 params["stroke-linecap"] != null) {
1922                 node = o.shape || node;
1923                 var fill = (node.getElementsByTagName(fillString) && node.getElementsByTagName(fillString)[0]),
1924                     newfill = false;
1925                 !fill && (newfill = fill = createNode(fillString));
1926                 if ("fill-opacity" in params || "opacity" in params) {
1927                     var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
1928                     opacity < 0 && (opacity = 0);
1929                     opacity > 1 && (opacity = 1);
1930                     fill.opacity = opacity;
1931                 }
1932                 params.fill && (fill.on = true);
1933                 if (fill.on == null || params.fill == "none") {
1934                     fill.on = false;
1935                 }
1936                 if (fill.on && params.fill) {
1937                     var isURL = params.fill.match(ISURL);
1938                     if (isURL) {
1939                         fill.src = isURL[1];
1940                         fill.type = "tile";
1941                     } else {
1942                         fill.color = R.getRGB(params.fill).hex;
1943                         fill.src = E;
1944                         fill.type = "solid";
1945                         if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill)) {
1946                             a.fill = "none";
1947                             a.gradient = params.fill;
1948                         }
1949                     }
1950                 }
1951                 newfill && node[appendChild](fill);
1952                 var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
1953                 newstroke = false;
1954                 !stroke && (newstroke = stroke = createNode("stroke"));
1955                 if ((params.stroke && params.stroke != "none") ||
1956                     params["stroke-width"] ||
1957                     params["stroke-opacity"] != null ||
1958                     params["stroke-dasharray"] ||
1959                     params["stroke-miterlimit"] ||
1960                     params["stroke-linejoin"] ||
1961                     params["stroke-linecap"]) {
1962                     stroke.on = true;
1963                 }
1964                 (params.stroke == "none" || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
1965                 var strokeColor = R.getRGB(params.stroke);
1966                 stroke.on && params.stroke && (stroke.color = strokeColor.hex);
1967                 opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
1968                 var width = (toFloat(params["stroke-width"]) || 1) * .75;
1969                 opacity < 0 && (opacity = 0);
1970                 opacity > 1 && (opacity = 1);
1971                 params["stroke-width"] == null && (width = a["stroke-width"]);
1972                 params["stroke-width"] && (stroke.weight = width);
1973                 width && width < 1 && (opacity *= width) && (stroke.weight = 1);
1974                 stroke.opacity = opacity;
1975                 
1976                 params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
1977                 stroke.miterlimit = params["stroke-miterlimit"] || 8;
1978                 params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
1979                 if (params["stroke-dasharray"]) {
1980                     var dasharray = {
1981                         "-": "shortdash",
1982                         ".": "shortdot",
1983                         "-.": "shortdashdot",
1984                         "-..": "shortdashdotdot",
1985                         ". ": "dot",
1986                         "- ": "dash",
1987                         "--": "longdash",
1988                         "- .": "dashdot",
1989                         "--.": "longdashdot",
1990                         "--..": "longdashdotdot"
1991                     };
1992                     stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
1993                 }
1994                 newstroke && node[appendChild](stroke);
1995             }
1996             if (res.type == "text") {
1997                 s = res.paper.span.style;
1998                 a.font && (s.font = a.font);
1999                 a["font-family"] && (s.fontFamily = a["font-family"]);
2000                 a["font-size"] && (s.fontSize = a["font-size"]);
2001                 a["font-weight"] && (s.fontWeight = a["font-weight"]);
2002                 a["font-style"] && (s.fontStyle = a["font-style"]);
2003                 res.node.string && (res.paper.span.innerHTML = Str(res.node.string)[rp](/</g, "&#60;")[rp](/&/g, "&#38;")[rp](/\n/g, "<br>"));
2004                 res.W = a.w = res.paper.span.offsetWidth;
2005                 res.H = a.h = res.paper.span.offsetHeight;
2006                 res.X = a.x;
2007                 res.Y = a.y + round(res.H / 2);
2008  
2009                 // text-anchor emulationm
2010                 switch (a["text-anchor"]) {
2011                     case "start":
2012                         res.node.style["v-text-align"] = "left";
2013                         res.bbx = round(res.W / 2);
2014                     break;
2015                     case "end":
2016                         res.node.style["v-text-align"] = "right";
2017                         res.bbx = -round(res.W / 2);
2018                     break;
2019                     default:
2020                         res.node.style["v-text-align"] = "center";
2021                     break;
2022                 }
2023             }
2024         };
2025         addGradientFill = function (o, gradient) {
2026             o.attrs = o.attrs || {};
2027             var attrs = o.attrs,
2028                 fill,
2029                 type = "linear",
2030                 fxfy = ".5 .5";
2031             o.attrs.gradient = gradient;
2032             gradient = Str(gradient)[rp](radial_gradient, function (all, fx, fy) {
2033                 type = "radial";
2034                 if (fx && fy) {
2035                     fx = toFloat(fx);
2036                     fy = toFloat(fy);
2037                     pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
2038                     fxfy = fx + S + fy;
2039                 }
2040                 return E;
2041             });
2042             gradient = gradient[split](/\s*\-\s*/);
2043             if (type == "linear") {
2044                 var angle = gradient.shift();
2045                 angle = -toFloat(angle);
2046                 if (isNaN(angle)) {
2047                     return null;
2048                 }
2049             }
2050             var dots = parseDots(gradient);
2051             if (!dots) {
2052                 return null;
2053             }
2054             o = o.shape || o.node;
2055             fill = o.getElementsByTagName(fillString)[0] || createNode(fillString);
2056             !fill.parentNode && o.appendChild(fill);
2057             if (dots[length]) {
2058                 fill.on = true;
2059                 fill.method = "none";
2060                 fill.color = dots[0].color;
2061                 fill.color2 = dots[dots[length] - 1].color;
2062                 var clrs = [];
2063                 for (var i = 0, ii = dots[length]; i < ii; i++) {
2064                     dots[i].offset && clrs[push](dots[i].offset + S + dots[i].color);
2065                 }
2066                 fill.colors && (fill.colors.value = clrs[length] ? clrs[join]() : "0% " + fill.color);
2067                 if (type == "radial") {
2068                     fill.type = "gradientradial";
2069                     fill.focus = "100%";
2070                     fill.focussize = fxfy;
2071                     fill.focusposition = fxfy;
2072                 } else {
2073                     fill.type = "gradient";
2074                     fill.angle = (270 - angle) % 360;
2075                 }
2076             }
2077             return 1;
2078         };
2079         Element = function (node, group, vml) {
2080             var Rotation = 0,
2081                 RotX = 0,
2082                 RotY = 0,
2083                 Scale = 1;
2084             this[0] = node;
2085             this.id = R._oid++;
2086             this.node = node;
2087             node.raphael = this;
2088             this.X = 0;
2089             this.Y = 0;
2090             this.attrs = {};
2091             this.Group = group;
2092             this.paper = vml;
2093             this._ = {
2094                 tx: 0,
2095                 ty: 0,
2096                 rt: {deg:0},
2097                 sx: 1,
2098                 sy: 1
2099             };
2100             !vml.bottom && (vml.bottom = this);
2101             this.prev = vml.top;
2102             vml.top && (vml.top.next = this);
2103             vml.top = this;
2104             this.next = null;
2105         };
2106         Element[proto].rotate = function (deg, cx, cy) {
2107             if (this.removed) {
2108                 return this;
2109             }
2110             if (deg == null) {
2111                 if (this._.rt.cx) {
2112                     return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S);
2113                 }
2114                 return this._.rt.deg;
2115             }
2116             deg = Str(deg)[split](separator);
2117             if (deg[length] - 1) {
2118                 cx = toFloat(deg[1]);
2119                 cy = toFloat(deg[2]);
2120             }
2121             deg = toFloat(deg[0]);
2122             if (cx != null) {
2123                 this._.rt.deg = deg;
2124             } else {
2125                 this._.rt.deg += deg;
2126             }
2127             cy == null && (cx = null);
2128             this._.rt.cx = cx;
2129             this._.rt.cy = cy;
2130             this.setBox(this.attrs, cx, cy);
2131             this.Group.style.rotation = this._.rt.deg;
2132             // gradient fix for rotation. TODO
2133             // var fill = (this.shape || this.node).getElementsByTagName(fillString);
2134             // fill = fill[0] || {};
2135             // var b = ((360 - this._.rt.deg) - 270) % 360;
2136             // !R.is(fill.angle, "undefined") && (fill.angle = b);
2137             return this;
2138         };
2139         Element[proto].setBox = function (params, cx, cy) {
2140             if (this.removed) {
2141                 return this;
2142             }
2143             var gs = this.Group.style,
2144                 os = (this.shape && this.shape.style) || this.node.style;
2145             params = params || {};
2146             for (var i in params) if (params[has](i)) {
2147                 this.attrs[i] = params[i];
2148             }
2149             cx = cx || this._.rt.cx;
2150             cy = cy || this._.rt.cy;
2151             var attr = this.attrs,
2152                 x,
2153                 y,
2154                 w,
2155                 h;
2156             switch (this.type) {
2157                 case "circle":
2158                     x = attr.cx - attr.r;
2159                     y = attr.cy - attr.r;
2160                     w = h = attr.r * 2;
2161                     break;
2162                 case "ellipse":
2163                     x = attr.cx - attr.rx;
2164                     y = attr.cy - attr.ry;
2165                     w = attr.rx * 2;
2166                     h = attr.ry * 2;
2167                     break;
2168                 case "image":
2169                     x = +attr.x;
2170                     y = +attr.y;
2171                     w = attr.width || 0;
2172                     h = attr.height || 0;
2173                     break;
2174                 case "text":
2175                     this.textpath.v = ["m", round(attr.x), ", ", round(attr.y - 2), "l", round(attr.x) + 1, ", ", round(attr.y - 2)][join](E);
2176                     x = attr.x - round(this.W / 2);
2177                     y = attr.y - this.H / 2;
2178                     w = this.W;
2179                     h = this.H;
2180                     break;
2181                 case "rect":
2182                 case "path":
2183                     if (!this.attrs.path) {
2184                         x = 0;
2185                         y = 0;
2186                         w = this.paper.width;
2187                         h = this.paper.height;
2188                     } else {
2189                         var dim = pathDimensions(this.attrs.path);
2190                         x = dim.x;
2191                         y = dim.y;
2192                         w = dim.width;
2193                         h = dim.height;
2194                     }
2195                     break;
2196                 default:
2197                     x = 0;
2198                     y = 0;
2199                     w = this.paper.width;
2200                     h = this.paper.height;
2201                     break;
2202             }
2203             cx = (cx == null) ? x + w / 2 : cx;
2204             cy = (cy == null) ? y + h / 2 : cy;
2205             var left = cx - this.paper.width / 2,
2206                 top = cy - this.paper.height / 2, t;
2207             gs.left != (t = left + "px") && (gs.left = t);
2208             gs.top != (t = top + "px") && (gs.top = t);
2209             this.X = pathlike[has](this.type) ? -left : x;
2210             this.Y = pathlike[has](this.type) ? -top : y;
2211             this.W = w;
2212             this.H = h;
2213             if (pathlike[has](this.type)) {
2214                 os.left != (t = -left * zoom + "px") && (os.left = t);
2215                 os.top != (t = -top * zoom + "px") && (os.top = t);
2216             } else if (this.type == "text") {
2217                 os.left != (t = -left + "px") && (os.left = t);
2218                 os.top != (t = -top + "px") && (os.top = t);
2219             } else {
2220                 gs.width != (t = this.paper.width + "px") && (gs.width = t);
2221                 gs.height != (t = this.paper.height + "px") && (gs.height = t);
2222                 os.left != (t = x - left + "px") && (os.left = t);
2223                 os.top != (t = y - top + "px") && (os.top = t);
2224                 os.width != (t = w + "px") && (os.width = t);
2225                 os.height != (t = h + "px") && (os.height = t);
2226             }
2227         };
2228         Element[proto].hide = function () {
2229             !this.removed && (this.Group.style.display = "none");
2230             return this;
2231         };
2232         Element[proto].show = function () {
2233             !this.removed && (this.Group.style.display = "block");
2234             return this;
2235         };
2236         Element[proto].getBBox = function () {
2237             if (this.removed) {
2238                 return this;
2239             }
2240             if (pathlike[has](this.type)) {
2241                 return pathDimensions(this.attrs.path);
2242             }
2243             return {
2244                 x: this.X + (this.bbx || 0),
2245                 y: this.Y,
2246                 width: this.W,
2247                 height: this.H
2248             };
2249         };
2250         Element[proto].remove = function () {
2251             if (this.removed) {
2252                 return;
2253             }
2254             tear(this, this.paper);
2255             this.node.parentNode.removeChild(this.node);
2256             this.Group.parentNode.removeChild(this.Group);
2257             this.shape && this.shape.parentNode.removeChild(this.shape);
2258             for (var i in this) {
2259                 delete this[i];
2260             }
2261             this.removed = true;
2262         };
2263         Element[proto].attr = function (name, value) {
2264             if (this.removed) {
2265                 return this;
2266             }
2267             if (name == null) {
2268                 var res = {};
2269                 for (var i in this.attrs) if (this.attrs[has](i)) {
2270                     res[i] = this.attrs[i];
2271                 }
2272                 this._.rt.deg && (res.rotation = this.rotate());
2273                 (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale());
2274                 res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
2275                 return res;
2276             }
2277             if (value == null && R.is(name, string)) {
2278                 if (name == "translation") {
2279                     return translate.call(this);
2280                 }
2281                 if (name == "rotation") {
2282                     return this.rotate();
2283                 }
2284                 if (name == "scale") {
2285                     return this.scale();
2286                 }
2287                 if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
2288                     return this.attrs.gradient;
2289                 }
2290                 return this.attrs[name];
2291             }
2292             if (this.attrs && value == null && R.is(name, array)) {
2293                 var ii, values = {};
2294                 for (i = 0, ii = name[length]; i < ii; i++) {
2295                     values[name[i]] = this.attr(name[i]);
2296                 }
2297                 return values;
2298             }
2299             var params;
2300             if (value != null) {
2301                 params = {};
2302                 params[name] = value;
2303             }
2304             value == null && R.is(name, "object") && (params = name);
2305             if (params) {
2306                 if (params.text && this.type == "text") {
2307                     this.node.string = params.text;
2308                 }
2309                 setFillAndStroke(this, params);
2310                 if (params.gradient && (({circle: 1, ellipse: 1})[has](this.type) || Str(params.gradient).charAt() != "r")) {
2311                     addGradientFill(this, params.gradient);
2312                 }
2313                 (!pathlike[has](this.type) || this._.rt.deg) && this.setBox(this.attrs);
2314             }
2315             return this;
2316         };
2317         Element[proto].toFront = function () {
2318             !this.removed && this.Group.parentNode[appendChild](this.Group);
2319             this.paper.top != this && tofront(this, this.paper);
2320             return this;
2321         };
2322         Element[proto].toBack = function () {
2323             if (this.removed) {
2324                 return this;
2325             }
2326             if (this.Group.parentNode.firstChild != this.Group) {
2327                 this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
2328                 toback(this, this.paper);
2329             }
2330             return this;
2331         };
2332         Element[proto].insertAfter = function (element) {
2333             if (this.removed) {
2334                 return this;
2335             }
2336             if (element.constructor == Set) {
2337                 element = element[element.length];
2338             }
2339             if (element.Group.nextSibling) {
2340                 element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling);
2341             } else {
2342                 element.Group.parentNode[appendChild](this.Group);
2343             }
2344             insertafter(this, element, this.paper);
2345             return this;
2346         };
2347         Element[proto].insertBefore = function (element) {
2348             if (this.removed) {
2349                 return this;
2350             }
2351             if (element.constructor == Set) {
2352                 element = element[0];
2353             }
2354             element.Group.parentNode.insertBefore(this.Group, element.Group);
2355             insertbefore(this, element, this.paper);
2356             return this;
2357         };
2358         var blurregexp = / progid:\S+Blur\([^\)]+\)/g;
2359         Element[proto].blur = function (size) {
2360             var s = this.node.runtimeStyle,
2361                 f = s.filter;
2362             f = f.replace(blurregexp, E);
2363             if (+size !== 0) {
2364                 this.attrs.blur = size;
2365                 s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
2366                 s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
2367             } else {
2368                 s.filter = f;
2369                 s.margin = 0;
2370                 delete this.attrs.blur;
2371             }
2372         };
2373  
2374         theCircle = function (vml, x, y, r) {
2375             var g = createNode("group"),
2376                 o = createNode("oval"),
2377                 ol = o.style;
2378             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2379             g.coordsize = coordsize;
2380             g.coordorigin = vml.coordorigin;
2381             g[appendChild](o);
2382             var res = new Element(o, g, vml);
2383             res.type = "circle";
2384             setFillAndStroke(res, {stroke: "#000", fill: "none"});
2385             res.attrs.cx = x;
2386             res.attrs.cy = y;
2387             res.attrs.r = r;
2388             res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
2389             vml.canvas[appendChild](g);
2390             return res;
2391         };
2392         function rectPath(x, y, w, h, r) {
2393             if (r) {
2394                 return R.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", x + r, y, w - r * 2, r, -r, h - r * 2, r * 2 - w, r * 2 - h);
2395             } else {
2396                 return R.format("M{0},{1}l{2},0,0,{3},{4},0z", x, y, w, h, -w);
2397             }
2398         }
2399         theRect = function (vml, x, y, w, h, r) {
2400             var path = rectPath(x, y, w, h, r),
2401                 res = vml.path(path),
2402                 a = res.attrs;
2403             res.X = a.x = x;
2404             res.Y = a.y = y;
2405             res.W = a.width = w;
2406             res.H = a.height = h;
2407             a.r = r;
2408             a.path = path;
2409             res.type = "rect";
2410             return res;
2411         };
2412         theEllipse = function (vml, x, y, rx, ry) {
2413             var g = createNode("group"),
2414                 o = createNode("oval"),
2415                 ol = o.style;
2416             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2417             g.coordsize = coordsize;
2418             g.coordorigin = vml.coordorigin;
2419             g[appendChild](o);
2420             var res = new Element(o, g, vml);
2421             res.type = "ellipse";
2422             setFillAndStroke(res, {stroke: "#000"});
2423             res.attrs.cx = x;
2424             res.attrs.cy = y;
2425             res.attrs.rx = rx;
2426             res.attrs.ry = ry;
2427             res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
2428             vml.canvas[appendChild](g);
2429             return res;
2430         };
2431         theImage = function (vml, src, x, y, w, h) {
2432             var g = createNode("group"),
2433                 o = createNode("image"),
2434                 ol = o.style;
2435             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2436             g.coordsize = coordsize;
2437             g.coordorigin = vml.coordorigin;
2438             o.src = src;
2439             g[appendChild](o);
2440             var res = new Element(o, g, vml);
2441             res.type = "image";
2442             res.attrs.src = src;
2443             res.attrs.x = x;
2444             res.attrs.y = y;
2445             res.attrs.w = w;
2446             res.attrs.h = h;
2447             res.setBox({x: x, y: y, width: w, height: h});
2448             vml.canvas[appendChild](g);
2449             return res;
2450         };
2451         theText = function (vml, x, y, text) {
2452             var g = createNode("group"),
2453                 el = createNode("shape"),
2454                 ol = el.style,
2455                 path = createNode("path"),
2456                 ps = path.style,
2457                 o = createNode("textpath");
2458             g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
2459             g.coordsize = coordsize;
2460             g.coordorigin = vml.coordorigin;
2461             path.v = R.format("m{0},{1}l{2},{1}", round(x * 10), round(y * 10), round(x * 10) + 1);
2462             path.textpathok = true;
2463             ol.width = vml.width;
2464             ol.height = vml.height;
2465             o.string = Str(text);
2466             o.on = true;
2467             el[appendChild](o);
2468             el[appendChild](path);
2469             g[appendChild](el);
2470             var res = new Element(o, g, vml);
2471             res.shape = el;
2472             res.textpath = path;
2473             res.type = "text";
2474             res.attrs.text = text;
2475             res.attrs.x = x;
2476             res.attrs.y = y;
2477             res.attrs.w = 1;
2478             res.attrs.h = 1;
2479             setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000"});
2480             res.setBox();
2481             vml.canvas[appendChild](g);
2482             return res;
2483         };
2484         setSize = function (width, height) {
2485             var cs = this.canvas.style;
2486             width == +width && (width += "px");
2487             height == +height && (height += "px");
2488             cs.width = width;
2489             cs.height = height;
2490             cs.clip = "rect(0 " + width + " " + height + " 0)";
2491             return this;
2492         };
2493         var createNode;
2494         doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
2495         try {
2496             !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
2497             createNode = function (tagName) {
2498                 return doc.createElement('<rvml:' + tagName + ' class="rvml">');
2499             };
2500         } catch (e) {
2501             createNode = function (tagName) {
2502                 return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
2503             };
2504         }
2505         create = function () {
2506             var con = getContainer[apply](0, arguments),
2507                 container = con.container,
2508                 height = con.height,
2509                 s,
2510                 width = con.width,
2511                 x = con.x,
2512                 y = con.y;
2513             if (!container) {
2514                 throw new Error("VML container not found.");
2515             }
2516             var res = new Paper,
2517                 c = res.canvas = doc.createElement("div"),
2518                 cs = c.style;
2519             x = x || 0;
2520             y = y || 0;
2521             width = width || 512;
2522             height = height || 342;
2523             width == +width && (width += "px");
2524             height == +height && (height += "px");
2525             res.width = 1e3;
2526             res.height = 1e3;
2527             res.coordsize = zoom * 1e3 + S + zoom * 1e3;
2528             res.coordorigin = "0 0";
2529             res.span = doc.createElement("span");
2530             res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
2531             c[appendChild](res.span);
2532             cs.cssText = R.format("width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
2533             if (container == 1) {
2534                 doc.body[appendChild](c);
2535                 cs.left = x + "px";
2536                 cs.top = y + "px";
2537                 cs.position = "absolute";
2538             } else {
2539                 if (container.firstChild) {
2540                     container.insertBefore(c, container.firstChild);
2541                 } else {
2542                     container[appendChild](c);
2543                 }
2544             }
2545             plugins.call(res, res, R.fn);
2546             return res;
2547         };
2548         Paper[proto].clear = function () {
2549             this.canvas.innerHTML = E;
2550             this.span = doc.createElement("span");
2551             this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
2552             this.canvas[appendChild](this.span);
2553             this.bottom = this.top = null;
2554         };
2555         Paper[proto].remove = function () {
2556             this.canvas.parentNode.removeChild(this.canvas);
2557             for (var i in this) {
2558                 this[i] = removed(i);
2559             }
2560             return true;
2561         };
2562     }
2563  
2564     // rest
2565     // WebKit rendering bug workaround method
2566     if ((navigator.vendor == "Apple Computer, Inc.") && (navigator.userAgent.match(/Version\/(.*?)\s/)[1] < 4 || win.navigator.platform.slice(0, 2) == "iP")) {
2567         Paper[proto].safari = function () {
2568             var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
2569             win.setTimeout(function () {rect.remove();});
2570         };
2571     } else {
2572         Paper[proto].safari = function () {};
2573     }
2574  
2575     // Events
2576     var preventDefault = function () {
2577         this.returnValue = false;
2578     },
2579     preventTouch = function () {
2580         return this.originalEvent.preventDefault();
2581     },
2582     stopPropagation = function () {
2583         this.cancelBubble = true;
2584     },
2585     stopTouch = function () {
2586         return this.originalEvent.stopPropagation();
2587     },
2588     addEvent = (function () {
2589         if (doc.addEventListener) {
2590             return function (obj, type, fn, element) {
2591                 var realName = supportsTouch && touchMap[type] ? touchMap[type] : type;
2592                 var f = function (e) {
2593                     if (supportsTouch && touchMap[has](type)) {
2594                         for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
2595                             if (e.targetTouches[i].target == obj) {
2596                                 var olde = e;
2597                                 e = e.targetTouches[i];
2598                                 e.originalEvent = olde;
2599                                 e.preventDefault = preventTouch;
2600                                 e.stopPropagation = stopTouch;
2601                                 break;
2602                             }
2603                         }
2604                     }
2605                     return fn.call(element, e);
2606                 };
2607                 obj.addEventListener(realName, f, false);
2608                 return function () {
2609                     obj.removeEventListener(realName, f, false);
2610                     return true;
2611                 };
2612             };
2613         } else if (doc.attachEvent) {
2614             return function (obj, type, fn, element) {
2615                 var f = function (e) {
2616                     e = e || win.event;
2617                     e.preventDefault = e.preventDefault || preventDefault;
2618                     e.stopPropagation = e.stopPropagation || stopPropagation;
2619                     return fn.call(element, e);
2620                 };
2621                 obj.attachEvent("on" + type, f);
2622                 var detacher = function () {
2623                     obj.detachEvent("on" + type, f);
2624                     return true;
2625                 };
2626                 return detacher;
2627             };
2628         }
2629     })(),
2630     drag = [],
2631     dragMove = function (e) {
2632         var x = e.clientX,
2633             y = e.clientY,
2634             dragi,
2635             j = drag.length;
2636         while (j--) {
2637             dragi = drag[j];
2638             if (supportsTouch) {
2639                 var i = e.touches.length,
2640                     touch;
2641                 while (i--) {
2642                     touch = e.touches[i];
2643                     if (touch.identifier == dragi.el._drag.id) {
2644                         x = touch.clientX;
2645                         y = touch.clientY;
2646                         (e.originalEvent ? e.originalEvent : e).preventDefault();
2647                         break;
2648                     }
2649                 }
2650             } else {
2651                 e.preventDefault();
2652             }
2653             dragi.move && dragi.move.call(dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y);
2654         }
2655     },
2656     dragUp = function () {
2657         R.unmousemove(dragMove).unmouseup(dragUp);
2658         var i = drag.length,
2659             dragi;
2660         while (i--) {
2661             dragi = drag[i];
2662             dragi.el._drag = {};
2663             dragi.end && dragi.end.call(dragi.el);
2664         }
2665         drag = [];
2666     };
2667     for (var i = events[length]; i--;) {
2668         (function (eventName) {
2669             R[eventName] = Element[proto][eventName] = function (fn) {
2670                 if (R.is(fn, "function")) {
2671                     this.events = this.events || [];
2672                     this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || doc, eventName, fn, this)});
2673                 }
2674                 return this;
2675             };
2676             R["un" + eventName] = Element[proto]["un" + eventName] = function (fn) {
2677                 var events = this.events,
2678                     l = events[length];
2679                 while (l--) if (events[l].name == eventName && events[l].f == fn) {
2680                     events[l].unbind();
2681                     events.splice(l, 1);
2682                     !events.length && delete this.events;
2683                     return this;
2684                 }
2685                 return this;
2686             };
2687         })(events[i]);
2688     }
2689     Element[proto].hover = function (f_in, f_out) {
2690         return this.mouseover(f_in).mouseout(f_out);
2691     };
2692     Element[proto].unhover = function (f_in, f_out) {
2693         return this.unmouseover(f_in).unmouseout(f_out);
2694     };
2695     Element[proto].drag = function (onmove, onstart, onend) {
2696         this._drag = {};
2697         this.mousedown(function (e) {
2698             (e.originalEvent || e).preventDefault();
2699             this._drag.x = e.clientX;
2700             this._drag.y = e.clientY;
2701             this._drag.id = e.identifier;
2702             onstart && onstart.call(this, e.clientX, e.clientY);
2703             !drag.length && R.mousemove(dragMove).mouseup(dragUp);
2704             drag.push({el: this, move: onmove, end: onend});
2705         });
2706         return this;
2707     };
2708     Element[proto].undrag = function (onmove, onstart, onend) {
2709         var i = drag.length;
2710         while (i--) {
2711             drag[i].el == this && (drag[i].move == onmove && drag[i].end == onend) && drag.splice(i, 1);
2712             !drag.length && R.unmousemove(dragMove).unmouseup(dragUp);
2713         }
2714     };
2715     Paper[proto].circle = function (x, y, r) {
2716         return theCircle(this, x || 0, y || 0, r || 0);
2717     };
2718     Paper[proto].rect = function (x, y, w, h, r) {
2719         return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
2720     };
2721     Paper[proto].ellipse = function (x, y, rx, ry) {
2722         return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0);
2723     };
2724     Paper[proto].path = function (pathString) {
2725         pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
2726         return thePath(R.format[apply](R, arguments), this);
2727     };
2728     Paper[proto].image = function (src, x, y, w, h) {
2729         return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
2730     };
2731     Paper[proto].text = function (x, y, text) {
2732         return theText(this, x || 0, y || 0, text || E);
2733     };
2734     Paper[proto].set = function (itemsArray) {
2735         arguments[length] > 1 && (itemsArray = Array[proto].splice.call(arguments, 0, arguments[length]));
2736         return new Set(itemsArray);
2737     };
2738     Paper[proto].setSize = setSize;
2739     Paper[proto].top = Paper[proto].bottom = null;
2740     Paper[proto].raphael = R;
2741     function x_y() {
2742         return this.x + S + this.y;
2743     }
2744     Element[proto].resetScale = function () {
2745         if (this.removed) {
2746             return this;
2747         }
2748         this._.sx = 1;
2749         this._.sy = 1;
2750         this.attrs.scale = "1 1";
2751     };
2752     Element[proto].scale = function (x, y, cx, cy) {
2753         if (this.removed) {
2754             return this;
2755         }
2756         if (x == null && y == null) {
2757             return {
2758                 x: this._.sx,
2759                 y: this._.sy,
2760                 toString: x_y
2761             };
2762         }
2763         y = y || x;
2764         !+y && (y = x);
2765         var dx,
2766             dy,
2767             dcx,
2768             dcy,
2769             a = this.attrs;
2770         if (x != 0) {
2771             var bb = this.getBBox(),
2772                 rcx = bb.x + bb.width / 2,
2773                 rcy = bb.y + bb.height / 2,
2774                 kx = x / this._.sx,
2775                 ky = y / this._.sy;
2776             cx = (+cx || cx == 0) ? cx : rcx;
2777             cy = (+cy || cy == 0) ? cy : rcy;
2778             var dirx = ~~(x / math.abs(x)),
2779                 diry = ~~(y / math.abs(y)),
2780                 s = this.node.style,
2781                 ncx = cx + (rcx - cx) * kx,
2782                 ncy = cy + (rcy - cy) * ky;
2783             switch (this.type) {
2784                 case "rect":
2785                 case "image":
2786                     var neww = a.width * dirx * kx,
2787                         newh = a.height * diry * ky;
2788                     this.attr({
2789                         height: newh,
2790                         r: a.r * mmin(dirx * kx, diry * ky),
2791                         width: neww,
2792                         x: ncx - neww / 2,
2793                         y: ncy - newh / 2
2794                     });
2795                     break;
2796                 case "circle":
2797                 case "ellipse":
2798                     this.attr({
2799                         rx: a.rx * dirx * kx,
2800                         ry: a.ry * diry * ky,
2801                         r: a.r * mmin(dirx * kx, diry * ky),
2802                         cx: ncx,
2803                         cy: ncy
2804                     });
2805                     break;
2806                 case "text":
2807                     this.attr({
2808                         x: ncx,
2809                         y: ncy
2810                     });
2811                     break;
2812                 case "path":
2813                     var path = pathToRelative(a.path),
2814                         skip = true;
2815                     for (var i = 0, ii = path[length]; i < ii; i++) {
2816                         var p = path[i],
2817                             P0 = upperCase.call(p[0]);
2818                         if (P0 == "M" && skip) {
2819                             continue;
2820                         } else {
2821                             skip = false;
2822                         }
2823                         if (P0 == "A") {
2824                             p[path[i][length] - 2] *= kx;
2825                             p[path[i][length] - 1] *= ky;
2826                             p[1] *= dirx * kx;
2827                             p[2] *= diry * ky;
2828                             p[5] = +!(dirx + diry ? !+p[5] : +p[5]);
2829                         } else if (P0 == "H") {
2830                             for (var j = 1, jj = p[length]; j < jj; j++) {
2831                                 p[j] *= kx;
2832                             }
2833                         } else if (P0 == "V") {
2834                             for (j = 1, jj = p[length]; j < jj; j++) {
2835                                 p[j] *= ky;
2836                             }
2837                          } else {
2838                             for (j = 1, jj = p[length]; j < jj; j++) {
2839                                 p[j] *= (j % 2) ? kx : ky;
2840                             }
2841                         }
2842                     }
2843                     var dim2 = pathDimensions(path);
2844                     dx = ncx - dim2.x - dim2.width / 2;
2845                     dy = ncy - dim2.y - dim2.height / 2;
2846                     path[0][1] += dx;
2847                     path[0][2] += dy;
2848                     this.attr({path: path});
2849                 break;
2850             }
2851             if (this.type in {text: 1, image:1} && (dirx != 1 || diry != 1)) {
2852                 if (this.transformations) {
2853                     this.transformations[2] = "scale("[concat](dirx, ",", diry, ")");
2854                     this.node[setAttribute]("transform", this.transformations[join](S));
2855                     dx = (dirx == -1) ? -a.x - (neww || 0) : a.x;
2856                     dy = (diry == -1) ? -a.y - (newh || 0) : a.y;
2857                     this.attr({x: dx, y: dy});
2858                     a.fx = dirx - 1;
2859                     a.fy = diry - 1;
2860                 } else {
2861                     this.node.filterMatrix = ms + ".Matrix(M11="[concat](dirx,
2862                         ", M12=0, M21=0, M22=", diry,
2863                         ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')");
2864                     s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E);
2865                 }
2866             } else {
2867                 if (this.transformations) {
2868                     this.transformations[2] = E;
2869                     this.node[setAttribute]("transform", this.transformations[join](S));
2870                     a.fx = 0;
2871                     a.fy = 0;
2872                 } else {
2873                     this.node.filterMatrix = E;
2874                     s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E);
2875                 }
2876             }
2877             a.scale = [x, y, cx, cy][join](S);
2878             this._.sx = x;
2879             this._.sy = y;
2880         }
2881         return this;
2882     };
2883     Element[proto].clone = function () {
2884         if (this.removed) {
2885             return null;
2886         }
2887         var attr = this.attr();
2888         delete attr.scale;
2889         delete attr.translation;
2890         return this.paper[this.type]().attr(attr);
2891     };
2892     var getPointAtSegmentLength = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
2893         var len = 0,
2894             old;
2895         for (var i = 0; i < 1.01; i+=.01) {
2896             var dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i);
2897             i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
2898             if (len >= length) {
2899                 return dot;
2900             }
2901             old = dot;
2902         }
2903     }),
2904     getLengthFactory = function (istotal, subpath) {
2905         return function (path, length, onlystart) {
2906             path = path2curve(path);
2907             var x, y, p, l, sp = "", subpaths = {}, point,
2908                 len = 0;
2909             for (var i = 0, ii = path.length; i < ii; i++) {
2910                 p = path[i];
2911                 if (p[0] == "M") {
2912                     x = +p[1];
2913                     y = +p[2];
2914                 } else {
2915                     l = segmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
2916                     if (len + l > length) {
2917                         if (subpath && !subpaths.start) {
2918                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
2919                             sp += ["C", point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
2920                             if (onlystart) {return sp;}
2921                             subpaths.start = sp;
2922                             sp = ["M", point.x, point.y + "C", point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]][join]();
2923                             len += l;
2924                             x = +p[5];
2925                             y = +p[6];
2926                             continue;
2927                         }
2928                         if (!istotal && !subpath) {
2929                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
2930                             return {x: point.x, y: point.y, alpha: point.alpha};
2931                         }
2932                     }
2933                     len += l;
2934                     x = +p[5];
2935                     y = +p[6];
2936                 }
2937                 sp += p;
2938             }
2939             subpaths.end = sp;
2940             point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], 1);
2941             point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
2942             return point;
2943         };
2944     },
2945     segmentLength = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
2946         var old = {x: 0, y: 0},
2947             len = 0;
2948         for (var i = 0; i < 1.01; i+=.01) {
2949             var dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i);
2950             i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
2951             old = dot;
2952         }
2953         return len;
2954     });
2955     var getTotalLength = getLengthFactory(1),
2956         getPointAtLength = getLengthFactory(),
2957         getSubpathsAtLength = getLengthFactory(0, 1);
2958     Element[proto].getTotalLength = function () {
2959         if (this.type != "path") {return;}
2960         if (this.node.getTotalLength) {
2961             return this.node.getTotalLength();
2962         }
2963         return getTotalLength(this.attrs.path);
2964     };
2965     Element[proto].getPointAtLength = function (length) {
2966         if (this.type != "path") {return;}
2967         if (this.node.getPointAtLength) {
2968             return this.node.getPointAtLength(length);
2969         }
2970         return getPointAtLength(this.attrs.path, length);
2971     };
2972     Element[proto].getSubpath = function (from, to) {
2973         if (this.type != "path") {return;}
2974         if (math.abs(this.getTotalLength() - to) < 1e-6) {
2975             return getSubpathsAtLength(this.attrs.path, from).end;
2976         }
2977         var a = getSubpathsAtLength(this.attrs.path, to, 1);
2978         return from ? getSubpathsAtLength(a, from).end : a;
2979     };
2980
2981     // animation easing formulas
2982     R.easing_formulas = {
2983         linear: function (n) {
2984             return n;
2985         },
2986         "<": function (n) {
2987             return pow(n, 3);
2988         },
2989         ">": function (n) {
2990             return pow(n - 1, 3) + 1;
2991         },
2992         "<>": function (n) {
2993             n = n * 2;
2994             if (n < 1) {
2995                 return pow(n, 3) / 2;
2996             }
2997             n -= 2;
2998             return (pow(n, 3) + 2) / 2;
2999         },
3000         backIn: function (n) {
3001             var s = 1.70158;
3002             return n * n * ((s + 1) * n - s);
3003         },
3004         backOut: function (n) {
3005             n = n - 1;
3006             var s = 1.70158;
3007             return n * n * ((s + 1) * n + s) + 1;
3008         },
3009         elastic: function (n) {
3010             if (n == 0 || n == 1) {
3011                 return n;
3012             }
3013             var p = .3,
3014                 s = p / 4;
3015             return pow(2, -10 * n) * math.sin((n - s) * (2 * math.PI) / p) + 1;
3016         },
3017         bounce: function (n) {
3018             var s = 7.5625,
3019                 p = 2.75,
3020                 l;
3021             if (n < (1 / p)) {
3022                 l = s * n * n;
3023             } else {
3024                 if (n < (2 / p)) {
3025                     n -= (1.5 / p);
3026                     l = s * n * n + .75;
3027                 } else {
3028                     if (n < (2.5 / p)) {
3029                         n -= (2.25 / p);
3030                         l = s * n * n + .9375;
3031                     } else {
3032                         n -= (2.625 / p);
3033                         l = s * n * n + .984375;
3034                     }
3035                 }
3036             }
3037             return l;
3038         }
3039     };
3040
3041     var animationElements = {length : 0},
3042         animation = function () {
3043             var Now = +new Date;
3044             for (var l in animationElements) if (l != "length" && animationElements[has](l)) {
3045                 var e = animationElements[l];
3046                 if (e.stop || e.el.removed) {
3047                     delete animationElements[l];
3048                     animationElements[length]--;
3049                     continue;
3050                 }
3051                 var time = Now - e.start,
3052                     ms = e.ms,
3053                     easing = e.easing,
3054                     from = e.from,
3055                     diff = e.diff,
3056                     to = e.to,
3057                     t = e.t,
3058                     prev = e.prev || 0,
3059                     that = e.el,
3060                     callback = e.callback,
3061                     set = {},
3062                     now;
3063                 if (time < ms) {
3064                     var pos = R.easing_formulas[easing] ? R.easing_formulas[easing](time / ms) : time / ms;
3065                     for (var attr in from) if (from[has](attr)) {
3066                         switch (availableAnimAttrs[attr]) {
3067                             case "along":
3068                                 now = pos * ms * diff[attr];
3069                                 to.back && (now = to.len - now);
3070                                 var point = getPointAtLength(to[attr], now);
3071                                 that.translate(diff.sx - diff.x || 0, diff.sy - diff.y || 0);
3072                                 diff.x = point.x;
3073                                 diff.y = point.y;
3074                                 that.translate(point.x - diff.sx, point.y - diff.sy);
3075                                 to.rot && that.rotate(diff.r + point.alpha, point.x, point.y);
3076                                 break;
3077                             case nu:
3078                                 now = +from[attr] + pos * ms * diff[attr];
3079                                 break;
3080                             case "colour":
3081                                 now = "rgb(" + [
3082                                     upto255(round(from[attr].r + pos * ms * diff[attr].r)),
3083                                     upto255(round(from[attr].g + pos * ms * diff[attr].g)),
3084                                     upto255(round(from[attr].b + pos * ms * diff[attr].b))
3085                                 ][join](",") + ")";
3086                                 break;
3087                             case "path":
3088                                 now = [];
3089                                 for (var i = 0, ii = from[attr][length]; i < ii; i++) {
3090                                     now[i] = [from[attr][i][0]];
3091                                     for (var j = 1, jj = from[attr][i][length]; j < jj; j++) {
3092                                         now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
3093                                     }
3094                                     now[i] = now[i][join](S);
3095                                 }
3096                                 now = now[join](S);
3097                                 break;
3098                             case "csv":
3099                                 switch (attr) {
3100                                     case "translation":
3101                                         var x = diff[attr][0] * (time - prev),
3102                                             y = diff[attr][1] * (time - prev);
3103                                         t.x += x;
3104                                         t.y += y;
3105                                         now = x + S + y;
3106                                     break;
3107                                     case "rotation":
3108                                         now = +from[attr][0] + pos * ms * diff[attr][0];
3109                                         from[attr][1] && (now += "," + from[attr][1] + "," + from[attr][2]);
3110                                     break;
3111                                     case "scale":
3112                                         now = [+from[attr][0] + pos * ms * diff[attr][0], +from[attr][1] + pos * ms * diff[attr][1], (2 in to[attr] ? to[attr][2] : E), (3 in to[attr] ? to[attr][3] : E)][join](S);
3113                                     break;
3114                                     case "clip-rect":
3115                                         now = [];
3116                                         i = 4;
3117                                         while (i--) {
3118                                             now[i] = +from[attr][i] + pos * ms * diff[attr][i];
3119                                         }
3120                                     break;
3121                                 }
3122                                 break;
3123                         }
3124                         set[attr] = now;
3125                     }
3126                     that.attr(set);
3127                     that._run && that._run.call(that);
3128                 } else {
3129                     if (to.along) {
3130                         point = getPointAtLength(to.along, to.len * !to.back);
3131                         that.translate(diff.sx - (diff.x || 0) + point.x - diff.sx, diff.sy - (diff.y || 0) + point.y - diff.sy);
3132                         to.rot && that.rotate(diff.r + point.alpha, point.x, point.y);
3133                     }
3134                     (t.x || t.y) && that.translate(-t.x, -t.y);
3135                     to.scale && (to.scale += E);
3136                     that.attr(to);
3137                     delete animationElements[l];
3138                     animationElements[length]--;
3139                     that.in_animation = null;
3140                     R.is(callback, "function") && callback.call(that);
3141                 }
3142                 e.prev = time;
3143             }
3144             R.svg && that && that.paper && that.paper.safari();
3145             animationElements[length] && win.setTimeout(animation);
3146         },
3147         upto255 = function (color) {
3148             return mmax(mmin(color, 255), 0);
3149         },
3150         translate = function (x, y) {
3151             if (x == null) {
3152                 return {x: this._.tx, y: this._.ty, toString: x_y};
3153             }
3154             this._.tx += +x;
3155             this._.ty += +y;
3156             switch (this.type) {
3157                 case "circle":
3158                 case "ellipse":
3159                     this.attr({cx: +x + this.attrs.cx, cy: +y + this.attrs.cy});
3160                     break;
3161                 case "rect":
3162                 case "image":
3163                 case "text":
3164                     this.attr({x: +x + this.attrs.x, y: +y + this.attrs.y});
3165                     break;
3166                 case "path":
3167                     var path = pathToRelative(this.attrs.path);
3168                     path[0][1] += +x;
3169                     path[0][2] += +y;
3170                     this.attr({path: path});
3171                 break;
3172             }
3173             return this;
3174         };
3175     Element[proto].animateWith = function (element, params, ms, easing, callback) {
3176         animationElements[element.id] && (params.start = animationElements[element.id].start);
3177         return this.animate(params, ms, easing, callback);
3178     };
3179     Element[proto].animateAlong = along();
3180     Element[proto].animateAlongBack = along(1);
3181     function along(isBack) {
3182         return function (path, ms, rotate, callback) {
3183             var params = {back: isBack};
3184             R.is(rotate, "function") ? (callback = rotate) : (params.rot = rotate);
3185             path && path.constructor == Element && (path = path.attrs.path);
3186             path && (params.along = path);
3187             return this.animate(params, ms, callback);
3188         };
3189     }
3190     Element[proto].onAnimation = function (f) {
3191         this._run = f || 0;
3192         return this;
3193     };
3194     Element[proto].animate = function (params, ms, easing, callback) {
3195         if (R.is(easing, "function") || !easing) {
3196             callback = easing || null;
3197         }
3198         var from = {},
3199             to = {},
3200             diff = {};
3201         for (var attr in params) if (params[has](attr)) {
3202             if (availableAnimAttrs[has](attr)) {
3203                 from[attr] = this.attr(attr);
3204                 (from[attr] == null) && (from[attr] = availableAttrs[attr]);
3205                 to[attr] = params[attr];
3206                 switch (availableAnimAttrs[attr]) {
3207                     case "along":
3208                         var len = getTotalLength(params[attr]);
3209                         var point = getPointAtLength(params[attr], len * !!params.back);
3210                         var bb = this.getBBox();
3211                         diff[attr] = len / ms;
3212                         diff.tx = bb.x;
3213                         diff.ty = bb.y;
3214                         diff.sx = point.x;
3215                         diff.sy = point.y;
3216                         to.rot = params.rot;
3217                         to.back = params.back;
3218                         to.len = len;
3219                         params.rot && (diff.r = toFloat(this.rotate()) || 0);
3220                         break;
3221                     case nu:
3222                         diff[attr] = (to[attr] - from[attr]) / ms;
3223                         break;
3224                     case "colour":
3225                         from[attr] = R.getRGB(from[attr]);
3226                         var toColour = R.getRGB(to[attr]);
3227                         diff[attr] = {
3228                             r: (toColour.r - from[attr].r) / ms,
3229                             g: (toColour.g - from[attr].g) / ms,
3230                             b: (toColour.b - from[attr].b) / ms
3231                         };
3232                         break;
3233                     case "path":
3234                         var pathes = path2curve(from[attr], to[attr]);
3235                         from[attr] = pathes[0];
3236                         var toPath = pathes[1];
3237                         diff[attr] = [];
3238                         for (var i = 0, ii = from[attr][length]; i < ii; i++) {
3239                             diff[attr][i] = [0];
3240                             for (var j = 1, jj = from[attr][i][length]; j < jj; j++) {
3241                                 diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
3242                             }
3243                         }
3244                         break;
3245                     case "csv":
3246                         var values = Str(params[attr])[split](separator),
3247                             from2 = Str(from[attr])[split](separator);
3248                         switch (attr) {
3249                             case "translation":
3250                                 from[attr] = [0, 0];
3251                                 diff[attr] = [values[0] / ms, values[1] / ms];
3252                             break;
3253                             case "rotation":
3254                                 from[attr] = (from2[1] == values[1] && from2[2] == values[2]) ? from2 : [0, values[1], values[2]];
3255                                 diff[attr] = [(values[0] - from[attr][0]) / ms, 0, 0];
3256                             break;
3257                             case "scale":
3258                                 params[attr] = values;
3259                                 from[attr] = Str(from[attr])[split](separator);
3260                                 diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][1]) / ms, 0, 0];
3261                             break;
3262                             case "clip-rect":
3263                                 from[attr] = Str(from[attr])[split](separator);
3264                                 diff[attr] = [];
3265                                 i = 4;
3266                                 while (i--) {
3267                                     diff[attr][i] = (values[i] - from[attr][i]) / ms;
3268                                 }
3269                             break;
3270                         }
3271                         to[attr] = values;
3272                 }
3273             }
3274         }
3275         this.stop();
3276         this.in_animation = 1;
3277         animationElements[this.id] = {
3278             start: params.start || +new Date,
3279             ms: ms,
3280             easing: easing,
3281             from: from,
3282             diff: diff,
3283             to: to,
3284             el: this,
3285             callback: callback,
3286             t: {x: 0, y: 0}
3287         };
3288         ++animationElements[length] == 1 && animation();
3289         return this;
3290     };
3291     Element[proto].stop = function () {
3292         animationElements[this.id] && animationElements[length]--;
3293         delete animationElements[this.id];
3294         return this;
3295     };
3296     Element[proto].translate = function (x, y) {
3297         return this.attr({translation: x + " " + y});
3298     };
3299     Element[proto][toString] = function () {
3300         return "Rapha\xebl\u2019s object";
3301     };
3302     R.ae = animationElements;
3303  
3304     // Set
3305     var Set = function (items) {
3306         this.items = [];
3307         this[length] = 0;
3308         this.type = "set";
3309         if (items) {
3310             for (var i = 0, ii = items[length]; i < ii; i++) {
3311                 if (items[i] && (items[i].constructor == Element || items[i].constructor == Set)) {
3312                     this[this.items[length]] = this.items[this.items[length]] = items[i];
3313                     this[length]++;
3314                 }
3315             }
3316         }
3317     };
3318     Set[proto][push] = function () {
3319         var item,
3320             len;
3321         for (var i = 0, ii = arguments[length]; i < ii; i++) {
3322             item = arguments[i];
3323             if (item && (item.constructor == Element || item.constructor == Set)) {
3324                 len = this.items[length];
3325                 this[len] = this.items[len] = item;
3326                 this[length]++;
3327             }
3328         }
3329         return this;
3330     };
3331     Set[proto].pop = function () {
3332         delete this[this[length]--];
3333         return this.items.pop();
3334     };
3335     for (var method in Element[proto]) if (Element[proto][has](method)) {
3336         Set[proto][method] = (function (methodname) {
3337             return function () {
3338                 for (var i = 0, ii = this.items[length]; i < ii; i++) {
3339                     this.items[i][methodname][apply](this.items[i], arguments);
3340                 }
3341                 return this;
3342             };
3343         })(method);
3344     }
3345     Set[proto].attr = function (name, value) {
3346         if (name && R.is(name, array) && R.is(name[0], "object")) {
3347             for (var j = 0, jj = name[length]; j < jj; j++) {
3348                 this.items[j].attr(name[j]);
3349             }
3350         } else {
3351             for (var i = 0, ii = this.items[length]; i < ii; i++) {
3352                 this.items[i].attr(name, value);
3353             }
3354         }
3355         return this;
3356     };
3357     Set[proto].animate = function (params, ms, easing, callback) {
3358         (R.is(easing, "function") || !easing) && (callback = easing || null);
3359         var len = this.items[length],
3360             i = len,
3361             item,
3362             set = this,
3363             collector;
3364         callback && (collector = function () {
3365             !--len && callback.call(set);
3366         });
3367         easing = R.is(easing, string) ? easing : collector;
3368         item = this.items[--i].animate(params, ms, easing, collector);
3369         while (i--) {
3370             this.items[i].animateWith(item, params, ms, easing, collector);
3371         }
3372         return this;
3373     };
3374     Set[proto].insertAfter = function (el) {
3375         var i = this.items[length];
3376         while (i--) {
3377             this.items[i].insertAfter(el);
3378         }
3379         return this;
3380     };
3381     Set[proto].getBBox = function () {
3382         var x = [],
3383             y = [],
3384             w = [],
3385             h = [];
3386         for (var i = this.items[length]; i--;) {
3387             var box = this.items[i].getBBox();
3388             x[push](box.x);
3389             y[push](box.y);
3390             w[push](box.x + box.width);
3391             h[push](box.y + box.height);
3392         }
3393         x = mmin[apply](0, x);
3394         y = mmin[apply](0, y);
3395         return {
3396             x: x,
3397             y: y,
3398             width: mmax[apply](0, w) - x,
3399             height: mmax[apply](0, h) - y
3400         };
3401     };
3402     Set[proto].clone = function (s) {
3403         s = new Set;
3404         for (var i = 0, ii = this.items[length]; i < ii; i++) {
3405             s[push](this.items[i].clone());
3406         }
3407         return s;
3408     };
3409
3410     R.registerFont = function (font) {
3411         if (!font.face) {
3412             return font;
3413         }
3414         this.fonts = this.fonts || {};
3415         var fontcopy = {
3416                 w: font.w,
3417                 face: {},
3418                 glyphs: {}
3419             },
3420             family = font.face["font-family"];
3421         for (var prop in font.face) if (font.face[has](prop)) {
3422             fontcopy.face[prop] = font.face[prop];
3423         }
3424         if (this.fonts[family]) {
3425             this.fonts[family][push](fontcopy);
3426         } else {
3427             this.fonts[family] = [fontcopy];
3428         }
3429         if (!font.svg) {
3430             fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
3431             for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
3432                 var path = font.glyphs[glyph];
3433                 fontcopy.glyphs[glyph] = {
3434                     w: path.w,
3435                     k: {},
3436                     d: path.d && "M" + path.d[rp](/[mlcxtrv]/g, function (command) {
3437                             return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
3438                         }) + "z"
3439                 };
3440                 if (path.k) {
3441                     for (var k in path.k) if (path[has](k)) {
3442                         fontcopy.glyphs[glyph].k[k] = path.k[k];
3443                     }
3444                 }
3445             }
3446         }
3447         return font;
3448     };
3449     Paper[proto].getFont = function (family, weight, style, stretch) {
3450         stretch = stretch || "normal";
3451         style = style || "normal";
3452         weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
3453         if (!R.fonts) {
3454             return;
3455         }
3456         var font = R.fonts[family];
3457         if (!font) {
3458             var name = new RegExp("(^|\\s)" + family[rp](/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
3459             for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
3460                 if (name.test(fontName)) {
3461                     font = R.fonts[fontName];
3462                     break;
3463                 }
3464             }
3465         }
3466         var thefont;
3467         if (font) {
3468             for (var i = 0, ii = font[length]; i < ii; i++) {
3469                 thefont = font[i];
3470                 if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
3471                     break;
3472                 }
3473             }
3474         }
3475         return thefont;
3476     };
3477     Paper[proto].print = function (x, y, string, font, size, origin) {
3478         origin = origin || "middle"; // baseline|middle
3479         var out = this.set(),
3480             letters = Str(string)[split](E),
3481             shift = 0,
3482             path = E,
3483             scale;
3484         R.is(font, string) && (font = this.getFont(font));
3485         if (font) {
3486             scale = (size || 16) / font.face["units-per-em"];
3487             var bb = font.face.bbox.split(separator),
3488                 top = +bb[0],
3489                 height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
3490             for (var i = 0, ii = letters[length]; i < ii; i++) {
3491                 var prev = i && font.glyphs[letters[i - 1]] || {},
3492                     curr = font.glyphs[letters[i]];
3493                 shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) : 0;
3494                 curr && curr.d && out[push](this.path(curr.d).attr({fill: "#000", stroke: "none", translation: [shift, 0]}));
3495             }
3496             out.scale(scale, scale, top, height).translate(x - top, y - height);
3497         }
3498         return out;
3499     };
3500
3501     var formatrg = /\{(\d+)\}/g;
3502     R.format = function (token, params) {
3503         var args = R.is(params, array) ? [0][concat](params) : arguments;
3504         token && R.is(token, string) && args[length] - 1 && (token = token[rp](formatrg, function (str, i) {
3505             return args[++i] == null ? E : args[i];
3506         }));
3507         return token || E;
3508     };
3509     R.ninja = function () {
3510         oldRaphael.was ? (Raphael = oldRaphael.is) : delete Raphael;
3511         return R;
3512     };
3513     R.el = Element[proto];
3514     return R;
3515 })();