Added split method to matrix, fixed caching a bit, first iteration of fix for correct...
[raphael] / raphael.js
1 /*
2  * Raphaël 2.0.0 - JavaScript Vector Library
3  *
4  * Copyright (c) 2011 Dmitry Baranovskiy (http://raphaeljs.com)
5  * Copyright (c) 2011 Sencha Labs (http://sencha.com)
6  * Licensed under the MIT (http://raphaeljs.com/license.html) license.
7  */
8 (function () {
9     /*\
10      * Raphael
11      [ method ]
12      **
13      * Creates a canvas object on which to draw.
14      * You must do this first, as all future calls to drawing methods
15      * from this instance will be bound to this canvas.
16      > Parameters
17      **
18      - container (HTMLElement|string) DOM element or its ID which is going to be a parent for drawing surface
19      - width (number)
20      - height (number)
21      - callback (function) #optional callback function which is going to be executed in the context of newly created paper
22      * or
23      - x (number)
24      - y (number)
25      - width (number)
26      - height (number)
27      - callback (function) #optional callback function which is going to be executed in the context of newly created paper
28      * or
29      - all (array) (first 3 or 4 elements in the array are equal to [containerID, width, height] or [x, y, width, height]. The rest are element descriptions in format {type: type, <attributes>})
30      - callback (function) #optional callback function which is going to be executed in the context of newly created paper
31      * or
32      - onReadyCallback (function) function that is going to be called on DOM ready event. You can also subscribe to this event via Eve’s “DOMLoad” event. In this case method returns `undefined`.
33      = (object) @Paper
34      > Usage
35      | // Each of the following examples create a canvas
36      | // that is 320px wide by 200px high.
37      | // Canvas is created at the viewport’s 10,50 coordinate.
38      | var paper = Raphael(10, 50, 320, 200);
39      | // Canvas is created at the top left corner of the #notepad element
40      | // (or its top right corner in dir="rtl" elements)
41      | var paper = Raphael(document.getElementById("notepad"), 320, 200);
42      | // Same as above
43      | var paper = Raphael("notepad", 320, 200);
44      | // Image dump
45      | var set = Raphael(["notepad", 320, 200, {
46      |     type: "rect",
47      |     x: 10,
48      |     y: 10,
49      |     width: 25,
50      |     height: 25,
51      |     stroke: "#f00"
52      | }, {
53      |     type: "text",
54      |     x: 30,
55      |     y: 40,
56      |     text: "Dump"
57      | }]);
58     \*/
59     function R(first) {
60         if (R.is(first, "function")) {
61             return loaded ? first() : eve.on("DOMload", first);
62         } else if (R.is(first, array)) {
63             var a = first,
64                 cnv = create[apply](R, a.splice(0, 3 + R.is(a[0], nu))),
65                 res = cnv.set(),
66                 i = 0,
67                 ii = a.length,
68                 j;
69             for (; i < ii; i++) {
70                 j = a[i] || {};
71                 elements[has](j.type) && res.push(cnv[j.type]().attr(j));
72             }
73             return res;
74         } else {
75             var args = Array.prototype.slice.call(arguments, 0);
76             if (R.is(args[args.length - 1], "function")) {
77                 var f = args.pop();
78                 return loaded ? f.call(create[apply](R, args)) : eve.on("DOMload", function () {
79                     f.call(create[apply](R, args));
80                 });
81             } else {
82                 return create[apply](R, arguments);
83             }
84         }
85     }
86     R.version = "2.0.0";
87     var loaded,
88         separator = /[, ]+/,
89         elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
90         formatrg = /\{(\d+)\}/g,
91         proto = "prototype",
92         has = "hasOwnProperty",
93         g = {
94             doc: document,
95             win: window
96         },
97         oldRaphael = {
98             was: Object.prototype[has].call(g.win, "Raphael"),
99             is: g.win.Raphael
100         },
101         Paper = function () {},
102         paperproto,
103         appendChild = "appendChild",
104         apply = "apply",
105         concat = "concat",
106         supportsTouch = "createTouch" in g.doc,
107         E = "",
108         S = " ",
109         Str = String,
110         split = "split",
111         events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend orientationchange touchcancel gesturestart gesturechange gestureend".split(S),
112         touchMap = {
113             mousedown: "touchstart",
114             mousemove: "touchmove",
115             mouseup: "touchend"
116         },
117         lowerCase = Str.prototype.toLowerCase,
118         math = Math,
119         mmax = math.max,
120         mmin = math.min,
121         abs = math.abs,
122         pow = math.pow,
123         PI = math.PI,
124         nu = "number",
125         string = "string",
126         array = "array",
127         toString = "toString",
128         fillString = "fill",
129         objectToString = Object.prototype.toString,
130         paper = {},
131         push = "push",
132         ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
133         colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
134         isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
135         bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
136         round = math.round,
137         setAttribute = "setAttribute",
138         toFloat = parseFloat,
139         toInt = parseInt,
140         ms = " progid:DXImageTransform.Microsoft",
141         upperCase = Str.prototype.toUpperCase,
142         availableAttrs = {"arrow-end": "none", "arrow-start": "none", 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, rx: 0, ry: 0, 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", transform: "", width: 0, x: 0, y: 0},
143         availableAnimAttrs = {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, rx: nu, ry: nu, stroke: "colour", "stroke-opacity": nu, "stroke-width": nu, transform: "transform", width: nu, x: nu, y: nu},
144         commaSpaces = /\s*,\s*/,
145         hsrg = {hs: 1, rg: 1},
146         p2s = /,?([achlmqrstvxz]),?/gi,
147         pathCommand = /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
148         tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
149         pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
150         radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,
151         sortByKey = function (a, b) {
152             return a.key - b.key;
153         },
154         sortByNumber = function (a, b) {
155             return a - b;
156         },
157         fun = function () {},
158         pipe = function (x) {
159             return x;
160         },
161         rectPath = function (x, y, w, h, r) {
162             if (r) {
163                 return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
164             }
165             return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
166         },
167         ellipsePath = function (x, y, rx, ry) {
168             if (ry == null) {
169                 ry = rx;
170             }
171             return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
172         },
173         getPath = {
174             path: function (el) {
175                 return el.attr("path");
176             },
177             circle: function (el) {
178                 var a = el.attrs;
179                 return ellipsePath(a.cx, a.cy, a.r);
180             },
181             ellipse: function (el) {
182                 var a = el.attrs;
183                 return ellipsePath(a.cx, a.cy, a.rx, a.ry);
184             },
185             rect: function (el) {
186                 var a = el.attrs;
187                 return rectPath(a.x, a.y, a.width, a.height, a.r);
188             },
189             image: function (el) {
190                 var a = el.attrs;
191                 return rectPath(a.x, a.y, a.width, a.height);
192             },
193             text: function (el) {
194                 var bbox = el._getBBox();
195                 return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
196             }
197         },
198         mapPath = function (path, matrix) {
199             if (!matrix) {
200                 return path;
201             }
202             var x, y, i, j, pathi;
203             path = path2curve(path);
204             for (i = 0, ii = path.length; i < ii; i++) {
205                 pathi = path[i];
206                 for (j = 1, jj = pathi.length; j < jj; j += 2) {
207                     x = matrix.x(pathi[j], pathi[j + 1]);
208                     y = matrix.y(pathi[j], pathi[j + 1]);
209                     pathi[j] = x;
210                     pathi[j + 1] = y;
211                 }
212             }
213             return path;
214         };
215
216     /*\
217      * Raphael.type
218      [ property (string) ]
219      **
220      * Can be “SVG”, “VML” or empty, depending on browser support.
221     \*/
222     R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
223     if (R.type == "VML") {
224         var d = g.doc.createElement("div"),
225             b;
226         d.innerHTML = '<v:shape adj="1"/>';
227         b = d.firstChild;
228         b.style.behavior = "url(#default#VML)";
229         if (!(b && typeof b.adj == "object")) {
230             return R.type = E;
231         }
232         d = null;
233     }
234     /*\
235      * Raphael.svg
236      [ property (boolean) ]
237      **
238      * `true` if browser supports SVG.
239     \*/
240     /*\
241      * Raphael.vml
242      [ property (boolean) ]
243      **
244      * `true` if browser supports VML.
245     \*/
246     R.svg = !(R.vml = R.type == "VML");
247     paperproto = Paper.prototype = R.prototype;
248     /*\
249      * Paper.customAttributes
250      [ property (object) ]
251      **
252      * If you have a set of attributes that you would like to represent
253      * as a function of some number you can do it easily with custom attributes:
254      > Usage
255      | paper.customAttributes.hue = function (num) {
256      |     num = num % 1;
257      |     return {fill: "hsb(" + num + ", .75, 1)"};
258      | };
259      | // Custom attribute “hue” will change fill
260      | // to be given hue with fixed saturation and brightness.
261      | // Now you can use it like this:
262      | var c = paper.circle(10, 10, 10).attr({hue: .45});
263      | // or even like this:
264      | c.animate({hue: 1}, 1e3);
265      | 
266      | // You could also create custom attribute
267      | // with multiple parameters:
268      | paper.customAttributes.hsb = function (h, s, b) {
269      |     return {fill: "hsb(" + [h, s, b].join(",") + ")"};
270      | };
271      | c.attr({hsb: ".5 .8 1"});
272      | c.animate({hsb: "1 0 .5"}, 1e3);
273     \*/
274     paperproto.customAttributes = {};
275     R._id = 0;
276     R._oid = 0;
277     /*\
278      * Raphael.fn
279      [ property (object) ]
280      **
281      * You can add your own method to the canvas. For example if you want to draw a pie chart,
282      * you can create your own pie chart function and ship it as a Raphaël plugin. To do this
283      * you need to extend the `Raphael.fn` object. Please note that you can create your own namespaces
284      * inside the `fn` object — methods will be run in the context of canvas anyway. You should alter
285      * the `fn` object before a Raphaël instance is created, otherwise it will take no effect.
286      > Usage
287      | Raphael.fn.arrow = function (x1, y1, x2, y2, size) {
288      |     return this.path( ... );
289      | };
290      | // or create namespace
291      | Raphael.fn.mystuff = {
292      |     arrow: function () {…},
293      |     star: function () {…},
294      |     // etc…
295      | };
296      | var paper = Raphael(10, 10, 630, 480);
297      | // then use it
298      | paper.arrow(10, 10, 30, 30, 5).attr({fill: "#f00"});
299      | paper.mystuff.arrow();
300      | paper.mystuff.star();
301     \*/
302     R.fn = {};
303     /*\
304      * Raphael.is
305      [ method ]
306      **
307      * Handfull replacement for `typeof` operator.
308      > Parameters
309      - o (…) any object or primitive
310      - type (string) name of the type, i.e. “string”, “function”, “number”, etc.
311      = (boolean) is given value is of given type
312     \*/
313     R.is = function (o, type) {
314         type = lowerCase.call(type);
315         if (type == "finite") {
316             return !isnan[has](+o);
317         }
318         return  (type == "null" && o === null) ||
319                 (type == typeof o) ||
320                 (type == "object" && o === Object(o)) ||
321                 (type == "array" && Array.isArray && Array.isArray(o)) ||
322                 objectToString.call(o).slice(8, -1).toLowerCase() == type;
323     };
324     /*\
325      * Raphael.angle
326      [ method ]
327      **
328      * Returns angle between two or three points
329      > Parameters
330      - x1 (number) x coord of first point
331      - y1 (number) y coord of first point
332      - x2 (number) x coord of second point
333      - y2 (number) y coord of second point
334      - x3 (number) #optional x coord of third point
335      - y3 (number) #optional y coord of third point
336      = (number) angle in degrees.
337     \*/
338     R.angle = function (x1, y1, x2, y2, x3, y3) {
339         if (x3 == null) {
340             var x = x1 - x2,
341                 y = y1 - y2;
342             if (!x && !y) {
343                 return 0;
344             }
345             return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
346         } else {
347             return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
348         }
349     };
350     /*\
351      * Raphael.rad
352      [ method ]
353      **
354      * Transform angle to radians
355      > Parameters
356      - deg (number) angle in degrees
357      = (number) angle in radians.
358     \*/
359     R.rad = function (deg) {
360         return deg % 360 * PI / 180;
361     };
362     /*\
363      * Raphael.deg
364      [ method ]
365      **
366      * Transform angle to degrees
367      > Parameters
368      - deg (number) angle in radians
369      = (number) angle in degrees.
370     \*/
371     R.deg = function (rad) {
372         return rad * 180 / PI % 360;
373     };
374     /*\
375      * Raphael.snapTo
376      [ method ]
377      **
378      * Snaps given value to given grid.
379      > Parameters
380      - values (array|number) given array of values or step of the grid
381      - value (number) value to adjust
382      - tolerance (number) #optional tolerance for snapping. Default is `10`.
383      = (number) adjusted value.
384     \*/
385     R.snapTo = function (values, value, tolerance) {
386         tolerance = R.is(tolerance, "finite") ? tolerance : 10;
387         if (R.is(values, array)) {
388             var i = values.length;
389             while (i--) if (abs(values[i] - value) <= tolerance) {
390                 return values[i];
391             }
392         } else {
393             values = +values;
394             var rem = value % values;
395             if (rem < tolerance) {
396                 return value - rem;
397             }
398             if (rem > values - tolerance) {
399                 return value - rem + values;
400             }
401         }
402         return value;
403     };
404     
405     var createUUID = (function (uuidRegEx, uuidReplacer) {
406         return function () {
407             return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
408         };
409     })(/[xy]/g, function (c) {
410         var r = math.random() * 16 | 0,
411             v = c == "x" ? r : (r & 3 | 8);
412         return v.toString(16);
413     });
414
415     /*\
416      * Raphael.setWindow
417      [ method ]
418      **
419      * Used when you need to draw in `&lt;iframe>`. Switched window to the iframe one.
420      > Parameters
421      - newwin (window) new window object
422     \*/
423     R.setWindow = function (newwin) {
424         eve("setWindow", R, g.win, newwin);
425         g.win = newwin;
426         g.doc = g.win.document;
427         if (initWin) {
428             initWin(g.win);
429         }
430     };
431     // colour utilities
432     var toHex = function (color) {
433         if (R.vml) {
434             // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
435             var trim = /^\s+|\s+$/g;
436             var bod;
437             try {
438                 var docum = new ActiveXObject("htmlfile");
439                 docum.write("<body>");
440                 docum.close();
441                 bod = docum.body;
442             } catch(e) {
443                 bod = createPopup().document.body;
444             }
445             var range = bod.createTextRange();
446             toHex = cacher(function (color) {
447                 try {
448                     bod.style.color = Str(color).replace(trim, E);
449                     var value = range.queryCommandValue("ForeColor");
450                     value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
451                     return "#" + ("000000" + value.toString(16)).slice(-6);
452                 } catch(e) {
453                     return "none";
454                 }
455             });
456         } else {
457             var i = g.doc.createElement("i");
458             i.title = "Rapha\xebl Colour Picker";
459             i.style.display = "none";
460             g.doc.body.appendChild(i);
461             toHex = cacher(function (color) {
462                 i.style.color = color;
463                 return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
464             });
465         }
466         return toHex(color);
467     },
468     hsbtoString = function () {
469         return "hsb(" + [this.h, this.s, this.b] + ")";
470     },
471     hsltoString = function () {
472         return "hsl(" + [this.h, this.s, this.l] + ")";
473     },
474     rgbtoString = function () {
475         return this.hex;
476     },
477     prepareRGB = function (r, g, b) {
478         if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
479             b = r.b;
480             g = r.g;
481             r = r.r;
482         }
483         if (g == null && R.is(r, string)) {
484             var clr = R.getRGB(r);
485             r = clr.r;
486             g = clr.g;
487             b = clr.b;
488         }
489         if (r > 1 || g > 1 || b > 1) {
490             r /= 255;
491             g /= 255;
492             b /= 255;
493         }
494         
495         return [r, g, b];
496     },
497     packageRGB = function (r, g, b, o) {
498         r *= 255;
499         g *= 255;
500         b *= 255;
501         var rgb = {
502             r: r,
503             g: g,
504             b: b,
505             hex: R.rgb(r, g, b),
506             toString: rgbtoString
507         };
508         R.is(o, "finite") && (rgb.opacity = o);
509         return rgb;
510     };
511     /*\
512      * Raphael.hsb2rgb
513      [ method ]
514      **
515      * Converts HSB values to RGB object.
516      > Parameters
517      - h (number) hue
518      - s (number) saturation
519      - v (number) value or brightness
520      = (object) RGB object in format:
521      o {
522      o     r (number) red,
523      o     g (number) green,
524      o     b (number) blue,
525      o     hex (string) color in HTML/CSS format: #••••••
526      o }
527     \*/
528     R.hsb2rgb = function (h, s, v, o) {
529         if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
530             v = h.b;
531             s = h.s;
532             h = h.h;
533             o = h.o;
534         }
535         h *= 360;
536         var R, G, B, X, C;
537         h = (h % 360) / 60;
538         C = v * s;
539         X = C * (1 - abs(h % 2 - 1));
540         R = G = B = v - C;
541
542         h = ~~h;
543         R += [C, X, 0, 0, X, C][h];
544         G += [X, C, C, X, 0, 0][h];
545         B += [0, 0, X, C, C, X][h];
546         return packageRGB(R, G, B, o);
547     };
548     /*\
549      * Raphael.hsl2rgb
550      [ method ]
551      **
552      * Converts HSL values to RGB object.
553      > Parameters
554      - h (number) hue
555      - s (number) saturation
556      - l (number) luminosity
557      = (object) RGB object in format:
558      o {
559      o     r (number) red,
560      o     g (number) green,
561      o     b (number) blue,
562      o     hex (string) color in HTML/CSS format: #••••••
563      o }
564     \*/
565     R.hsl2rgb = function (h, s, l, o) {
566         if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
567             l = h.l;
568             s = h.s;
569             h = h.h;
570         }
571         if (h > 1 || s > 1 || l > 1) {
572             h /= 360;
573             s /= 100;
574             l /= 100;
575         }
576         h *= 360;
577         var R, G, B, X, C;
578         h = (h % 360) / 60;
579         C = 2 * s * (l < .5 ? l : 1 - l);
580         X = C * (1 - abs(h % 2 - 1));
581         R = G = B = l - C / 2;
582
583         h = ~~h;
584         R += [C, X, 0, 0, X, C][h];
585         G += [X, C, C, X, 0, 0][h];
586         B += [0, 0, X, C, C, X][h];
587         return packageRGB(R, G, B, o);
588     };
589     /*\
590      * Raphael.rgb2hsb
591      [ method ]
592      **
593      * Converts RGB values to HSB object.
594      > Parameters
595      - r (number) red
596      - g (number) green
597      - b (number) blue
598      = (object) HSB object in format:
599      o {
600      o     h (number) hue
601      o     s (number) saturation
602      o     b (number) brightness
603      o }
604     \*/
605     R.rgb2hsb = function (r, g, b) {
606         b = prepareRGB(r, g, b);
607         r = b[0];
608         g = b[1];
609         b = b[2];
610
611         var H, S, V, C;
612         V = mmax(r, g, b);
613         C = V - mmin(r, g, b);
614         H = (C == 0 ? null :
615              V == r ? (g - b) / C :
616              V == g ? (b - r) / C + 2 :
617                       (r - g) / C + 4);
618         H = (H % 6) * 60;
619         S = C == 0 ? 0 : C / V;
620         return {h: H, s: S, b: V, toString: hsbtoString};
621     };
622     /*\
623      * Raphael.rgb2hsl
624      [ method ]
625      **
626      * Converts RGB values to HSL object.
627      > Parameters
628      - r (number) red
629      - g (number) green
630      - b (number) blue
631      = (object) HSL object in format:
632      o {
633      o     h (number) hue
634      o     s (number) saturation
635      o     l (number) luminosity
636      o }
637     \*/
638     R.rgb2hsl = function (r, g, b) {
639         b = prepareRGB(r, g, b);
640         r = b[0];
641         g = b[1];
642         b = b[2];
643
644         var H, S, L, M, m, C;
645         M = mmax(r, g, b);
646         m = mmin(r, g, b);
647         C = M - m;
648         H = (C == 0 ? null :
649              M == r ? (g - b) / C :
650              M == g ? (b - r) / C + 2 :
651                       (r - g) / C + 4);
652         H = (H % 6) * 60;
653         L = (M + m) / 2;
654         S = (C == 0 ? 0 :
655              L < .5 ? C / (2 * L) :
656                       C / (2 - 2 * L));
657         return {h: H, s: S, l: L, toString: hsltoString};
658     };
659     R._path2string = function () {
660         return this.join(",").replace(p2s, "$1");
661     };
662     function repush(array, item) {
663         for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
664             return array.push(array.splice(i, 1)[0]);
665         }
666     }
667     function cacher(f, scope, postprocessor) {
668         function newf() {
669             var arg = Array.prototype.slice.call(arguments, 0),
670                 args = arg.join("\u2400"),
671                 cache = newf.cache = newf.cache || {},
672                 count = newf.count = newf.count || [];
673             if (cache[has](args)) {
674                 repush(count, args);
675                 return postprocessor ? postprocessor(cache[args]) : cache[args];
676             }
677             count.length >= 1e3 && delete cache[count.shift()];
678             count.push(args);
679             cache[args] = f[apply](scope, arg);
680             return postprocessor ? postprocessor(cache[args]) : cache[args];
681         }
682         return newf;
683     }
684
685     function preload(src, f) {
686         var img = g.doc.createElement("img");
687         img.style.cssText = "position:absolute;left:-9999em;top-9999em";
688         img.onload = function () {
689             f.call(this);
690             this.onload = null;
691             g.doc.body.removeChild(this);
692         };
693         img.onerror = function () {
694             g.doc.body.removeChild(this);
695         };
696         g.doc.body.appendChild(img);
697         img.src = src;
698     }
699
700     /*\
701      * Raphael.getRGB
702      [ method ]
703      **
704      * Parses colour string as RGB object
705      > Parameters
706      - colour (string) colour string in one of formats:
707      # <ul>
708      #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
709      #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
710      #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
711      #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
712      #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
713      #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
714      #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
715      #     <li>hsl(•••, •••, •••) — same as hsb</li>
716      #     <li>hsl(•••%, •••%, •••%) — same as hsb</li>
717      # </ul>
718      = (object) RGB object in format:
719      o {
720      o     r (number) red,
721      o     g (number) green,
722      o     b (number) blue
723      o     hex (string) color in HTML/CSS format: #••••••,
724      o     error (boolean) true if string can’t be parsed
725      o }
726     \*/
727     R.getRGB = cacher(function (colour) {
728         if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
729             return {r: -1, g: -1, b: -1, hex: "none", error: 1};
730         }
731         if (colour == "none") {
732             return {r: -1, g: -1, b: -1, hex: "none"};
733         }
734         !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
735         var res,
736             red,
737             green,
738             blue,
739             opacity,
740             t,
741             values,
742             rgb = colour.match(colourRegExp);
743         if (rgb) {
744             if (rgb[2]) {
745                 blue = toInt(rgb[2].substring(5), 16);
746                 green = toInt(rgb[2].substring(3, 5), 16);
747                 red = toInt(rgb[2].substring(1, 3), 16);
748             }
749             if (rgb[3]) {
750                 blue = toInt((t = rgb[3].charAt(3)) + t, 16);
751                 green = toInt((t = rgb[3].charAt(2)) + t, 16);
752                 red = toInt((t = rgb[3].charAt(1)) + t, 16);
753             }
754             if (rgb[4]) {
755                 values = rgb[4].split(commaSpaces);
756                 red = toFloat(values[0]);
757                 values[0].slice(-1) == "%" && (red *= 2.55);
758                 green = toFloat(values[1]);
759                 values[1].slice(-1) == "%" && (green *= 2.55);
760                 blue = toFloat(values[2]);
761                 values[2].slice(-1) == "%" && (blue *= 2.55);
762                 rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
763                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
764             }
765             if (rgb[5]) {
766                 values = rgb[5].split(commaSpaces);
767                 red = toFloat(values[0]);
768                 values[0].slice(-1) == "%" && (red *= 2.55);
769                 green = toFloat(values[1]);
770                 values[1].slice(-1) == "%" && (green *= 2.55);
771                 blue = toFloat(values[2]);
772                 values[2].slice(-1) == "%" && (blue *= 2.55);
773                 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
774                 rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
775                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
776                 return R.hsb2rgb(red, green, blue, opacity);
777             }
778             if (rgb[6]) {
779                 values = rgb[6].split(commaSpaces);
780                 red = toFloat(values[0]);
781                 values[0].slice(-1) == "%" && (red *= 2.55);
782                 green = toFloat(values[1]);
783                 values[1].slice(-1) == "%" && (green *= 2.55);
784                 blue = toFloat(values[2]);
785                 values[2].slice(-1) == "%" && (blue *= 2.55);
786                 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
787                 rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
788                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
789                 return R.hsl2rgb(red, green, blue, opacity);
790             }
791             rgb = {r: red, g: green, b: blue};
792             rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
793             R.is(opacity, "finite") && (rgb.opacity = opacity);
794             return rgb;
795         }
796         return {r: -1, g: -1, b: -1, hex: "none", error: 1};
797     }, R);
798     /*\
799      * Raphael.hsb
800      [ method ]
801      **
802      * Converts HSB values to hex representation of the colour.
803      > Parameters
804      - h (number) hue
805      - s (number) saturation
806      - b (number) value or brightness
807      = (string) hex representation of the colour.
808     \*/
809     R.hsb = cacher(function (h, s, b) {
810         return R.hsb2rgb(h, s, b).hex;
811     });
812     /*\
813      * Raphael.hsl
814      [ method ]
815      **
816      * Converts HSL values to hex representation of the colour.
817      > Parameters
818      - h (number) hue
819      - s (number) saturation
820      - l (number) luminosity
821      = (string) hex representation of the colour.
822     \*/
823     R.hsl = cacher(function (h, s, l) {
824         return R.hsl2rgb(h, s, l).hex;
825     });
826     /*\
827      * Raphael.rgb
828      [ method ]
829      **
830      * Converts RGB values to hex representation of the colour.
831      > Parameters
832      - r (number) red
833      - g (number) green
834      - b (number) blue
835      = (string) hex representation of the colour.
836     \*/
837     R.rgb = cacher(function (r, g, b) {
838         return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
839     });
840     /*\
841      * Raphael.getColor
842      [ method ]
843      **
844      * On each call returns next colour in the spectrum. To reset it back to red call @Raphael.getColor.reset
845      > Parameters
846      - value (number) #optional brightness, default is `0.75`
847      = (string) hex representation of the colour.
848     \*/
849     R.getColor = function (value) {
850         var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
851             rgb = this.hsb2rgb(start.h, start.s, start.b);
852         start.h += .075;
853         if (start.h > 1) {
854             start.h = 0;
855             start.s -= .2;
856             start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
857         }
858         return rgb.hex;
859     };
860     /*\
861      * Raphael.getColor.reset
862      [ method ]
863      **
864      * Resets spectrum position for @Raphael.getColor back to red.
865     \*/
866     R.getColor.reset = function () {
867         delete this.start;
868     };
869
870     /*\
871      * Raphael.parsePathString
872      [ method ]
873      **
874      * Utility method
875      **
876      * Parses given path string into an array of arrays of path segments.
877      > Parameters
878      - pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
879      = (array) array of segments.
880     \*/
881     R.parsePathString = cacher(function (pathString) {
882         if (!pathString) {
883             return null;
884         }
885         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
886             data = [];
887         if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
888             data = pathClone(pathString);
889         }
890         if (!data.length) {
891             Str(pathString).replace(pathCommand, function (a, b, c) {
892                 var params = [],
893                     name = lowerCase.call(b);
894                 c.replace(pathValues, function (a, b) {
895                     b && params.push(+b);
896                 });
897                 if (name == "m" && params.length > 2) {
898                     data.push([b][concat](params.splice(0, 2)));
899                     name = "l";
900                     b = b == "m" ? "l" : "L";
901                 }
902                 while (params.length >= paramCounts[name]) {
903                     data.push([b][concat](params.splice(0, paramCounts[name])));
904                     if (!paramCounts[name]) {
905                         break;
906                     }
907                 }
908             });
909         }
910         data.toString = R._path2string;
911         return data;
912     });
913     /*\
914      * Raphael.parseTransformString
915      [ method ]
916      **
917      * Utility method
918      **
919      * Parses given path string into an array of transformations.
920      > Parameters
921      - TString (string|array) transform string or array of transformations (in the last case it will be returned straight away)
922      = (array) array of transformations.
923     \*/
924     R.parseTransformString = cacher(function (TString) {
925         if (!TString) {
926             return null;
927         }
928         var paramCounts = {r: 3, s: 4, t: 2, m: 6},
929             data = [];
930         if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
931             data = pathClone(TString);
932         }
933         if (!data.length) {
934             Str(TString).replace(tCommand, function (a, b, c) {
935                 var params = [],
936                     name = lowerCase.call(b);
937                 c.replace(pathValues, function (a, b) {
938                     b && params.push(+b);
939                 });
940                 data.push([name][concat](params));
941             });
942         }
943         data.toString = R._path2string;
944         return data;
945     });
946     /*\
947      * Raphael.findDotsAtSegment
948      [ method ]
949      **
950      * Utility method
951      **
952      * Find dot coordinates on the given cubic bezier curve at the given t.
953      > Parameters
954      - p1x (number) x of the first point of the curve
955      - p1y (number) y of the first point of the curve
956      - c1x (number) x of the first anchor of the curve
957      - c1y (number) y of the first anchor of the curve
958      - c2x (number) x of the second anchor of the curve
959      - c2y (number) y of the second anchor of the curve
960      - p2x (number) x of the second point of the curve
961      - p2y (number) y of the second point of the curve
962      - t (number) position on the curve (0..1)
963      = (object) point information in format:
964      o {
965      o     x: (number) x coordinate of the point
966      o     y: (number) y coordinate of the point
967      o     m: {
968      o         x: (number) x coordinate of the left anchor
969      o         y: (number) y coordinate of the left anchor
970      o     }
971      o     n: {
972      o         x: (number) x coordinate of the right anchor
973      o         y: (number) y coordinate of the right anchor
974      o     }
975      o     start: {
976      o         x: (number) x coordinate of the start of the curve
977      o         y: (number) y coordinate of the start of the curve
978      o     }
979      o     end: {
980      o         x: (number) x coordinate of the end of the curve
981      o         y: (number) y coordinate of the end of the curve
982      o     }
983      o     alpha: (number) angle of the curve derivative at the point
984      o }
985     \*/
986     R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
987         var t1 = 1 - t,
988             x = pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
989             y = pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y,
990             mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x),
991             my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y),
992             nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x),
993             ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y),
994             ax = (1 - t) * p1x + t * c1x,
995             ay = (1 - t) * p1y + t * c1y,
996             cx = (1 - t) * c2x + t * p2x,
997             cy = (1 - t) * c2y + t * p2y,
998             alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
999         (mx > nx || my < ny) && (alpha += 180);
1000         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};
1001     };
1002     var pathDimensions = cacher(function (path) {
1003         if (!path) {
1004             return {x: 0, y: 0, width: 0, height: 0};
1005         }
1006         path = path2curve(path);
1007         var x = 0, 
1008             y = 0,
1009             X = [],
1010             Y = [],
1011             p;
1012         for (var i = 0, ii = path.length; i < ii; i++) {
1013             p = path[i];
1014             if (p[0] == "M") {
1015                 x = p[1];
1016                 y = p[2];
1017                 X.push(x);
1018                 Y.push(y);
1019             } else {
1020                 var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
1021                 X = X[concat](dim.min.x, dim.max.x);
1022                 Y = Y[concat](dim.min.y, dim.max.y);
1023                 x = p[5];
1024                 y = p[6];
1025             }
1026         }
1027         var xmin = mmin[apply](0, X),
1028             ymin = mmin[apply](0, Y);
1029         return {
1030             x: xmin,
1031             y: ymin,
1032             width: mmax[apply](0, X) - xmin,
1033             height: mmax[apply](0, Y) - ymin
1034         };
1035     }, null, function (o) {
1036         return {
1037             x: o.x,
1038             y: o.y,
1039             width: o.width,
1040             height: o.height
1041         };
1042     }),
1043         pathClone = function (pathArray) {
1044             var res = [];
1045             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
1046                 pathArray = R.parsePathString(pathArray);
1047             }
1048             for (var i = 0, ii = pathArray.length; i < ii; i++) {
1049                 res[i] = [];
1050                 for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
1051                     res[i][j] = pathArray[i][j];
1052                 }
1053             }
1054             res.toString = R._path2string;
1055             return res;
1056         },
1057         pathToRelative = cacher(function (pathArray) {
1058             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
1059                 pathArray = R.parsePathString(pathArray);
1060             }
1061             var res = [],
1062                 x = 0,
1063                 y = 0,
1064                 mx = 0,
1065                 my = 0,
1066                 start = 0;
1067             if (pathArray[0][0] == "M") {
1068                 x = pathArray[0][1];
1069                 y = pathArray[0][2];
1070                 mx = x;
1071                 my = y;
1072                 start++;
1073                 res.push(["M", x, y]);
1074             }
1075             for (var i = start, ii = pathArray.length; i < ii; i++) {
1076                 var r = res[i] = [],
1077                     pa = pathArray[i];
1078                 if (pa[0] != lowerCase.call(pa[0])) {
1079                     r[0] = lowerCase.call(pa[0]);
1080                     switch (r[0]) {
1081                         case "a":
1082                             r[1] = pa[1];
1083                             r[2] = pa[2];
1084                             r[3] = pa[3];
1085                             r[4] = pa[4];
1086                             r[5] = pa[5];
1087                             r[6] = +(pa[6] - x).toFixed(3);
1088                             r[7] = +(pa[7] - y).toFixed(3);
1089                             break;
1090                         case "v":
1091                             r[1] = +(pa[1] - y).toFixed(3);
1092                             break;
1093                         case "m":
1094                             mx = pa[1];
1095                             my = pa[2];
1096                         default:
1097                             for (var j = 1, jj = pa.length; j < jj; j++) {
1098                                 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
1099                             }
1100                     }
1101                 } else {
1102                     r = res[i] = [];
1103                     if (pa[0] == "m") {
1104                         mx = pa[1] + x;
1105                         my = pa[2] + y;
1106                     }
1107                     for (var k = 0, kk = pa.length; k < kk; k++) {
1108                         res[i][k] = pa[k];
1109                     }
1110                 }
1111                 var len = res[i].length;
1112                 switch (res[i][0]) {
1113                     case "z":
1114                         x = mx;
1115                         y = my;
1116                         break;
1117                     case "h":
1118                         x += +res[i][len - 1];
1119                         break;
1120                     case "v":
1121                         y += +res[i][len - 1];
1122                         break;
1123                     default:
1124                         x += +res[i][len - 2];
1125                         y += +res[i][len - 1];
1126                 }
1127             }
1128             res.toString = R._path2string;
1129             return res;
1130         }, 0, pathClone),
1131         pathToAbsolute = cacher(function (pathArray) {
1132             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
1133                 pathArray = R.parsePathString(pathArray);
1134             }
1135             var res = [],
1136                 x = 0,
1137                 y = 0,
1138                 mx = 0,
1139                 my = 0,
1140                 start = 0;
1141             if (pathArray[0][0] == "M") {
1142                 x = +pathArray[0][1];
1143                 y = +pathArray[0][2];
1144                 mx = x;
1145                 my = y;
1146                 start++;
1147                 res[0] = ["M", x, y];
1148             }
1149             for (var i = start, ii = pathArray.length; i < ii; i++) {
1150                 var r = res[i] = [],
1151                     pa = pathArray[i];
1152                 if (pa[0] != upperCase.call(pa[0])) {
1153                     r[0] = upperCase.call(pa[0]);
1154                     switch (r[0]) {
1155                         case "A":
1156                             r[1] = pa[1];
1157                             r[2] = pa[2];
1158                             r[3] = pa[3];
1159                             r[4] = pa[4];
1160                             r[5] = pa[5];
1161                             r[6] = +(pa[6] + x);
1162                             r[7] = +(pa[7] + y);
1163                             break;
1164                         case "V":
1165                             r[1] = +pa[1] + y;
1166                             break;
1167                         case "H":
1168                             r[1] = +pa[1] + x;
1169                             break;
1170                         case "M":
1171                             mx = +pa[1] + x;
1172                             my = +pa[2] + y;
1173                         default:
1174                             for (var j = 1, jj = pa.length; j < jj; j++) {
1175                                 r[j] = +pa[j] + ((j % 2) ? x : y);
1176                             }
1177                     }
1178                 } else {
1179                     for (var k = 0, kk = pa.length; k < kk; k++) {
1180                         res[i][k] = pa[k];
1181                     }
1182                 }
1183                 switch (r[0]) {
1184                     case "Z":
1185                         x = mx;
1186                         y = my;
1187                         break;
1188                     case "H":
1189                         x = r[1];
1190                         break;
1191                     case "V":
1192                         y = r[1];
1193                         break;
1194                     case "M":
1195                         mx = res[i][res[i].length - 2];
1196                         my = res[i][res[i].length - 1];
1197                     default:
1198                         x = res[i][res[i].length - 2];
1199                         y = res[i][res[i].length - 1];
1200                 }
1201             }
1202             res.toString = R._path2string;
1203             return res;
1204         }, null, pathClone),
1205         l2c = function (x1, y1, x2, y2) {
1206             return [x1, y1, x2, y2, x2, y2];
1207         },
1208         q2c = function (x1, y1, ax, ay, x2, y2) {
1209             var _13 = 1 / 3,
1210                 _23 = 2 / 3;
1211             return [
1212                     _13 * x1 + _23 * ax,
1213                     _13 * y1 + _23 * ay,
1214                     _13 * x2 + _23 * ax,
1215                     _13 * y2 + _23 * ay,
1216                     x2,
1217                     y2
1218                 ];
1219         },
1220         a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
1221             // for more information of where this math came from visit:
1222             // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1223             var _120 = PI * 120 / 180,
1224                 rad = PI / 180 * (+angle || 0),
1225                 res = [],
1226                 xy,
1227                 rotate = cacher(function (x, y, rad) {
1228                     var X = x * math.cos(rad) - y * math.sin(rad),
1229                         Y = x * math.sin(rad) + y * math.cos(rad);
1230                     return {x: X, y: Y};
1231                 });
1232             if (!recursive) {
1233                 xy = rotate(x1, y1, -rad);
1234                 x1 = xy.x;
1235                 y1 = xy.y;
1236                 xy = rotate(x2, y2, -rad);
1237                 x2 = xy.x;
1238                 y2 = xy.y;
1239                 var cos = math.cos(PI / 180 * angle),
1240                     sin = math.sin(PI / 180 * angle),
1241                     x = (x1 - x2) / 2,
1242                     y = (y1 - y2) / 2;
1243                 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
1244                 if (h > 1) {
1245                     h = math.sqrt(h);
1246                     rx = h * rx;
1247                     ry = h * ry;
1248                 }
1249                 var rx2 = rx * rx,
1250                     ry2 = ry * ry,
1251                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
1252                         math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
1253                     cx = k * rx * y / ry + (x1 + x2) / 2,
1254                     cy = k * -ry * x / rx + (y1 + y2) / 2,
1255                     f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
1256                     f2 = math.asin(((y2 - cy) / ry).toFixed(9));
1257
1258                 f1 = x1 < cx ? PI - f1 : f1;
1259                 f2 = x2 < cx ? PI - f2 : f2;
1260                 f1 < 0 && (f1 = PI * 2 + f1);
1261                 f2 < 0 && (f2 = PI * 2 + f2);
1262                 if (sweep_flag && f1 > f2) {
1263                     f1 = f1 - PI * 2;
1264                 }
1265                 if (!sweep_flag && f2 > f1) {
1266                     f2 = f2 - PI * 2;
1267                 }
1268             } else {
1269                 f1 = recursive[0];
1270                 f2 = recursive[1];
1271                 cx = recursive[2];
1272                 cy = recursive[3];
1273             }
1274             var df = f2 - f1;
1275             if (abs(df) > _120) {
1276                 var f2old = f2,
1277                     x2old = x2,
1278                     y2old = y2;
1279                 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
1280                 x2 = cx + rx * math.cos(f2);
1281                 y2 = cy + ry * math.sin(f2);
1282                 res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
1283             }
1284             df = f2 - f1;
1285             var c1 = math.cos(f1),
1286                 s1 = math.sin(f1),
1287                 c2 = math.cos(f2),
1288                 s2 = math.sin(f2),
1289                 t = math.tan(df / 4),
1290                 hx = 4 / 3 * rx * t,
1291                 hy = 4 / 3 * ry * t,
1292                 m1 = [x1, y1],
1293                 m2 = [x1 + hx * s1, y1 - hy * c1],
1294                 m3 = [x2 + hx * s2, y2 - hy * c2],
1295                 m4 = [x2, y2];
1296             m2[0] = 2 * m1[0] - m2[0];
1297             m2[1] = 2 * m1[1] - m2[1];
1298             if (recursive) {
1299                 return [m2, m3, m4][concat](res);
1300             } else {
1301                 res = [m2, m3, m4][concat](res).join().split(",");
1302                 var newres = [];
1303                 for (var i = 0, ii = res.length; i < ii; i++) {
1304                     newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
1305                 }
1306                 return newres;
1307             }
1308         },
1309         findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
1310             var t1 = 1 - t;
1311             return {
1312                 x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
1313                 y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
1314             };
1315         },
1316         curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
1317             var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
1318                 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
1319                 c = p1x - c1x,
1320                 t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
1321                 t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
1322                 y = [p1y, p2y],
1323                 x = [p1x, p2x],
1324                 dot;
1325             abs(t1) > "1e12" && (t1 = .5);
1326             abs(t2) > "1e12" && (t2 = .5);
1327             if (t1 > 0 && t1 < 1) {
1328                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
1329                 x.push(dot.x);
1330                 y.push(dot.y);
1331             }
1332             if (t2 > 0 && t2 < 1) {
1333                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
1334                 x.push(dot.x);
1335                 y.push(dot.y);
1336             }
1337             a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
1338             b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
1339             c = p1y - c1y;
1340             t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
1341             t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
1342             abs(t1) > "1e12" && (t1 = .5);
1343             abs(t2) > "1e12" && (t2 = .5);
1344             if (t1 > 0 && t1 < 1) {
1345                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
1346                 x.push(dot.x);
1347                 y.push(dot.y);
1348             }
1349             if (t2 > 0 && t2 < 1) {
1350                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
1351                 x.push(dot.x);
1352                 y.push(dot.y);
1353             }
1354             return {
1355                 min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
1356                 max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
1357             };
1358         }),
1359         path2curve = cacher(function (path, path2) {
1360             var p = pathToAbsolute(path),
1361                 p2 = path2 && pathToAbsolute(path2),
1362                 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
1363                 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
1364                 processPath = function (path, d) {
1365                     var nx, ny;
1366                     if (!path) {
1367                         return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
1368                     }
1369                     !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
1370                     switch (path[0]) {
1371                         case "M":
1372                             d.X = path[1];
1373                             d.Y = path[2];
1374                             break;
1375                         case "A":
1376                             path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
1377                             break;
1378                         case "S":
1379                             nx = d.x + (d.x - (d.bx || d.x));
1380                             ny = d.y + (d.y - (d.by || d.y));
1381                             path = ["C", nx, ny][concat](path.slice(1));
1382                             break;
1383                         case "T":
1384                             d.qx = d.x + (d.x - (d.qx || d.x));
1385                             d.qy = d.y + (d.y - (d.qy || d.y));
1386                             path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
1387                             break;
1388                         case "Q":
1389                             d.qx = path[1];
1390                             d.qy = path[2];
1391                             path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
1392                             break;
1393                         case "L":
1394                             path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
1395                             break;
1396                         case "H":
1397                             path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
1398                             break;
1399                         case "V":
1400                             path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
1401                             break;
1402                         case "Z":
1403                             path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
1404                             break;
1405                     }
1406                     return path;
1407                 },
1408                 fixArc = function (pp, i) {
1409                     if (pp[i].length > 7) {
1410                         pp[i].shift();
1411                         var pi = pp[i];
1412                         while (pi.length) {
1413                             pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
1414                         }
1415                         pp.splice(i, 1);
1416                         ii = mmax(p.length, p2 && p2.length || 0);
1417                     }
1418                 },
1419                 fixM = function (path1, path2, a1, a2, i) {
1420                     if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
1421                         path2.splice(i, 0, ["M", a2.x, a2.y]);
1422                         a1.bx = 0;
1423                         a1.by = 0;
1424                         a1.x = path1[i][1];
1425                         a1.y = path1[i][2];
1426                         ii = mmax(p.length, p2 && p2.length || 0);
1427                     }
1428                 };
1429             for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
1430                 p[i] = processPath(p[i], attrs);
1431                 fixArc(p, i);
1432                 p2 && (p2[i] = processPath(p2[i], attrs2));
1433                 p2 && fixArc(p2, i);
1434                 fixM(p, p2, attrs, attrs2, i);
1435                 fixM(p2, p, attrs2, attrs, i);
1436                 var seg = p[i],
1437                     seg2 = p2 && p2[i],
1438                     seglen = seg.length,
1439                     seg2len = p2 && seg2.length;
1440                 attrs.x = seg[seglen - 2];
1441                 attrs.y = seg[seglen - 1];
1442                 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
1443                 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
1444                 attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
1445                 attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
1446                 attrs2.x = p2 && seg2[seg2len - 2];
1447                 attrs2.y = p2 && seg2[seg2len - 1];
1448             }
1449             return p2 ? [p, p2] : p;
1450         }, null, pathClone),
1451         parseDots = cacher(function (gradient) {
1452             var dots = [];
1453             for (var i = 0, ii = gradient.length; i < ii; i++) {
1454                 var dot = {},
1455                     par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
1456                 dot.color = R.getRGB(par[1]);
1457                 if (dot.color.error) {
1458                     return null;
1459                 }
1460                 dot.color = dot.color.hex;
1461                 par[2] && (dot.offset = par[2] + "%");
1462                 dots.push(dot);
1463             }
1464             for (i = 1, ii = dots.length - 1; i < ii; i++) {
1465                 if (!dots[i].offset) {
1466                     var start = toFloat(dots[i - 1].offset || 0),
1467                         end = 0;
1468                     for (var j = i + 1; j < ii; j++) {
1469                         if (dots[j].offset) {
1470                             end = dots[j].offset;
1471                             break;
1472                         }
1473                     }
1474                     if (!end) {
1475                         end = 100;
1476                         j = ii;
1477                     }
1478                     end = toFloat(end);
1479                     var d = (end - start) / (j - i + 1);
1480                     for (; i < j; i++) {
1481                         start += d;
1482                         dots[i].offset = start + "%";
1483                     }
1484                 }
1485             }
1486             return dots;
1487         }),
1488         getContainer = function (x, y, w, h) {
1489             var container;
1490             container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
1491             if (container == null) {
1492                 return;
1493             }
1494             if (container.tagName) {
1495                 if (y == null) {
1496                     return {
1497                         container: container,
1498                         width: container.style.pixelWidth || container.offsetWidth,
1499                         height: container.style.pixelHeight || container.offsetHeight
1500                     };
1501                 } else {
1502                     return {container: container, width: y, height: w};
1503                 }
1504             }
1505             return {container: 1, x: x, y: y, width: w, height: h};
1506         },
1507         plugins = function (con, add) {
1508             var that = this;
1509             for (var prop in add) {
1510                 if (add[has](prop) && !(prop in con)) {
1511                     switch (typeof add[prop]) {
1512                         case "function":
1513                             (function (f) {
1514                                 con[prop] = con === that ? f : function () { return f[apply](that, arguments); };
1515                             })(add[prop]);
1516                         break;
1517                         case "object":
1518                             con[prop] = con[prop] || {};
1519                             plugins.call(this, con[prop], add[prop]);
1520                         break;
1521                         default:
1522                             con[prop] = add[prop];
1523                         break;
1524                     }
1525                 }
1526             }
1527         },
1528         tear = function (el, paper) {
1529             el == paper.top && (paper.top = el.prev);
1530             el == paper.bottom && (paper.bottom = el.next);
1531             el.next && (el.next.prev = el.prev);
1532             el.prev && (el.prev.next = el.next);
1533         },
1534         tofront = function (el, paper) {
1535             if (paper.top === el) {
1536                 return;
1537             }
1538             tear(el, paper);
1539             el.next = null;
1540             el.prev = paper.top;
1541             paper.top.next = el;
1542             paper.top = el;
1543         },
1544         toback = function (el, paper) {
1545             if (paper.bottom === el) {
1546                 return;
1547             }
1548             tear(el, paper);
1549             el.next = paper.bottom;
1550             el.prev = null;
1551             paper.bottom.prev = el;
1552             paper.bottom = el;
1553         },
1554         insertafter = function (el, el2, paper) {
1555             tear(el, paper);
1556             el2 == paper.top && (paper.top = el);
1557             el2.next && (el2.next.prev = el);
1558             el.next = el2.next;
1559             el.prev = el2;
1560             el2.next = el;
1561         },
1562         insertbefore = function (el, el2, paper) {
1563             tear(el, paper);
1564             el2 == paper.bottom && (paper.bottom = el);
1565             el2.prev && (el2.prev.next = el);
1566             el.prev = el2.prev;
1567             el2.prev = el;
1568             el.next = el2;
1569         },
1570         removed = function (methodname) {
1571             return function () {
1572                 throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
1573             };
1574         },
1575         extractTransform = function (el, tstr) {
1576             if (tstr == null) {
1577                 return el._.transform;
1578             }
1579             tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
1580             var tdata = R.parseTransformString(tstr),
1581                 deg = 0,
1582                 dx = 0,
1583                 dy = 0,
1584                 sx = 1,
1585                 sy = 1,
1586                 _ = el._,
1587                 m = new Matrix;
1588             _.transform = tdata || [];
1589             if (tdata) {
1590                 for (var i = 0, ii = tdata.length; i < ii; i++) {
1591                     var t = tdata[i],
1592                         tlen = t.length,
1593                         bb;
1594                     t[0] = Str(t[0]).toLowerCase();
1595                     if (t[0] == "t" && tlen == 3) {
1596                         m.translate(t[1], t[2]);
1597                     } else if (t[0] == "r") {
1598                         if (tlen == 2) {
1599                             bb = bb || el.getBBox(1);
1600                             m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
1601                             deg += t[1];
1602                         } else if (tlen == 4) {
1603                             m.rotate(t[1], t[2], t[3]);
1604                             deg += t[1];
1605                         }
1606                     } else if (t[0] == "s") {
1607                         if (tlen == 2 || tlen == 3) {
1608                             bb = bb || el.getBBox(1);
1609                             m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
1610                             sx *= t[1];
1611                             sy *= t[tlen - 1];
1612                         } else if (tlen == 5) {
1613                             m.scale(t[1], t[2], t[3], t[4]);
1614                             sx *= t[1];
1615                             sy *= t[2];
1616                         }
1617                     } else if (t[0] == "m" && tlen == 7) {
1618                         m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
1619                     }
1620                     _.dirtyT = 1;
1621                     el.matrix = m;
1622                 }
1623             }
1624
1625             el.matrix = m;
1626
1627             _.sx = sx;
1628             _.sy = sy;
1629             _.deg = deg;
1630             _.dx = dx = m.e;
1631             _.dy = dy = m.f;
1632
1633             if (sx == 1 && sy == 1 && !deg && _.bbox) {
1634                 _.bbox.x += +dx;
1635                 _.bbox.y += +dy;
1636             } else {
1637                 _.dirtyT = 1;
1638             }
1639         },
1640         getEmpty = function (item) {
1641             switch (item[0]) {
1642                 case "t": return ["t", 0, 0];
1643                 case "m": return ["m", 1, 0, 0, 1, 0, 0];
1644                 case "r": if (item.length == 4) {
1645                     return ["r", 0, item[2], item[3]];
1646                 } else {
1647                     return ["r", 0];
1648                 }
1649                 case "s": if (item.length == 5) {
1650                     return ["s", 1, 1, item[3], item[4]];
1651                 } else if (item.length == 3) {
1652                     return ["s", 1, 1];
1653                 } else {
1654                     return ["s", 1];
1655                 }
1656             }
1657         },
1658         equaliseTransform = function (t1, t2) {
1659             t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
1660             t1 = R.parseTransformString(t1) || [];
1661             t2 = R.parseTransformString(t2) || [];
1662             var maxlength = mmax(t1.length, t2.length),
1663                 from = [],
1664                 to = [],
1665                 i = 0, j, jj,
1666                 tt1, tt2;
1667             for (; i < maxlength; i++) {
1668                 tt1 = t1[i] || getEmpty(t2[i]);
1669                 tt2 = t2[i] || getEmpty(tt1);
1670                 if ((tt1[0] != tt2[0]) ||
1671                     (tt1[0] == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
1672                     (tt1[0] == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
1673                     ) {
1674                     return;
1675                 }
1676                 from[i] = [];
1677                 to[i] = [];
1678                 for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
1679                     j in tt1 && (from[i][j] = tt1[j]);
1680                     j in tt2 && (to[i][j] = tt2[j]);
1681                 }
1682             }
1683             return {
1684                 from: from,
1685                 to: to
1686             };
1687         };
1688     /*\
1689      * Raphael.pathToRelative
1690      [ method ]
1691      **
1692      * Utility method
1693      **
1694      * Converts path to relative form
1695      > Parameters
1696      - pathString (string|array) path string or array of segments
1697      = (array) array of segments.
1698     \*/
1699     R.pathToRelative = pathToRelative;
1700     /*\
1701      * Raphael.path2curve
1702      [ method ]
1703      **
1704      * Utility method
1705      **
1706      * Converts path to a new path where all segments are cubic bezier curves.
1707      > Parameters
1708      - pathString (string|array) path string or array of segments
1709      = (array) array of segments.
1710     \*/
1711     R.path2curve = path2curve;
1712     /*\
1713      * Raphael.matrix
1714      [ method ]
1715      **
1716      * Utility method
1717      **
1718      * Returns matrix based on given parameters.
1719      > Parameters
1720      - a (number)
1721      - b (number)
1722      - c (number)
1723      - d (number)
1724      - e (number)
1725      - f (number)
1726      = (object) @Matrix
1727     \*/
1728     R.matrix = function (a, b, c, d, e, f) {
1729         return new Matrix(a, b, c, d, e, f);
1730     };
1731     function Matrix(a, b, c, d, e, f) {
1732         if (a != null) {
1733             this.a = +a;
1734             this.b = +b;
1735             this.c = +c;
1736             this.d = +d;
1737             this.e = +e;
1738             this.f = +f;
1739         } else {
1740             this.a = 1;
1741             this.b = 0;
1742             this.c = 0;
1743             this.d = 1;
1744             this.e = 0;
1745             this.f = 0;
1746         }
1747     }
1748     (function (matrixproto) {
1749         matrixproto.add = function (a, b, c, d, e, f) {
1750             var out = [[], [], []],
1751                 m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
1752                 matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
1753                 x, y, z, res;
1754
1755             if (a && a instanceof Matrix) {
1756                 matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
1757             }
1758
1759             for (x = 0; x < 3; x++) {
1760                 for (y = 0; y < 3; y++) {
1761                     res = 0;
1762                     for (z = 0; z < 3; z++) {
1763                         res += m[x][z] * matrix[z][y];
1764                     }
1765                     out[x][y] = res;
1766                 }
1767             }
1768             this.a = out[0][0];
1769             this.b = out[1][0];
1770             this.c = out[0][1];
1771             this.d = out[1][1];
1772             this.e = out[0][2];
1773             this.f = out[1][2];
1774         };
1775         matrixproto.invert = function () {
1776             var me = this,
1777                 x = me.a * me.d - me.b * me.c;
1778             return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
1779         };
1780         matrixproto.clone = function () {
1781             return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
1782         };
1783         matrixproto.translate = function (x, y) {
1784             this.add(1, 0, 0, 1, x, y);
1785         };
1786         matrixproto.scale = function (x, y, cx, cy) {
1787             y == null && (y = x);
1788             this.add(1, 0, 0, 1, cx, cy);
1789             this.add(x, 0, 0, y, 0, 0);
1790             this.add(1, 0, 0, 1, -cx, -cy);
1791         };
1792         matrixproto.rotate = function (a, x, y) {
1793             a = R.rad(a);
1794             var cos = +math.cos(a).toFixed(9),
1795                 sin = +math.sin(a).toFixed(9);
1796             this.add(cos, sin, -sin, cos, x, y);
1797             this.add(1, 0, 0, 1, -x, -y);
1798         };
1799         matrixproto.x = function (x, y) {
1800             return x * this.a + y * this.c + this.e;
1801         };
1802         matrixproto.y = function (x, y) {
1803             return x * this.b + y * this.d + this.f;
1804         };
1805         matrixproto.get = function (i) {
1806             return +this[Str.fromCharCode(97 + i)].toFixed(4);
1807         };
1808         matrixproto.toString = function () {
1809             return R.svg ?
1810                 "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
1811                 [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
1812         };
1813         matrixproto.toFilter = function () {
1814             return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
1815                 ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
1816                 ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
1817         };
1818         matrixproto.offset = function () {
1819             return [this.e.toFixed(4), this.f.toFixed(4)];
1820         };
1821         function norm(a) {
1822             return a[0] * a[0] + a[1] * a[1];
1823         }
1824         function normalize(a) {
1825             var mag = math.sqrt(norm(a));
1826             a[0] /= mag;
1827             a[1] /= mag;
1828         }
1829         matrixproto.split = function () {
1830             var out = {};
1831             // translation
1832             out.dx = this.e;
1833             out.dy = this.f;
1834             this.e = this.f = 0;
1835
1836             // scale and shear
1837             var row = [[this.a, this.c], [this.b, this.d]];
1838             out.scalex = math.sqrt(norm(row[0]));
1839             normalize(row[0]);
1840
1841             out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
1842             row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
1843
1844             out.scaley = math.sqrt(norm(row[1]));
1845             normalize(row[1]);
1846             out.shear /= out.scaley;
1847
1848             // rotation
1849             out.rotate = R.deg(math.asin(-row[0][1]));
1850
1851             out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
1852
1853             return out;
1854         };
1855         matrixproto.toTransformString = function () {
1856             var s = this.split();
1857             if (s.isSimple) {
1858                 return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0];
1859             } else {
1860                 return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
1861             }
1862         };
1863     })(Matrix.prototype);
1864     
1865     // SVG
1866     if (R.svg) {
1867         var xlink = "http://www.w3.org/1999/xlink",
1868             markers = {
1869                 block: "M5,0 0,2.5 5,5z",
1870                 classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
1871                 diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
1872                 open: "M6,1 1,3.5 6,6",
1873                 oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
1874             },
1875             markerCounter = {};
1876         R.toString = function () {
1877             return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
1878         };
1879         var $ = function (el, attr) {
1880             if (attr) {
1881                 if (typeof el == "string") {
1882                     el = $(el);
1883                 }
1884                 for (var key in attr) if (attr[has](key)) {
1885                     if (key.substring(0, 6) == "xlink:") {
1886                         el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
1887                     } else {
1888                         el[setAttribute](key, Str(attr[key]));
1889                     }
1890                 }
1891             } else {
1892                 el = g.doc.createElementNS("http://www.w3.org/2000/svg", el);
1893                 el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
1894             }
1895             return el;
1896         },
1897         thePath = function (pathString, SVG) {
1898             var el = $("path");
1899             SVG.canvas && SVG.canvas.appendChild(el);
1900             var p = new Element(el, SVG);
1901             p.type = "path";
1902             setFillAndStroke(p, {fill: "none", stroke: "#000", path: pathString});
1903             return p;
1904         },
1905         gradients = {},
1906         rgGrad = /^url\(#(.*)\)$/,
1907         removeGradientFill = function (node, paper) {
1908             var oid = node.getAttribute(fillString);
1909             oid = oid && oid.match(rgGrad);
1910             if (oid && !--gradients[oid[1]]) {
1911                 delete gradients[oid[1]];
1912                 paper.defs.removeChild(g.doc.getElementById(oid[1]));
1913             }
1914         },
1915         addGradientFill = function (element, gradient) {
1916             var type = "linear",
1917                 id = element.id + gradient,
1918                 fx = .5, fy = .5,
1919                 o = element.node,
1920                 SVG = element.paper,
1921                 s = o.style,
1922                 el = g.doc.getElementById(id);
1923             if (!el) {
1924                 gradient = Str(gradient).replace(radial_gradient, function (all, _fx, _fy) {
1925                     type = "radial";
1926                     if (_fx && _fy) {
1927                         fx = toFloat(_fx);
1928                         fy = toFloat(_fy);
1929                         var dir = ((fy > .5) * 2 - 1);
1930                         pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
1931                             (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
1932                             fy != .5 &&
1933                             (fy = fy.toFixed(5) - 1e-5 * dir);
1934                     }
1935                     return E;
1936                 });
1937                 gradient = gradient.split(/\s*\-\s*/);
1938                 if (type == "linear") {
1939                     var angle = gradient.shift();
1940                     angle = -toFloat(angle);
1941                     if (isNaN(angle)) {
1942                         return null;
1943                     }
1944                     var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
1945                         max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
1946                     vector[2] *= max;
1947                     vector[3] *= max;
1948                     if (vector[2] < 0) {
1949                         vector[0] = -vector[2];
1950                         vector[2] = 0;
1951                     }
1952                     if (vector[3] < 0) {
1953                         vector[1] = -vector[3];
1954                         vector[3] = 0;
1955                     }
1956                 }
1957                 var dots = parseDots(gradient);
1958                 if (!dots) {
1959                     return null;
1960                 }
1961                 if (element.gradient) {
1962                     SVG.defs.removeChild(element.gradient);
1963                     delete element.gradient;
1964                 }
1965
1966                 el = $(type + "Gradient", {id: id});
1967                 element.gradient = el;
1968                 $(el, type == "radial" ? {
1969                     fx: fx,
1970                     fy: fy
1971                 } : {
1972                     x1: vector[0],
1973                     y1: vector[1],
1974                     x2: vector[2],
1975                     y2: vector[3],
1976                     gradientTransform: element.matrix.invert()
1977                 });
1978                 SVG.defs.appendChild(el);
1979                 for (var i = 0, ii = dots.length; i < ii; i++) {
1980                     el.appendChild($("stop", {
1981                         offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
1982                         "stop-color": dots[i].color || "#fff"
1983                     }));
1984                 }
1985             }
1986             $(o, {
1987                 fill: "url(#" + id + ")",
1988                 opacity: 1,
1989                 "fill-opacity": 1
1990             });
1991             s.fill = E;
1992             s.opacity = 1;
1993             s.fillOpacity = 1;
1994             return 1;
1995         },
1996         updatePosition = function (o) {
1997             var bbox = o.getBBox(1);
1998             $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
1999         },
2000         addArrow = function (o, value, isEnd) {
2001             if (o.type == "path") {
2002                 var values = Str(value).toLowerCase().split("-"),
2003                     p = o.paper,
2004                     se = isEnd ? "end" : "start",
2005                     node = o.node,
2006                     attrs = o.attrs,
2007                     stroke = attrs["stroke-width"],
2008                     i = values.length,
2009                     type = "classic",
2010                     from,
2011                     to,
2012                     dx,
2013                     refX,
2014                     attr,
2015                     w = 3,
2016                     h = 3,
2017                     t = 5;
2018                 while (i--) {
2019                     switch (values[i]) {
2020                         case "block":
2021                         case "classic":
2022                         case "oval":
2023                         case "diamond":
2024                         case "open":
2025                         case "none":
2026                             type = values[i];
2027                             break;
2028                         case "wide": h = 5; break;
2029                         case "narrow": h = 2; break;
2030                         case "long": w = 5; break;
2031                         case "short": w = 2; break;
2032                     }
2033                 }
2034                 if (type == "open") {
2035                     w += 2;
2036                     h += 2;
2037                     t += 2;
2038                     dx = 1;
2039                     refX = isEnd ? 4 : 1;
2040                     attr = {
2041                         fill: "none",
2042                         stroke: attrs.stroke
2043                     };
2044                 } else {
2045                     refX = dx = w / 2;
2046                     attr = {
2047                         fill: attrs.stroke,
2048                         stroke: "none"
2049                     };
2050                 }
2051                 if (o._.arrows) {
2052                     if (isEnd) {
2053                         o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
2054                         o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
2055                     } else {
2056                         o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
2057                         o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
2058                     }
2059                 } else {
2060                     o._.arrows = {};
2061                 }
2062                 if (type != "none") {
2063                     var pathId = "raphael-marker-" + type,
2064                         markerId = "raphael-marker-" + se + type + w + h;
2065                     if (!g.doc.getElementById(pathId)) {
2066                         p.defs.appendChild($($("path"), {
2067                             "stroke-linecap": "round",
2068                             d: markers[type],
2069                             id: pathId
2070                         }));
2071                         markerCounter[pathId] = 1;
2072                     } else {
2073                         markerCounter[pathId]++;
2074                     }
2075                     var marker = g.doc.getElementById(markerId),
2076                         use;
2077                     if (!marker) {
2078                         marker = $($("marker"), {
2079                             id: markerId,
2080                             markerHeight: h,
2081                             markerWidth: w,
2082                             orient: "auto",
2083                             refX: refX,
2084                             refY: h / 2
2085                         });
2086                         use = $($("use"), {
2087                             "xlink:href": "#" + pathId,
2088                             transform: (isEnd ? " rotate(180 " + w / 2 + " " + h / 2 + ") " : S) + "scale(" + w / t + "," + h / t + ")",
2089                             "stroke-width": 1 / ((w / t + h / t) / 2)
2090                         });
2091                         marker.appendChild(use);
2092                         p.defs.appendChild(marker);
2093                         markerCounter[markerId] = 1;
2094                     } else {
2095                         markerCounter[markerId]++;
2096                         use = marker.getElementsByTagName("use")[0];
2097                     }
2098                     $(use, attr);
2099                     var delta = dx * (type != "diamond" && type != "oval");
2100                     if (isEnd) {
2101                         from = o._.arrows.startdx * stroke || 0;
2102                         to = R.getTotalLength(attrs.path) - delta * stroke;
2103                     } else {
2104                         from = delta * stroke;
2105                         to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
2106                     }
2107                     attr = {};
2108                     attr["marker-" + se] = "url(#" + markerId + ")";
2109                     if (to || from) {
2110                         attr.d = Raphael.getSubpath(attrs.path, from, to);
2111                     }
2112                     $(node, attr);
2113                     o._.arrows[se + "Path"] = pathId;
2114                     o._.arrows[se + "Marker"] = markerId;
2115                     o._.arrows[se + "dx"] = delta;
2116                     o._.arrows[se + "Type"] = type;
2117                     o._.arrows[se + "String"] = value;
2118                 } else {
2119                     if (isEnd) {
2120                         from = o._.arrows.startdx * stroke || 0;
2121                         to = R.getTotalLength(attrs.path) - from;
2122                     } else {
2123                         from = 0;
2124                         to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
2125                     }
2126                     o._.arrows[se + "Path"] && $(node, {d: Raphael.getSubpath(attrs.path, from, to)});
2127                     delete o._.arrows[se + "Path"];
2128                     delete o._.arrows[se + "Marker"];
2129                     delete o._.arrows[se + "dx"];
2130                     delete o._.arrows[se + "Type"];
2131                     delete o._.arrows[se + "String"];
2132                 }
2133                 for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
2134                     var item = g.doc.getElementById(attr);
2135                     item && item.parentNode.removeChild(item);
2136                 }
2137             }
2138         },
2139         setFillAndStroke = function (o, params) {
2140             var dasharray = {
2141                     "": [0],
2142                     "none": [0],
2143                     "-": [3, 1],
2144                     ".": [1, 1],
2145                     "-.": [3, 1, 1, 1],
2146                     "-..": [3, 1, 1, 1, 1, 1],
2147                     ". ": [1, 3],
2148                     "- ": [4, 3],
2149                     "--": [8, 3],
2150                     "- .": [4, 3, 1, 3],
2151                     "--.": [8, 3, 1, 3],
2152                     "--..": [8, 3, 1, 3, 1, 3]
2153                 },
2154                 node = o.node,
2155                 attrs = o.attrs,
2156                 addDashes = function (o, value) {
2157                     value = dasharray[lowerCase.call(value)];
2158                     if (value) {
2159                         var width = o.attrs["stroke-width"] || "1",
2160                             butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
2161                             dashes = [],
2162                             i = value.length;
2163                         while (i--) {
2164                             dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
2165                         }
2166                         $(node, {"stroke-dasharray": dashes.join(",")});
2167                     }
2168                 };
2169             for (var att in params) {
2170                 if (params[has](att)) {
2171                     if (!availableAttrs[has](att)) {
2172                         continue;
2173                     }
2174                     var value = params[att];
2175                     attrs[att] = value;
2176                     switch (att) {
2177                         case "blur":
2178                             o.blur(value);
2179                             break;
2180                         case "href":
2181                         case "title":
2182                         case "target":
2183                             var pn = node.parentNode;
2184                             if (lowerCase.call(pn.tagName) != "a") {
2185                                 var hl = $("a");
2186                                 pn.insertBefore(hl, node);
2187                                 hl.appendChild(node);
2188                                 pn = hl;
2189                             }
2190                             if (att == "target" && value == "blank") {
2191                                 pn.setAttributeNS(xlink, "show", "new");
2192                             } else {
2193                                 pn.setAttributeNS(xlink, att, value);
2194                             }
2195                             break;
2196                         case "cursor":
2197                             node.style.cursor = value;
2198                             break;
2199                         case "transform":
2200                             o.transform(value);
2201                             break;
2202                         case "arrow-start":
2203                             addArrow(o, value);
2204                             break;
2205                         case "arrow-end":
2206                             addArrow(o, value, 1);
2207                             break;
2208                         case "clip-rect":
2209                             var rect = Str(value).split(separator);
2210                             if (rect.length == 4) {
2211                                 o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
2212                                 var el = $("clipPath"),
2213                                     rc = $("rect");
2214                                 el.id = createUUID();
2215                                 $(rc, {
2216                                     x: rect[0],
2217                                     y: rect[1],
2218                                     width: rect[2],
2219                                     height: rect[3]
2220                                 });
2221                                 el.appendChild(rc);
2222                                 o.paper.defs.appendChild(el);
2223                                 $(node, {"clip-path": "url(#" + el.id + ")"});
2224                                 o.clip = rc;
2225                             }
2226                             if (!value) {
2227                                 var clip = g.doc.getElementById(node.getAttribute("clip-path").replace(/(^url\(#|\)$)/g, E));
2228                                 clip && clip.parentNode.removeChild(clip);
2229                                 $(node, {"clip-path": E});
2230                                 delete o.clip;
2231                             }
2232                         break;
2233                         case "path":
2234                             if (o.type == "path") {
2235                                 $(node, {d: value ? attrs.path = pathToAbsolute(value) : "M0,0"});
2236                                 o._.dirty = 1;
2237                                 if (o._.arrows) {
2238                                     "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
2239                                     "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
2240                                 }
2241                             }
2242                             break;
2243                         case "width":
2244                             node[setAttribute](att, value);
2245                             o._.dirty = 1;
2246                             if (attrs.fx) {
2247                                 att = "x";
2248                                 value = attrs.x;
2249                             } else {
2250                                 break;
2251                             }
2252                         case "x":
2253                             if (attrs.fx) {
2254                                 value = -attrs.x - (attrs.width || 0);
2255                             }
2256                         case "rx":
2257                             if (att == "rx" && o.type == "rect") {
2258                                 break;
2259                             }
2260                         case "cx":
2261                             node[setAttribute](att, value);
2262                             o.pattern && updatePosition(o);
2263                             o._.dirty = 1;
2264                             break;
2265                         case "height":
2266                             node[setAttribute](att, value);
2267                             o._.dirty = 1;
2268                             if (attrs.fy) {
2269                                 att = "y";
2270                                 value = attrs.y;
2271                             } else {
2272                                 break;
2273                             }
2274                         case "y":
2275                             if (attrs.fy) {
2276                                 value = -attrs.y - (attrs.height || 0);
2277                             }
2278                         case "ry":
2279                             if (att == "ry" && o.type == "rect") {
2280                                 break;
2281                             }
2282                         case "cy":
2283                             node[setAttribute](att, value);
2284                             o.pattern && updatePosition(o);
2285                             o._.dirty = 1;
2286                             break;
2287                         case "r":
2288                             if (o.type == "rect") {
2289                                 $(node, {rx: value, ry: value});
2290                             } else {
2291                                 node[setAttribute](att, value);
2292                             }
2293                             o._.dirty = 1;
2294                             break;
2295                         case "src":
2296                             if (o.type == "image") {
2297                                 node.setAttributeNS(xlink, "href", value);
2298                             }
2299                             break;
2300                         case "stroke-width":
2301                             if (o._.sx != 1 || o._.sy != 1) {
2302                                 value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
2303                             }
2304                             if (o.paper._vbSize) {
2305                                 value *= o.paper._vbSize;
2306                             }
2307                             node[setAttribute](att, value);
2308                             if (attrs["stroke-dasharray"]) {
2309                                 addDashes(o, attrs["stroke-dasharray"]);
2310                             }
2311                             if (o._.arrows) {
2312                                 "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
2313                                 "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
2314                             }
2315                             break;
2316                         case "stroke-dasharray":
2317                             addDashes(o, value);
2318                             break;
2319                         case fillString:
2320                             var isURL = Str(value).match(ISURL);
2321                             if (isURL) {
2322                                 el = $("pattern");
2323                                 var ig = $("image");
2324                                 el.id = createUUID();
2325                                 $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
2326                                 $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
2327                                 el.appendChild(ig);
2328
2329                                 (function (el) {
2330                                     preload(isURL[1], function () {
2331                                         var w = this.offsetWidth,
2332                                             h = this.offsetHeight;
2333                                         $(el, {width: w, height: h});
2334                                         $(ig, {width: w, height: h});
2335                                         o.paper.safari();
2336                                     });
2337                                 })(el);
2338                                 o.paper.defs.appendChild(el);
2339                                 node.style.fill = "url(#" + el.id + ")";
2340                                 $(node, {fill: "url(#" + el.id + ")"});
2341                                 o.pattern = el;
2342                                 o.pattern && updatePosition(o);
2343                                 break;
2344                             }
2345                             var clr = R.getRGB(value);
2346                             if (!clr.error) {
2347                                 delete params.gradient;
2348                                 delete attrs.gradient;
2349                                 !R.is(attrs.opacity, "undefined") &&
2350                                     R.is(params.opacity, "undefined") &&
2351                                     $(node, {opacity: attrs.opacity});
2352                                 !R.is(attrs["fill-opacity"], "undefined") &&
2353                                     R.is(params["fill-opacity"], "undefined") &&
2354                                     $(node, {"fill-opacity": attrs["fill-opacity"]});
2355                             } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
2356                                 if ("opacity" in attrs || "fill-opacity" in attrs) {
2357                                     var gradient = g.doc.getElementById(node.getAttribute(fillString).replace(/^url\(#|\)$/g, E));
2358                                     if (gradient) {
2359                                         var stops = gradient.getElementsByTagName("stop");
2360                                         $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
2361                                     }
2362                                 }
2363                                 attrs.gradient = value;
2364                                 attrs.fill = "none";
2365                                 break;
2366                             }
2367                             clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
2368                         case "stroke":
2369                             clr = R.getRGB(value);
2370                             node[setAttribute](att, clr.hex);
2371                             att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
2372                             if (att == "stroke" && o._.arrows) {
2373                                 "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
2374                                 "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
2375                             }
2376                             break;
2377                         case "gradient":
2378                             (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
2379                             break;
2380                         case "opacity":
2381                             if (attrs.gradient && !attrs[has]("stroke-opacity")) {
2382                                 $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
2383                             }
2384                             // fall
2385                         case "fill-opacity":
2386                             if (attrs.gradient) {
2387                                 gradient = g.doc.getElementById(node.getAttribute(fillString).replace(/^url\(#|\)$/g, E));
2388                                 if (gradient) {
2389                                     stops = gradient.getElementsByTagName("stop");
2390                                     $(stops[stops.length - 1], {"stop-opacity": value});
2391                                 }
2392                                 break;
2393                             }
2394                         default:
2395                             att == "font-size" && (value = toInt(value, 10) + "px");
2396                             var cssrule = att.replace(/(\-.)/g, function (w) {
2397                                 return upperCase.call(w.substring(1));
2398                             });
2399                             node.style[cssrule] = value;
2400                             o._.dirty = 1;
2401                             node[setAttribute](att, value);
2402                             break;
2403                     }
2404                 }
2405             }
2406
2407             tuneText(o, params);
2408         },
2409         leading = 1.2,
2410         tuneText = function (el, params) {
2411             if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
2412                 return;
2413             }
2414             var a = el.attrs,
2415                 node = el.node,
2416                 fontSize = node.firstChild ? toInt(g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
2417
2418             if (params[has]("text")) {
2419                 a.text = params.text;
2420                 while (node.firstChild) {
2421                     node.removeChild(node.firstChild);
2422                 }
2423                 var texts = Str(params.text).split("\n"),
2424                     tspans = [],
2425                     tspan;
2426                 for (var i = 0, ii = texts.length; i < ii; i++) if (texts[i]) {
2427                     tspan = $("tspan");
2428                     i && $(tspan, {dy: fontSize * leading, x: a.x});
2429                     tspan.appendChild(g.doc.createTextNode(texts[i]));
2430                     node.appendChild(tspan);
2431                     tspans[i] = tspan;
2432                 }
2433             } else {
2434                 tspans = node.getElementsByTagName("tspan");
2435                 for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
2436                     $(tspans[i], {dy: fontSize * leading, x: a.x});
2437                 } else {
2438                     $(tspans[0], {dy: 0});
2439                 }
2440             }
2441             $(node, {x: a.x, y: a.y});
2442             el._.dirty = 1;
2443             var bb = el._getBBox(),
2444                 dif = a.y - (bb.y + bb.height / 2);
2445             dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
2446         },
2447         Element = function (node, svg) {
2448             var X = 0,
2449                 Y = 0;
2450             /*\
2451              * Element.node
2452              [ property (object) ]
2453              **
2454              * Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
2455              > Usage
2456              | // draw a circle at coordinate 10,10 with radius of 10
2457              | var c = paper.circle(10, 10, 10);
2458              | c.node.onclick = function () {
2459              |     c.attr("fill", "red");
2460              | };
2461             \*/
2462             this[0] = this.node = node;
2463             /*\
2464              * Element.raphael
2465              [ property (object) ]
2466              **
2467              * Internal reference to @Raphael object. In case it is not available.
2468              > Usage
2469              | Raphael.el.red = function () {
2470              |     var hsb = this.paper.raphael.rgb2hsb(this.attr("fill"));
2471              |     hsb.h = 1;
2472              |     this.attr({fill: this.paper.raphael.hsb2rgb(hsb).hex});
2473              | }
2474             \*/
2475             node.raphael = true;
2476             /*\
2477              * Element.id
2478              [ property (number) ]
2479              **
2480              * Unique id of the element. Especially usesful when you want to listen to events of the element, 
2481              * because all events are fired in format `<module>.<action>.<id>`. Also useful for @Paper.getById method.
2482             \*/
2483             this.id = R._oid++;
2484             node.raphaelid = this.id;
2485             this.matrix = new Matrix;
2486             this.realPath = null;
2487             /*\
2488              * Element.paper
2489              [ property (object) ]
2490              **
2491              * Internal reference to “paper” where object drawn. Mainly for use in plugins and element extensions.
2492              > Usage
2493              | Raphael.el.cross = function () {
2494              |     this.attr({fill: "red"});
2495              |     this.paper.path("M10,10L50,50M50,10L10,50")
2496              |         .attr({stroke: "red"});
2497              | }
2498             \*/
2499             this.paper = svg;
2500             this.attrs = this.attrs || {};
2501             this._ = {
2502                 transform: [],
2503                 sx: 1,
2504                 sy: 1,
2505                 deg: 0,
2506                 dx: 0,
2507                 dy: 0,
2508                 dirty: 1
2509             };
2510             !svg.bottom && (svg.bottom = this);
2511             /*\
2512              * Element.prev
2513              [ property (object) ]
2514              **
2515              * Reference to the previous element in the hierarchy.
2516             \*/
2517             this.prev = svg.top;
2518             svg.top && (svg.top.next = this);
2519             svg.top = this;
2520             /*\
2521              * Element.next
2522              [ property (object) ]
2523              **
2524              * Reference to the next element in the hierarchy.
2525             \*/
2526             this.next = null;
2527         },
2528         elproto = Element.prototype;
2529         /*\
2530          * Element.rotate
2531          [ method ]
2532          **
2533          * Adds rotation by given angle around given point to the list of
2534          * transformations of the element.
2535          > Parameters
2536          - deg (number) angle in degrees
2537          - cx (number) #optional x coordinate of the centre of rotation
2538          - cy (number) #optional y coordinate of the centre of rotation
2539          * If cx & cy aren’t specified centre of the shape is used as a point of rotation.
2540          = (object) @Element
2541         \*/
2542         elproto.rotate = function (deg, cx, cy) {
2543             if (this.removed) {
2544                 return this;
2545             }
2546             deg = Str(deg).split(separator);
2547             if (deg.length - 1) {
2548                 cx = toFloat(deg[1]);
2549                 cy = toFloat(deg[2]);
2550             }
2551             deg = toFloat(deg[0]);
2552             (cy == null) && (cx = cy);
2553             if (cx == null || cy == null) {
2554                 var bbox = this.getBBox(1);
2555                 cx = bbox.x + bbox.width / 2;
2556                 cy = bbox.y + bbox.height / 2;
2557             }
2558             this.transform(this._.transform.concat([["r", deg, cx, cy]]));
2559             return this;
2560         };
2561         /*\
2562          * Element.scale
2563          [ method ]
2564          **
2565          * Adds scale by given amount relative to given point to the list of
2566          * transformations of the element.
2567          > Parameters
2568          - sx (number) horisontal scale amount
2569          - sy (number) vertical scale amount
2570          - cx (number) #optional x coordinate of the centre of scale
2571          - cy (number) #optional y coordinate of the centre of scale
2572          * If cx & cy aren’t specified centre of the shape is used instead.
2573          = (object) @Element
2574         \*/
2575         elproto.scale = function (sx, sy, cx, cy) {
2576             if (this.removed) {
2577                 return this;
2578             }
2579             sx = Str(sx).split(separator);
2580             if (sx.length - 1) {
2581                 sy = toFloat(sx[1]);
2582                 cx = toFloat(sx[2]);
2583                 cy = toFloat(sx[3]);
2584             }
2585             sx = toFloat(sx[0]);
2586             (sy == null) && (sy = sx);
2587             (cy == null) && (cx = cy);
2588             if (cx == null || cy == null) {
2589                 var bbox = this.getBBox(1);
2590             }
2591             cx = cx == null ? bbox.x + bbox.width / 2 : cx;
2592             cy = cy == null ? bbox.y + bbox.height / 2 : cy;
2593             this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
2594             return this;
2595         };
2596         /*\
2597          * Element.translate
2598          [ method ]
2599          **
2600          * Adds translation by given amount to the list of transformations of the element.
2601          > Parameters
2602          - dx (number) horisontal shift
2603          - dy (number) vertical shift
2604          = (object) @Element
2605         \*/
2606         elproto.translate = function (dx, dy) {
2607             if (this.removed) {
2608                 return this;
2609             }
2610             dx = Str(dx).split(separator);
2611             if (dx.length - 1) {
2612                 dy = toFloat(dx[1]);
2613             }
2614             dx = toFloat(dx[0]) || 0;
2615             dy = +dy || 0;
2616             this.transform(this._.transform.concat([["t", dx, dy]]));
2617             return this;
2618         };
2619         /*\
2620          * Element.transform
2621          [ method ]
2622          **
2623          * Adds transformation to the element which is separate to other attributes,
2624          * i.e. translation doesn’t change `x` or `y` of the rectange. The format
2625          * of transformation string is similar to the path string syntax:
2626          | "t100,100r30,100,100s2,2,100,100r45s1.5"
2627          * Each letter is a command. There are four commands: `t` is for translate, `r` is for rotate, `s` is for
2628          * scale and `m` is for matrix.
2629          *
2630          * So, the example line above could be read like “translate by 100, 100; rotate 30° around 100, 100; scale twice around 100, 100;
2631          * rotate 45° around centre; scale 1.5 times relative to centre”. As you can see rotate and scale commands have origin
2632          * coordinates as optional parameters, the default is the centre point of the element.
2633          * Matrix accepts six parameters.
2634          > Usage
2635          | var el = paper.rect(10, 20, 300, 200);
2636          | // translate 100, 100, rotate 45°, translate -100, 0
2637          | el.transform("t100,100r45t-100,0");
2638          | // if you want you can append or prepend transformations
2639          | el.transform("...t50,50");
2640          | el.transform("s2...");
2641          | // or even wrap
2642          | el.transform("t50,50...t-50-50");
2643          | // to reset transformation call method with empty string
2644          | el.transform("");
2645          | // to get current value call it without parameters
2646          | console.log(el.transform());
2647          > Parameters
2648          - tstr (string) #optional transformation string
2649          * If tstr isn’t specified
2650          = (string) current transformation string
2651          * else
2652          = (object) @Element
2653         \*/
2654         elproto.transform = function (tstr) {
2655             var _ = this._;
2656             if (tstr == null) {
2657                 return _.transform;
2658             }
2659             extractTransform(this, tstr);
2660
2661             this.clip && $(this.clip, {transform: this.matrix.invert()});
2662             this.pattern && updatePosition(this);
2663             this.node && $(this.node, {transform: this.matrix});
2664             
2665             if (_.sx != 1 || _.sy != 1) {
2666                 var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
2667                 this.attr({"stroke-width": sw});
2668             }
2669
2670             return this;
2671         };
2672         /*\
2673          * Element.hide
2674          [ method ]
2675          **
2676          * Makes element invisible. See @Element.show.
2677          = (object) @Element
2678         \*/
2679         elproto.hide = function () {
2680             !this.removed && this.paper.safari(this.node.style.display = "none");
2681             return this;
2682         };
2683         /*\
2684          * Element.show
2685          [ method ]
2686          **
2687          * Makes element visible. See @Element.hide.
2688          = (object) @Element
2689         \*/
2690         elproto.show = function () {
2691             !this.removed && this.paper.safari(this.node.style.display = "");
2692             return this;
2693         };
2694         /*\
2695          * Element.remove
2696          [ method ]
2697          **
2698          * Removes element form the paper.
2699         \*/
2700         elproto.remove = function () {
2701             if (this.removed) {
2702                 return;
2703             }
2704             eve.unbind("*.*." + this.id);
2705             tear(this, this.paper);
2706             this.node.parentNode.removeChild(this.node);
2707             for (var i in this) {
2708                 delete this[i];
2709             }
2710             this.removed = true;
2711         };
2712         elproto._getBBox = function () {
2713             if (this.node.style.display == "none") {
2714                 this.show();
2715                 var hide = true;
2716             }
2717             var bbox = {};
2718             try {
2719                 bbox = this.node.getBBox();
2720             } catch(e) {
2721                 // Firefox 3.0.x plays badly here
2722             } finally {
2723                 bbox = bbox || {};
2724             }
2725             hide && this.hide();
2726             return bbox;
2727         };
2728         /*\
2729          * Element.attr
2730          [ method ]
2731          **
2732          * Sets the attributes of the element.
2733          > Parameters
2734          - attrName (string) attribute’s name
2735          - value (string) value
2736          * or
2737          - params (object) object of name/value pairs
2738          * or
2739          - attrName (string) attribute’s name
2740          * or
2741          - attrNames (array) in this case method returns array of current values for given attribute names
2742          = (object) @Element if attrsName & value or params are passed in.
2743          = (...) value of the attribute if only attrsName is passed in.
2744          = (array) array of values of the attribute if attrsNames is passed in.
2745          = (object) object of attributes if nothing is passed in.
2746          > Possible parameters
2747          # <p>Please refer to the <a href="http://www.w3.org/TR/SVG/" title="The W3C Recommendation for the SVG language describes these properties in detail.">SVG specification</a> for an explanation of these parameters.</p>
2748          o arrow-end (string) arrowhead on the end of the path. The format for string is `<type>[-<width>[-<length>]]`. Possible types: `classic`, `block`, `open`, `oval`, `diamond`, `none`, width: `wide`, `narrow`, `midium`, length: `long`, `short`, `midium`.
2749          o clip-rect (string) comma or space separated values: x, y, width and height
2750          o cursor (string) CSS type of the cursor
2751          o cx (number)
2752          o cy (number)
2753          o fill (string) colour, gradient or image
2754          o fill-opacity (number)
2755          o font (string)
2756          o font-family (string)
2757          o font-size (number) font size in pixels
2758          o font-weight (string)
2759          o height (number)
2760          o href (string) URL, if specified element behaves as hyperlink
2761          o opacity (number)
2762          o path (string) SVG path string format
2763          o r (number)
2764          o rx (number)
2765          o ry (number)
2766          o src (string) image URL, only works for @Element.image element
2767          o stroke (string) stroke colour
2768          o stroke-dasharray (string) [“”, “`-`”, “`.`”, “`-.`”, “`-..`”, “`. `”, “`- `”, “`--`”, “`- .`”, “`--.`”, “`--..`”]
2769          o stroke-linecap (string) [“`butt`”, “`square`”, “`round`”]
2770          o stroke-linejoin (string) [“`bevel`”, “`round`”, “`miter`”]
2771          o stroke-miterlimit (number)
2772          o stroke-opacity (number)
2773          o stroke-width (number) stroke width in pixels, default is '1'
2774          o target (string) used with href
2775          o text (string) contents of the text element. Use `\n` for multiline text
2776          o text-anchor (string) [“`start`”, “`middle`”, “`end`”], default is “`middle`”
2777          o title (string) will create tooltip with a given text
2778          o transform (string) see @Element.transform
2779          o width (number)
2780          o x (number)
2781          o y (number)
2782          > Gradients
2783          * Linear gradient format: “`‹angle›-‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`90-#fff-#000`” – 90°
2784          * gradient from white to black or “`0-#fff-#f00:20-#000`” – 0° gradient from white via red (at 20%) to black.
2785          *
2786          * radial gradient: “`r[(‹fx›, ‹fy›)]‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`r#fff-#000`” –
2787          * gradient from white to black or “`r(0.25, 0.75)#fff-#000`” – gradient from white to black with focus point
2788          * at 0.25, 0.75. Focus point coordinates are in 0..1 range. Radial gradients can only be applied to circles and ellipses.
2789          > Path String
2790          # <p>Please refer to <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path’s data attribute’s format are described in the SVG specification.">SVG documentation regarding path string</a>. Raphaël fully supports it.</p>
2791          > Colour Parsing
2792          # <ul>
2793          #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
2794          #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
2795          #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
2796          #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
2797          #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
2798          #     <li>rgba(•••, •••, •••, •••) — red, green and blue channels’ values: (“<code>rgba(200,&nbsp;100,&nbsp;0, .5)</code>”)</li>
2799          #     <li>rgba(•••%, •••%, •••%, •••%) — same as above, but in %: (“<code>rgba(100%,&nbsp;175%,&nbsp;0%, 50%)</code>”)</li>
2800          #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
2801          #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
2802          #     <li>hsba(•••, •••, •••, •••) — same as above, but with opacity</li>
2803          #     <li>hsl(•••, •••, •••) — almost the same as hsb, see <a href="http://en.wikipedia.org/wiki/HSL_and_HSV" title="HSL and HSV - Wikipedia, the free encyclopedia">Wikipedia page</a></li>
2804          #     <li>hsl(•••%, •••%, •••%) — same as above, but in %</li>
2805          #     <li>hsla(•••, •••, •••) — same as above, but with opacity</li>
2806          #     <li>Optionally for hsb and hsl you could specify hue as a degree: “<code>hsl(240deg,&nbsp;1,&nbsp;.5)</code>” or, if you want to go fancy, “<code>hsl(240°,&nbsp;1,&nbsp;.5)</code>”</li>
2807          # </ul>
2808         \*/
2809         elproto.attr = function (name, value) {
2810             if (this.removed) {
2811                 return this;
2812             }
2813             if (name == null) {
2814                 var res = {};
2815                 for (var i in this.attrs) if (this.attrs[has](i)) {
2816                     res[i] = this.attrs[i];
2817                 }
2818                 res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
2819                 res.transform = this._.transform;
2820                 return res;
2821             }
2822             if (value == null && R.is(name, string)) {
2823                 if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
2824                     return this.attrs.gradient;
2825                 }
2826                 if (name == "transform") {
2827                     return this._.transform;
2828                 }
2829                 if (name in this.attrs) {
2830                     return this.attrs[name];
2831                 } else if (R.is(this.paper.customAttributes[name], "function")) {
2832                     return this.paper.customAttributes[name].def;
2833                 } else {
2834                     return availableAttrs[name];
2835                 }
2836             }
2837             if (value == null && R.is(name, array)) {
2838                 var values = {};
2839                 for (var j = 0, jj = name.length; j < jj; j++) {
2840                     values[name[j]] = this.attr(name[j]);
2841                 }
2842                 return values;
2843             }
2844             if (value != null) {
2845                 var params = {};
2846                 params[name] = value;
2847             } else if (name != null && R.is(name, "object")) {
2848                 params = name;
2849             }
2850             for (var key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
2851                 var par = this.paper.customAttributes[key].apply(this, [][concat](params[key]));
2852                 this.attrs[key] = params[key];
2853                 for (var subkey in par) if (par[has](subkey)) {
2854                     params[subkey] = par[subkey];
2855                 }
2856             }
2857             setFillAndStroke(this, params);
2858             return this;
2859         };
2860         /*\
2861          * Element.toFront
2862          [ method ]
2863          **
2864          * Moves the element so it is the closest to the viewer’s eyes, on top of other elements.
2865          = (object) @Element
2866         \*/
2867         elproto.toFront = function () {
2868             if (this.removed) {
2869                 return this;
2870             }
2871             this.node.parentNode.appendChild(this.node);
2872             var svg = this.paper;
2873             svg.top != this && tofront(this, svg);
2874             return this;
2875         };
2876         /*\
2877          * Element.toBack
2878          [ method ]
2879          **
2880          * Moves the element so it is the furthest from the viewer’s eyes, behind other elements.
2881          = (object) @Element
2882         \*/
2883         elproto.toBack = function () {
2884             if (this.removed) {
2885                 return this;
2886             }
2887             if (this.node.parentNode.firstChild != this.node) {
2888                 this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
2889                 toback(this, this.paper);
2890                 var svg = this.paper;
2891             }
2892             return this;
2893         };
2894         /*\
2895          * Element.insertAfter
2896          [ method ]
2897          **
2898          * Inserts current object after the given one.
2899          = (object) @Element
2900         \*/
2901         elproto.insertAfter = function (element) {
2902             if (this.removed) {
2903                 return this;
2904             }
2905             var node = element.node || element[element.length - 1].node;
2906             if (node.nextSibling) {
2907                 node.parentNode.insertBefore(this.node, node.nextSibling);
2908             } else {
2909                 node.parentNode.appendChild(this.node);
2910             }
2911             insertafter(this, element, this.paper);
2912             return this;
2913         };
2914         /*\
2915          * Element.insertBefore
2916          [ method ]
2917          **
2918          * Inserts current object before the given one.
2919          = (object) @Element
2920         \*/
2921         elproto.insertBefore = function (element) {
2922             if (this.removed) {
2923                 return this;
2924             }
2925             var node = element.node || element[0].node;
2926             node.parentNode.insertBefore(this.node, node);
2927             insertbefore(this, element, this.paper);
2928             return this;
2929         };
2930         elproto.blur = function (size) {
2931             // Experimental. No Safari support. Use it on your own risk.
2932             var t = this;
2933             if (+size !== 0) {
2934                 var fltr = $("filter"),
2935                     blur = $("feGaussianBlur");
2936                 t.attrs.blur = size;
2937                 fltr.id = createUUID();
2938                 $(blur, {stdDeviation: +size || 1.5});
2939                 fltr.appendChild(blur);
2940                 t.paper.defs.appendChild(fltr);
2941                 t._blur = fltr;
2942                 $(t.node, {filter: "url(#" + fltr.id + ")"});
2943             } else {
2944                 if (t._blur) {
2945                     t._blur.parentNode.removeChild(t._blur);
2946                     delete t._blur;
2947                     delete t.attrs.blur;
2948                 }
2949                 t.node.removeAttribute("filter");
2950             }
2951         };
2952         var theCircle = function (svg, x, y, r) {
2953             var el = $("circle");
2954             svg.canvas && svg.canvas.appendChild(el);
2955             var res = new Element(el, svg);
2956             res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
2957             res.type = "circle";
2958             $(el, res.attrs);
2959             return res;
2960         },
2961         theRect = function (svg, x, y, w, h, r) {
2962             var el = $("rect");
2963             svg.canvas && svg.canvas.appendChild(el);
2964             var res = new Element(el, svg);
2965             res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
2966             res.type = "rect";
2967             $(el, res.attrs);
2968             return res;
2969         },
2970         theEllipse = function (svg, x, y, rx, ry) {
2971             var el = $("ellipse");
2972             svg.canvas && svg.canvas.appendChild(el);
2973             var res = new Element(el, svg);
2974             res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
2975             res.type = "ellipse";
2976             $(el, res.attrs);
2977             return res;
2978         },
2979         theImage = function (svg, src, x, y, w, h) {
2980             var el = $("image");
2981             $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
2982             el.setAttributeNS(xlink, "href", src);
2983             svg.canvas && svg.canvas.appendChild(el);
2984             var res = new Element(el, svg);
2985             res.attrs = {x: x, y: y, width: w, height: h, src: src};
2986             res.type = "image";
2987             return res;
2988         },
2989         theText = function (svg, x, y, text) {
2990             var el = $("text");
2991             // $(el, {x: x, y: y, "text-anchor": "middle"});
2992             svg.canvas && svg.canvas.appendChild(el);
2993             var res = new Element(el, svg);
2994             res.attrs = {
2995                 x: x,
2996                 y: y,
2997                 "text-anchor": "middle",
2998                 text: text,
2999                 font: availableAttrs.font,
3000                 stroke: "none",
3001                 fill: "#000"
3002             };
3003             res.type = "text";
3004             setFillAndStroke(res, res.attrs);
3005             return res;
3006         },
3007         setSize = function (width, height) {
3008             this.width = width || this.width;
3009             this.height = height || this.height;
3010             this.canvas[setAttribute]("width", this.width);
3011             this.canvas[setAttribute]("height", this.height);
3012             if (this._viewBox) {
3013                 this.setViewBox.apply(this, this._viewBox);
3014             }
3015             return this;
3016         },
3017         create = function () {
3018             var con = getContainer[apply](0, arguments),
3019                 container = con && con.container,
3020                 x = con.x,
3021                 y = con.y,
3022                 width = con.width,
3023                 height = con.height;
3024             if (!container) {
3025                 throw new Error("SVG container not found.");
3026             }
3027             var cnvs = $("svg"),
3028                 css = "overflow:hidden;",
3029                 isFloating;
3030             x = x || 0;
3031             y = y || 0;
3032             width = width || 512;
3033             height = height || 342;
3034             $(cnvs, {
3035                 height: height,
3036                 version: 1.1,
3037                 width: width,
3038                 xmlns: "http://www.w3.org/2000/svg"
3039             });
3040             if (container == 1) {
3041                 cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
3042                 g.doc.body.appendChild(cnvs);
3043                 isFloating = 1;
3044             } else {
3045                 cnvs.style.cssText = css + "position:relative";
3046                 if (container.firstChild) {
3047                     container.insertBefore(cnvs, container.firstChild);
3048                 } else {
3049                     container.appendChild(cnvs);
3050                 }
3051             }
3052             container = new Paper;
3053             container.width = width;
3054             container.height = height;
3055             container.canvas = cnvs;
3056             plugins.call(container, container, R.fn);
3057             container.clear();
3058             container._left = container._top = 0;
3059             isFloating && (container.renderfix = fun);
3060             container.renderfix();
3061             return container;
3062         },
3063         
3064         setViewBox = function (x, y, w, h, fit) {
3065             eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
3066             var size = mmax(w / this.width, h / this.height),
3067                 top = this.top,
3068                 aspectRatio = fit ? "meet" : "xMinYMin",
3069                 vb,
3070                 sw;
3071             if (x == null) {
3072                 if (this._vbSize) {
3073                     size = 1;
3074                 }
3075                 delete this._vbSize;
3076                 vb = "0 0 " + this.width + S + this.height;
3077             } else {
3078                 this._vbSize = size;
3079                 vb = x + S + y + S + w + S + h;
3080             }
3081             $(this.canvas, {
3082                 viewBox: vb,
3083                 preserveAspectRatio: aspectRatio
3084             });
3085             while (size && top) {
3086                 sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
3087                 top.attr({"stroke-width": sw});
3088                 top._.dirty = 1;
3089                 top._.dirtyT = 1;
3090                 top = top.prev;
3091             }
3092             this._viewBox = [x, y, w, h, !!fit];
3093             return this;
3094         };
3095         /*\
3096          * Paper.renderfix
3097          [ method ]
3098          **
3099          * Fixes the issue of Firefox and IE9 regarding subpixel rendering. If paper is dependant
3100          * on other elements after reflow it could shift half pixel which cause for lines to lost their crispness.
3101          * This method fixes the issue.
3102          **
3103            Special thanks to Mariusz Nowak (http://www.medikoo.com/) for this method.
3104         \*/
3105         paperproto.renderfix = function () {
3106             var cnvs = this.canvas,
3107                 s = cnvs.style,
3108                 pos = cnvs.getScreenCTM(),
3109                 left = -pos.e % 1,
3110                 top = -pos.f % 1;
3111             if (left || top) {
3112                 if (left) {
3113                     this._left = (this._left + left) % 1;
3114                     s.left = this._left + "px";
3115                 }
3116                 if (top) {
3117                     this._top = (this._top + top) % 1;
3118                     s.top = this._top + "px";
3119                 }
3120             }
3121         };
3122         /*\
3123          * Paper.clear
3124          [ method ]
3125          **
3126          * Clears the paper, i.e. removes all the elements.
3127         \*/
3128         paperproto.clear = function () {
3129             eve("clear", this);
3130             var c = this.canvas;
3131             while (c.firstChild) {
3132                 c.removeChild(c.firstChild);
3133             }
3134             this.bottom = this.top = null;
3135             (this.desc = $("desc")).appendChild(g.doc.createTextNode("Created with Rapha\xebl " + R.version));
3136             c.appendChild(this.desc);
3137             c.appendChild(this.defs = $("defs"));
3138         };
3139         /*\
3140          * Paper.remove
3141          [ method ]
3142          **
3143          * Removes the paper from the DOM.
3144         \*/
3145         paperproto.remove = function () {
3146             eve("remove", this);
3147             this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
3148             for (var i in this) {
3149                 this[i] = removed(i);
3150             }
3151         };
3152     }
3153
3154     // VML
3155     if (R.vml) {
3156         var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
3157             bites = /([clmz]),?([^clmz]*)/gi,
3158             blurregexp = / progid:\S+Blur\([^\)]+\)/g,
3159             val = /-?[^,\s-]+/g,
3160             cssDot = "position:absolute;left:0;top:0;width:1px;height:1px",
3161             zoom = 21600,
3162             pathTypes = {path: 1, rect: 1},
3163             ovalTypes = {circle: 1, ellipse: 1},
3164             path2vml = function (path) {
3165                 var total =  /[ahqstv]/ig,
3166                     command = pathToAbsolute;
3167                 Str(path).match(total) && (command = path2curve);
3168                 total = /[clmz]/g;
3169                 if (command == pathToAbsolute && !Str(path).match(total)) {
3170                     var res = Str(path).replace(bites, function (all, command, args) {
3171                         var vals = [],
3172                             isMove = lowerCase.call(command) == "m",
3173                             res = map[command];
3174                         args.replace(val, function (value) {
3175                             if (isMove && vals.length == 2) {
3176                                 res += vals + map[command == "m" ? "l" : "L"];
3177                                 vals = [];
3178                             }
3179                             vals.push(round(value * zoom));
3180                         });
3181                         return res + vals;
3182                     });
3183                     return res;
3184                 }
3185                 var pa = command(path), p, r;
3186                 res = [];
3187                 for (var i = 0, ii = pa.length; i < ii; i++) {
3188                     p = pa[i];
3189                     r = lowerCase.call(pa[i][0]);
3190                     r == "z" && (r = "x");
3191                     for (var j = 1, jj = p.length; j < jj; j++) {
3192                         r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
3193                     }
3194                     res.push(r);
3195                 }
3196                 return res.join(S);
3197             },
3198             compensation = function (deg, dx, dy) {
3199                 var m = new Matrix;
3200                 m.rotate(-deg, .5, .5);
3201                 return {
3202                     dx: m.x(dx, dy),
3203                     dy: m.y(dx, dy)
3204                 };
3205             },
3206             setCoords = function (p, sx, sy, dx, dy, deg) {
3207                 var _ = p._,
3208                     m = p.matrix,
3209                     // sp = m.split(),
3210                     // sx = sp.scalex,
3211                     // sy = sp.scaley,
3212                     // deg = sp.rotate,
3213                     // dx = sp.dx,
3214                     // dy = sp.dy,
3215                     fillpos = _.fillpos,
3216                     o = p.node,
3217                     s = o.style,
3218                     y = 1,
3219                     flip = "",
3220                     dxdy,
3221                     kx = zoom / sx,
3222                     ky = zoom / sy;
3223                 s.visibility = "hidden";
3224                 o.coordsize = abs(kx) + S + abs(ky);
3225                 s.rotation = deg * (sx * sy < 0 ? -1 : 1);
3226                 if (deg) {
3227                     var c = compensation(deg, dx, dy);
3228                     dx = c.dx;
3229                     dy = c.dy;
3230                 }
3231                 sx < 0 && (flip += "x");
3232                 sy < 0 && (flip += " y") && (y = -1);
3233                 s.flip = flip;
3234                 o.coordorigin = (dx * -kx) + S + (dy * -ky);
3235                 if (fillpos || _.fillsize) {
3236                     var fill = o.getElementsByTagName(fillString);
3237                     fill = fill && fill[0];
3238                     o.removeChild(fill);
3239                     if (fillpos) {
3240                         c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
3241                         fill.position = c.dx * y + S + c.dy * y;
3242                     }
3243                     if (_.fillsize) {
3244                         fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
3245                     }
3246                     o.appendChild(fill);
3247                 }
3248                 s.visibility = "visible";
3249             };
3250         R.toString = function () {
3251             return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
3252         };
3253         addArrow = function (o, value, isEnd) {
3254             var values = Str(value).toLowerCase().split("-"),
3255                 se = isEnd ? "end" : "start",
3256                 i = values.length,
3257                 type = "classic",
3258                 w = "medium",
3259                 h = "medium";
3260             while (i--) {
3261                 switch (values[i]) {
3262                     case "block":
3263                     case "classic":
3264                     case "oval":
3265                     case "diamond":
3266                     case "open":
3267                     case "none":
3268                         type = values[i];
3269                         break;
3270                     case "wide":
3271                     case "narrow": h = values[i]; break;
3272                     case "long":
3273                     case "short": w = values[i]; break;
3274                 }
3275             }
3276             var stroke = o.node.getElementsByTagName("stroke")[0];
3277             stroke[se + "arrow"] = type;
3278             stroke[se + "arrowlength"] = w;
3279             stroke[se + "arrowwidth"] = h;
3280         };
3281         setFillAndStroke = function (o, params) {
3282             o.paper.canvas.style.display = "none";
3283             o.attrs = o.attrs || {};
3284             var node = o.node,
3285                 a = o.attrs,
3286                 s = node.style,
3287                 xy,
3288                 newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
3289                 isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
3290                 res = o;
3291
3292
3293             for (var par in params) if (params[has](par)) {
3294                 a[par] = params[par];
3295             }
3296             if (newpath) {
3297                 a.path = getPath[o.type](o);
3298                 o._.dirty = 1;
3299             }
3300             params.href && (node.href = params.href);
3301             params.title && (node.title = params.title);
3302             params.target && (node.target = params.target);
3303             params.cursor && (s.cursor = params.cursor);
3304             "blur" in params && o.blur(params.blur);
3305             "transform" in params && o.transform(params.transform);
3306             if (params.path && o.type == "path" || newpath) {
3307                 node.path = path2vml(a.path);
3308             }
3309             if (isOval) {
3310                 var cx = a.cx,
3311                     cy = a.cy,
3312                     rx = a.rx || a.r || 0,
3313                     ry = a.ry || a.r || 0;
3314                 node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
3315             }
3316             if ("clip-rect" in params) {
3317                 var rect = Str(params["clip-rect"]).split(separator);
3318                 if (rect.length == 4) {
3319                     rect[2] = +rect[2] + (+rect[0]);
3320                     rect[3] = +rect[3] + (+rect[1]);
3321                     var div = node.clipRect || g.doc.createElement("div"),
3322                         dstyle = div.style,
3323                         group = node.parentNode;
3324                     dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
3325                     if (!node.clipRect) {
3326                         dstyle.position = "absolute";
3327                         dstyle.top = 0;
3328                         dstyle.left = 0;
3329                         dstyle.width = o.paper.width + "px";
3330                         dstyle.height = o.paper.height + "px";
3331                         group.parentNode.insertBefore(div, group);
3332                         div.appendChild(group);
3333                         node.clipRect = div;
3334                     }
3335                 }
3336                 if (!params["clip-rect"]) {
3337                     node.clipRect && (node.clipRect.style.clip = E);
3338                 }
3339             }
3340             if (o.textpath) {
3341                 var textpathStyle = o.textpath.style;
3342                 params.font && (textpathStyle.font = params.font);
3343                 params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
3344                 params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
3345                 params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
3346                 params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
3347             }
3348             if ("arrow-start" in params) {
3349                 addArrow(res, params["arrow-start"]);
3350             }
3351             if ("arrow-end" in params) {
3352                 addArrow(res, params["arrow-end"], 1);
3353             }
3354             if (params.opacity != null || 
3355                 params["stroke-width"] != null ||
3356                 params.fill != null ||
3357                 params.src != null ||
3358                 params.stroke != null ||
3359                 params["stroke-width"] != null ||
3360                 params["stroke-opacity"] != null ||
3361                 params["fill-opacity"] != null ||
3362                 params["stroke-dasharray"] != null ||
3363                 params["stroke-miterlimit"] != null ||
3364                 params["stroke-linejoin"] != null ||
3365                 params["stroke-linecap"] != null) {
3366                 var fill = node.getElementsByTagName(fillString),
3367                     newfill = false;
3368                 fill = fill && fill[0];
3369                 !fill && (newfill = fill = createNode(fillString));
3370                 if (o.type == "image" && params.src) {
3371                     fill.src = params.src;
3372                 }
3373                 if ("fill-opacity" in params || "opacity" in params) {
3374                     var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
3375                     opacity = mmin(mmax(opacity, 0), 1);
3376                     fill.opacity = opacity;
3377                 }
3378                 params.fill && (fill.on = true);
3379                 if (fill.on == null || params.fill == "none" || params.fill === null) {
3380                     fill.on = false;
3381                 }
3382                 if (fill.on && params.fill) {
3383                     var isURL = Str(params.fill).match(ISURL);
3384                     if (isURL) {
3385                         fill.parentNode == node && node.removeChild(fill);
3386                         fill.rotate = true;
3387                         fill.src = isURL[1];
3388                         fill.type = "tile";
3389                         var bbox = o.getBBox(1);
3390                         fill.position = bbox.x + S + bbox.y;
3391                         o._.fillpos = [bbox.x, bbox.y];
3392
3393                         preload(isURL[1], function () {
3394                             o._.fillsize = [this.offsetWidth, this.offsetHeight];
3395                         });
3396                     } else {
3397                         fill.color = R.getRGB(params.fill).hex;
3398                         fill.src = E;
3399                         fill.type = "solid";
3400                         if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
3401                             a.fill = "none";
3402                             a.gradient = params.fill;
3403                             fill.rotate = false;
3404                         }
3405                     }
3406                 }
3407                 node.appendChild(fill);
3408                 var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
3409                 newstroke = false;
3410                 !stroke && (newstroke = stroke = createNode("stroke"));
3411                 if ((params.stroke && params.stroke != "none") ||
3412                     params["stroke-width"] ||
3413                     params["stroke-opacity"] != null ||
3414                     params["stroke-dasharray"] ||
3415                     params["stroke-miterlimit"] ||
3416                     params["stroke-linejoin"] ||
3417                     params["stroke-linecap"]) {
3418                     stroke.on = true;
3419                 }
3420                 (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
3421                 var strokeColor = R.getRGB(params.stroke);
3422                 stroke.on && params.stroke && (stroke.color = strokeColor.hex);
3423                 opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
3424                 var width = (toFloat(params["stroke-width"]) || 1) * .75;
3425                 opacity = mmin(mmax(opacity, 0), 1);
3426                 params["stroke-width"] == null && (width = a["stroke-width"]);
3427                 params["stroke-width"] && (stroke.weight = width);
3428                 width && width < 1 && (opacity *= width) && (stroke.weight = 1);
3429                 stroke.opacity = opacity;
3430                 
3431                 params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
3432                 stroke.miterlimit = params["stroke-miterlimit"] || 8;
3433                 params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
3434                 if (params["stroke-dasharray"]) {
3435                     var dasharray = {
3436                         "-": "shortdash",
3437                         ".": "shortdot",
3438                         "-.": "shortdashdot",
3439                         "-..": "shortdashdotdot",
3440                         ". ": "dot",
3441                         "- ": "dash",
3442                         "--": "longdash",
3443                         "- .": "dashdot",
3444                         "--.": "longdashdot",
3445                         "--..": "longdashdotdot"
3446                     };
3447                     stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
3448                 }
3449                 newstroke && node.appendChild(stroke);
3450             }
3451             if (res.type == "text") {
3452                 res.paper.canvas.style.display = E;
3453                 var span = res.paper.span,
3454                     m = 100,
3455                     fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
3456                 s = span.style;
3457                 a.font && (s.font = a.font);
3458                 a["font-family"] && (s.fontFamily = a["font-family"]);
3459                 a["font-weight"] && (s.fontWeight = a["font-weight"]);
3460                 a["font-style"] && (s.fontStyle = a["font-style"]);
3461                 fontSize = toFloat(fontSize ? fontSize[0] : a["font-size"]);
3462                 s.fontSize = fontSize * m + "px";
3463                 res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
3464                 var brect = span.getBoundingClientRect();
3465                 res.W = a.w = (brect.right - brect.left) / m;
3466                 res.H = a.h = (brect.bottom - brect.top) / m;
3467                 res.paper.canvas.style.display = "none";
3468                 res.X = a.x;
3469                 res.Y = a.y + res.H / 2;
3470
3471                 ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
3472                 var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
3473                 for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
3474                     res._.dirty = 1;
3475                     break;
3476                 }
3477                 
3478                 // text-anchor emulation
3479                 switch (a["text-anchor"]) {
3480                     case "start":
3481                         res.textpath.style["v-text-align"] = "left";
3482                         res.bbx = res.W / 2;
3483                     break;
3484                     case "end":
3485                         res.textpath.style["v-text-align"] = "right";
3486                         res.bbx = -res.W / 2;
3487                     break;
3488                     default:
3489                         res.textpath.style["v-text-align"] = "center";
3490                         res.bbx = 0;
3491                     break;
3492                 }
3493                 res.textpath.style["v-text-kern"] = true;
3494             }
3495             res.paper.canvas.style.display = E;
3496         };
3497         addGradientFill = function (o, gradient, fill) {
3498             o.attrs = o.attrs || {};
3499             var attrs = o.attrs,
3500                 type = "linear",
3501                 fxfy = ".5 .5";
3502             o.attrs.gradient = gradient;
3503             gradient = Str(gradient).replace(radial_gradient, function (all, fx, fy) {
3504                 type = "radial";
3505                 if (fx && fy) {
3506                     fx = toFloat(fx);
3507                     fy = toFloat(fy);
3508                     pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
3509                     fxfy = fx + S + fy;
3510                 }
3511                 return E;
3512             });
3513             gradient = gradient.split(/\s*\-\s*/);
3514             if (type == "linear") {
3515                 var angle = gradient.shift();
3516                 angle = -toFloat(angle);
3517                 if (isNaN(angle)) {
3518                     return null;
3519                 }
3520             }
3521             var dots = parseDots(gradient);
3522             if (!dots) {
3523                 return null;
3524             }
3525             o = o.shape || o.node;
3526             if (dots.length) {
3527                 o.removeChild(fill);
3528                 fill.on = true;
3529                 fill.method = "none";
3530                 fill.color = dots[0].color;
3531                 fill.color2 = dots[dots.length - 1].color;
3532                 var clrs = [];
3533                 for (var i = 0, ii = dots.length; i < ii; i++) {
3534                     dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
3535                 }
3536                 fill.colors && (fill.colors.value = clrs.length ? clrs.join() : "0% " + fill.color);
3537                 if (type == "radial") {
3538                     fill.type = "gradientTitle";
3539                     fill.focus = "100%";
3540                     fill.focussize = "0 0";
3541                     fill.focusposition = fxfy;
3542                     fill.angle = 0;
3543                 } else {
3544                     // fill.rotate= true;
3545                     fill.type = "gradient";
3546                     fill.angle = (270 - angle) % 360;
3547                 }
3548                 o.appendChild(fill);
3549                 // alert(fill.outerHTML);
3550             }
3551             return 1;
3552         };
3553         Element = function (node, vml) {
3554             this[0] = this.node = node;
3555             node.raphael = true;
3556             this.id = R._oid++;
3557             node.raphaelid = this.id;
3558             this.X = 0;
3559             this.Y = 0;
3560             this.attrs = {};
3561             this.paper = vml;
3562             this.matrix = new Matrix;
3563             this._ = {
3564                 transform: [],
3565                 sx: 1,
3566                 sy: 1,
3567                 dx: 0,
3568                 dy: 0,
3569                 deg: 0,
3570                 dirty: 1,
3571                 dirtyT: 1
3572             };
3573             !vml.bottom && (vml.bottom = this);
3574             this.prev = vml.top;
3575             vml.top && (vml.top.next = this);
3576             vml.top = this;
3577             this.next = null;
3578         };
3579         elproto = Element.prototype;
3580         elproto.transform = function (tstr) {
3581             if (tstr == null) {
3582                 return this._.transform;
3583             }
3584             extractTransform(this, tstr);
3585             var matrix = this.matrix.clone(),
3586                 vbs = this.paper._viewBoxShift,
3587                 skew = this.skew,
3588                 o = this.node;
3589             matrix.translate(-.5, -.5);
3590             if (vbs) {
3591                 matrix.scale(vbs.scale, vbs.scale, -1, -1);
3592                 matrix.translate(vbs.dx, vbs.dy);
3593             }
3594             if (Str(this.attrs.fill).indexOf("-") + 1 || this.type == "image") {
3595                 skew.matrix = "1 0 0 1";
3596                 skew.offset = "0 0";
3597                 var split = matrix.split();
3598                 if (!split.isSimple) {
3599                     o.style.filter = matrix.toFilter();
3600                     var bb = this.getBBox(),
3601                         bbt = this.getBBox(1),
3602                         dx = bb.x - bbt.x,
3603                         dy = bb.y - bbt.y;
3604                     o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
3605                     setCoords(this, 1, 1, dx, dy, 0);
3606                 } else {
3607                     o.style.filter = E;
3608                     alert("clean");
3609                     setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
3610                 }
3611             } else {
3612                 o.style.filter = E;
3613                 skew.matrix = Str(matrix);
3614                 skew.offset = matrix.offset();
3615             }
3616             return this;
3617         };
3618         elproto.rotate = function (deg, cx, cy) {
3619             if (this.removed) {
3620                 return this;
3621             }
3622             if (deg == null) {
3623                 return;
3624             }
3625             deg = Str(deg).split(separator);
3626             if (deg.length - 1) {
3627                 cx = toFloat(deg[1]);
3628                 cy = toFloat(deg[2]);
3629             }
3630             deg = toFloat(deg[0]);
3631             (cy == null) && (cx = cy);
3632             if (cx == null || cy == null) {
3633                 var bbox = this.getBBox(1);
3634                 cx = bbox.x + bbox.width / 2;
3635                 cy = bbox.y + bbox.height / 2;
3636             }
3637             this._.dirtyT = 1;
3638             this.transform(this._.transform.concat([["r", deg, cx, cy]]));
3639             return this;
3640         };
3641         elproto.translate = function (dx, dy) {
3642             if (this.removed) {
3643                 return this;
3644             }
3645             dx = Str(dx).split(separator);
3646             if (dx.length - 1) {
3647                 dy = toFloat(dx[1]);
3648             }
3649             dx = toFloat(dx[0]) || 0;
3650             dy = +dy || 0;
3651             if (this._.bbox) {
3652                 this._.bbox.x += dx;
3653                 this._.bbox.y += dy;
3654             }
3655             this.transform(this._.transform.concat([["t", dx, dy]]));
3656             return this;
3657         };
3658         elproto.scale = function (sx, sy, cx, cy) {
3659             if (this.removed) {
3660                 return this;
3661             }
3662             sx = Str(sx).split(separator);
3663             if (sx.length - 1) {
3664                 sy = toFloat(sx[1]);
3665                 cx = toFloat(sx[2]);
3666                 cy = toFloat(sx[3]);
3667                 isNaN(cx) && (cx = null);
3668                 isNaN(cy) && (cy = null);
3669             }
3670             sx = toFloat(sx[0]);
3671             (sy == null) && (sy = sx);
3672             (cy == null) && (cx = cy);
3673             if (cx == null || cy == null) {
3674                 var bbox = this.getBBox(1);
3675             }
3676             cx = cx == null ? bbox.x + bbox.width / 2 : cx;
3677             cy = cy == null ? bbox.y + bbox.height / 2 : cy;
3678             
3679             this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
3680             this._.dirtyT = 1;
3681             return this;
3682         };
3683         elproto.hide = function () {
3684             !this.removed && (this.node.style.display = "none");
3685             return this;
3686         };
3687         elproto.show = function () {
3688             !this.removed && (this.node.style.display = E);
3689             return this;
3690         };
3691         elproto._getBBox = function () {
3692             if (this.removed) {
3693                 return {};
3694             }
3695             if (this.type == "text") {
3696                 return {
3697                     x: this.X + (this.bbx || 0) - this.W / 2,
3698                     y: this.Y - this.H,
3699                     width: this.W,
3700                     height: this.H
3701                 };
3702             } else {
3703                 return pathDimensions(this.attrs.path);
3704             }
3705         };
3706         elproto.remove = function () {
3707             if (this.removed) {
3708                 return;
3709             }
3710             eve.unbind("*.*." + this.id);
3711             tear(this, this.paper);
3712             this.node.parentNode.removeChild(this.node);
3713             this.shape && this.shape.parentNode.removeChild(this.shape);
3714             for (var i in this) {
3715                 delete this[i];
3716             }
3717             this.removed = true;
3718         };
3719         elproto.attr = function (name, value) {
3720             if (this.removed) {
3721                 return this;
3722             }
3723             if (name == null) {
3724                 var res = {};
3725                 for (var i in this.attrs) if (this.attrs[has](i)) {
3726                     res[i] = this.attrs[i];
3727                 }
3728                 res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
3729                 res.transform = this._.transform;
3730                 return res;
3731             }
3732             if (value == null && R.is(name, "string")) {
3733                 if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
3734                     return this.attrs.gradient;
3735                 }
3736                 if (name in this.attrs) {
3737                     return this.attrs[name];
3738                 } else if (R.is(this.paper.customAttributes[name], "function")) {
3739                     return this.paper.customAttributes[name].def;
3740                 } else {
3741                     return availableAttrs[name];
3742                 }
3743             }
3744             if (this.attrs && value == null && R.is(name, array)) {
3745                 var ii, values = {};
3746                 for (i = 0, ii = name.length; i < ii; i++) {
3747                     values[name[i]] = this.attr(name[i]);
3748                 }
3749                 return values;
3750             }
3751             var params;
3752             if (value != null) {
3753                 params = {};
3754                 params[name] = value;
3755             }
3756             value == null && R.is(name, "object") && (params = name);
3757             for (var key in params) {
3758                 eve("attr." + key + "." + this.id, this, params[key]);
3759             }
3760             if (params) {
3761                 for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
3762                     var par = this.paper.customAttributes[key].apply(this, [][concat](params[key]));
3763                     this.attrs[key] = params[key];
3764                     for (var subkey in par) if (par[has](subkey)) {
3765                         params[subkey] = par[subkey];
3766                     }
3767                 }
3768                 // this.paper.canvas.style.display = "none";
3769                 if (params.text && this.type == "text") {
3770                     this.textpath.string = params.text;
3771                 }
3772                 setFillAndStroke(this, params);
3773                 // this.paper.canvas.style.display = E;
3774             }
3775             return this;
3776         };
3777         elproto.toFront = function () {
3778             !this.removed && this.node.parentNode.appendChild(this.node);
3779             this.paper.top != this && tofront(this, this.paper);
3780             return this;
3781         };
3782         elproto.toBack = function () {
3783             if (this.removed) {
3784                 return this;
3785             }
3786             if (this.node.parentNode.firstChild != this.node) {
3787                 this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
3788                 toback(this, this.paper);
3789             }
3790             return this;
3791         };
3792         elproto.insertAfter = function (element) {
3793             if (this.removed) {
3794                 return this;
3795             }
3796             if (element.constructor == Set) {
3797                 element = element[element.length - 1];
3798             }
3799             if (element.node.nextSibling) {
3800                 element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
3801             } else {
3802                 element.node.parentNode.appendChild(this.node);
3803             }
3804             insertafter(this, element, this.paper);
3805             return this;
3806         };
3807         elproto.insertBefore = function (element) {
3808             if (this.removed) {
3809                 return this;
3810             }
3811             if (element.constructor == Set) {
3812                 element = element[0];
3813             }
3814             element.node.parentNode.insertBefore(this.node, element.node);
3815             insertbefore(this, element, this.paper);
3816             return this;
3817         };
3818         elproto.blur = function (size) {
3819             var s = this.node.runtimeStyle,
3820                 f = s.filter;
3821             f = f.replace(blurregexp, E);
3822             if (+size !== 0) {
3823                 this.attrs.blur = size;
3824                 s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
3825                 s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
3826             } else {
3827                 s.filter = f;
3828                 s.margin = 0;
3829                 delete this.attrs.blur;
3830             }
3831         };
3832
3833         thePath = function (pathString, vml) {
3834             var el = createNode("shape");
3835             el.style.cssText = cssDot;
3836             el.coordsize = zoom + S + zoom;
3837             el.coordorigin = vml.coordorigin;
3838             var p = new Element(el, vml),
3839                 attr = {fill: "none", stroke: "#000"};
3840             pathString && (attr.path = pathString);
3841             p.type = "path";
3842             p.path = [];
3843             p.Path = E;
3844             setFillAndStroke(p, attr);
3845             vml.canvas.appendChild(el);
3846             var skew = createNode("skew");
3847             skew.on = true;
3848             el.appendChild(skew);
3849             p.skew = skew;
3850             p.transform(E);
3851             return p;
3852         };
3853         theRect = function (vml, x, y, w, h, r) {
3854             var path = rectPath(x, y, w, h, r),
3855                 res = vml.path(path),
3856                 a = res.attrs;
3857             res.X = a.x = x;
3858             res.Y = a.y = y;
3859             res.W = a.width = w;
3860             res.H = a.height = h;
3861             a.r = r;
3862             a.path = path;
3863             res.type = "rect";
3864             return res;
3865         };
3866         theEllipse = function (vml, x, y, rx, ry) {
3867             var res = vml.path(),
3868                 a = res.attrs;
3869             res.X = x - rx;
3870             res.Y = y - ry;
3871             res.W = rx * 2;
3872             res.H = ry * 2;
3873             res.type = "ellipse";
3874             setFillAndStroke(res, {
3875                 cx: x,
3876                 cy: y,
3877                 rx: rx,
3878                 ry: ry
3879             });
3880             return res;
3881         };
3882         theCircle = function (vml, x, y, r) {
3883             var res = vml.path(),
3884                 a = res.attrs;
3885             res.X = x - r;
3886             res.Y = y - r;
3887             res.W = res.H = r * 2;
3888             res.type = "circle";
3889             setFillAndStroke(res, {
3890                 cx: x,
3891                 cy: y,
3892                 r: r
3893             });
3894             return res;
3895         };
3896         theImage = function (vml, src, x, y, w, h) {
3897             var path = rectPath(x, y, w, h),
3898                 res = vml.path(path).attr({stroke: "none"}),
3899                 a = res.attrs,
3900                 node = res.node,
3901                 fill = node.getElementsByTagName(fillString)[0];
3902             a.src = src;
3903             res.X = a.x = x;
3904             res.Y = a.y = y;
3905             res.W = a.width = w;
3906             res.H = a.height = h;
3907             a.path = path;
3908             res.type = "image";
3909             fill.parentNode == node && node.removeChild(fill);
3910             fill.rotate = true;
3911             fill.src = src;
3912             fill.type = "tile";
3913             res._.fillpos = [x, y];
3914             res._.fillsize = [w, h];
3915             node.appendChild(fill);
3916             setCoords(res, 1, 1, 0, 0, 0);
3917             return res;
3918         };
3919         theText = function (vml, x, y, text) {
3920             var el = createNode("shape"),
3921                 path = createNode("path"),
3922                 o = createNode("textpath");
3923             x = x || 0;
3924             y = y || 0;
3925             text = text || "";
3926             path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
3927             path.textpathok = true;
3928             o.string = Str(text);
3929             o.on = true;
3930             el.style.cssText = "position:absolute;left:0;top:0;width:1px;height:1px";
3931             el.coordsize = zoom + S + zoom;
3932             el.coordorigin = "0 0";
3933             var p = new Element(el, vml),
3934                 attr = {fill: "#000", stroke: "none", font: availableAttrs.font, text: text};
3935             p.shape = el;
3936             p.path = path;
3937             p.textpath = o;
3938             p.type = "text";
3939             p.attrs.text = Str(text);
3940             p.attrs.x = x;
3941             p.attrs.y = y;
3942             p.attrs.w = 1;
3943             p.attrs.h = 1;
3944             setFillAndStroke(p, attr);
3945             el.appendChild(o);
3946             el.appendChild(path);
3947             vml.canvas.appendChild(el);
3948             var skew = createNode("skew");
3949             skew.on = true;
3950             el.appendChild(skew);
3951             p.skew = skew;
3952             p.transform(E);
3953             return p;
3954         };
3955         setSize = function (width, height) {
3956             var cs = this.canvas.style;
3957             this.width = width;
3958             this.height = height;
3959             width == +width && (width += "px");
3960             height == +height && (height += "px");
3961             cs.width = width;
3962             cs.height = height;
3963             cs.clip = "rect(0 " + width + " " + height + " 0)";
3964             if (this._viewBox) {
3965                 setViewBox.apply(this, this._viewBox);
3966             }
3967             return this;
3968         };
3969         setViewBox = function (x, y, w, h, fit) {
3970             eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
3971             var width = this.width,
3972                 height = this.height,
3973                 size = 1 / mmax(w / width, h / height),
3974                 H, W;
3975             if (fit) {
3976                 H = height / h;
3977                 W = width / w;
3978                 if (w * H < width) {
3979                     x -= (width - w * H) / 2 / H;
3980                 }
3981                 if (h * W < height) {
3982                     y -= (height - h * W) / 2 / W;
3983                 }
3984             }
3985             this._viewBox = [x, y, w, h, !!fit];
3986             this._viewBoxShift = {
3987                 dx: -x,
3988                 dy: -y,
3989                 scale: size
3990             };
3991             this.forEach(function (el) {
3992                 el.transform("...");
3993             });
3994             return this;
3995         };
3996         var createNode,
3997             initWin = function (win) {
3998                 var doc = win.document;
3999                 doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
4000                 try {
4001                     !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
4002                     createNode = function (tagName) {
4003                         return doc.createElement('<rvml:' + tagName + ' class="rvml">');
4004                     };
4005                 } catch (e) {
4006                     createNode = function (tagName) {
4007                         return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
4008                     };
4009                 }
4010             };
4011         initWin(g.win);
4012         create = function () {
4013             var con = getContainer[apply](0, arguments),
4014                 container = con.container,
4015                 height = con.height,
4016                 s,
4017                 width = con.width,
4018                 x = con.x,
4019                 y = con.y;
4020             if (!container) {
4021                 throw new Error("VML container not found.");
4022             }
4023             var res = new Paper,
4024                 c = res.canvas = g.doc.createElement("div"),
4025                 cs = c.style;
4026             x = x || 0;
4027             y = y || 0;
4028             width = width || 512;
4029             height = height || 342;
4030             res.width = width;
4031             res.height = height;
4032             width == +width && (width += "px");
4033             height == +height && (height += "px");
4034             res.coordsize = zoom * 1e3 + S + zoom * 1e3;
4035             res.coordorigin = "0 0";
4036             res.span = g.doc.createElement("span");
4037             res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
4038             c.appendChild(res.span);
4039             cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
4040             if (container == 1) {
4041                 g.doc.body.appendChild(c);
4042                 cs.left = x + "px";
4043                 cs.top = y + "px";
4044                 cs.position = "absolute";
4045             } else {
4046                 if (container.firstChild) {
4047                     container.insertBefore(c, container.firstChild);
4048                 } else {
4049                     container.appendChild(c);
4050                 }
4051             }
4052             plugins.call(res, res, R.fn);
4053             res.renderfix = fun;
4054             return res;
4055         };
4056         paperproto.clear = function () {
4057             eve("clear", this);
4058             this.canvas.innerHTML = E;
4059             this.span = g.doc.createElement("span");
4060             this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
4061             this.canvas.appendChild(this.span);
4062             this.bottom = this.top = null;
4063         };
4064         paperproto.remove = function () {
4065             eve("remove", this);
4066             this.canvas.parentNode.removeChild(this.canvas);
4067             for (var i in this) {
4068                 this[i] = removed(i);
4069             }
4070             return true;
4071         };
4072     }
4073  
4074     // WebKit rendering bug workaround method
4075     var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
4076     if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
4077         (navigator.vendor == "Google Inc." && version && version[1] < 8)) {
4078         /*\
4079          * Paper.safari
4080          [ method ]
4081          **
4082          * There is an inconvenient rendering bug in Safari (WebKit):
4083          * sometimes the rendering should be forced.
4084          * This method should help with dealing with this bug.
4085         \*/
4086         paperproto.safari = function () {
4087             var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
4088             setTimeout(function () {rect.remove();});
4089         };
4090     } else {
4091         paperproto.safari = fun;
4092     }
4093  
4094     // Events
4095     var preventDefault = function () {
4096         this.returnValue = false;
4097     },
4098     preventTouch = function () {
4099         return this.originalEvent.preventDefault();
4100     },
4101     stopPropagation = function () {
4102         this.cancelBubble = true;
4103     },
4104     stopTouch = function () {
4105         return this.originalEvent.stopPropagation();
4106     },
4107     addEvent = (function () {
4108         if (g.doc.addEventListener) {
4109             return function (obj, type, fn, element) {
4110                 var realName = supportsTouch && touchMap[type] ? touchMap[type] : type;
4111                 var f = function (e) {
4112                     if (supportsTouch && touchMap[has](type)) {
4113                         for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
4114                             if (e.targetTouches[i].target == obj) {
4115                                 var olde = e;
4116                                 e = e.targetTouches[i];
4117                                 e.originalEvent = olde;
4118                                 e.preventDefault = preventTouch;
4119                                 e.stopPropagation = stopTouch;
4120                                 break;
4121                             }
4122                         }
4123                     }
4124                     return fn.call(element, e);
4125                 };
4126                 obj.addEventListener(realName, f, false);
4127                 return function () {
4128                     obj.removeEventListener(realName, f, false);
4129                     return true;
4130                 };
4131             };
4132         } else if (g.doc.attachEvent) {
4133             return function (obj, type, fn, element) {
4134                 var f = function (e) {
4135                     e = e || g.win.event;
4136                     e.preventDefault = e.preventDefault || preventDefault;
4137                     e.stopPropagation = e.stopPropagation || stopPropagation;
4138                     return fn.call(element, e);
4139                 };
4140                 obj.attachEvent("on" + type, f);
4141                 var detacher = function () {
4142                     obj.detachEvent("on" + type, f);
4143                     return true;
4144                 };
4145                 return detacher;
4146             };
4147         }
4148     })(),
4149     drag = [],
4150     dragMove = function (e) {
4151         var x = e.clientX,
4152             y = e.clientY,
4153             scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
4154             scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
4155             dragi,
4156             j = drag.length;
4157         while (j--) {
4158             dragi = drag[j];
4159             if (supportsTouch) {
4160                 var i = e.touches.length,
4161                     touch;
4162                 while (i--) {
4163                     touch = e.touches[i];
4164                     if (touch.identifier == dragi.el._drag.id) {
4165                         x = touch.clientX;
4166                         y = touch.clientY;
4167                         (e.originalEvent ? e.originalEvent : e).preventDefault();
4168                         break;
4169                     }
4170                 }
4171             } else {
4172                 e.preventDefault();
4173             }
4174             var node = dragi.el.node,
4175                 o,
4176                 next = node.nextSibling,
4177                 parent = node.parentNode,
4178                 display = node.style.display;
4179             g.win.opera && parent.removeChild(node);
4180             node.style.display = "none";
4181             o = dragi.el.paper.getElementByPoint(x, y);
4182             node.style.display = display;
4183             g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
4184             o && eve("drag.over." + dragi.el.id, dragi.el, o);
4185             x += scrollX;
4186             y += scrollY;
4187             eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
4188         }
4189     },
4190     dragUp = function (e) {
4191         R.unmousemove(dragMove).unmouseup(dragUp);
4192         var i = drag.length,
4193             dragi;
4194         while (i--) {
4195             dragi = drag[i];
4196             dragi.el._drag = {};
4197             eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
4198         }
4199         drag = [];
4200     };
4201     for (var i = events.length; i--;) {
4202         (function (eventName) {
4203             R[eventName] = Element.prototype[eventName] = function (fn, scope) {
4204                 if (R.is(fn, "function")) {
4205                     this.events = this.events || [];
4206                     this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
4207                 }
4208                 return this;
4209             };
4210             R["un" + eventName] = Element.prototype["un" + eventName] = function (fn) {
4211                 var events = this.events,
4212                     l = events.length;
4213                 while (l--) if (events[l].name == eventName && events[l].f == fn) {
4214                     events[l].unbind();
4215                     events.splice(l, 1);
4216                     !events.length && delete this.events;
4217                     return this;
4218                 }
4219                 return this;
4220             };
4221         })(events[i]);
4222     }
4223     /*\
4224      * Element.hover
4225      [ method ]
4226      **
4227      * Adds event handlers for hover for the element.
4228      > Parameters
4229      - f_in (function) handler for hover in
4230      - f_out (function) handler for hover out
4231      - icontext (object) #optional context for hover in handler
4232      - ocontext (object) #optional context for hover out handler
4233      = (object) @Element
4234     \*/
4235     elproto.hover = function (f_in, f_out, scope_in, scope_out) {
4236         return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
4237     };
4238     /*\
4239      * Element.unhover
4240      [ method ]
4241      **
4242      * Removes event handlers for hover for the element.
4243      > Parameters
4244      - f_in (function) handler for hover in
4245      - f_out (function) handler for hover out
4246      = (object) @Element
4247     \*/
4248     elproto.unhover = function (f_in, f_out) {
4249         return this.unmouseover(f_in).unmouseout(f_out);
4250     };
4251     /*\
4252      * Element.drag
4253      [ method ]
4254      **
4255      * Adds event handlers for drag of the element.
4256      > Parameters
4257      - onmove (function) handler for moving
4258      - onstart (function) handler for drag start
4259      - onend (function) handler for drag end
4260      - mcontext (object) #optional context for moving handler
4261      - scontext (object) #optional context for drag start handler
4262      - econtext (object) #optional context for drag end handler
4263      * Additionaly following `drag` events will be triggered: `drag.start.<id>` on start, 
4264      * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element 
4265      * `drag.over.<id>` will be fired as well.
4266      *
4267      * Start event and start handler will be called in specified context or in context of the element with following parameters:
4268      o x (number) x position of the mouse
4269      o y (number) y position of the mouse
4270      o event (object) DOM event object
4271      * Move event and move handler will be called in specified context or in context of the element with following parameters:
4272      o dx (number) shift by x from the start point
4273      o dy (number) shift by y from the start point
4274      o x (number) x position of the mouse
4275      o y (number) y position of the mouse
4276      o event (object) DOM event object
4277      * End event and end handler will be called in specified context or in context of the element with following parameters:
4278      o event (object) DOM event object
4279      = (object) @Element
4280     \*/
4281     elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
4282         function start(e) {
4283             (e.originalEvent || e).preventDefault();
4284             var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
4285                 scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
4286             this._drag.x = e.clientX + scrollX;
4287             this._drag.y = e.clientY + scrollY;
4288             this._drag.id = e.identifier;
4289             !drag.length && R.mousemove(dragMove).mouseup(dragUp);
4290             drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
4291             onstart && eve.on("drag.start." + this.id, onstart);
4292             onmove && eve.on("drag.move." + this.id, onmove);
4293             onend && eve.on("drag.end." + this.id, onend);
4294             eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
4295         }
4296         this._drag = {};
4297         this.mousedown(start);
4298         return this;
4299     };
4300     /*\
4301      * Element.onDragOver
4302      [ method ]
4303      **
4304      * Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id).
4305      > Parameters
4306      - f (function) handler for event
4307     \*/
4308     elproto.onDragOver = function (f) {
4309         f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id);
4310     };
4311     /*\
4312      * Element.undrag
4313      [ method ]
4314      **
4315      * Removes all drag event handlers from given element.
4316     \*/
4317     elproto.undrag = function () {
4318         var i = drag.length;
4319         while (i--) if (drag[i].el == this) {
4320             R.unmousedown(drag[i].start);
4321             drag.splice(i++, 1);
4322             eve.unbind("drag.*." + this.id);
4323         }
4324         !drag.length && R.unmousemove(dragMove).unmouseup(dragUp);
4325     };
4326     /*\
4327      * Paper.circle
4328      [ method ]
4329      **
4330      * Draws a circle.
4331      **
4332      > Parameters
4333      **
4334      - x (number) x coordinate of the centre
4335      - y (number) y coordinate of the centre
4336      - r (number) radius
4337      = (object) Raphaël element object with type “circle”
4338      **
4339      > Usage
4340      | var c = paper.circle(50, 50, 40);
4341     \*/
4342     paperproto.circle = function (x, y, r) {
4343         return theCircle(this, x || 0, y || 0, r || 0);
4344     };
4345     /*\
4346      * Paper.rect
4347      [ method ]
4348      *
4349      * Draws a rectangle.
4350      **
4351      > Parameters
4352      **
4353      - x (number) x coordinate of the top left corner
4354      - y (number) y coordinate of the top left corner
4355      - width (number) width
4356      - height (number) height
4357      - r (number) #optional radius for rounded corners, default is 0
4358      = (object) Raphaël element object with type “rect”
4359      **
4360      > Usage
4361      | // regular rectangle
4362      | var c = paper.rect(10, 10, 50, 50);
4363      | // rectangle with rounded corners
4364      | var c = paper.rect(40, 40, 50, 50, 10);
4365     \*/
4366     paperproto.rect = function (x, y, w, h, r) {
4367         return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
4368     };
4369     /*\
4370      * Paper.ellipse
4371      [ method ]
4372      **
4373      * Draws an ellipse.
4374      **
4375      > Parameters
4376      **
4377      - x (number) x coordinate of the centre
4378      - y (number) y coordinate of the centre
4379      - rx (number) horizontal radius
4380      - ry (number) vertical radius
4381      = (object) Raphaël element object with type “ellipse”
4382      **
4383      > Usage
4384      | var c = paper.ellipse(50, 50, 40, 20);
4385     \*/
4386     paperproto.ellipse = function (x, y, rx, ry) {
4387         return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0);
4388     };
4389     /*\
4390      * Paper.path
4391      [ method ]
4392      **
4393      * Creates a path element by given path data string.
4394      **
4395      > Parameters
4396      **
4397      - pathString (string) path data in SVG path string format.
4398      = (object) Raphaël element object with type “ellipse”
4399      # Details of a path's data attribute's format are described in the <a href="http://www.w3.org/TR/SVG/paths.html#PathData">SVG specification</a>.
4400      **
4401      > Usage
4402      | var c = paper.path("M10 10L90 90");
4403      | // draw a diagonal line:
4404      | // move to 10,10, line to 90,90
4405     \*/
4406     paperproto.path = function (pathString) {
4407         pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
4408         return thePath(R.format[apply](R, arguments), this);
4409     };
4410     /*\
4411      * Paper.image
4412      [ method ]
4413      **
4414      * Embeds an image into the surface.
4415      **
4416      > Parameters
4417      **
4418      - src (string) URI of the source image
4419      - x (number) x coordinate position
4420      - y (number) y coordinate position
4421      - width (number) width of the image
4422      - height (number) height of the image
4423      = (object) Raphaël element object with type “image”
4424      **
4425      > Usage
4426      | var c = paper.image("apple.png", 10, 10, 80, 80);
4427     \*/
4428     paperproto.image = function (src, x, y, w, h) {
4429         return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
4430     };
4431     /*\
4432      * Paper.text
4433      [ method ]
4434      **
4435      * Draws a text string. If you need line breaks, put “\n” in the string.
4436      **
4437      > Parameters
4438      **
4439      - x (number) x coordinate position
4440      - y (number) y coordinate position
4441      - text (string) The text string to draw
4442      = (object) Raphaël element object with type “text”
4443      **
4444      > Usage
4445      | var t = paper.text(50, 50, "Raphaël\nkicks\nbutt!");
4446     \*/
4447     paperproto.text = function (x, y, text) {
4448         return theText(this, x || 0, y || 0, Str(text));
4449     };
4450     /*\
4451      * Paper.set
4452      [ method ]
4453      **
4454      * Creates array-like object to keep and operate several elements at once.
4455      * Warning: it doesn’t create any elements for itself in the page, it just groups existing elements.
4456      * Sets act as pseudo elements — all methods available to an element can be used on a set.
4457      = (object) array-like object that represents set of elements
4458      **
4459      > Usage
4460      | var st = paper.set();
4461      | st.push(
4462      |     paper.circle(10, 10, 5),
4463      |     paper.circle(30, 10, 5)
4464      | );
4465      | st.attr({fill: "red"});
4466     \*/
4467     paperproto.set = function (itemsArray) {
4468         arguments.length > 1 && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
4469         return new Set(itemsArray);
4470     };
4471     /*\
4472      * Paper.setSize
4473      [ method ]
4474      **
4475      * If you need to change dimensions of the canvas call this method
4476      **
4477      > Parameters
4478      **
4479      - width (number) new width of the canvas
4480      - height (number) new height of the canvas
4481      > Usage
4482      | var st = paper.set();
4483      | st.push(
4484      |     paper.circle(10, 10, 5),
4485      |     paper.circle(30, 10, 5)
4486      | );
4487      | st.attr({fill: "red"});
4488     \*/
4489     paperproto.setSize = setSize;
4490     /*\
4491      * Paper.setViewBox
4492      [ method ]
4493      **
4494      * Sets the view box of the paper. Practically it gives you ability to zoom and pan whole paper surface by 
4495      * specifying new boundaries.
4496      **
4497      > Parameters
4498      **
4499      x, y, w, h, fit
4500      - x (number) new x position, default is `0`
4501      - y (number) new y position, default is `0`
4502      - w (number) new width of the canvas
4503      - h (number) new height of the canvas
4504      - fit (boolean) `true` if you want graphics to fit into new boundary box
4505     \*/
4506     paperproto.setViewBox = setViewBox;
4507     /*\
4508      * Paper.top
4509      [ property ]
4510      **
4511      * Points to the topmost element on the paper
4512     \*/
4513     /*\
4514      * Paper.bottom
4515      [ property ]
4516      **
4517      * Points to the bottom element on the paper
4518     \*/
4519     paperproto.top = paperproto.bottom = null;
4520     /*\
4521      * Paper.raphael
4522      [ property ]
4523      **
4524      * Points to the @Raphael object/function
4525     \*/
4526     paperproto.raphael = R;
4527     var getOffset = function (elem) {
4528         var box = elem.getBoundingClientRect(),
4529             doc = elem.ownerDocument,
4530             body = doc.body,
4531             docElem = doc.documentElement,
4532             clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
4533             top  = box.top  + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
4534             left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
4535         return {
4536             y: top,
4537             x: left
4538         };
4539     };
4540     /*\
4541      * Paper.getElementByPoint
4542      [ method ]
4543      **
4544      * Returns you topmost element under given point.
4545      **
4546      = (object) Raphaël element object
4547      > Parameters
4548      **
4549      - x (number) x coordinate from the top left corner of the window
4550      - y (number) y coordinate from the top left corner of the window
4551      > Usage
4552      | paper.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
4553     \*/
4554     paperproto.getElementByPoint = function (x, y) {
4555         var paper = this,
4556             svg = paper.canvas,
4557             target = g.doc.elementFromPoint(x, y);
4558         if (g.win.opera && target.tagName == "svg") {
4559             var so = getOffset(svg),
4560                 sr = svg.createSVGRect();
4561             sr.x = x - so.x;
4562             sr.y = y - so.y;
4563             sr.width = sr.height = 1;
4564             var hits = svg.getIntersectionList(sr, null);
4565             if (hits.length) {
4566                 target = hits[hits.length - 1];
4567             }
4568         }
4569         if (!target) {
4570             return null;
4571         }
4572         while (target.parentNode && target != svg.parentNode && !target.raphael) {
4573             target = target.parentNode;
4574         }
4575         target == paper.canvas.parentNode && (target = svg);
4576         target = target && target.raphael ? paper.getById(target.raphaelid) : null;
4577         return target;
4578     };
4579     /*\
4580      * Paper.getById
4581      [ method ]
4582      **
4583      * Returns you element by its internal ID.
4584      **
4585      > Parameters
4586      **
4587      - id (number) id
4588      = (object) Raphaël element object
4589     \*/
4590     paperproto.getById = function (id) {
4591         var bot = this.bottom;
4592         while (bot) {
4593             if (bot.id == id) {
4594                 return bot;
4595             }
4596             bot = bot.next;
4597         }
4598         return null;
4599     };
4600     /*\
4601      * Paper.forEach
4602      [ method ]
4603      **
4604      * Executes given function for each element on the paper
4605      *
4606      * If callback function returns `false` it will stop loop running.
4607      **
4608      > Parameters
4609      **
4610      - callback (function) function to run
4611      - thisArg (object) context object for the callback
4612      = (object) Paper object
4613     \*/
4614     paperproto.forEach = function (callback, thisArg) {
4615         var bot = this.bottom;
4616         while (bot) {
4617             if (callback.call(thisArg, bot) === false) {
4618                 return this;
4619             }
4620             bot = bot.next;
4621         }
4622         return this;
4623     };
4624     function x_y() {
4625         return this.x + S + this.y;
4626     }
4627     function x_y_w_h() {
4628         return this.x + S + this.y + S + this.width + "\xd7" + this.height;
4629     }
4630     /*\
4631      * Element.getBBox
4632      [ method ]
4633      **
4634      * Return bounding box for a given element
4635      **
4636      > Parameters
4637      **
4638      - isWithoutTransform (boolean) flag, `true` if you want to have bounding box before transformations. Default is `false`.
4639      = (object) Bounding box object:
4640      o {
4641      o     x: (number) top left corner x
4642      o     y: (number) top left corner y
4643      o     width: (number) width
4644      o     height: (number) height
4645      o }
4646     \*/
4647     elproto.getBBox = function (isWithoutTransform) {
4648         if (this.removed) {
4649             return {};
4650         }
4651         var _ = this._;
4652         if (isWithoutTransform) {
4653             if (_.dirty || !_.bboxwt) {
4654                 this.realPath = getPath[this.type](this);
4655                 _.bboxwt = pathDimensions(this.realPath);
4656                 _.bboxwt.toString = x_y_w_h;
4657                 _.dirty = 0;
4658             }
4659             return _.bboxwt;
4660         }
4661         if (_.dirty || _.dirtyT || !_.bbox) {
4662             if (_.dirty || !this.realPath) {
4663                 _.bboxwt = 0;
4664                 this.realPath = getPath[this.type](this);
4665             }
4666             _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
4667             _.bbox.toString = x_y_w_h;
4668             _.dirty = _.dirtyT = 0;
4669         }
4670         return _.bbox;
4671     };
4672     /*\
4673      * Element.clone
4674      [ method ]
4675      **
4676      = (object) clone of a given element
4677      **
4678     \*/
4679     elproto.clone = function () {
4680         if (this.removed) {
4681             return null;
4682         }
4683         return this.paper[this.type]().attr(this.attr());
4684     };
4685     /*\
4686      * Element.glow
4687      [ method ]
4688      **
4689      * Return set of elements that create glow-like effect around given element. See @Paper.set.
4690      *
4691      * Note: Glow is not connected to the element. If you change element attributes it won’t adjust itself.
4692      **
4693      > Parameters
4694      **
4695      - glow (object) #optional parameters object with all properties optional:
4696      o {
4697      o     width (number) size of the glow, default is `10`
4698      o     fill (boolean) will it be filled, default is `false`
4699      o     opacity: opacity, default is `0.5`
4700      o     offsetx: horizontal offset, default is `0`
4701      o     offsety: vertical offset, default is `0`
4702      o     color: glow colour, default is `black`
4703      o }
4704      = (object) @Paper.set of elements that represents glow
4705     \*/
4706     elproto.glow = function (glow) {
4707         if (this.type == "text") {
4708             return null;
4709         }
4710         glow = glow || {};
4711         var s = {
4712             width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
4713             fill: glow.fill || false,
4714             opacity: glow.opacity || .5,
4715             offsetx: glow.offsetx || 0,
4716             offsety: glow.offsety || 0,
4717             color: glow.color || "#000"
4718         },
4719             c = s.width / 2,
4720             r = this.paper,
4721             out = r.set(),
4722             path = this.realPath || getPath[this.type](this);
4723         path = this.matrix ? mapPath(path, this.matrix) : path;
4724         for (var i = 1; i < c + 1; i++) {
4725             out.push(r.path(path).attr({
4726                 stroke: s.color,
4727                 fill: s.fill ? s.color : "none",
4728                 "stroke-linejoin": "round",
4729                 "stroke-linecap": "round",
4730                 "stroke-width": +(s.width / c * i).toFixed(3),
4731                 opacity: +(s.opacity / c).toFixed(3)
4732             }));
4733         }
4734         return out.insertBefore(this).translate(s.offsetx, s.offsety);
4735     };
4736     var curveslengths = {},
4737     getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
4738         var len = 0,
4739             precision = 100,
4740             name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(),
4741             cache = curveslengths[name],
4742             old, dot;
4743         !cache && (curveslengths[name] = cache = {data: []});
4744         cache.timer && clearTimeout(cache.timer);
4745         cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3);
4746         if (length != null && !cache.precision) {
4747             var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
4748             cache.precision = ~~total * 10;
4749             cache.data = [];
4750         }
4751         precision = cache.precision || precision;
4752         for (var i = 0; i < precision + 1; i++) {
4753             if (cache.data[i * precision]) {
4754                 dot = cache.data[i * precision];
4755             } else {
4756                 dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision);
4757                 cache.data[i * precision] = dot;
4758             }
4759             i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
4760             if (length != null && len >= length) {
4761                 return dot;
4762             }
4763             old = dot;
4764         }
4765         if (length == null) {
4766             return len;
4767         }
4768     },
4769     getLengthFactory = function (istotal, subpath) {
4770         return function (path, length, onlystart) {
4771             path = path2curve(path);
4772             var x, y, p, l, sp = "", subpaths = {}, point,
4773                 len = 0;
4774             for (var i = 0, ii = path.length; i < ii; i++) {
4775                 p = path[i];
4776                 if (p[0] == "M") {
4777                     x = +p[1];
4778                     y = +p[2];
4779                 } else {
4780                     l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
4781                     if (len + l > length) {
4782                         if (subpath && !subpaths.start) {
4783                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
4784                             sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
4785                             if (onlystart) {return sp;}
4786                             subpaths.start = sp;
4787                             sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
4788                             len += l;
4789                             x = +p[5];
4790                             y = +p[6];
4791                             continue;
4792                         }
4793                         if (!istotal && !subpath) {
4794                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
4795                             return {x: point.x, y: point.y, alpha: point.alpha};
4796                         }
4797                     }
4798                     len += l;
4799                     x = +p[5];
4800                     y = +p[6];
4801                 }
4802                 sp += p.shift() + p;
4803             }
4804             subpaths.end = sp;
4805             point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], 1);
4806             point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
4807             return point;
4808         };
4809     };
4810     var getTotalLength = getLengthFactory(1),
4811         getPointAtLength = getLengthFactory(),
4812         getSubpathsAtLength = getLengthFactory(0, 1);
4813     /*\
4814      * Raphael.getTotalLength
4815      [ method ]
4816      **
4817      * Returns length of the given path in pixels.
4818      **
4819      > Parameters
4820      **
4821      - path (string) SVG path string.
4822      **
4823      = (number) length.
4824     \*/
4825     R.getTotalLength = getTotalLength;
4826     /*\
4827      * Raphael.getPointAtLength
4828      [ method ]
4829      **
4830      * Return coordinates of the point located at the given length on the given path.
4831      **
4832      > Parameters
4833      **
4834      - path (string) SVG path string
4835      - length (number)
4836      **
4837      = (object) representation of the point:
4838      o {
4839      o     x: (number) x coordinate
4840      o     y: (number) y coordinate
4841      o     alpha: (number) angle of derivative
4842      o }
4843     \*/
4844     R.getPointAtLength = getPointAtLength;
4845     /*\
4846      * Raphael.getSubpath
4847      [ method ]
4848      **
4849      * Return subpath of a given path from given length to given length.
4850      **
4851      > Parameters
4852      **
4853      - path (string) SVG path string
4854      - from (number) position of the start of the segment
4855      - to (number) position of the end of the segment
4856      **
4857      = (string) pathstring for the segment
4858     \*/
4859     R.getSubpath = function (path, from, to) {
4860         if (abs(this.getTotalLength(path) - to) < 1e-6) {
4861             return getSubpathsAtLength(path, from).end;
4862         }
4863         var a = getSubpathsAtLength(path, to, 1);
4864         return from ? getSubpathsAtLength(a, from).end : a;
4865     };
4866     /*\
4867      * Element.getTotalLength
4868      [ method ]
4869      **
4870      * Returns length of the path in pixels. Only works for element of “path” type.
4871      = (number) length.
4872     \*/
4873     elproto.getTotalLength = function () {
4874         if (this.type != "path") {return;}
4875         if (this.node.getTotalLength) {
4876             return this.node.getTotalLength();
4877         }
4878         return getTotalLength(this.attrs.path);
4879     };
4880     /*\
4881      * Element.getPointAtLength
4882      [ method ]
4883      **
4884      * Return coordinates of the point located at the given length on the given path. Only works for element of “path” type.
4885      **
4886      > Parameters
4887      **
4888      - length (number)
4889      **
4890      = (object) representation of the point:
4891      o {
4892      o     x: (number) x coordinate
4893      o     y: (number) y coordinate
4894      o     alpha: (number) angle of derivative
4895      o }
4896     \*/
4897     elproto.getPointAtLength = function (length) {
4898         if (this.type != "path") {return;}
4899         return getPointAtLength(this.attrs.path, length);
4900     };
4901     /*\
4902      * Element.getSubpath
4903      [ method ]
4904      **
4905      * Return subpath of a given element from given length to given length. Only works for element of “path” type.
4906      **
4907      > Parameters
4908      **
4909      - from (number) position of the start of the segment
4910      - to (number) position of the end of the segment
4911      **
4912      = (string) pathstring for the segment
4913     \*/
4914     elproto.getSubpath = function (from, to) {
4915         if (this.type != "path") {return;}
4916         return R.getSubpath(this.attrs.path, from, to);
4917     };
4918     /*\
4919      * Raphael.easing_formulas
4920      [ property ]
4921      **
4922      * Object that contains easing formulas for animation. You could extend it with your own. By default it has following list of easing:
4923      # <ul>
4924      #     <li>“linear”</li>
4925      #     <li>“&lt;” or “easeIn” or “ease-in”</li>
4926      #     <li>“>” or “easeOut” or “ease-out”</li>
4927      #     <li>“&lt;>” or “easeInOut” or “ease-in-out”</li>
4928      #     <li>“backIn” or “back-in”</li>
4929      #     <li>“backOut” or “back-out”</li>
4930      #     <li>“elastic”</li>
4931      #     <li>“bounce”</li>
4932      # </ul>
4933      # <p>See also <a href="http://raphaeljs.com/easing.html">Easing demo</a>.</p>
4934     \*/
4935     var ef = R.easing_formulas = {
4936         linear: function (n) {
4937             return n;
4938         },
4939         "<": function (n) {
4940             return pow(n, 1.7);
4941         },
4942         ">": function (n) {
4943             return pow(n, .48);
4944         },
4945         "<>": function (n) {
4946             var q = .48 - n / 1.04,
4947                 Q = math.sqrt(.1734 + q * q),
4948                 x = Q - q,
4949                 X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
4950                 y = -Q - q,
4951                 Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
4952                 t = X + Y + .5;
4953             return (1 - t) * 3 * t * t + t * t * t;
4954         },
4955         backIn: function (n) {
4956             var s = 1.70158;
4957             return n * n * ((s + 1) * n - s);
4958         },
4959         backOut: function (n) {
4960             n = n - 1;
4961             var s = 1.70158;
4962             return n * n * ((s + 1) * n + s) + 1;
4963         },
4964         elastic: function (n) {
4965             if (n == !!n) {
4966                 return n;
4967             }
4968             return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
4969         },
4970         bounce: function (n) {
4971             var s = 7.5625,
4972                 p = 2.75,
4973                 l;
4974             if (n < (1 / p)) {
4975                 l = s * n * n;
4976             } else {
4977                 if (n < (2 / p)) {
4978                     n -= (1.5 / p);
4979                     l = s * n * n + .75;
4980                 } else {
4981                     if (n < (2.5 / p)) {
4982                         n -= (2.25 / p);
4983                         l = s * n * n + .9375;
4984                     } else {
4985                         n -= (2.625 / p);
4986                         l = s * n * n + .984375;
4987                     }
4988                 }
4989             }
4990             return l;
4991         }
4992     };
4993     ef.easeIn = ef["ease-in"] = ef["<"];
4994     ef.easeOut = ef["ease-out"] = ef[">"];
4995     ef.easeInOut = ef["ease-in-out"] = ef["<>"];
4996     ef["back-in"] = ef.backIn;
4997     ef["back-out"] = ef.backOut;
4998
4999     var animationElements = [],
5000         requestAnimFrame = window.requestAnimationFrame       ||
5001                            window.webkitRequestAnimationFrame ||
5002                            window.mozRequestAnimationFrame    ||
5003                            window.oRequestAnimationFrame      ||
5004                            window.msRequestAnimationFrame     ||
5005                            function (callback) {
5006                                setTimeout(callback, 16);
5007                            },
5008         animation = function () {
5009             var Now = +new Date,
5010                 l = 0;
5011             for (; l < animationElements.length; l++) {
5012                 var e = animationElements[l];
5013                 if (e.el.removed || e.paused) {
5014                     continue;
5015                 }
5016                 var time = Now - e.start,
5017                     ms = e.ms,
5018                     easing = e.easing,
5019                     from = e.from,
5020                     diff = e.diff,
5021                     to = e.to,
5022                     t = e.t,
5023                     that = e.el,
5024                     set = {},
5025                     now;
5026                 if (e.initstatus) {
5027                     time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
5028                     e.status = e.initstatus;
5029                     delete e.initstatus;
5030                     e.stop && animationElements.splice(l--, 1);
5031                 } else {
5032                     e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
5033                 }
5034                 if (time < 0) {
5035                     continue;
5036                 }
5037                 if (time < ms) {
5038                     var pos = easing(time / ms);
5039                     for (var attr in from) if (from[has](attr)) {
5040                         switch (availableAnimAttrs[attr]) {
5041                             case nu:
5042                                 now = +from[attr] + pos * ms * diff[attr];
5043                                 break;
5044                             case "colour":
5045                                 now = "rgb(" + [
5046                                     upto255(round(from[attr].r + pos * ms * diff[attr].r)),
5047                                     upto255(round(from[attr].g + pos * ms * diff[attr].g)),
5048                                     upto255(round(from[attr].b + pos * ms * diff[attr].b))
5049                                 ].join(",") + ")";
5050                                 break;
5051                             case "path":
5052                                 now = [];
5053                                 for (var i = 0, ii = from[attr].length; i < ii; i++) {
5054                                     now[i] = [from[attr][i][0]];
5055                                     for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
5056                                         now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
5057                                     }
5058                                     now[i] = now[i].join(S);
5059                                 }
5060                                 now = now.join(S);
5061                                 break;
5062                             case "transform":
5063                                 if (diff[attr].real) {
5064                                     now = [];
5065                                     for (i = 0, ii = from[attr].length; i < ii; i++) {
5066                                         now[i] = [from[attr][i][0]];
5067                                         for (j = 1, jj = from[attr][i].length; j < jj; j++) {
5068                                             now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
5069                                         }
5070                                     }
5071                                 } else {
5072                                     var get = function (i) {
5073                                         return +from[attr][i] + pos * ms * diff[attr][i];
5074                                     };
5075                                     // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
5076                                     now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
5077                                 }
5078                                 break;
5079                             case "csv":
5080                                 if (attr == "clip-rect") {
5081                                     now = [];
5082                                     i = 4;
5083                                     while (i--) {
5084                                         now[i] = +from[attr][i] + pos * ms * diff[attr][i];
5085                                     }
5086                                 }
5087                                 break;
5088                             default:
5089                                 var from2 = [].concat(from[attr]);
5090                                 now = [];
5091                                 i = that.paper.customAttributes[attr].length;
5092                                 while (i--) {
5093                                     now[i] = +from2[i] + pos * ms * diff[attr][i];
5094                                 }
5095                                 break;
5096                         }
5097                         set[attr] = now;
5098                     }
5099                     that.attr(set);
5100                     (function (id, that, anim) {
5101                         setTimeout(function () {
5102                             eve("anim.frame." + id, that, anim);
5103                         });
5104                     })(that.id, that, e.anim);
5105                 } else {
5106                     (function(f, el, a) {
5107                         setTimeout(function() {
5108                             eve("anim.finish." + el.id, el, a);
5109                             R.is(f, "function") && f.call(el);
5110                         });
5111                     })(e.callback, that, e.anim);
5112                     that.attr(to);
5113                     animationElements.splice(l--, 1);
5114                     if (e.repeat > 1 && !e.next) {
5115                         runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
5116                     }
5117                     if (e.next && !e.stop) {
5118                         runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
5119                     }
5120                 }
5121             }
5122             R.svg && that && that.paper && that.paper.safari();
5123             animationElements.length && requestAnimFrame(animation);
5124         },
5125         upto255 = function (color) {
5126             return color > 255 ? 255 : color < 0 ? 0 : color;
5127         };
5128     /*\
5129      * Element.animateWith
5130      [ method ]
5131      **
5132      * Acts similar to @Element.animate, but ensure that given animation runs in sync with another given element.
5133      **
5134      > Parameters
5135      **
5136      - params (object) final attributes for the element, see also @Element.attr
5137      - ms (number) number of milliseconds for animation to run
5138      - easing (string) #optional easing type. Accept on of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
5139      - callback (function) #optional callback function. Will be called at the end of animation.
5140      * or
5141      - animation (object) animation object, see @Raphael.animation
5142      **
5143      = (object) original element
5144     \*/
5145     elproto.animateWith = function (element, params, ms, easing, callback) {
5146         this.animate(params, ms, easing, callback);
5147         var start, el;
5148         for (var i = 0, ii = animationElements.length; i < ii; i++) {
5149             el = animationElements[i];
5150             if (el.el.id == element.id) {
5151                 start = el.timestamp;
5152             } else if (el.el.id == this.id) {
5153                 el.start = start;
5154             }
5155         }
5156         return this.animate(params, ms, easing, callback);
5157     };
5158     function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
5159         var cx = 3 * p1x,
5160             bx = 3 * (p2x - p1x) - cx,
5161             ax = 1 - cx - bx,
5162             cy = 3 * p1y,
5163             by = 3 * (p2y - p1y) - cy,
5164             ay = 1 - cy - by;
5165         function sampleCurveX(t) {
5166             return ((ax * t + bx) * t + cx) * t;
5167         }
5168         function solve(x, epsilon) {
5169             var t = solveCurveX(x, epsilon);
5170             return ((ay * t + by) * t + cy) * t;
5171         }
5172         function solveCurveX(x, epsilon) {
5173             var t0, t1, t2, x2, d2, i;
5174             for(t2 = x, i = 0; i < 8; i++) {
5175                 x2 = sampleCurveX(t2) - x;
5176                 if (abs(x2) < epsilon) {
5177                     return t2;
5178                 }
5179                 d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
5180                 if (abs(d2) < 1e-6) {
5181                     break;
5182                 }
5183                 t2 = t2 - x2 / d2;
5184             }
5185             t0 = 0;
5186             t1 = 1;
5187             t2 = x;
5188             if (t2 < t0) {
5189                 return t0;
5190             }
5191             if (t2 > t1) {
5192                 return t1;
5193             }
5194             while (t0 < t1) {
5195                 x2 = sampleCurveX(t2);
5196                 if (abs(x2 - x) < epsilon) {
5197                     return t2;
5198                 }
5199                 if (x > x2) {
5200                     t0 = t2;
5201                 } else {
5202                     t1 = t2;
5203                 }
5204                 t2 = (t1 - t0) / 2 + t0;
5205             }
5206             return t2;
5207         }
5208         return solve(t, 1 / (200 * duration));
5209     }
5210     elproto.onAnimation = function (f) {
5211         f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id);
5212         return this;
5213     };
5214     function Animation(anim, ms) {
5215         var percents = [];
5216         this.anim = anim;
5217         this.ms = ms;
5218         this.times = 1;
5219         if (this.anim) {
5220             for (var attr in this.anim) if (this.anim[has](attr)) {
5221                 percents.push(+attr);
5222             }
5223             percents.sort(sortByNumber);
5224         }
5225         this.top = percents[percents.length - 1];
5226         this.percents = percents;
5227     }
5228     /*\
5229      * Animation.delay
5230      [ method ]
5231      **
5232      * Creates a copy of existing animation object with given delay.
5233      **
5234      > Parameters
5235      **
5236      - delay (number) number of ms to pass between animation start and actual animation
5237      **
5238      = (object) new altered Animation object
5239     \*/
5240     Animation.prototype.delay = function (delay) {
5241         var a = new Animation(this.anim, this.ms);
5242         a.times = this.times;
5243         a.del = +delay || 0;
5244         return a;
5245     };
5246     /*\
5247      * Animation.repeat
5248      [ method ]
5249      **
5250      * Creates a copy of existing animation object with given repetition.
5251      **
5252      > Parameters
5253      **
5254      - repeat (number) number iterations of animation. For infinite animation pass `Infinity`
5255      **
5256      = (object) new altered Animation object
5257     \*/
5258     Animation.prototype.repeat = function (times) { 
5259         var a = new Animation(this.anim, this.ms);
5260         a.del = this.del;
5261         a.times = math.floor(mmax(times, 0)) || 1;
5262         return a;
5263     };
5264     function runAnimation(anim, element, percent, status, totalOrigin, times) {
5265         percent = toFloat(percent);
5266         var params,
5267             isInAnim,
5268             isInAnimSet,
5269             percents = [],
5270             next,
5271             prev,
5272             timestamp,
5273             ms = anim.ms,
5274             from = {},
5275             to = {},
5276             diff = {};
5277         if (status) {
5278             for (i = 0, ii = animationElements.length; i < ii; i++) {
5279                 var e = animationElements[i];
5280                 if (e.el.id == element.id && e.anim == anim) {
5281                     if (e.percent != percent) {
5282                         animationElements.splice(i, 1);
5283                         isInAnimSet = 1;
5284                     } else {
5285                         isInAnim = e;
5286                     }
5287                     element.attr(e.totalOrigin);
5288                     break;
5289                 }
5290             }
5291         } else {
5292             status = +to; // NaN
5293         }
5294         for (var i = 0, ii = anim.percents.length; i < ii; i++) {
5295             if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
5296                 percent = anim.percents[i];
5297                 prev = anim.percents[i - 1] || 0;
5298                 ms = ms / anim.top * (percent - prev);
5299                 next = anim.percents[i + 1];
5300                 params = anim.anim[percent];
5301                 break;
5302             } else if (status) {
5303                 element.attr(anim.anim[anim.percents[i]]);
5304             }
5305         }
5306         if (!params) {
5307             return;
5308         }
5309         if (!isInAnim) {
5310             for (attr in params) if (params[has](attr)) {
5311                 if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
5312                     from[attr] = element.attr(attr);
5313                     (from[attr] == null) && (from[attr] = availableAttrs[attr]);
5314                     to[attr] = params[attr];
5315                     switch (availableAnimAttrs[attr]) {
5316                         case nu:
5317                             diff[attr] = (to[attr] - from[attr]) / ms;
5318                             break;
5319                         case "colour":
5320                             from[attr] = R.getRGB(from[attr]);
5321                             var toColour = R.getRGB(to[attr]);
5322                             diff[attr] = {
5323                                 r: (toColour.r - from[attr].r) / ms,
5324                                 g: (toColour.g - from[attr].g) / ms,
5325                                 b: (toColour.b - from[attr].b) / ms
5326                             };
5327                             break;
5328                         case "path":
5329                             var pathes = path2curve(from[attr], to[attr]),
5330                                 toPath = pathes[1];
5331                             from[attr] = pathes[0];
5332                             diff[attr] = [];
5333                             for (i = 0, ii = from[attr].length; i < ii; i++) {
5334                                 diff[attr][i] = [0];
5335                                 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
5336                                     diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
5337                                 }
5338                             }
5339                             break;
5340                         case "transform":
5341                             var _ = element._,
5342                                 eq = equaliseTransform(_[attr], to[attr]);
5343                             if (eq) {
5344                                 from[attr] = eq.from;
5345                                 to[attr] = eq.to;
5346                                 diff[attr] = [];
5347                                 diff[attr].real = true;
5348                                 for (i = 0, ii = from[attr].length; i < ii; i++) {
5349                                     diff[attr][i] = [from[attr][i][0]];
5350                                     for (j = 1, jj = from[attr][i].length; j < jj; j++) {
5351                                         diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
5352                                     }
5353                                 }
5354                             } else {
5355                                 var m = (element.matrix || new Matrix),
5356                                     to2 = {_:{transform: _.transform}, getBBox: function () { return element.getBBox(); }};
5357                                 from[attr] = [
5358                                     m.a,
5359                                     m.b,
5360                                     m.c,
5361                                     m.d,
5362                                     m.e,
5363                                     m.f
5364                                 ];
5365                                 extractTransform(to2, to[attr]);
5366                                 to[attr] = to2._.transform;
5367                                 diff[attr] = [
5368                                     (to2.matrix.a - m.a) / ms,
5369                                     (to2.matrix.b - m.b) / ms,
5370                                     (to2.matrix.c - m.c) / ms,
5371                                     (to2.matrix.d - m.d) / ms,
5372                                     (to2.matrix.e - m.e) / ms,
5373                                     (to2.matrix.e - m.f) / ms
5374                                 ];
5375                                 // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
5376                                 // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
5377                                 // extractTransform(to2, to[attr]);
5378                                 // diff[attr] = [
5379                                 //     (to2._.sx - _.sx) / ms,
5380                                 //     (to2._.sy - _.sy) / ms,
5381                                 //     (to2._.deg - _.deg) / ms,
5382                                 //     (to2._.dx - _.dx) / ms,
5383                                 //     (to2._.dy - _.dy) / ms
5384                                 // ];
5385                             }
5386                             break;
5387                         case "csv":
5388                             var values = Str(params[attr]).split(separator),
5389                                 from2 = Str(from[attr]).split(separator);
5390                             if (attr == "clip-rect") {
5391                                 from[attr] = from2;
5392                                 diff[attr] = [];
5393                                 i = from2.length;
5394                                 while (i--) {
5395                                     diff[attr][i] = (values[i] - from[attr][i]) / ms;
5396                                 }
5397                             }
5398                             to[attr] = values;
5399                             break;
5400                         default:
5401                             values = [].concat(params[attr]);
5402                             from2 = [].concat(from[attr]);
5403                             diff[attr] = [];
5404                             i = element.paper.customAttributes[attr].length;
5405                             while (i--) {
5406                                 diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
5407                             }
5408                             break;
5409                     }
5410                 }
5411             }
5412             var easing = params.easing,
5413                 easyeasy = R.easing_formulas[easing];
5414             if (!easyeasy) {
5415                 easyeasy = Str(easing).match(bezierrg);
5416                 if (easyeasy && easyeasy.length == 5) {
5417                     var curve = easyeasy;
5418                     easyeasy = function (t) {
5419                         return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
5420                     };
5421                 } else {
5422                     easyeasy = pipe;
5423                 }
5424             }
5425             timestamp = params.start || anim.start || +new Date;
5426             e = {
5427                 anim: anim,
5428                 percent: percent,
5429                 timestamp: timestamp,
5430                 start: timestamp + (anim.del || 0),
5431                 status: 0,
5432                 initstatus: status || 0,
5433                 stop: false,
5434                 ms: ms,
5435                 easing: easyeasy,
5436                 from: from,
5437                 diff: diff,
5438                 to: to,
5439                 el: element,
5440                 callback: params.callback,
5441                 prev: prev,
5442                 next: next,
5443                 repeat: times || anim.times,
5444                 origin: element.attr(),
5445                 totalOrigin: totalOrigin
5446             };
5447             animationElements.push(e);
5448             if (status && !isInAnim) {
5449                 e.stop = true;
5450                 e.start = new Date - ms * status;
5451                 if (animationElements.length == 1) {
5452                     return animation();
5453                 }
5454             }
5455             animationElements.length == 1 && requestAnimFrame(animation);
5456         } else {
5457             isInAnim.initstatus = status;
5458             isInAnim.start = new Date - isInAnim.ms * status;
5459         }
5460         eve("anim.start." + element.id, element, anim);
5461     }
5462     /*\
5463      * Raphael.animation
5464      [ method ]
5465      **
5466      * Creates an animation object that can be passed to the @Element.animate or @Element.animateWith methods.
5467      * See also @Animation.delay and @Animation.repeat methods.
5468      **
5469      > Parameters
5470      **
5471      - params (object) final attributes for the element, see also @Element.attr
5472      - ms (number) number of milliseconds for animation to run
5473      - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
5474      - callback (function) #optional callback function. Will be called at the end of animation.
5475      **
5476      = (object) @Animation
5477     \*/
5478     R.animation = function (params, ms, easing, callback) {
5479         if (R.is(easing, "function") || !easing) {
5480             callback = callback || easing || null;
5481             easing = null;
5482         }
5483         params = Object(params);
5484         ms = +ms || 0;
5485         var p = {},
5486             json,
5487             attr;
5488         for (attr in params) if (params[has](attr) && toFloat(attr) != attr) {
5489             json = true;
5490             p[attr] = params[attr];
5491         }
5492         if (!json) {
5493             return new Animation(params, ms);
5494         } else {
5495             easing && (p.easing = easing);
5496             callback && (p.callback = callback);
5497             return new Animation({100: p}, ms);
5498         }
5499     };
5500     /*\
5501      * Element.animate
5502      [ method ]
5503      **
5504      * Creates and starts animation for given element.
5505      **
5506      > Parameters
5507      **
5508      - params (object) final attributes for the element, see also @Element.attr
5509      - ms (number) number of milliseconds for animation to run
5510      - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
5511      - callback (function) #optional callback function. Will be called at the end of animation.
5512      * or
5513      - animation (object) animation object, see @Raphael.animation
5514      **
5515      = (object) original element
5516     \*/
5517     elproto.animate = function (params, ms, easing, callback) {
5518         var element = this;
5519         if (element.removed) {
5520             callback && callback.call(element);
5521             return element;
5522         }
5523         var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
5524         runAnimation(anim, element, anim.percents[0], null, element.attr());
5525         return element;
5526     };
5527     /*\
5528      * Element.setTime
5529      [ method ]
5530      **
5531      * Sets the status of animation of the element in milliseconds. Similar to @Element.status method.
5532      **
5533      > Parameters
5534      **
5535      - anim (object) animation object
5536      - value (number) number of milliseconds from the beginning of the animation
5537      **
5538      = (object) original element if `value` is specified
5539      * Note, that during animation following events are triggered:
5540      *
5541      * On each animation frame event `anim.frame.<id>`, on start `anim.start.<id>` and on end `anim.finish.<id>`.
5542     \*/
5543     elproto.setTime = function (anim, value) {
5544         if (anim && value != null) {
5545             this.status(anim, mmin(value, anim.ms) / anim.ms);
5546         }
5547         return this;
5548     };
5549     /*\
5550      * Element.status
5551      [ method ]
5552      **
5553      * Gets or sets the status of animation of the element.
5554      **
5555      > Parameters
5556      **
5557      - anim (object) #optional animation object
5558      - value (number) #optional 0 – 1. If specified, method works like a setter and sets the status of a given animation to the value. This will cause animation to jump to the given position.
5559      **
5560      = (number) status
5561      * or
5562      = (array) status if `anim` is not specified. Array of objects in format:
5563      o {
5564      o     anim: (object) animation object
5565      o     status: (number) status
5566      o }
5567      * or
5568      = (object) original element if `value` is specified
5569     \*/
5570     elproto.status = function (anim, value) {
5571         var out = [],
5572             i = 0,
5573             len,
5574             e;
5575         if (value != null) {
5576             runAnimation(anim, this, -1, mmin(value, 1));
5577             return this;
5578         } else {
5579             len = animationElements.length;
5580             for (; i < len; i++) {
5581                 e = animationElements[i];
5582                 if (e.el.id == this.id && (!anim || e.anim == anim)) {
5583                     if (anim) {
5584                         return e.status;
5585                     }
5586                     out.push({anim: e.anim, status: e.status});
5587                 }
5588             }
5589             if (anim) {
5590                 return 0;
5591             }
5592             return out;
5593         }
5594     };
5595     /*\
5596      * Element.pause
5597      [ method ]
5598      **
5599      * Stops animation of the element with ability to resume it later on.
5600      **
5601      > Parameters
5602      **
5603      - anim (object) #optional animation object
5604      **
5605      = (object) original element
5606     \*/
5607     elproto.pause = function (anim) {
5608         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
5609             if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) {
5610                 animationElements[i].paused = true;
5611             }
5612         }
5613         return this;
5614     };
5615     /*\
5616      * Element.resume
5617      [ method ]
5618      **
5619      * Resumes animation if it was paused with @Element.pause method.
5620      **
5621      > Parameters
5622      **
5623      - anim (object) #optional animation object
5624      **
5625      = (object) original element
5626     \*/
5627     elproto.resume = function (anim) {
5628         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
5629             var e = animationElements[i];
5630             if (eve("anim.resume." + this.id, this, e.anim) !== false) {
5631                 delete e.paused;
5632                 this.status(e.anim, e.status);
5633             }
5634         }
5635         return this;
5636     };
5637     /*\
5638      * Element.stop
5639      [ method ]
5640      **
5641      * Stops animation of the element.
5642      **
5643      > Parameters
5644      **
5645      - anim (object) #optional animation object
5646      **
5647      = (object) original element
5648     \*/
5649     elproto.stop = function (anim) {
5650         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
5651             if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) {
5652                 animationElements.splice(i--, 1);
5653             }
5654         }
5655         return this;
5656     };
5657     elproto.toString = function () {
5658         return "Rapha\xebl\u2019s object";
5659     };
5660
5661     // Set
5662     var Set = function (items) {
5663         this.items = [];
5664         this.length = 0;
5665         this.type = "set";
5666         if (items) {
5667             for (var i = 0, ii = items.length; i < ii; i++) {
5668                 if (items[i] && (items[i].constructor == Element || items[i].constructor == Set)) {
5669                     this[this.items.length] = this.items[this.items.length] = items[i];
5670                     this.length++;
5671                 }
5672             }
5673         }
5674     },
5675     setproto = Set.prototype;
5676     /*\
5677      * Set.push
5678      [ method ]
5679      **
5680      * Adds each argument to the current set.
5681      = (object) original element
5682     \*/
5683     setproto.push = function () {
5684         var item,
5685             len;
5686         for (var i = 0, ii = arguments.length; i < ii; i++) {
5687             item = arguments[i];
5688             if (item && (item.constructor == Element || item.constructor == Set)) {
5689                 len = this.items.length;
5690                 this[len] = this.items[len] = item;
5691                 this.length++;
5692             }
5693         }
5694         return this;
5695     };
5696     /*\
5697      * Set.pop
5698      [ method ]
5699      **
5700      * Removes last element and returns it.
5701      = (object) element
5702     \*/
5703     setproto.pop = function () {
5704         this.length && delete this[this.length--];
5705         return this.items.pop();
5706     };
5707     /*\
5708      * Set.forEach
5709      [ method ]
5710      **
5711      * Executes given function for each element in the set.
5712      *
5713      * If function returns `false` it will stop loop running.
5714      **
5715      > Parameters
5716      **
5717      - callback (function) function to run
5718      - thisArg (object) context object for the callback
5719      = (object) Set object
5720     \*/
5721     setproto.forEach = function (callback, thisArg) {
5722         for (var i = 0, ii = this.items.length; i < ii; i++) {
5723             if (callback.call(thisArg, this.items[i]) === false) {
5724                 return this;
5725             }
5726         }
5727         return this;
5728     };
5729     for (var method in elproto) if (elproto[has](method)) {
5730         setproto[method] = (function (methodname) {
5731             return function () {
5732                 var arg = arguments;
5733                 return this.forEach(function (el) {
5734                     el[methodname][apply](el, arg);
5735                 });
5736             };
5737         })(method);
5738     }
5739     setproto.attr = function (name, value) {
5740         if (name && R.is(name, array) && R.is(name[0], "object")) {
5741             for (var j = 0, jj = name.length; j < jj; j++) {
5742                 this.items[j].attr(name[j]);
5743             }
5744         } else {
5745             for (var i = 0, ii = this.items.length; i < ii; i++) {
5746                 this.items[i].attr(name, value);
5747             }
5748         }
5749         return this;
5750     };
5751     setproto.animate = function (params, ms, easing, callback) {
5752         (R.is(easing, "function") || !easing) && (callback = easing || null);
5753         var len = this.items.length,
5754             i = len,
5755             item,
5756             set = this,
5757             collector;
5758         callback && (collector = function () {
5759             !--len && callback.call(set);
5760         });
5761         easing = R.is(easing, string) ? easing : collector;
5762         var anim = params instanceof Animation ? params : R.animation(params, ms, easing, collector);
5763         item = this.items[--i].animate(anim);
5764         while (i--) {
5765             this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim);
5766         }
5767         return this;
5768     };
5769     setproto.insertAfter = function (el) {
5770         var i = this.items.length;
5771         while (i--) {
5772             this.items[i].insertAfter(el);
5773         }
5774         return this;
5775     };
5776     setproto.getBBox = function () {
5777         var x = [],
5778             y = [],
5779             w = [],
5780             h = [];
5781         for (var i = this.items.length; i--;) if (!this.items[i].removed) {
5782             var box = this.items[i].getBBox();
5783             x.push(box.x);
5784             y.push(box.y);
5785             w.push(box.x + box.width);
5786             h.push(box.y + box.height);
5787         }
5788         x = mmin[apply](0, x);
5789         y = mmin[apply](0, y);
5790         return {
5791             x: x,
5792             y: y,
5793             width: mmax[apply](0, w) - x,
5794             height: mmax[apply](0, h) - y
5795         };
5796     };
5797     setproto.clone = function (s) {
5798         s = new Set;
5799         for (var i = 0, ii = this.items.length; i < ii; i++) {
5800             s.push(this.items[i].clone());
5801         }
5802         return s;
5803     };
5804     setproto.toString = function () {
5805         return "Rapha\xebl\u2018s set";
5806     };
5807
5808     R.registerFont = function (font) {
5809         if (!font.face) {
5810             return font;
5811         }
5812         this.fonts = this.fonts || {};
5813         var fontcopy = {
5814                 w: font.w,
5815                 face: {},
5816                 glyphs: {}
5817             },
5818             family = font.face["font-family"];
5819         for (var prop in font.face) if (font.face[has](prop)) {
5820             fontcopy.face[prop] = font.face[prop];
5821         }
5822         if (this.fonts[family]) {
5823             this.fonts[family].push(fontcopy);
5824         } else {
5825             this.fonts[family] = [fontcopy];
5826         }
5827         if (!font.svg) {
5828             fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
5829             for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
5830                 var path = font.glyphs[glyph];
5831                 fontcopy.glyphs[glyph] = {
5832                     w: path.w,
5833                     k: {},
5834                     d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
5835                             return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
5836                         }) + "z"
5837                 };
5838                 if (path.k) {
5839                     for (var k in path.k) if (path[has](k)) {
5840                         fontcopy.glyphs[glyph].k[k] = path.k[k];
5841                     }
5842                 }
5843             }
5844         }
5845         return font;
5846     };
5847     paperproto.getFont = function (family, weight, style, stretch) {
5848         stretch = stretch || "normal";
5849         style = style || "normal";
5850         weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
5851         if (!R.fonts) {
5852             return;
5853         }
5854         var font = R.fonts[family];
5855         if (!font) {
5856             var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
5857             for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
5858                 if (name.test(fontName)) {
5859                     font = R.fonts[fontName];
5860                     break;
5861                 }
5862             }
5863         }
5864         var thefont;
5865         if (font) {
5866             for (var i = 0, ii = font.length; i < ii; i++) {
5867                 thefont = font[i];
5868                 if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
5869                     break;
5870                 }
5871             }
5872         }
5873         return thefont;
5874     };
5875     paperproto.print = function (x, y, string, font, size, origin, letter_spacing) {
5876         origin = origin || "middle"; // baseline|middle
5877         letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
5878         var out = this.set(),
5879             letters = Str(string).split(E),
5880             shift = 0,
5881             path = E,
5882             scale;
5883         R.is(font, string) && (font = this.getFont(font));
5884         if (font) {
5885             scale = (size || 16) / font.face["units-per-em"];
5886             var bb = font.face.bbox.split(separator),
5887                 top = +bb[0],
5888                 height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
5889             for (var i = 0, ii = letters.length; i < ii; i++) {
5890                 var prev = i && font.glyphs[letters[i - 1]] || {},
5891                     curr = font.glyphs[letters[i]];
5892                 shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
5893                 curr && curr.d && out.push(this.path(curr.d).attr({fill: "#000", stroke: "none", transform: [["t", shift, 0]]}));
5894             }
5895             out.scale(scale, scale, top, height).translate(x - top, y - height);
5896         }
5897         return out;
5898     };
5899
5900     R.format = function (token, params) {
5901         var args = R.is(params, array) ? [0][concat](params) : arguments;
5902         token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
5903             return args[++i] == null ? E : args[i];
5904         }));
5905         return token || E;
5906     };
5907     R.ninja = function () {
5908         oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
5909         return R;
5910     };
5911     /*\
5912      * Raphael.el
5913      [ property (object) ]
5914      **
5915      * You can add your own method to elements. This is usefull when you want to hack default functionality or
5916      * want to wrap some common transformation or attributes in one method. In difference to canvas methods,
5917      * you can redefine element method at any time. Expending element methods wouldn’t affect set.
5918      > Usage
5919      | Raphael.el.red = function () {
5920      |     this.attr({fill: "#f00"});
5921      | };
5922      | // then use it
5923      | paper.circle(100, 100, 20).red();
5924     \*/
5925     R.el = elproto;
5926     R.st = setproto;
5927     // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
5928     (function (doc, loaded, f) {
5929         if (doc.readyState == null && doc.addEventListener){
5930             doc.addEventListener(loaded, f = function () {
5931                 doc.removeEventListener(loaded, f, false);
5932                 doc.readyState = "complete";
5933             }, false);
5934             doc.readyState = "loading";
5935         }
5936         function isLoaded() {
5937             (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : eve("DOMload");
5938         }
5939         isLoaded();
5940     })(document, "DOMContentLoaded");
5941
5942     oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);
5943
5944     /*
5945      * Eve 0.2.3 - JavaScript Events Library
5946      *
5947      * Copyright (c) 2010 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/)
5948      * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
5949      */
5950
5951     var eve = R.eve = (function () {
5952         var version = "0.2.3",
5953             has = "hasOwnProperty",
5954             separator = /[\.\/]/,
5955             wildcard = "*",
5956             fun = function () {},
5957             numsort = function (a, b) {
5958                 return a - b;
5959             },
5960             current_event,
5961             events = {n: {}},
5962         /*\
5963          * eve
5964          [ method ]
5965          **
5966          * Fires event with given `name`, given scope and other parameters.
5967          **
5968          > Arguments
5969          **
5970          - name (string) name of the event, dot (`.`) or slash (`/`) separated
5971          - scope (object) context for the event handlers
5972          - varargs (...) the rest of arguments will be sent to event handlers
5973          **
5974          = (array) array of errors, if any. Each element of the array is in format:
5975          o {
5976          o     error (string) error message
5977          o     func (function) handler that caused error
5978          o }
5979         \*/
5980             eve = function (name, scope) {
5981                 var e = events,
5982                     args = Array.prototype.slice.call(arguments, 2),
5983                     listeners = eve.listeners(name),
5984                     z = 0,
5985                     f = false,
5986                     l,
5987                     indexed = [],
5988                     queue = {},
5989                     errors = [];
5990                 current_event = name;
5991                 for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
5992                     indexed.push(listeners[i].zIndex);
5993                     if (listeners[i].zIndex < 0) {
5994                         queue[listeners[i].zIndex] = listeners[i];
5995                     }
5996                 }
5997                 indexed.sort(numsort);
5998                 while (indexed[z] < 0) {
5999                     l = queue[indexed[z++]];
6000                     if (l.apply(scope, args) === f) {
6001                         return f;
6002                     }
6003                 }
6004                 for (i = 0; i < ii; i++) {
6005                     l = listeners[i];
6006                     if ("zIndex" in l) {
6007                         if (l.zIndex == indexed[z]) {
6008                             if (l.apply(scope, args) === f) {
6009                                 return f;
6010                             }
6011                             do {
6012                                 z++;
6013                                 l = queue[indexed[z]];
6014                                 if (l) {
6015                                     if (l.apply(scope, args) === f) {
6016                                         return f;
6017                                     }
6018                                 }
6019                             } while (l)
6020                         } else {
6021                             queue[l.zIndex] = l;
6022                         }
6023                     } else {
6024                         if (l.apply(scope, args) === f) {
6025                             return f;
6026                         }
6027                     }
6028                 }
6029             };
6030         /*\
6031          * eve.listeners
6032          [ method ]
6033          **
6034          * Internal method which gives you array of all event handlers that will be triggered by the given `name`.
6035          **
6036          > Arguments
6037          **
6038          - name (string) name of the event, dot (`.`) or slash (`/`) separated
6039          **
6040          = (array) array of event handlers
6041         \*/
6042         eve.listeners = function (name) {
6043             var names = name.split(separator),
6044                 e = events,
6045                 item,
6046                 items,
6047                 k,
6048                 i,
6049                 ii,
6050                 j,
6051                 jj,
6052                 nes,
6053                 es = [e],
6054                 out = [];
6055             for (i = 0, ii = names.length; i < ii; i++) {
6056                 nes = [];
6057                 for (j = 0, jj = es.length; j < jj; j++) {
6058                     e = es[j].n;
6059                     items = [e[names[i]], e[wildcard]];
6060                     k = 2;
6061                     while (k--) {
6062                         item = items[k];
6063                         if (item) {
6064                             nes.push(item);
6065                             out = out.concat(item.f || []);
6066                         }
6067                     }
6068                 }
6069                 es = nes;
6070             }
6071             return out;
6072         };
6073     
6074         /*\
6075          * eve.on
6076          [ method ]
6077          **
6078          * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
6079          | eve.on("*.under.*", f);
6080          | eve("mouse.under.floor"); // triggers f
6081          * Use @eve to trigger the listener.
6082          **
6083          > Arguments
6084          **
6085          - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
6086          - f (function) event handler function
6087          **
6088          = (function) returned function accept one number parameter that represents z-index of the handler. It is optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. 
6089          > Example:
6090          | eve.on("mouse", eat)(2);
6091          | eve.on("mouse", scream);
6092          | eve.on("mouse", catch)(1);
6093          * This will ensure that `catch` function will be called before `eat`.
6094          * If you want to put you hadler before not indexed handlers specify negative value.
6095          * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
6096         \*/
6097         eve.on = function (name, f) {
6098             var names = name.split(separator),
6099                 e = events;
6100             for (var i = 0, ii = names.length; i < ii; i++) {
6101                 e = e.n;
6102                 !e[names[i]] && (e[names[i]] = {n: {}});
6103                 e = e[names[i]];
6104             }
6105             e.f = e.f || [];
6106             for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
6107                 return fun;
6108             }
6109             e.f.push(f);
6110             return function (zIndex) {
6111                 if (+zIndex == +zIndex) {
6112                     f.zIndex = +zIndex;
6113                 }
6114             };
6115         };
6116         /*\
6117          * eve.nt
6118          [ method ]
6119          **
6120          * Could be used inside event handler to figure out actual name of the event.
6121          **
6122          > Arguments
6123          **
6124          - subname (string) #optional subname of the event
6125          **
6126          = (string) name of the event, if `subname` is not specified
6127          * or
6128          = (boolean) `true`, if current event’s name contains `subname`
6129         \*/
6130         eve.nt = function (subname) {
6131             if (subname) {
6132                 return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
6133             }
6134             return current_event;
6135         };
6136         /*\
6137          * eve.unbind
6138          [ method ]
6139          **
6140          * Removes given function from the list of event listeners assigned to given name.
6141          **
6142          > Arguments
6143          **
6144          - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
6145          - f (function) event handler function
6146         \*/
6147         eve.unbind = function (name, f) {
6148             var names = name.split(separator),
6149                 e,
6150                 key,
6151                 splice,
6152                 cur = [events];
6153             for (var i = 0, ii = names.length; i < ii; i++) {
6154                 for (var j = 0; j < cur.length; j += splice.length - 2) {
6155                     splice = [j, 1];
6156                     e = cur[j].n;
6157                     if (names[i] != wildcard) {
6158                         if (e[names[i]]) {
6159                             splice.push(e[names[i]]);
6160                         }
6161                     } else {
6162                         for (key in e) if (e[has](key)) {
6163                             splice.push(e[key]);
6164                         }
6165                     }
6166                     cur.splice.apply(cur, splice);
6167                 }
6168             }
6169             for (i = 0, ii = cur.length; i < ii; i++) {
6170                 e = cur[i];
6171                 while (e.n) {
6172                     if (f) {
6173                         if (e.f) {
6174                             for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
6175                                 e.f.splice(i, 1);
6176                                 break;
6177                             }
6178                             !e.f.length && delete e.f;
6179                         }
6180                         for (key in e.n) if (e.n[has](key) && e.n[key].f) {
6181                             var funcs = e.n[key].f;
6182                             for (i = 0, ii = funcs.length; i < ii; i++) if (funcs[i] == f) {
6183                                 funcs.splice(i, 1);
6184                                 break;
6185                             }
6186                             !funcs.length && delete e.n[key].f;
6187                         }
6188                     } else {
6189                         delete e.f;
6190                         for (key in e.n) if (e.n[has](key) && e.n[key].f) {
6191                             delete e.n[key].f;
6192                         }
6193                     }
6194                     e = e.n;
6195                 }
6196             }
6197         };
6198         /*\
6199          * eve.version
6200          [ property (string) ]
6201          **
6202          * Current version of the library.
6203         \*/
6204         eve.version = version;
6205         eve.toString = function () {
6206             return "You are running Eve " + version;
6207         };
6208         return eve;
6209     })();
6210     
6211     // Eve finished
6212     
6213     eve.on("DOMload", function () {
6214         loaded = true;
6215     });
6216     
6217     
6218 })();