Fixes
[raphael] / raphael.core.js
1 // ┌─────────────────────────────────────────────────────────────────────┐ \\
2 // │ "Raphaël 2.0" - JavaScript Vector Library                           │ \\
3 // ├─────────────────────────────────────────────────────────────────────┤ \\
4 // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
5 // │ Copyright (c) 2008-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 = R._engine.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(R._engine.create[apply](R, args)) : eve.on("DOMload", function () {
79                     f.call(R._engine.create[apply](R, args));
80                 });
81             } else {
82                 return R._engine.create[apply](R, arguments);
83             }
84         }
85     }
86     R.version = "2.0.0";
87     R.eve = eve;
88     var loaded,
89         separator = /[, ]+/,
90         elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
91         formatrg = /\{(\d+)\}/g,
92         proto = "prototype",
93         has = "hasOwnProperty",
94         g = {
95             doc: document,
96             win: window
97         },
98         oldRaphael = {
99             was: Object.prototype[has].call(g.win, "Raphael"),
100             is: g.win.Raphael
101         },
102         Paper = function () {},
103         paperproto,
104         appendChild = "appendChild",
105         apply = "apply",
106         concat = "concat",
107         supportsTouch = "createTouch" in g.doc,
108         E = "",
109         S = " ",
110         Str = String,
111         split = "split",
112         events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel".split(S),
113         touchMap = {
114             mousedown: "touchstart",
115             mousemove: "touchmove",
116             mouseup: "touchend"
117         },
118         lowerCase = Str.prototype.toLowerCase,
119         math = Math,
120         mmax = math.max,
121         mmin = math.min,
122         abs = math.abs,
123         pow = math.pow,
124         PI = math.PI,
125         nu = "number",
126         string = "string",
127         array = "array",
128         toString = "toString",
129         fillString = "fill",
130         objectToString = Object.prototype.toString,
131         paper = {},
132         push = "push",
133         ISURL = R._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
134         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,
135         isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
136         bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
137         round = math.round,
138         setAttribute = "setAttribute",
139         toFloat = parseFloat,
140         toInt = parseInt,
141         upperCase = Str.prototype.toUpperCase,
142         availableAttrs = R._availableAttrs = {
143             "arrow-end": "none",
144             "arrow-start": "none",
145             blur: 0,
146             "clip-rect": "0 0 1e9 1e9",
147             cursor: "default",
148             cx: 0,
149             cy: 0,
150             fill: "#fff",
151             "fill-opacity": 1,
152             font: '10px "Arial"',
153             "font-family": '"Arial"',
154             "font-size": "10",
155             "font-style": "normal",
156             "font-weight": 400,
157             gradient: 0,
158             height: 0,
159             href: "http://raphaeljs.com/",
160             opacity: 1,
161             path: "M0,0",
162             r: 0,
163             rx: 0,
164             ry: 0,
165             src: "",
166             stroke: "#000",
167             "stroke-dasharray": "",
168             "stroke-linecap": "butt",
169             "stroke-linejoin": "butt",
170             "stroke-miterlimit": 0,
171             "stroke-opacity": 1,
172             "stroke-width": 1,
173             target: "_blank",
174             "text-anchor": "middle",
175             title: "Raphael",
176             transform: "",
177             width: 0,
178             x: 0,
179             y: 0
180         },
181         availableAnimAttrs = R._availableAnimAttrs = {
182             blur: nu,
183             "clip-rect": "csv",
184             cx: nu,
185             cy: nu,
186             fill: "colour",
187             "fill-opacity": nu,
188             "font-size": nu,
189             height: nu,
190             opacity: nu,
191             path: "path",
192             r: nu,
193             rx: nu,
194             ry: nu,
195             stroke: "colour",
196             "stroke-opacity": nu,
197             "stroke-width": nu,
198             transform: "transform",
199             width: nu,
200             x: nu,
201             y: nu
202         },
203         commaSpaces = /\s*,\s*/,
204         hsrg = {hs: 1, rg: 1},
205         p2s = /,?([achlmqrstvxz]),?/gi,
206         pathCommand = /([achlmrqstvz])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
207         tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
208         pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)\s*,?\s*/ig,
209         radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,
210         eldata = {},
211         sortByKey = function (a, b) {
212             return a.key - b.key;
213         },
214         sortByNumber = function (a, b) {
215             return toFloat(a) - toFloat(b);
216         },
217         fun = function () {},
218         pipe = function (x) {
219             return x;
220         },
221         rectPath = R._rectPath = function (x, y, w, h, r) {
222             if (r) {
223                 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"]];
224             }
225             return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
226         },
227         ellipsePath = function (x, y, rx, ry) {
228             if (ry == null) {
229                 ry = rx;
230             }
231             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"]];
232         },
233         getPath = R._getPath = {
234             path: function (el) {
235                 return el.attr("path");
236             },
237             circle: function (el) {
238                 var a = el.attrs;
239                 return ellipsePath(a.cx, a.cy, a.r);
240             },
241             ellipse: function (el) {
242                 var a = el.attrs;
243                 return ellipsePath(a.cx, a.cy, a.rx, a.ry);
244             },
245             rect: function (el) {
246                 var a = el.attrs;
247                 return rectPath(a.x, a.y, a.width, a.height, a.r);
248             },
249             image: function (el) {
250                 var a = el.attrs;
251                 return rectPath(a.x, a.y, a.width, a.height);
252             },
253             text: function (el) {
254                 var bbox = el._getBBox();
255                 return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
256             }
257         },
258         mapPath = function (path, matrix) {
259             if (!matrix) {
260                 return path;
261             }
262             var x, y, i, j, pathi;
263             path = path2curve(path);
264             for (i = 0, ii = path.length; i < ii; i++) {
265                 pathi = path[i];
266                 for (j = 1, jj = pathi.length; j < jj; j += 2) {
267                     x = matrix.x(pathi[j], pathi[j + 1]);
268                     y = matrix.y(pathi[j], pathi[j + 1]);
269                     pathi[j] = x;
270                     pathi[j + 1] = y;
271                 }
272             }
273             return path;
274         };
275
276     R._g = g;
277     /*\
278      * Raphael.type
279      [ property (string) ]
280      **
281      * Can be “SVG”, “VML” or empty, depending on browser support.
282     \*/
283     R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
284     if (R.type == "VML") {
285         var d = g.doc.createElement("div"),
286             b;
287         d.innerHTML = '<v:shape adj="1"/>';
288         b = d.firstChild;
289         b.style.behavior = "url(#default#VML)";
290         if (!(b && typeof b.adj == "object")) {
291             return (R.type = E);
292         }
293         d = null;
294     }
295     /*\
296      * Raphael.svg
297      [ property (boolean) ]
298      **
299      * `true` if browser supports SVG.
300     \*/
301     /*\
302      * Raphael.vml
303      [ property (boolean) ]
304      **
305      * `true` if browser supports VML.
306     \*/
307     R.svg = !(R.vml = R.type == "VML");
308     R._Paper = Paper;
309     /*\
310      * Raphael.fn
311      [ property (object) ]
312      **
313      * You can add your own method to the canvas. For example if you want to draw a pie chart,
314      * you can create your own pie chart function and ship it as a Raphaël plugin. To do this
315      * you need to extend the `Raphael.fn` object. Please note that you can create your own namespaces
316      * inside the `fn` object — methods will be run in the context of canvas anyway. You should alter
317      * the `fn` object before a Raphaël instance is created, otherwise it will take no effect.
318      > Usage
319      | Raphael.fn.arrow = function (x1, y1, x2, y2, size) {
320      |     return this.path( ... );
321      | };
322      | // or create namespace
323      | Raphael.fn.mystuff = {
324      |     arrow: function () {…},
325      |     star: function () {…},
326      |     // etc…
327      | };
328      | var paper = Raphael(10, 10, 630, 480);
329      | // then use it
330      | paper.arrow(10, 10, 30, 30, 5).attr({fill: "#f00"});
331      | paper.mystuff.arrow();
332      | paper.mystuff.star();
333     \*/
334     R.fn = paperproto = Paper.prototype = R.prototype;
335     /*\
336      * Paper.customAttributes
337      [ property (object) ]
338      **
339      * If you have a set of attributes that you would like to represent
340      * as a function of some number you can do it easily with custom attributes:
341      > Usage
342      | paper.customAttributes.hue = function (num) {
343      |     num = num % 1;
344      |     return {fill: "hsb(" + num + ", .75, 1)"};
345      | };
346      | // Custom attribute “hue” will change fill
347      | // to be given hue with fixed saturation and brightness.
348      | // Now you can use it like this:
349      | var c = paper.circle(10, 10, 10).attr({hue: .45});
350      | // or even like this:
351      | c.animate({hue: 1}, 1e3);
352      | 
353      | // You could also create custom attribute
354      | // with multiple parameters:
355      | paper.customAttributes.hsb = function (h, s, b) {
356      |     return {fill: "hsb(" + [h, s, b].join(",") + ")"};
357      | };
358      | c.attr({hsb: ".5 .8 1"});
359      | c.animate({hsb: "1 0 .5"}, 1e3);
360     \*/
361     paperproto.customAttributes = {};
362     R._id = 0;
363     R._oid = 0;
364     /*\
365      * Raphael.is
366      [ method ]
367      **
368      * Handfull replacement for `typeof` operator.
369      > Parameters
370      - o (…) any object or primitive
371      - type (string) name of the type, i.e. “string”, “function”, “number”, etc.
372      = (boolean) is given value is of given type
373     \*/
374     R.is = function (o, type) {
375         type = lowerCase.call(type);
376         if (type == "finite") {
377             return !isnan[has](+o);
378         }
379         if (type == "array") {
380             return o instanceof Array;
381         }
382         return  (type == "null" && o === null) ||
383                 (type == typeof o) ||
384                 (type == "object" && o === Object(o)) ||
385                 (type == "array" && Array.isArray && Array.isArray(o)) ||
386                 objectToString.call(o).slice(8, -1).toLowerCase() == type;
387     };
388     /*\
389      * Raphael.angle
390      [ method ]
391      **
392      * Returns angle between two or three points
393      > Parameters
394      - x1 (number) x coord of first point
395      - y1 (number) y coord of first point
396      - x2 (number) x coord of second point
397      - y2 (number) y coord of second point
398      - x3 (number) #optional x coord of third point
399      - y3 (number) #optional y coord of third point
400      = (number) angle in degrees.
401     \*/
402     R.angle = function (x1, y1, x2, y2, x3, y3) {
403         if (x3 == null) {
404             var x = x1 - x2,
405                 y = y1 - y2;
406             if (!x && !y) {
407                 return 0;
408             }
409             return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
410         } else {
411             return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
412         }
413     };
414     /*\
415      * Raphael.rad
416      [ method ]
417      **
418      * Transform angle to radians
419      > Parameters
420      - deg (number) angle in degrees
421      = (number) angle in radians.
422     \*/
423     R.rad = function (deg) {
424         return deg % 360 * PI / 180;
425     };
426     /*\
427      * Raphael.deg
428      [ method ]
429      **
430      * Transform angle to degrees
431      > Parameters
432      - deg (number) angle in radians
433      = (number) angle in degrees.
434     \*/
435     R.deg = function (rad) {
436         return rad * 180 / PI % 360;
437     };
438     /*\
439      * Raphael.snapTo
440      [ method ]
441      **
442      * Snaps given value to given grid.
443      > Parameters
444      - values (array|number) given array of values or step of the grid
445      - value (number) value to adjust
446      - tolerance (number) #optional tolerance for snapping. Default is `10`.
447      = (number) adjusted value.
448     \*/
449     R.snapTo = function (values, value, tolerance) {
450         tolerance = R.is(tolerance, "finite") ? tolerance : 10;
451         if (R.is(values, array)) {
452             var i = values.length;
453             while (i--) if (abs(values[i] - value) <= tolerance) {
454                 return values[i];
455             }
456         } else {
457             values = +values;
458             var rem = value % values;
459             if (rem < tolerance) {
460                 return value - rem;
461             }
462             if (rem > values - tolerance) {
463                 return value - rem + values;
464             }
465         }
466         return value;
467     };
468     
469     var createUUID = R._createUUID = (function (uuidRegEx, uuidReplacer) {
470         return function () {
471             return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
472         };
473     })(/[xy]/g, function (c) {
474         var r = math.random() * 16 | 0,
475             v = c == "x" ? r : (r & 3 | 8);
476         return v.toString(16);
477     });
478
479     /*\
480      * Raphael.setWindow
481      [ method ]
482      **
483      * Used when you need to draw in `&lt;iframe>`. Switched window to the iframe one.
484      > Parameters
485      - newwin (window) new window object
486     \*/
487     R.setWindow = function (newwin) {
488         eve("setWindow", R, g.win, newwin);
489         g.win = newwin;
490         g.doc = g.win.document;
491         if (initWin) {
492             initWin(g.win);
493         }
494     };
495     var toHex = function (color) {
496         if (R.vml) {
497             // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
498             var trim = /^\s+|\s+$/g;
499             var bod;
500             try {
501                 var docum = new ActiveXObject("htmlfile");
502                 docum.write("<body>");
503                 docum.close();
504                 bod = docum.body;
505             } catch(e) {
506                 bod = createPopup().document.body;
507             }
508             var range = bod.createTextRange();
509             toHex = cacher(function (color) {
510                 try {
511                     bod.style.color = Str(color).replace(trim, E);
512                     var value = range.queryCommandValue("ForeColor");
513                     value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
514                     return "#" + ("000000" + value.toString(16)).slice(-6);
515                 } catch(e) {
516                     return "none";
517                 }
518             });
519         } else {
520             var i = g.doc.createElement("i");
521             i.title = "Rapha\xebl Colour Picker";
522             i.style.display = "none";
523             g.doc.body.appendChild(i);
524             toHex = cacher(function (color) {
525                 i.style.color = color;
526                 return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
527             });
528         }
529         return toHex(color);
530     },
531     hsbtoString = function () {
532         return "hsb(" + [this.h, this.s, this.b] + ")";
533     },
534     hsltoString = function () {
535         return "hsl(" + [this.h, this.s, this.l] + ")";
536     },
537     rgbtoString = function () {
538         return this.hex;
539     },
540     prepareRGB = function (r, g, b) {
541         if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
542             b = r.b;
543             g = r.g;
544             r = r.r;
545         }
546         if (g == null && R.is(r, string)) {
547             var clr = R.getRGB(r);
548             r = clr.r;
549             g = clr.g;
550             b = clr.b;
551         }
552         if (r > 1 || g > 1 || b > 1) {
553             r /= 255;
554             g /= 255;
555             b /= 255;
556         }
557         
558         return [r, g, b];
559     },
560     packageRGB = function (r, g, b, o) {
561         r *= 255;
562         g *= 255;
563         b *= 255;
564         var rgb = {
565             r: r,
566             g: g,
567             b: b,
568             hex: R.rgb(r, g, b),
569             toString: rgbtoString
570         };
571         R.is(o, "finite") && (rgb.opacity = o);
572         return rgb;
573     };
574     
575     /*\
576      * Raphael.color
577      [ method ]
578      **
579      * Parses the color string and returns object with all values for the given color.
580      > Parameters
581      - clr (string) color string in one of the supported formats (see @Raphael.getRGB)
582      = (object) Combined RGB & HSB object in format:
583      o {
584      o     r (number) red,
585      o     g (number) green,
586      o     b (number) blue,
587      o     hex (string) color in HTML/CSS format: #••••••,
588      o     error (boolean) `true` if string can’t be parsed,
589      o     h (number) hue,
590      o     s (number) saturation,
591      o     v (number) value (brightness),
592      o     l (number) lightness
593      o }
594     \*/
595     R.color = function (clr) {
596         var rgb;
597         if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
598             rgb = R.hsb2rgb(clr);
599             clr.r = rgb.r;
600             clr.g = rgb.g;
601             clr.b = rgb.b;
602             clr.hex = rgb.hex;
603         } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
604             rgb = R.hsl2rgb(clr);
605             clr.r = rgb.r;
606             clr.g = rgb.g;
607             clr.b = rgb.b;
608             clr.hex = rgb.hex;
609         } else {
610             if (R.is(clr, "string")) {
611                 clr = R.getRGB(clr);
612             }
613             if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
614                 rgb = R.rgb2hsl(clr);
615                 clr.h = rgb.h;
616                 clr.s = rgb.s;
617                 clr.l = rgb.l;
618                 rgb = R.rgb2hsb(clr);
619                 clr.v = rgb.b;
620             } else {
621                 clr = {hex: "none"};
622                 crl.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
623             }
624         }
625         clr.toString = rgbtoString;
626         return clr;
627     };
628     /*\
629      * Raphael.hsb2rgb
630      [ method ]
631      **
632      * Converts HSB values to RGB object.
633      > Parameters
634      - h (number) hue
635      - s (number) saturation
636      - v (number) value or brightness
637      = (object) RGB object in format:
638      o {
639      o     r (number) red,
640      o     g (number) green,
641      o     b (number) blue,
642      o     hex (string) color in HTML/CSS format: #••••••
643      o }
644     \*/
645     R.hsb2rgb = function (h, s, v, o) {
646         if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
647             v = h.b;
648             s = h.s;
649             h = h.h;
650             o = h.o;
651         }
652         h *= 360;
653         var R, G, B, X, C;
654         h = (h % 360) / 60;
655         C = v * s;
656         X = C * (1 - abs(h % 2 - 1));
657         R = G = B = v - C;
658
659         h = ~~h;
660         R += [C, X, 0, 0, X, C][h];
661         G += [X, C, C, X, 0, 0][h];
662         B += [0, 0, X, C, C, X][h];
663         return packageRGB(R, G, B, o);
664     };
665     /*\
666      * Raphael.hsl2rgb
667      [ method ]
668      **
669      * Converts HSL values to RGB object.
670      > Parameters
671      - h (number) hue
672      - s (number) saturation
673      - l (number) luminosity
674      = (object) RGB object in format:
675      o {
676      o     r (number) red,
677      o     g (number) green,
678      o     b (number) blue,
679      o     hex (string) color in HTML/CSS format: #••••••
680      o }
681     \*/
682     R.hsl2rgb = function (h, s, l, o) {
683         if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
684             l = h.l;
685             s = h.s;
686             h = h.h;
687         }
688         if (h > 1 || s > 1 || l > 1) {
689             h /= 360;
690             s /= 100;
691             l /= 100;
692         }
693         h *= 360;
694         var R, G, B, X, C;
695         h = (h % 360) / 60;
696         C = 2 * s * (l < .5 ? l : 1 - l);
697         X = C * (1 - abs(h % 2 - 1));
698         R = G = B = l - C / 2;
699
700         h = ~~h;
701         R += [C, X, 0, 0, X, C][h];
702         G += [X, C, C, X, 0, 0][h];
703         B += [0, 0, X, C, C, X][h];
704         return packageRGB(R, G, B, o);
705     };
706     /*\
707      * Raphael.rgb2hsb
708      [ method ]
709      **
710      * Converts RGB values to HSB object.
711      > Parameters
712      - r (number) red
713      - g (number) green
714      - b (number) blue
715      = (object) HSB object in format:
716      o {
717      o     h (number) hue
718      o     s (number) saturation
719      o     b (number) brightness
720      o }
721     \*/
722     R.rgb2hsb = function (r, g, b) {
723         b = prepareRGB(r, g, b);
724         r = b[0];
725         g = b[1];
726         b = b[2];
727
728         var H, S, V, C;
729         V = mmax(r, g, b);
730         C = V - mmin(r, g, b);
731         H = (C == 0 ? null :
732              V == r ? (g - b) / C :
733              V == g ? (b - r) / C + 2 :
734                       (r - g) / C + 4
735             );
736         H = ((H + 360) % 6) * 60 / 360;
737         S = C == 0 ? 0 : C / V;
738         return {h: H, s: S, b: V, toString: hsbtoString};
739     };
740     /*\
741      * Raphael.rgb2hsl
742      [ method ]
743      **
744      * Converts RGB values to HSL object.
745      > Parameters
746      - r (number) red
747      - g (number) green
748      - b (number) blue
749      = (object) HSL object in format:
750      o {
751      o     h (number) hue
752      o     s (number) saturation
753      o     l (number) luminosity
754      o }
755     \*/
756     R.rgb2hsl = function (r, g, b) {
757         b = prepareRGB(r, g, b);
758         r = b[0];
759         g = b[1];
760         b = b[2];
761
762         var H, S, L, M, m, C;
763         M = mmax(r, g, b);
764         m = mmin(r, g, b);
765         C = M - m;
766         H = (C == 0 ? null :
767              M == r ? (g - b) / C :
768              M == g ? (b - r) / C + 2 :
769                       (r - g) / C + 4);
770         H = ((H + 360) % 6) * 60 / 360;
771         L = (M + m) / 2;
772         S = (C == 0 ? 0 :
773              L < .5 ? C / (2 * L) :
774                       C / (2 - 2 * L));
775         return {h: H, s: S, l: L, toString: hsltoString};
776     };
777     R._path2string = function () {
778         return this.join(",").replace(p2s, "$1");
779     };
780     function repush(array, item) {
781         for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
782             return array.push(array.splice(i, 1)[0]);
783         }
784     }
785     function cacher(f, scope, postprocessor) {
786         function newf() {
787             var arg = Array.prototype.slice.call(arguments, 0),
788                 args = arg.join("\u2400"),
789                 cache = newf.cache = newf.cache || {},
790                 count = newf.count = newf.count || [];
791             if (cache[has](args)) {
792                 repush(count, args);
793                 return postprocessor ? postprocessor(cache[args]) : cache[args];
794             }
795             count.length >= 1e3 && delete cache[count.shift()];
796             count.push(args);
797             cache[args] = f[apply](scope, arg);
798             return postprocessor ? postprocessor(cache[args]) : cache[args];
799         }
800         return newf;
801     }
802
803     var preload = R._preload = function (src, f) {
804         var img = g.doc.createElement("img");
805         img.style.cssText = "position:absolute;left:-9999em;top-9999em";
806         img.onload = function () {
807             f.call(this);
808             this.onload = null;
809             g.doc.body.removeChild(this);
810         };
811         img.onerror = function () {
812             g.doc.body.removeChild(this);
813         };
814         g.doc.body.appendChild(img);
815         img.src = src;
816     };
817     
818     function clrToString() {
819         return this.hex;
820     }
821
822     /*\
823      * Raphael.getRGB
824      [ method ]
825      **
826      * Parses colour string as RGB object
827      > Parameters
828      - colour (string) colour string in one of formats:
829      # <ul>
830      #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
831      #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
832      #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
833      #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
834      #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
835      #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
836      #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
837      #     <li>hsl(•••, •••, •••) — same as hsb</li>
838      #     <li>hsl(•••%, •••%, •••%) — same as hsb</li>
839      # </ul>
840      = (object) RGB object in format:
841      o {
842      o     r (number) red,
843      o     g (number) green,
844      o     b (number) blue
845      o     hex (string) color in HTML/CSS format: #••••••,
846      o     error (boolean) true if string can’t be parsed
847      o }
848     \*/
849     R.getRGB = cacher(function (colour) {
850         if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
851             return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
852         }
853         if (colour == "none") {
854             return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString};
855         }
856         !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
857         var res,
858             red,
859             green,
860             blue,
861             opacity,
862             t,
863             values,
864             rgb = colour.match(colourRegExp);
865         if (rgb) {
866             if (rgb[2]) {
867                 blue = toInt(rgb[2].substring(5), 16);
868                 green = toInt(rgb[2].substring(3, 5), 16);
869                 red = toInt(rgb[2].substring(1, 3), 16);
870             }
871             if (rgb[3]) {
872                 blue = toInt((t = rgb[3].charAt(3)) + t, 16);
873                 green = toInt((t = rgb[3].charAt(2)) + t, 16);
874                 red = toInt((t = rgb[3].charAt(1)) + t, 16);
875             }
876             if (rgb[4]) {
877                 values = rgb[4].split(commaSpaces);
878                 red = toFloat(values[0]);
879                 values[0].slice(-1) == "%" && (red *= 2.55);
880                 green = toFloat(values[1]);
881                 values[1].slice(-1) == "%" && (green *= 2.55);
882                 blue = toFloat(values[2]);
883                 values[2].slice(-1) == "%" && (blue *= 2.55);
884                 rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
885                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
886             }
887             if (rgb[5]) {
888                 values = rgb[5].split(commaSpaces);
889                 red = toFloat(values[0]);
890                 values[0].slice(-1) == "%" && (red *= 2.55);
891                 green = toFloat(values[1]);
892                 values[1].slice(-1) == "%" && (green *= 2.55);
893                 blue = toFloat(values[2]);
894                 values[2].slice(-1) == "%" && (blue *= 2.55);
895                 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
896                 rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
897                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
898                 return R.hsb2rgb(red, green, blue, opacity);
899             }
900             if (rgb[6]) {
901                 values = rgb[6].split(commaSpaces);
902                 red = toFloat(values[0]);
903                 values[0].slice(-1) == "%" && (red *= 2.55);
904                 green = toFloat(values[1]);
905                 values[1].slice(-1) == "%" && (green *= 2.55);
906                 blue = toFloat(values[2]);
907                 values[2].slice(-1) == "%" && (blue *= 2.55);
908                 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
909                 rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
910                 values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
911                 return R.hsl2rgb(red, green, blue, opacity);
912             }
913             rgb = {r: red, g: green, b: blue, toString: clrToString};
914             rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
915             R.is(opacity, "finite") && (rgb.opacity = opacity);
916             return rgb;
917         }
918         return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
919     }, R);
920     /*\
921      * Raphael.hsb
922      [ method ]
923      **
924      * Converts HSB values to hex representation of the colour.
925      > Parameters
926      - h (number) hue
927      - s (number) saturation
928      - b (number) value or brightness
929      = (string) hex representation of the colour.
930     \*/
931     R.hsb = cacher(function (h, s, b) {
932         return R.hsb2rgb(h, s, b).hex;
933     });
934     /*\
935      * Raphael.hsl
936      [ method ]
937      **
938      * Converts HSL values to hex representation of the colour.
939      > Parameters
940      - h (number) hue
941      - s (number) saturation
942      - l (number) luminosity
943      = (string) hex representation of the colour.
944     \*/
945     R.hsl = cacher(function (h, s, l) {
946         return R.hsl2rgb(h, s, l).hex;
947     });
948     /*\
949      * Raphael.rgb
950      [ method ]
951      **
952      * Converts RGB values to hex representation of the colour.
953      > Parameters
954      - r (number) red
955      - g (number) green
956      - b (number) blue
957      = (string) hex representation of the colour.
958     \*/
959     R.rgb = cacher(function (r, g, b) {
960         return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
961     });
962     /*\
963      * Raphael.getColor
964      [ method ]
965      **
966      * On each call returns next colour in the spectrum. To reset it back to red call @Raphael.getColor.reset
967      > Parameters
968      - value (number) #optional brightness, default is `0.75`
969      = (string) hex representation of the colour.
970     \*/
971     R.getColor = function (value) {
972         var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
973             rgb = this.hsb2rgb(start.h, start.s, start.b);
974         start.h += .075;
975         if (start.h > 1) {
976             start.h = 0;
977             start.s -= .2;
978             start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
979         }
980         return rgb.hex;
981     };
982     /*\
983      * Raphael.getColor.reset
984      [ method ]
985      **
986      * Resets spectrum position for @Raphael.getColor back to red.
987     \*/
988     R.getColor.reset = function () {
989         delete this.start;
990     };
991
992     // http://schepers.cc/getting-to-the-point
993     function catmullRom2bezier(crp) {
994         var d = [];
995         for (var i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
996             var p = [{x: +crp[i],     y: +crp[i + 1]},
997                      {x: +crp[i],     y: +crp[i + 1]},
998                      {x: +crp[i + 2], y: +crp[i + 3]},
999                      {x: +crp[i + 4], y: +crp[i + 5]}];
1000             if (iLen - 4 == i) {
1001                 p[0] = {x: +crp[i - 2], y: +crp[i - 1]};
1002                 p[3] = p[2];
1003             } else if (i) {
1004                 p[0] = {x: +crp[i - 2], y: +crp[i - 1]};
1005             }
1006             d.push(["C",
1007                 (-p[0].x + 6 * p[1].x + p[2].x) / 6,
1008                 (-p[0].y + 6 * p[1].y + p[2].y) / 6,
1009                 (p[1].x + 6 * p[2].x - p[3].x) / 6,
1010                 (p[1].y + 6*p[2].y - p[3].y) / 6,
1011                 p[2].x,
1012                 p[2].y
1013             ]);
1014         }
1015
1016         return d;
1017     }
1018     /*\
1019      * Raphael.parsePathString
1020      [ method ]
1021      **
1022      * Utility method
1023      **
1024      * Parses given path string into an array of arrays of path segments.
1025      > Parameters
1026      - pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
1027      = (array) array of segments.
1028     \*/
1029     R.parsePathString = cacher(function (pathString) {
1030         if (!pathString) {
1031             return null;
1032         }
1033         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
1034             data = [];
1035         if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
1036             data = pathClone(pathString);
1037         }
1038         if (!data.length) {
1039             Str(pathString).replace(pathCommand, function (a, b, c) {
1040                 var params = [],
1041                     name = b.toLowerCase();
1042                 c.replace(pathValues, function (a, b) {
1043                     b && params.push(+b);
1044                 });
1045                 if (name == "m" && params.length > 2) {
1046                     data.push([b][concat](params.splice(0, 2)));
1047                     name = "l";
1048                     b = b == "m" ? "l" : "L";
1049                 }
1050                 if (name == "r") {
1051                     data.push([b][concat](params));
1052                 } else while (params.length >= paramCounts[name]) {
1053                     data.push([b][concat](params.splice(0, paramCounts[name])));
1054                     if (!paramCounts[name]) {
1055                         break;
1056                     }
1057                 }
1058             });
1059         }
1060         data.toString = R._path2string;
1061         return data;
1062     });
1063     /*\
1064      * Raphael.parseTransformString
1065      [ method ]
1066      **
1067      * Utility method
1068      **
1069      * Parses given path string into an array of transformations.
1070      > Parameters
1071      - TString (string|array) transform string or array of transformations (in the last case it will be returned straight away)
1072      = (array) array of transformations.
1073     \*/
1074     R.parseTransformString = cacher(function (TString) {
1075         if (!TString) {
1076             return null;
1077         }
1078         var paramCounts = {r: 3, s: 4, t: 2, m: 6},
1079             data = [];
1080         if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
1081             data = pathClone(TString);
1082         }
1083         if (!data.length) {
1084             Str(TString).replace(tCommand, function (a, b, c) {
1085                 var params = [],
1086                     name = lowerCase.call(b);
1087                 c.replace(pathValues, function (a, b) {
1088                     b && params.push(+b);
1089                 });
1090                 data.push([name][concat](params));
1091             });
1092         }
1093         data.toString = R._path2string;
1094         return data;
1095     });
1096     /*\
1097      * Raphael.findDotsAtSegment
1098      [ method ]
1099      **
1100      * Utility method
1101      **
1102      * Find dot coordinates on the given cubic bezier curve at the given t.
1103      > Parameters
1104      - p1x (number) x of the first point of the curve
1105      - p1y (number) y of the first point of the curve
1106      - c1x (number) x of the first anchor of the curve
1107      - c1y (number) y of the first anchor of the curve
1108      - c2x (number) x of the second anchor of the curve
1109      - c2y (number) y of the second anchor of the curve
1110      - p2x (number) x of the second point of the curve
1111      - p2y (number) y of the second point of the curve
1112      - t (number) position on the curve (0..1)
1113      = (object) point information in format:
1114      o {
1115      o     x: (number) x coordinate of the point
1116      o     y: (number) y coordinate of the point
1117      o     m: {
1118      o         x: (number) x coordinate of the left anchor
1119      o         y: (number) y coordinate of the left anchor
1120      o     }
1121      o     n: {
1122      o         x: (number) x coordinate of the right anchor
1123      o         y: (number) y coordinate of the right anchor
1124      o     }
1125      o     start: {
1126      o         x: (number) x coordinate of the start of the curve
1127      o         y: (number) y coordinate of the start of the curve
1128      o     }
1129      o     end: {
1130      o         x: (number) x coordinate of the end of the curve
1131      o         y: (number) y coordinate of the end of the curve
1132      o     }
1133      o     alpha: (number) angle of the curve derivative at the point
1134      o }
1135     \*/
1136     R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
1137         var t1 = 1 - t,
1138             t13 = pow(t1, 3),
1139             t12 = pow(t1, 2),
1140             t2 = t * t,
1141             t3 = t2 * t,
1142             x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
1143             y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
1144             mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
1145             my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
1146             nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
1147             ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
1148             ax = t1 * p1x + t * c1x,
1149             ay = t1 * p1y + t * c1y,
1150             cx = t1 * c2x + t * p2x,
1151             cy = t1 * c2y + t * p2y,
1152             alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
1153         (mx > nx || my < ny) && (alpha += 180);
1154         return {
1155             x: x,
1156             y: y,
1157             m: {x: mx, y: my},
1158             n: {x: nx, y: ny},
1159             start: {x: ax, y: ay},
1160             end: {x: cx, y: cy},
1161             alpha: alpha
1162         };
1163     };
1164     var pathDimensions = cacher(function (path) {
1165         if (!path) {
1166             return {x: 0, y: 0, width: 0, height: 0};
1167         }
1168         path = path2curve(path);
1169         var x = 0, 
1170             y = 0,
1171             X = [],
1172             Y = [],
1173             p;
1174         for (var i = 0, ii = path.length; i < ii; i++) {
1175             p = path[i];
1176             if (p[0] == "M") {
1177                 x = p[1];
1178                 y = p[2];
1179                 X.push(x);
1180                 Y.push(y);
1181             } else {
1182                 var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
1183                 X = X[concat](dim.min.x, dim.max.x);
1184                 Y = Y[concat](dim.min.y, dim.max.y);
1185                 x = p[5];
1186                 y = p[6];
1187             }
1188         }
1189         var xmin = mmin[apply](0, X),
1190             ymin = mmin[apply](0, Y);
1191         return {
1192             x: xmin,
1193             y: ymin,
1194             width: mmax[apply](0, X) - xmin,
1195             height: mmax[apply](0, Y) - ymin
1196         };
1197     }, null, function (o) {
1198         return {
1199             x: o.x,
1200             y: o.y,
1201             width: o.width,
1202             height: o.height
1203         };
1204     }),
1205         pathClone = function (pathArray) {
1206             var res = [];
1207             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
1208                 pathArray = R.parsePathString(pathArray);
1209             }
1210             for (var i = 0, ii = pathArray.length; i < ii; i++) {
1211                 res[i] = [];
1212                 for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
1213                     res[i][j] = pathArray[i][j];
1214                 }
1215             }
1216             res.toString = R._path2string;
1217             return res;
1218         },
1219         pathToRelative = R._pathToRelative = cacher(function (pathArray) {
1220             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
1221                 pathArray = R.parsePathString(pathArray);
1222             }
1223             var res = [],
1224                 x = 0,
1225                 y = 0,
1226                 mx = 0,
1227                 my = 0,
1228                 start = 0;
1229             if (pathArray[0][0] == "M") {
1230                 x = pathArray[0][1];
1231                 y = pathArray[0][2];
1232                 mx = x;
1233                 my = y;
1234                 start++;
1235                 res.push(["M", x, y]);
1236             }
1237             for (var i = start, ii = pathArray.length; i < ii; i++) {
1238                 var r = res[i] = [],
1239                     pa = pathArray[i];
1240                 if (pa[0] != lowerCase.call(pa[0])) {
1241                     r[0] = lowerCase.call(pa[0]);
1242                     switch (r[0]) {
1243                         case "a":
1244                             r[1] = pa[1];
1245                             r[2] = pa[2];
1246                             r[3] = pa[3];
1247                             r[4] = pa[4];
1248                             r[5] = pa[5];
1249                             r[6] = +(pa[6] - x).toFixed(3);
1250                             r[7] = +(pa[7] - y).toFixed(3);
1251                             break;
1252                         case "v":
1253                             r[1] = +(pa[1] - y).toFixed(3);
1254                             break;
1255                         case "m":
1256                             mx = pa[1];
1257                             my = pa[2];
1258                         default:
1259                             for (var j = 1, jj = pa.length; j < jj; j++) {
1260                                 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
1261                             }
1262                     }
1263                 } else {
1264                     r = res[i] = [];
1265                     if (pa[0] == "m") {
1266                         mx = pa[1] + x;
1267                         my = pa[2] + y;
1268                     }
1269                     for (var k = 0, kk = pa.length; k < kk; k++) {
1270                         res[i][k] = pa[k];
1271                     }
1272                 }
1273                 var len = res[i].length;
1274                 switch (res[i][0]) {
1275                     case "z":
1276                         x = mx;
1277                         y = my;
1278                         break;
1279                     case "h":
1280                         x += +res[i][len - 1];
1281                         break;
1282                     case "v":
1283                         y += +res[i][len - 1];
1284                         break;
1285                     default:
1286                         x += +res[i][len - 2];
1287                         y += +res[i][len - 1];
1288                 }
1289             }
1290             res.toString = R._path2string;
1291             return res;
1292         }, 0, pathClone),
1293         pathToAbsolute = R._pathToAbsolute = cacher(function (pathArray) {
1294             if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
1295                 pathArray = R.parsePathString(pathArray);
1296             }
1297             if (!pathArray || !pathArray.length) {
1298                 return [["M", 0, 0]];
1299             }
1300             var res = [],
1301                 x = 0,
1302                 y = 0,
1303                 mx = 0,
1304                 my = 0,
1305                 start = 0;
1306             if (pathArray[0][0] == "M") {
1307                 x = +pathArray[0][1];
1308                 y = +pathArray[0][2];
1309                 mx = x;
1310                 my = y;
1311                 start++;
1312                 res[0] = ["M", x, y];
1313             }
1314             for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
1315                 res.push(r = []);
1316                 pa = pathArray[i];
1317                 if (pa[0] != upperCase.call(pa[0])) {
1318                     r[0] = upperCase.call(pa[0]);
1319                     switch (r[0]) {
1320                         case "A":
1321                             r[1] = pa[1];
1322                             r[2] = pa[2];
1323                             r[3] = pa[3];
1324                             r[4] = pa[4];
1325                             r[5] = pa[5];
1326                             r[6] = +(pa[6] + x);
1327                             r[7] = +(pa[7] + y);
1328                             break;
1329                         case "V":
1330                             r[1] = +pa[1] + y;
1331                             break;
1332                         case "H":
1333                             r[1] = +pa[1] + x;
1334                             break;
1335                         case "R":
1336                             var dots = [x, y][concat](pa.slice(1));
1337                             for (var j = 2, jj = dots.length; j < jj; j++) {
1338                                 dots[j] = +dots[j] + x;
1339                                 dots[++j] = +dots[j] + y;
1340                             }
1341                             res.pop();
1342                             res = res[concat](catmullRom2bezier(dots));
1343                             break;
1344                         case "M":
1345                             mx = +pa[1] + x;
1346                             my = +pa[2] + y;
1347                         default:
1348                             for (j = 1, jj = pa.length; j < jj; j++) {
1349                                 r[j] = +pa[j] + ((j % 2) ? x : y);
1350                             }
1351                     }
1352                 } else if (pa[0] == "R") {
1353                     dots = [x, y][concat](pa.slice(1));
1354                     res.pop();
1355                     res = res[concat](catmullRom2bezier(dots));
1356                     r = ["R"][concat](pa.slice(-2));
1357                 } else {
1358                     for (var k = 0, kk = pa.length; k < kk; k++) {
1359                         r[k] = pa[k];
1360                     }
1361                 }
1362                 switch (r[0]) {
1363                     case "Z":
1364                         x = mx;
1365                         y = my;
1366                         break;
1367                     case "H":
1368                         x = r[1];
1369                         break;
1370                     case "V":
1371                         y = r[1];
1372                         break;
1373                     case "M":
1374                         mx = r[r.length - 2];
1375                         my = r[r.length - 1];
1376                     default:
1377                         x = r[r.length - 2];
1378                         y = r[r.length - 1];
1379                 }
1380             }
1381             res.toString = R._path2string;
1382             return res;
1383         }, null, pathClone),
1384         l2c = function (x1, y1, x2, y2) {
1385             return [x1, y1, x2, y2, x2, y2];
1386         },
1387         q2c = function (x1, y1, ax, ay, x2, y2) {
1388             var _13 = 1 / 3,
1389                 _23 = 2 / 3;
1390             return [
1391                     _13 * x1 + _23 * ax,
1392                     _13 * y1 + _23 * ay,
1393                     _13 * x2 + _23 * ax,
1394                     _13 * y2 + _23 * ay,
1395                     x2,
1396                     y2
1397                 ];
1398         },
1399         a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
1400             // for more information of where this math came from visit:
1401             // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1402             var _120 = PI * 120 / 180,
1403                 rad = PI / 180 * (+angle || 0),
1404                 res = [],
1405                 xy,
1406                 rotate = cacher(function (x, y, rad) {
1407                     var X = x * math.cos(rad) - y * math.sin(rad),
1408                         Y = x * math.sin(rad) + y * math.cos(rad);
1409                     return {x: X, y: Y};
1410                 });
1411             if (!recursive) {
1412                 xy = rotate(x1, y1, -rad);
1413                 x1 = xy.x;
1414                 y1 = xy.y;
1415                 xy = rotate(x2, y2, -rad);
1416                 x2 = xy.x;
1417                 y2 = xy.y;
1418                 var cos = math.cos(PI / 180 * angle),
1419                     sin = math.sin(PI / 180 * angle),
1420                     x = (x1 - x2) / 2,
1421                     y = (y1 - y2) / 2;
1422                 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
1423                 if (h > 1) {
1424                     h = math.sqrt(h);
1425                     rx = h * rx;
1426                     ry = h * ry;
1427                 }
1428                 var rx2 = rx * rx,
1429                     ry2 = ry * ry,
1430                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
1431                         math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
1432                     cx = k * rx * y / ry + (x1 + x2) / 2,
1433                     cy = k * -ry * x / rx + (y1 + y2) / 2,
1434                     f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
1435                     f2 = math.asin(((y2 - cy) / ry).toFixed(9));
1436
1437                 f1 = x1 < cx ? PI - f1 : f1;
1438                 f2 = x2 < cx ? PI - f2 : f2;
1439                 f1 < 0 && (f1 = PI * 2 + f1);
1440                 f2 < 0 && (f2 = PI * 2 + f2);
1441                 if (sweep_flag && f1 > f2) {
1442                     f1 = f1 - PI * 2;
1443                 }
1444                 if (!sweep_flag && f2 > f1) {
1445                     f2 = f2 - PI * 2;
1446                 }
1447             } else {
1448                 f1 = recursive[0];
1449                 f2 = recursive[1];
1450                 cx = recursive[2];
1451                 cy = recursive[3];
1452             }
1453             var df = f2 - f1;
1454             if (abs(df) > _120) {
1455                 var f2old = f2,
1456                     x2old = x2,
1457                     y2old = y2;
1458                 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
1459                 x2 = cx + rx * math.cos(f2);
1460                 y2 = cy + ry * math.sin(f2);
1461                 res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
1462             }
1463             df = f2 - f1;
1464             var c1 = math.cos(f1),
1465                 s1 = math.sin(f1),
1466                 c2 = math.cos(f2),
1467                 s2 = math.sin(f2),
1468                 t = math.tan(df / 4),
1469                 hx = 4 / 3 * rx * t,
1470                 hy = 4 / 3 * ry * t,
1471                 m1 = [x1, y1],
1472                 m2 = [x1 + hx * s1, y1 - hy * c1],
1473                 m3 = [x2 + hx * s2, y2 - hy * c2],
1474                 m4 = [x2, y2];
1475             m2[0] = 2 * m1[0] - m2[0];
1476             m2[1] = 2 * m1[1] - m2[1];
1477             if (recursive) {
1478                 return [m2, m3, m4][concat](res);
1479             } else {
1480                 res = [m2, m3, m4][concat](res).join().split(",");
1481                 var newres = [];
1482                 for (var i = 0, ii = res.length; i < ii; i++) {
1483                     newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
1484                 }
1485                 return newres;
1486             }
1487         },
1488         findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
1489             var t1 = 1 - t;
1490             return {
1491                 x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
1492                 y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
1493             };
1494         },
1495         curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
1496             var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
1497                 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
1498                 c = p1x - c1x,
1499                 t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
1500                 t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
1501                 y = [p1y, p2y],
1502                 x = [p1x, p2x],
1503                 dot;
1504             abs(t1) > "1e12" && (t1 = .5);
1505             abs(t2) > "1e12" && (t2 = .5);
1506             if (t1 > 0 && t1 < 1) {
1507                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
1508                 x.push(dot.x);
1509                 y.push(dot.y);
1510             }
1511             if (t2 > 0 && t2 < 1) {
1512                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
1513                 x.push(dot.x);
1514                 y.push(dot.y);
1515             }
1516             a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
1517             b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
1518             c = p1y - c1y;
1519             t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
1520             t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
1521             abs(t1) > "1e12" && (t1 = .5);
1522             abs(t2) > "1e12" && (t2 = .5);
1523             if (t1 > 0 && t1 < 1) {
1524                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
1525                 x.push(dot.x);
1526                 y.push(dot.y);
1527             }
1528             if (t2 > 0 && t2 < 1) {
1529                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
1530                 x.push(dot.x);
1531                 y.push(dot.y);
1532             }
1533             return {
1534                 min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
1535                 max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
1536             };
1537         }),
1538         path2curve = R._path2curve = cacher(function (path, path2) {
1539             var p = pathToAbsolute(path),
1540                 p2 = path2 && pathToAbsolute(path2),
1541                 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
1542                 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
1543                 processPath = function (path, d) {
1544                     var nx, ny;
1545                     if (!path) {
1546                         return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
1547                     }
1548                     !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
1549                     switch (path[0]) {
1550                         case "M":
1551                             d.X = path[1];
1552                             d.Y = path[2];
1553                             break;
1554                         case "A":
1555                             path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
1556                             break;
1557                         case "S":
1558                             nx = d.x + (d.x - (d.bx || d.x));
1559                             ny = d.y + (d.y - (d.by || d.y));
1560                             path = ["C", nx, ny][concat](path.slice(1));
1561                             break;
1562                         case "T":
1563                             d.qx = d.x + (d.x - (d.qx || d.x));
1564                             d.qy = d.y + (d.y - (d.qy || d.y));
1565                             path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
1566                             break;
1567                         case "Q":
1568                             d.qx = path[1];
1569                             d.qy = path[2];
1570                             path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
1571                             break;
1572                         case "L":
1573                             path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
1574                             break;
1575                         case "H":
1576                             path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
1577                             break;
1578                         case "V":
1579                             path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
1580                             break;
1581                         case "Z":
1582                             path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
1583                             break;
1584                     }
1585                     return path;
1586                 },
1587                 fixArc = function (pp, i) {
1588                     if (pp[i].length > 7) {
1589                         pp[i].shift();
1590                         var pi = pp[i];
1591                         while (pi.length) {
1592                             pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
1593                         }
1594                         pp.splice(i, 1);
1595                         ii = mmax(p.length, p2 && p2.length || 0);
1596                     }
1597                 },
1598                 fixM = function (path1, path2, a1, a2, i) {
1599                     if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
1600                         path2.splice(i, 0, ["M", a2.x, a2.y]);
1601                         a1.bx = 0;
1602                         a1.by = 0;
1603                         a1.x = path1[i][1];
1604                         a1.y = path1[i][2];
1605                         ii = mmax(p.length, p2 && p2.length || 0);
1606                     }
1607                 };
1608             for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
1609                 p[i] = processPath(p[i], attrs);
1610                 fixArc(p, i);
1611                 p2 && (p2[i] = processPath(p2[i], attrs2));
1612                 p2 && fixArc(p2, i);
1613                 fixM(p, p2, attrs, attrs2, i);
1614                 fixM(p2, p, attrs2, attrs, i);
1615                 var seg = p[i],
1616                     seg2 = p2 && p2[i],
1617                     seglen = seg.length,
1618                     seg2len = p2 && seg2.length;
1619                 attrs.x = seg[seglen - 2];
1620                 attrs.y = seg[seglen - 1];
1621                 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
1622                 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
1623                 attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
1624                 attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
1625                 attrs2.x = p2 && seg2[seg2len - 2];
1626                 attrs2.y = p2 && seg2[seg2len - 1];
1627             }
1628             return p2 ? [p, p2] : p;
1629         }, null, pathClone),
1630         parseDots = R._parseDots = cacher(function (gradient) {
1631             var dots = [];
1632             for (var i = 0, ii = gradient.length; i < ii; i++) {
1633                 var dot = {},
1634                     par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
1635                 dot.color = R.getRGB(par[1]);
1636                 if (dot.color.error) {
1637                     return null;
1638                 }
1639                 dot.color = dot.color.hex;
1640                 par[2] && (dot.offset = par[2] + "%");
1641                 dots.push(dot);
1642             }
1643             for (i = 1, ii = dots.length - 1; i < ii; i++) {
1644                 if (!dots[i].offset) {
1645                     var start = toFloat(dots[i - 1].offset || 0),
1646                         end = 0;
1647                     for (var j = i + 1; j < ii; j++) {
1648                         if (dots[j].offset) {
1649                             end = dots[j].offset;
1650                             break;
1651                         }
1652                     }
1653                     if (!end) {
1654                         end = 100;
1655                         j = ii;
1656                     }
1657                     end = toFloat(end);
1658                     var d = (end - start) / (j - i + 1);
1659                     for (; i < j; i++) {
1660                         start += d;
1661                         dots[i].offset = start + "%";
1662                     }
1663                 }
1664             }
1665             return dots;
1666         }),
1667         tear = R._tear = function (el, paper) {
1668             el == paper.top && (paper.top = el.prev);
1669             el == paper.bottom && (paper.bottom = el.next);
1670             el.next && (el.next.prev = el.prev);
1671             el.prev && (el.prev.next = el.next);
1672         },
1673         tofront = R._tofront = function (el, paper) {
1674             if (paper.top === el) {
1675                 return;
1676             }
1677             tear(el, paper);
1678             el.next = null;
1679             el.prev = paper.top;
1680             paper.top.next = el;
1681             paper.top = el;
1682         },
1683         toback = R._toback = function (el, paper) {
1684             if (paper.bottom === el) {
1685                 return;
1686             }
1687             tear(el, paper);
1688             el.next = paper.bottom;
1689             el.prev = null;
1690             paper.bottom.prev = el;
1691             paper.bottom = el;
1692         },
1693         insertafter = R._insertafter = function (el, el2, paper) {
1694             tear(el, paper);
1695             el2 == paper.top && (paper.top = el);
1696             el2.next && (el2.next.prev = el);
1697             el.next = el2.next;
1698             el.prev = el2;
1699             el2.next = el;
1700         },
1701         insertbefore = R._insertbefore = function (el, el2, paper) {
1702             tear(el, paper);
1703             el2 == paper.bottom && (paper.bottom = el);
1704             el2.prev && (el2.prev.next = el);
1705             el.prev = el2.prev;
1706             el2.prev = el;
1707             el.next = el2;
1708         },
1709         removed = function (methodname) {
1710             return function () {
1711                 throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
1712             };
1713         },
1714         extractTransform = R._extractTransform = function (el, tstr) {
1715             if (tstr == null) {
1716                 return el._.transform;
1717             }
1718             tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
1719             var tdata = R.parseTransformString(tstr),
1720                 deg = 0,
1721                 dx = 0,
1722                 dy = 0,
1723                 sx = 1,
1724                 sy = 1,
1725                 _ = el._,
1726                 m = new Matrix;
1727             _.transform = tdata || [];
1728             if (tdata) {
1729                 for (var i = 0, ii = tdata.length; i < ii; i++) {
1730                     var t = tdata[i],
1731                         tlen = t.length,
1732                         bb;
1733                     t[0] = Str(t[0]).toLowerCase();
1734                     if (t[0] == "t" && tlen == 3) {
1735                         m.translate(t[1], t[2]);
1736                     } else if (t[0] == "r") {
1737                         if (tlen == 2) {
1738                             bb = bb || el.getBBox(1);
1739                             m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
1740                             deg += t[1];
1741                         } else if (tlen == 4) {
1742                             m.rotate(t[1], t[2], t[3]);
1743                             deg += t[1];
1744                         }
1745                     } else if (t[0] == "s") {
1746                         if (tlen == 2 || tlen == 3) {
1747                             bb = bb || el.getBBox(1);
1748                             m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
1749                             sx *= t[1];
1750                             sy *= t[tlen - 1];
1751                         } else if (tlen == 5) {
1752                             m.scale(t[1], t[2], t[3], t[4]);
1753                             sx *= t[1];
1754                             sy *= t[2];
1755                         }
1756                     } else if (t[0] == "m" && tlen == 7) {
1757                         m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
1758                     }
1759                     _.dirtyT = 1;
1760                     el.matrix = m;
1761                 }
1762             }
1763
1764             el.matrix = m;
1765
1766             _.sx = sx;
1767             _.sy = sy;
1768             _.deg = deg;
1769             _.dx = dx = m.e;
1770             _.dy = dy = m.f;
1771
1772             if (sx == 1 && sy == 1 && !deg && _.bbox) {
1773                 _.bbox.x += +dx;
1774                 _.bbox.y += +dy;
1775             } else {
1776                 _.dirtyT = 1;
1777             }
1778         },
1779         getEmpty = function (item) {
1780             switch (item[0]) {
1781                 case "t": return ["t", 0, 0];
1782                 case "m": return ["m", 1, 0, 0, 1, 0, 0];
1783                 case "r": if (item.length == 4) {
1784                     return ["r", 0, item[2], item[3]];
1785                 } else {
1786                     return ["r", 0];
1787                 }
1788                 case "s": if (item.length == 5) {
1789                     return ["s", 1, 1, item[3], item[4]];
1790                 } else if (item.length == 3) {
1791                     return ["s", 1, 1];
1792                 } else {
1793                     return ["s", 1];
1794                 }
1795             }
1796         },
1797         equaliseTransform = R._equaliseTransform = function (t1, t2) {
1798             t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
1799             t1 = R.parseTransformString(t1) || [];
1800             t2 = R.parseTransformString(t2) || [];
1801             var maxlength = mmax(t1.length, t2.length),
1802                 from = [],
1803                 to = [],
1804                 i = 0, j, jj,
1805                 tt1, tt2;
1806             for (; i < maxlength; i++) {
1807                 tt1 = t1[i] || getEmpty(t2[i]);
1808                 tt2 = t2[i] || getEmpty(tt1);
1809                 if ((tt1[0] != tt2[0]) ||
1810                     (tt1[0] == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
1811                     (tt1[0] == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
1812                     ) {
1813                     return;
1814                 }
1815                 from[i] = [];
1816                 to[i] = [];
1817                 for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
1818                     j in tt1 && (from[i][j] = tt1[j]);
1819                     j in tt2 && (to[i][j] = tt2[j]);
1820                 }
1821             }
1822             return {
1823                 from: from,
1824                 to: to
1825             };
1826         };
1827     R._getContainer = function (x, y, w, h) {
1828         var container;
1829         container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
1830         if (container == null) {
1831             return;
1832         }
1833         if (container.tagName) {
1834             if (y == null) {
1835                 return {
1836                     container: container,
1837                     width: container.style.pixelWidth || container.offsetWidth,
1838                     height: container.style.pixelHeight || container.offsetHeight
1839                 };
1840             } else {
1841                 return {
1842                     container: container,
1843                     width: y,
1844                     height: w
1845                 };
1846             }
1847         }
1848         return {
1849             container: 1,
1850             x: x,
1851             y: y,
1852             width: w,
1853             height: h
1854         };
1855     };
1856     /*\
1857      * Raphael.pathToRelative
1858      [ method ]
1859      **
1860      * Utility method
1861      **
1862      * Converts path to relative form
1863      > Parameters
1864      - pathString (string|array) path string or array of segments
1865      = (array) array of segments.
1866     \*/
1867     R.pathToRelative = pathToRelative;
1868     R._engine = {};
1869     /*\
1870      * Raphael.path2curve
1871      [ method ]
1872      **
1873      * Utility method
1874      **
1875      * Converts path to a new path where all segments are cubic bezier curves.
1876      > Parameters
1877      - pathString (string|array) path string or array of segments
1878      = (array) array of segments.
1879     \*/
1880     R.path2curve = path2curve;
1881     /*\
1882      * Raphael.matrix
1883      [ method ]
1884      **
1885      * Utility method
1886      **
1887      * Returns matrix based on given parameters.
1888      > Parameters
1889      - a (number)
1890      - b (number)
1891      - c (number)
1892      - d (number)
1893      - e (number)
1894      - f (number)
1895      = (object) @Matrix
1896     \*/
1897     R.matrix = function (a, b, c, d, e, f) {
1898         return new Matrix(a, b, c, d, e, f);
1899     };
1900     function Matrix(a, b, c, d, e, f) {
1901         if (a != null) {
1902             this.a = +a;
1903             this.b = +b;
1904             this.c = +c;
1905             this.d = +d;
1906             this.e = +e;
1907             this.f = +f;
1908         } else {
1909             this.a = 1;
1910             this.b = 0;
1911             this.c = 0;
1912             this.d = 1;
1913             this.e = 0;
1914             this.f = 0;
1915         }
1916     }
1917     (function (matrixproto) {
1918         /*\
1919          * Matrix.add
1920          [ method ]
1921          **
1922          * Adds given matrix to existing one.
1923          > Parameters
1924          - a (number)
1925          - b (number)
1926          - c (number)
1927          - d (number)
1928          - e (number)
1929          - f (number)
1930          or
1931          - matrix (object) @Matrix
1932         \*/
1933         matrixproto.add = function (a, b, c, d, e, f) {
1934             var out = [[], [], []],
1935                 m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
1936                 matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
1937                 x, y, z, res;
1938
1939             if (a && a instanceof Matrix) {
1940                 matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
1941             }
1942
1943             for (x = 0; x < 3; x++) {
1944                 for (y = 0; y < 3; y++) {
1945                     res = 0;
1946                     for (z = 0; z < 3; z++) {
1947                         res += m[x][z] * matrix[z][y];
1948                     }
1949                     out[x][y] = res;
1950                 }
1951             }
1952             this.a = out[0][0];
1953             this.b = out[1][0];
1954             this.c = out[0][1];
1955             this.d = out[1][1];
1956             this.e = out[0][2];
1957             this.f = out[1][2];
1958         };
1959         /*\
1960          * Matrix.invert
1961          [ method ]
1962          **
1963          * Returns inverted version of the matrix
1964          = (object) @Matrix
1965         \*/
1966         matrixproto.invert = function () {
1967             var me = this,
1968                 x = me.a * me.d - me.b * me.c;
1969             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);
1970         };
1971         /*\
1972          * Matrix.clone
1973          [ method ]
1974          **
1975          * Returns copy of the matrix
1976          = (object) @Matrix
1977         \*/
1978         matrixproto.clone = function () {
1979             return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
1980         };
1981         /*\
1982          * Matrix.translate
1983          [ method ]
1984          **
1985          * Translate the matrix
1986          > Parameters
1987          - x (number)
1988          - y (number)
1989         \*/
1990         matrixproto.translate = function (x, y) {
1991             this.add(1, 0, 0, 1, x, y);
1992         };
1993         /*\
1994          * Matrix.scale
1995          [ method ]
1996          **
1997          * Scales the matrix
1998          > Parameters
1999          - x (number)
2000          - y (number) #optional
2001          - cx (number) #optional
2002          - cy (number) #optional
2003         \*/
2004         matrixproto.scale = function (x, y, cx, cy) {
2005             y == null && (y = x);
2006             (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
2007             this.add(x, 0, 0, y, 0, 0);
2008             (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
2009         };
2010         /*\
2011          * Matrix.rotate
2012          [ method ]
2013          **
2014          * Rotates the matrix
2015          > Parameters
2016          - a (number)
2017          - x (number)
2018          - y (number)
2019         \*/
2020         matrixproto.rotate = function (a, x, y) {
2021             a = R.rad(a);
2022             x = x || 0;
2023             y = y || 0;
2024             var cos = +math.cos(a).toFixed(9),
2025                 sin = +math.sin(a).toFixed(9);
2026             this.add(cos, sin, -sin, cos, x, y);
2027             this.add(1, 0, 0, 1, -x, -y);
2028         };
2029         /*\
2030          * Matrix.x
2031          [ method ]
2032          **
2033          * Return x coordinate for given point after transformation described by the matrix. See also @Matrix.y
2034          > Parameters
2035          - x (number)
2036          - y (number)
2037          = (number) x
2038         \*/
2039         matrixproto.x = function (x, y) {
2040             return x * this.a + y * this.c + this.e;
2041         };
2042         /*\
2043          * Matrix.y
2044          [ method ]
2045          **
2046          * Return y coordinate for given point after transformation described by the matrix. See also @Matrix.x
2047          > Parameters
2048          - x (number)
2049          - y (number)
2050          = (number) y
2051         \*/
2052         matrixproto.y = function (x, y) {
2053             return x * this.b + y * this.d + this.f;
2054         };
2055         matrixproto.get = function (i) {
2056             return +this[Str.fromCharCode(97 + i)].toFixed(4);
2057         };
2058         matrixproto.toString = function () {
2059             return R.svg ?
2060                 "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
2061                 [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
2062         };
2063         matrixproto.toFilter = function () {
2064             return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
2065                 ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
2066                 ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
2067         };
2068         matrixproto.offset = function () {
2069             return [this.e.toFixed(4), this.f.toFixed(4)];
2070         };
2071         function norm(a) {
2072             return a[0] * a[0] + a[1] * a[1];
2073         }
2074         function normalize(a) {
2075             var mag = math.sqrt(norm(a));
2076             a[0] && (a[0] /= mag);
2077             a[1] && (a[1] /= mag);
2078         }
2079         /*\
2080          * Matrix.split
2081          [ method ]
2082          **
2083          * Splits matrix into primitive transformations
2084          = (object) in format:
2085          o dx (number) translation by x
2086          o dy (number) translation by y
2087          o scalex (number) scale by x
2088          o scaley (number) scale by y
2089          o shear (number) shear
2090          o rotate (number) rotation in deg
2091          o isSimple (boolean) could it be represented via simple transformations
2092         \*/
2093         matrixproto.split = function () {
2094             var out = {};
2095             // translation
2096             out.dx = this.e;
2097             out.dy = this.f;
2098
2099             // scale and shear
2100             var row = [[this.a, this.c], [this.b, this.d]];
2101             out.scalex = math.sqrt(norm(row[0]));
2102             normalize(row[0]);
2103
2104             out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
2105             row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
2106
2107             out.scaley = math.sqrt(norm(row[1]));
2108             normalize(row[1]);
2109             out.shear /= out.scaley;
2110
2111             // rotation
2112             var sin = -row[0][1],
2113                 cos = row[1][1];
2114             if (cos < 0) {
2115                 out.rotate = R.deg(math.acos(cos));
2116                 if (sin < 0) {
2117                     out.rotate = 360 - out.rotate;
2118                 }
2119             } else {
2120                 out.rotate = R.deg(math.asin(sin));
2121             }
2122
2123             out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
2124             out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
2125             out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
2126             return out;
2127         };
2128         /*\
2129          * Matrix.toTransformString
2130          [ method ]
2131          **
2132          * Return transform string that represents given matrix
2133          = (string) transform string
2134         \*/
2135         matrixproto.toTransformString = function () {
2136             var s = this.split();
2137             if (s.isSimple) {
2138                 return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0];
2139             } else {
2140                 return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
2141             }
2142         };
2143     })(Matrix.prototype);
2144
2145     // WebKit rendering bug workaround method
2146     var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
2147     if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
2148         (navigator.vendor == "Google Inc." && version && version[1] < 8)) {
2149         /*\
2150          * Paper.safari
2151          [ method ]
2152          **
2153          * There is an inconvenient rendering bug in Safari (WebKit):
2154          * sometimes the rendering should be forced.
2155          * This method should help with dealing with this bug.
2156         \*/
2157         paperproto.safari = function () {
2158             var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
2159             setTimeout(function () {rect.remove();});
2160         };
2161     } else {
2162         paperproto.safari = fun;
2163     }
2164  
2165     var preventDefault = function () {
2166         this.returnValue = false;
2167     },
2168     preventTouch = function () {
2169         return this.originalEvent.preventDefault();
2170     },
2171     stopPropagation = function () {
2172         this.cancelBubble = true;
2173     },
2174     stopTouch = function () {
2175         return this.originalEvent.stopPropagation();
2176     },
2177     addEvent = (function () {
2178         if (g.doc.addEventListener) {
2179             return function (obj, type, fn, element) {
2180                 var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
2181                     f = function (e) {
2182                         var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
2183                             scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
2184                             x = e.clientX + scrollX,
2185                             y = e.clientY + scrollY;
2186                     if (supportsTouch && touchMap[has](type)) {
2187                         for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
2188                             if (e.targetTouches[i].target == obj) {
2189                                 var olde = e;
2190                                 e = e.targetTouches[i];
2191                                 e.originalEvent = olde;
2192                                 e.preventDefault = preventTouch;
2193                                 e.stopPropagation = stopTouch;
2194                                 break;
2195                             }
2196                         }
2197                     }
2198                     return fn.call(element, e, x, y);
2199                 };
2200                 obj.addEventListener(realName, f, false);
2201                 return function () {
2202                     obj.removeEventListener(realName, f, false);
2203                     return true;
2204                 };
2205             };
2206         } else if (g.doc.attachEvent) {
2207             return function (obj, type, fn, element) {
2208                 var f = function (e) {
2209                     e = e || g.win.event;
2210                     var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
2211                         scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
2212                         x = e.clientX + scrollX,
2213                         y = e.clientY + scrollY;
2214                     e.preventDefault = e.preventDefault || preventDefault;
2215                     e.stopPropagation = e.stopPropagation || stopPropagation;
2216                     return fn.call(element, e, x, y);
2217                 };
2218                 obj.attachEvent("on" + type, f);
2219                 var detacher = function () {
2220                     obj.detachEvent("on" + type, f);
2221                     return true;
2222                 };
2223                 return detacher;
2224             };
2225         }
2226     })(),
2227     drag = [],
2228     dragMove = function (e) {
2229         var x = e.clientX,
2230             y = e.clientY,
2231             scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
2232             scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
2233             dragi,
2234             j = drag.length;
2235         while (j--) {
2236             dragi = drag[j];
2237             if (supportsTouch) {
2238                 var i = e.touches.length,
2239                     touch;
2240                 while (i--) {
2241                     touch = e.touches[i];
2242                     if (touch.identifier == dragi.el._drag.id) {
2243                         x = touch.clientX;
2244                         y = touch.clientY;
2245                         (e.originalEvent ? e.originalEvent : e).preventDefault();
2246                         break;
2247                     }
2248                 }
2249             } else {
2250                 e.preventDefault();
2251             }
2252             var node = dragi.el.node,
2253                 o,
2254                 next = node.nextSibling,
2255                 parent = node.parentNode,
2256                 display = node.style.display;
2257             g.win.opera && parent.removeChild(node);
2258             node.style.display = "none";
2259             o = dragi.el.paper.getElementByPoint(x, y);
2260             node.style.display = display;
2261             g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
2262             o && eve("drag.over." + dragi.el.id, dragi.el, o);
2263             x += scrollX;
2264             y += scrollY;
2265             eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
2266         }
2267     },
2268     dragUp = function (e) {
2269         R.unmousemove(dragMove).unmouseup(dragUp);
2270         var i = drag.length,
2271             dragi;
2272         while (i--) {
2273             dragi = drag[i];
2274             dragi.el._drag = {};
2275             eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
2276         }
2277         drag = [];
2278     },
2279     /*\
2280      * Raphael.el
2281      [ property (object) ]
2282      **
2283      * You can add your own method to elements. This is usefull when you want to hack default functionality or
2284      * want to wrap some common transformation or attributes in one method. In difference to canvas methods,
2285      * you can redefine element method at any time. Expending element methods wouldn’t affect set.
2286      > Usage
2287      | Raphael.el.red = function () {
2288      |     this.attr({fill: "#f00"});
2289      | };
2290      | // then use it
2291      | paper.circle(100, 100, 20).red();
2292     \*/
2293     elproto = R.el = {};
2294     /*\
2295      * Element.click
2296      [ method ]
2297      **
2298      * Adds event handler for click for the element.
2299      > Parameters
2300      - handler (function) handler for the event
2301      = (object) @Element
2302     \*/
2303     /*\
2304      * Element.unclick
2305      [ method ]
2306      **
2307      * Removes event handler for click for the element.
2308      > Parameters
2309      - handler (function) handler for the event
2310      = (object) @Element
2311     \*/
2312     
2313     /*\
2314      * Element.dblclick
2315      [ method ]
2316      **
2317      * Adds event handler for double click for the element.
2318      > Parameters
2319      - handler (function) handler for the event
2320      = (object) @Element
2321     \*/
2322     /*\
2323      * Element.undblclick
2324      [ method ]
2325      **
2326      * Removes event handler for double click for the element.
2327      > Parameters
2328      - handler (function) handler for the event
2329      = (object) @Element
2330     \*/
2331     
2332     /*\
2333      * Element.mousedown
2334      [ method ]
2335      **
2336      * Adds event handler for mousedown for the element.
2337      > Parameters
2338      - handler (function) handler for the event
2339      = (object) @Element
2340     \*/
2341     /*\
2342      * Element.unmousedown
2343      [ method ]
2344      **
2345      * Removes event handler for mousedown for the element.
2346      > Parameters
2347      - handler (function) handler for the event
2348      = (object) @Element
2349     \*/
2350     
2351     /*\
2352      * Element.mousemove
2353      [ method ]
2354      **
2355      * Adds event handler for mousemove for the element.
2356      > Parameters
2357      - handler (function) handler for the event
2358      = (object) @Element
2359     \*/
2360     /*\
2361      * Element.unmousemove
2362      [ method ]
2363      **
2364      * Removes event handler for mousemove for the element.
2365      > Parameters
2366      - handler (function) handler for the event
2367      = (object) @Element
2368     \*/
2369     
2370     /*\
2371      * Element.mouseout
2372      [ method ]
2373      **
2374      * Adds event handler for mouseout for the element.
2375      > Parameters
2376      - handler (function) handler for the event
2377      = (object) @Element
2378     \*/
2379     /*\
2380      * Element.unmouseout
2381      [ method ]
2382      **
2383      * Removes event handler for mouseout for the element.
2384      > Parameters
2385      - handler (function) handler for the event
2386      = (object) @Element
2387     \*/
2388     
2389     /*\
2390      * Element.mouseover
2391      [ method ]
2392      **
2393      * Adds event handler for mouseover for the element.
2394      > Parameters
2395      - handler (function) handler for the event
2396      = (object) @Element
2397     \*/
2398     /*\
2399      * Element.unmouseover
2400      [ method ]
2401      **
2402      * Removes event handler for mouseover for the element.
2403      > Parameters
2404      - handler (function) handler for the event
2405      = (object) @Element
2406     \*/
2407     
2408     /*\
2409      * Element.mouseup
2410      [ method ]
2411      **
2412      * Adds event handler for mouseup for the element.
2413      > Parameters
2414      - handler (function) handler for the event
2415      = (object) @Element
2416     \*/
2417     /*\
2418      * Element.unmouseup
2419      [ method ]
2420      **
2421      * Removes event handler for mouseup for the element.
2422      > Parameters
2423      - handler (function) handler for the event
2424      = (object) @Element
2425     \*/
2426     
2427     /*\
2428      * Element.touchstart
2429      [ method ]
2430      **
2431      * Adds event handler for touchstart for the element.
2432      > Parameters
2433      - handler (function) handler for the event
2434      = (object) @Element
2435     \*/
2436     /*\
2437      * Element.untouchstart
2438      [ method ]
2439      **
2440      * Removes event handler for touchstart for the element.
2441      > Parameters
2442      - handler (function) handler for the event
2443      = (object) @Element
2444     \*/
2445     
2446     /*\
2447      * Element.touchmove
2448      [ method ]
2449      **
2450      * Adds event handler for touchmove for the element.
2451      > Parameters
2452      - handler (function) handler for the event
2453      = (object) @Element
2454     \*/
2455     /*\
2456      * Element.untouchmove
2457      [ method ]
2458      **
2459      * Removes event handler for touchmove for the element.
2460      > Parameters
2461      - handler (function) handler for the event
2462      = (object) @Element
2463     \*/
2464     
2465     /*\
2466      * Element.touchend
2467      [ method ]
2468      **
2469      * Adds event handler for touchend for the element.
2470      > Parameters
2471      - handler (function) handler for the event
2472      = (object) @Element
2473     \*/
2474     /*\
2475      * Element.untouchend
2476      [ method ]
2477      **
2478      * Removes event handler for touchend for the element.
2479      > Parameters
2480      - handler (function) handler for the event
2481      = (object) @Element
2482     \*/
2483     
2484     /*\
2485      * Element.touchcancel
2486      [ method ]
2487      **
2488      * Adds event handler for touchcancel for the element.
2489      > Parameters
2490      - handler (function) handler for the event
2491      = (object) @Element
2492     \*/
2493     /*\
2494      * Element.untouchcancel
2495      [ method ]
2496      **
2497      * Removes event handler for touchcancel for the element.
2498      > Parameters
2499      - handler (function) handler for the event
2500      = (object) @Element
2501     \*/
2502     for (var i = events.length; i--;) {
2503         (function (eventName) {
2504             R[eventName] = elproto[eventName] = function (fn, scope) {
2505                 if (R.is(fn, "function")) {
2506                     this.events = this.events || [];
2507                     this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
2508                 }
2509                 return this;
2510             };
2511             R["un" + eventName] = elproto["un" + eventName] = function (fn) {
2512                 var events = this.events,
2513                     l = events.length;
2514                 while (l--) if (events[l].name == eventName && events[l].f == fn) {
2515                     events[l].unbind();
2516                     events.splice(l, 1);
2517                     !events.length && delete this.events;
2518                     return this;
2519                 }
2520                 return this;
2521             };
2522         })(events[i]);
2523     }
2524     
2525     /*\
2526      * Element.data
2527      [ method ]
2528      **
2529      * Adds or retrieves given value asociated with given key.
2530      ** 
2531      * See also @Element.removeData
2532      > Parameters
2533      - key (string) key to store data
2534      - value (any) #optional value to store
2535      = (object) @Element
2536      * or, if value is not specified:
2537      = (any) value
2538      > Usage
2539      | for (var i = 0, i < 5, i++) {
2540      |     paper.circle(10 + 15 * i, 10, 10)
2541      |          .attr({fill: "#000"})
2542      |          .data("i", i)
2543      |          .click(function () {
2544      |             alert(this.data("i"));
2545      |          });
2546      | }
2547     \*/
2548     elproto.data = function (key, value) {
2549         var data = eldata[this.id] = eldata[this.id] || {};
2550         if (arguments.length == 1) {
2551             if (R.is(key, "object")) {
2552                 for (var i in key) if (key[has](i)) {
2553                     this.data(i, key[i]);
2554                 }
2555                 return this;
2556             }
2557             eve("data.get." + this.id, this, data[key], key);
2558             return data[key];
2559         }
2560         data[key] = value;
2561         eve("data.set." + this.id, this, value, key);
2562         return this;
2563     };
2564     /*\
2565      * Element.removeData
2566      [ method ]
2567      **
2568      * Removes value associated with an element by given key.
2569      * If key is not provided, removes all the data of the element.
2570      > Parameters
2571      - key (string) #optional key
2572      = (object) @Element
2573     \*/
2574     elproto.removeData = function (key) {
2575         if (key == null) {
2576             eldata[this.id] = {};
2577         } else {
2578             eldata[this.id] && delete eldata[this.id][key];
2579         }
2580         return this;
2581     };
2582     /*\
2583      * Element.hover
2584      [ method ]
2585      **
2586      * Adds event handlers for hover for the element.
2587      > Parameters
2588      - f_in (function) handler for hover in
2589      - f_out (function) handler for hover out
2590      - icontext (object) #optional context for hover in handler
2591      - ocontext (object) #optional context for hover out handler
2592      = (object) @Element
2593     \*/
2594     elproto.hover = function (f_in, f_out, scope_in, scope_out) {
2595         return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
2596     };
2597     /*\
2598      * Element.unhover
2599      [ method ]
2600      **
2601      * Removes event handlers for hover for the element.
2602      > Parameters
2603      - f_in (function) handler for hover in
2604      - f_out (function) handler for hover out
2605      = (object) @Element
2606     \*/
2607     elproto.unhover = function (f_in, f_out) {
2608         return this.unmouseover(f_in).unmouseout(f_out);
2609     };
2610     /*\
2611      * Element.drag
2612      [ method ]
2613      **
2614      * Adds event handlers for drag of the element.
2615      > Parameters
2616      - onmove (function) handler for moving
2617      - onstart (function) handler for drag start
2618      - onend (function) handler for drag end
2619      - mcontext (object) #optional context for moving handler
2620      - scontext (object) #optional context for drag start handler
2621      - econtext (object) #optional context for drag end handler
2622      * Additionaly following `drag` events will be triggered: `drag.start.<id>` on start, 
2623      * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element 
2624      * `drag.over.<id>` will be fired as well.
2625      *
2626      * Start event and start handler will be called in specified context or in context of the element with following parameters:
2627      o x (number) x position of the mouse
2628      o y (number) y position of the mouse
2629      o event (object) DOM event object
2630      * Move event and move handler will be called in specified context or in context of the element with following parameters:
2631      o dx (number) shift by x from the start point
2632      o dy (number) shift by y from the start point
2633      o x (number) x position of the mouse
2634      o y (number) y position of the mouse
2635      o event (object) DOM event object
2636      * End event and end handler will be called in specified context or in context of the element with following parameters:
2637      o event (object) DOM event object
2638      = (object) @Element
2639     \*/
2640     elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
2641         function start(e) {
2642             (e.originalEvent || e).preventDefault();
2643             var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
2644                 scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
2645             this._drag.x = e.clientX + scrollX;
2646             this._drag.y = e.clientY + scrollY;
2647             this._drag.id = e.identifier;
2648             !drag.length && R.mousemove(dragMove).mouseup(dragUp);
2649             drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
2650             onstart && eve.on("drag.start." + this.id, onstart);
2651             onmove && eve.on("drag.move." + this.id, onmove);
2652             onend && eve.on("drag.end." + this.id, onend);
2653             eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
2654         }
2655         this._drag = {};
2656         this.mousedown(start);
2657         return this;
2658     };
2659     /*\
2660      * Element.onDragOver
2661      [ method ]
2662      **
2663      * Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id).
2664      > Parameters
2665      - f (function) handler for event
2666     \*/
2667     elproto.onDragOver = function (f) {
2668         f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id);
2669     };
2670     /*\
2671      * Element.undrag
2672      [ method ]
2673      **
2674      * Removes all drag event handlers from given element.
2675     \*/
2676     elproto.undrag = function () {
2677         var i = drag.length;
2678         while (i--) if (drag[i].el == this) {
2679             R.unmousedown(drag[i].start);
2680             drag.splice(i++, 1);
2681             eve.unbind("drag.*." + this.id);
2682         }
2683         !drag.length && R.unmousemove(dragMove).unmouseup(dragUp);
2684     };
2685     /*\
2686      * Paper.circle
2687      [ method ]
2688      **
2689      * Draws a circle.
2690      **
2691      > Parameters
2692      **
2693      - x (number) x coordinate of the centre
2694      - y (number) y coordinate of the centre
2695      - r (number) radius
2696      = (object) Raphaël element object with type “circle”
2697      **
2698      > Usage
2699      | var c = paper.circle(50, 50, 40);
2700     \*/
2701     paperproto.circle = function (x, y, r) {
2702         var out = R._engine.circle(this, x || 0, y || 0, r || 0);
2703         this.__set__ && this.__set__.push(out);
2704         return out;
2705     };
2706     /*\
2707      * Paper.rect
2708      [ method ]
2709      *
2710      * Draws a rectangle.
2711      **
2712      > Parameters
2713      **
2714      - x (number) x coordinate of the top left corner
2715      - y (number) y coordinate of the top left corner
2716      - width (number) width
2717      - height (number) height
2718      - r (number) #optional radius for rounded corners, default is 0
2719      = (object) Raphaël element object with type “rect”
2720      **
2721      > Usage
2722      | // regular rectangle
2723      | var c = paper.rect(10, 10, 50, 50);
2724      | // rectangle with rounded corners
2725      | var c = paper.rect(40, 40, 50, 50, 10);
2726     \*/
2727     paperproto.rect = function (x, y, w, h, r) {
2728         var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
2729         this.__set__ && this.__set__.push(out);
2730         return out;
2731     };
2732     /*\
2733      * Paper.ellipse
2734      [ method ]
2735      **
2736      * Draws an ellipse.
2737      **
2738      > Parameters
2739      **
2740      - x (number) x coordinate of the centre
2741      - y (number) y coordinate of the centre
2742      - rx (number) horizontal radius
2743      - ry (number) vertical radius
2744      = (object) Raphaël element object with type “ellipse”
2745      **
2746      > Usage
2747      | var c = paper.ellipse(50, 50, 40, 20);
2748     \*/
2749     paperproto.ellipse = function (x, y, rx, ry) {
2750         var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
2751         this.__set__ && this.__set__.push(out);
2752         return out;
2753     };
2754     /*\
2755      * Paper.path
2756      [ method ]
2757      **
2758      * Creates a path element by given path data string.
2759      **
2760      > Parameters
2761      **
2762      - pathString (string) path data in SVG path string format.
2763      = (object) Raphaël element object with type “path”
2764      # 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>.
2765      **
2766      > Usage
2767      | var c = paper.path("M10 10L90 90");
2768      | // draw a diagonal line:
2769      | // move to 10,10, line to 90,90
2770     \*/
2771     paperproto.path = function (pathString) {
2772         pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
2773         var out = R._engine.path(R.format[apply](R, arguments), this);
2774         this.__set__ && this.__set__.push(out);
2775         return out;
2776     };
2777     /*\
2778      * Paper.image
2779      [ method ]
2780      **
2781      * Embeds an image into the surface.
2782      **
2783      > Parameters
2784      **
2785      - src (string) URI of the source image
2786      - x (number) x coordinate position
2787      - y (number) y coordinate position
2788      - width (number) width of the image
2789      - height (number) height of the image
2790      = (object) Raphaël element object with type “image”
2791      **
2792      > Usage
2793      | var c = paper.image("apple.png", 10, 10, 80, 80);
2794     \*/
2795     paperproto.image = function (src, x, y, w, h) {
2796         var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
2797         this.__set__ && this.__set__.push(out);
2798         return out;
2799     };
2800     /*\
2801      * Paper.text
2802      [ method ]
2803      **
2804      * Draws a text string. If you need line breaks, put “\n” in the string.
2805      **
2806      > Parameters
2807      **
2808      - x (number) x coordinate position
2809      - y (number) y coordinate position
2810      - text (string) The text string to draw
2811      = (object) Raphaël element object with type “text”
2812      **
2813      > Usage
2814      | var t = paper.text(50, 50, "Raphaël\nkicks\nbutt!");
2815     \*/
2816     paperproto.text = function (x, y, text) {
2817         var out = R._engine.text(this, x || 0, y || 0, Str(text));
2818         this.__set__ && this.__set__.push(out);
2819         return out;
2820     };
2821     /*\
2822      * Paper.set
2823      [ method ]
2824      **
2825      * Creates array-like object to keep and operate several elements at once.
2826      * Warning: it doesn’t create any elements for itself in the page, it just groups existing elements.
2827      * Sets act as pseudo elements — all methods available to an element can be used on a set.
2828      = (object) array-like object that represents set of elements
2829      **
2830      > Usage
2831      | var st = paper.set();
2832      | st.push(
2833      |     paper.circle(10, 10, 5),
2834      |     paper.circle(30, 10, 5)
2835      | );
2836      | st.attr({fill: "red"}); // changes the fill of both circles
2837     \*/
2838     paperproto.set = function (itemsArray) {
2839         !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
2840         var out = new Set(itemsArray);
2841         this.__set__ && this.__set__.push(out);
2842         return out;
2843     };
2844     /*\
2845      * Paper.setStart
2846      [ method ]
2847      **
2848      * Creates @Paper.set. All elements that will be created after calling this method and before calling
2849      * @Paper.setFinish will be added to the set.
2850      **
2851      > Usage
2852      | paper.setStart();
2853      | paper.circle(10, 10, 5),
2854      | paper.circle(30, 10, 5)
2855      | var st = paper.setFinish();
2856      | st.attr({fill: "red"}); // changes the fill of both circles
2857     \*/
2858     paperproto.setStart = function (set) {
2859         this.__set__ = set || this.set();
2860     };
2861     /*\
2862      * Paper.setFinish
2863      [ method ]
2864      **
2865      * See @Paper.setStart. This method finishes catching and returns resulting set.
2866      **
2867      = (object) set
2868     \*/
2869     paperproto.setFinish = function (set) {
2870         var out = this.__set__;
2871         delete this.__set__;
2872         return out;
2873     };
2874     /*\
2875      * Paper.setSize
2876      [ method ]
2877      **
2878      * If you need to change dimensions of the canvas call this method
2879      **
2880      > Parameters
2881      **
2882      - width (number) new width of the canvas
2883      - height (number) new height of the canvas
2884      > Usage
2885      | var st = paper.set();
2886      | st.push(
2887      |     paper.circle(10, 10, 5),
2888      |     paper.circle(30, 10, 5)
2889      | );
2890      | st.attr({fill: "red"});
2891     \*/
2892     paperproto.setSize = function (width, height) {
2893         return R._engine.setSize.call(this, width, height);
2894     };
2895     /*\
2896      * Paper.setViewBox
2897      [ method ]
2898      **
2899      * Sets the view box of the paper. Practically it gives you ability to zoom and pan whole paper surface by 
2900      * specifying new boundaries.
2901      **
2902      > Parameters
2903      **
2904      - x (number) new x position, default is `0`
2905      - y (number) new y position, default is `0`
2906      - w (number) new width of the canvas
2907      - h (number) new height of the canvas
2908      - fit (boolean) `true` if you want graphics to fit into new boundary box
2909     \*/
2910     paperproto.setViewBox = function (x, y, w, h, fit) {
2911         return R._engine.setViewBox.call(this, x, y, w, h, fit);
2912     };
2913     /*\
2914      * Paper.top
2915      [ property ]
2916      **
2917      * Points to the topmost element on the paper
2918     \*/
2919     /*\
2920      * Paper.bottom
2921      [ property ]
2922      **
2923      * Points to the bottom element on the paper
2924     \*/
2925     paperproto.top = paperproto.bottom = null;
2926     /*\
2927      * Paper.raphael
2928      [ property ]
2929      **
2930      * Points to the @Raphael object/function
2931     \*/
2932     paperproto.raphael = R;
2933     var getOffset = function (elem) {
2934         var box = elem.getBoundingClientRect(),
2935             doc = elem.ownerDocument,
2936             body = doc.body,
2937             docElem = doc.documentElement,
2938             clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
2939             top  = box.top  + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
2940             left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
2941         return {
2942             y: top,
2943             x: left
2944         };
2945     };
2946     /*\
2947      * Paper.getElementByPoint
2948      [ method ]
2949      **
2950      * Returns you topmost element under given point.
2951      **
2952      = (object) Raphaël element object
2953      > Parameters
2954      **
2955      - x (number) x coordinate from the top left corner of the window
2956      - y (number) y coordinate from the top left corner of the window
2957      > Usage
2958      | paper.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
2959     \*/
2960     paperproto.getElementByPoint = function (x, y) {
2961         var paper = this,
2962             svg = paper.canvas,
2963             target = g.doc.elementFromPoint(x, y);
2964         if (g.win.opera && target.tagName == "svg") {
2965             var so = getOffset(svg),
2966                 sr = svg.createSVGRect();
2967             sr.x = x - so.x;
2968             sr.y = y - so.y;
2969             sr.width = sr.height = 1;
2970             var hits = svg.getIntersectionList(sr, null);
2971             if (hits.length) {
2972                 target = hits[hits.length - 1];
2973             }
2974         }
2975         if (!target) {
2976             return null;
2977         }
2978         while (target.parentNode && target != svg.parentNode && !target.raphael) {
2979             target = target.parentNode;
2980         }
2981         target == paper.canvas.parentNode && (target = svg);
2982         target = target && target.raphael ? paper.getById(target.raphaelid) : null;
2983         return target;
2984     };
2985     /*\
2986      * Paper.getById
2987      [ method ]
2988      **
2989      * Returns you element by its internal ID.
2990      **
2991      > Parameters
2992      **
2993      - id (number) id
2994      = (object) Raphaël element object
2995     \*/
2996     paperproto.getById = function (id) {
2997         var bot = this.bottom;
2998         while (bot) {
2999             if (bot.id == id) {
3000                 return bot;
3001             }
3002             bot = bot.next;
3003         }
3004         return null;
3005     };
3006     /*\
3007      * Paper.forEach
3008      [ method ]
3009      **
3010      * Executes given function for each element on the paper
3011      *
3012      * If callback function returns `false` it will stop loop running.
3013      **
3014      > Parameters
3015      **
3016      - callback (function) function to run
3017      - thisArg (object) context object for the callback
3018      = (object) Paper object
3019     \*/
3020     paperproto.forEach = function (callback, thisArg) {
3021         var bot = this.bottom;
3022         while (bot) {
3023             if (callback.call(thisArg, bot) === false) {
3024                 return this;
3025             }
3026             bot = bot.next;
3027         }
3028         return this;
3029     };
3030     function x_y() {
3031         return this.x + S + this.y;
3032     }
3033     function x_y_w_h() {
3034         return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
3035     }
3036     /*\
3037      * Element.getBBox
3038      [ method ]
3039      **
3040      * Return bounding box for a given element
3041      **
3042      > Parameters
3043      **
3044      - isWithoutTransform (boolean) flag, `true` if you want to have bounding box before transformations. Default is `false`.
3045      = (object) Bounding box object:
3046      o {
3047      o     x: (number) top left corner x
3048      o     y: (number) top left corner y
3049      o     width: (number) width
3050      o     height: (number) height
3051      o }
3052     \*/
3053     elproto.getBBox = function (isWithoutTransform) {
3054         if (this.removed) {
3055             return {};
3056         }
3057         var _ = this._;
3058         if (isWithoutTransform) {
3059             if (_.dirty || !_.bboxwt) {
3060                 this.realPath = getPath[this.type](this);
3061                 _.bboxwt = pathDimensions(this.realPath);
3062                 _.bboxwt.toString = x_y_w_h;
3063                 _.dirty = 0;
3064             }
3065             return _.bboxwt;
3066         }
3067         if (_.dirty || _.dirtyT || !_.bbox) {
3068             if (_.dirty || !this.realPath) {
3069                 _.bboxwt = 0;
3070                 this.realPath = getPath[this.type](this);
3071             }
3072             _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
3073             _.bbox.toString = x_y_w_h;
3074             _.dirty = _.dirtyT = 0;
3075         }
3076         return _.bbox;
3077     };
3078     /*\
3079      * Element.clone
3080      [ method ]
3081      **
3082      = (object) clone of a given element
3083      **
3084     \*/
3085     elproto.clone = function () {
3086         if (this.removed) {
3087             return null;
3088         }
3089         return this.paper[this.type]().attr(this.attr());
3090     };
3091     /*\
3092      * Element.glow
3093      [ method ]
3094      **
3095      * Return set of elements that create glow-like effect around given element. See @Paper.set.
3096      *
3097      * Note: Glow is not connected to the element. If you change element attributes it won’t adjust itself.
3098      **
3099      > Parameters
3100      **
3101      - glow (object) #optional parameters object with all properties optional:
3102      o {
3103      o     width (number) size of the glow, default is `10`
3104      o     fill (boolean) will it be filled, default is `false`
3105      o     opacity (number) opacity, default is `0.5`
3106      o     offsetx (number) horizontal offset, default is `0`
3107      o     offsety (number) vertical offset, default is `0`
3108      o     color (string) glow colour, default is `black`
3109      o }
3110      = (object) @Paper.set of elements that represents glow
3111     \*/
3112     elproto.glow = function (glow) {
3113         if (this.type == "text") {
3114             return null;
3115         }
3116         glow = glow || {};
3117         var s = {
3118             width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
3119             fill: glow.fill || false,
3120             opacity: glow.opacity || .5,
3121             offsetx: glow.offsetx || 0,
3122             offsety: glow.offsety || 0,
3123             color: glow.color || "#000"
3124         },
3125             c = s.width / 2,
3126             r = this.paper,
3127             out = r.set(),
3128             path = this.realPath || getPath[this.type](this);
3129         path = this.matrix ? mapPath(path, this.matrix) : path;
3130         for (var i = 1; i < c + 1; i++) {
3131             out.push(r.path(path).attr({
3132                 stroke: s.color,
3133                 fill: s.fill ? s.color : "none",
3134                 "stroke-linejoin": "round",
3135                 "stroke-linecap": "round",
3136                 "stroke-width": +(s.width / c * i).toFixed(3),
3137                 opacity: +(s.opacity / c).toFixed(3)
3138             }));
3139         }
3140         return out.insertBefore(this).translate(s.offsetx, s.offsety);
3141     };
3142     var curveslengths = {},
3143     getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
3144         var len = 0,
3145             precision = 100,
3146             name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(),
3147             cache = curveslengths[name],
3148             old, dot;
3149         !cache && (curveslengths[name] = cache = {data: []});
3150         cache.timer && clearTimeout(cache.timer);
3151         cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3);
3152         if (length != null && !cache.precision) {
3153             var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
3154             cache.precision = ~~total * 10;
3155             cache.data = [];
3156         }
3157         precision = cache.precision || precision;
3158         for (var i = 0; i < precision + 1; i++) {
3159             if (cache.data[i * precision]) {
3160                 dot = cache.data[i * precision];
3161             } else {
3162                 dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision);
3163                 cache.data[i * precision] = dot;
3164             }
3165             i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
3166             if (length != null && len >= length) {
3167                 return dot;
3168             }
3169             old = dot;
3170         }
3171         if (length == null) {
3172             return len;
3173         }
3174     },
3175     getLengthFactory = function (istotal, subpath) {
3176         return function (path, length, onlystart) {
3177             path = path2curve(path);
3178             var x, y, p, l, sp = "", subpaths = {}, point,
3179                 len = 0;
3180             for (var i = 0, ii = path.length; i < ii; i++) {
3181                 p = path[i];
3182                 if (p[0] == "M") {
3183                     x = +p[1];
3184                     y = +p[2];
3185                 } else {
3186                     l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
3187                     if (len + l > length) {
3188                         if (subpath && !subpaths.start) {
3189                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
3190                             sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
3191                             if (onlystart) {return sp;}
3192                             subpaths.start = sp;
3193                             sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
3194                             len += l;
3195                             x = +p[5];
3196                             y = +p[6];
3197                             continue;
3198                         }
3199                         if (!istotal && !subpath) {
3200                             point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
3201                             return {x: point.x, y: point.y, alpha: point.alpha};
3202                         }
3203                     }
3204                     len += l;
3205                     x = +p[5];
3206                     y = +p[6];
3207                 }
3208                 sp += p.shift() + p;
3209             }
3210             subpaths.end = sp;
3211             point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], 1);
3212             point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
3213             return point;
3214         };
3215     };
3216     var getTotalLength = getLengthFactory(1),
3217         getPointAtLength = getLengthFactory(),
3218         getSubpathsAtLength = getLengthFactory(0, 1);
3219     /*\
3220      * Raphael.getTotalLength
3221      [ method ]
3222      **
3223      * Returns length of the given path in pixels.
3224      **
3225      > Parameters
3226      **
3227      - path (string) SVG path string.
3228      **
3229      = (number) length.
3230     \*/
3231     R.getTotalLength = getTotalLength;
3232     /*\
3233      * Raphael.getPointAtLength
3234      [ method ]
3235      **
3236      * Return coordinates of the point located at the given length on the given path.
3237      **
3238      > Parameters
3239      **
3240      - path (string) SVG path string
3241      - length (number)
3242      **
3243      = (object) representation of the point:
3244      o {
3245      o     x: (number) x coordinate
3246      o     y: (number) y coordinate
3247      o     alpha: (number) angle of derivative
3248      o }
3249     \*/
3250     R.getPointAtLength = getPointAtLength;
3251     /*\
3252      * Raphael.getSubpath
3253      [ method ]
3254      **
3255      * Return subpath of a given path from given length to given length.
3256      **
3257      > Parameters
3258      **
3259      - path (string) SVG path string
3260      - from (number) position of the start of the segment
3261      - to (number) position of the end of the segment
3262      **
3263      = (string) pathstring for the segment
3264     \*/
3265     R.getSubpath = function (path, from, to) {
3266         if (abs(this.getTotalLength(path) - to) < 1e-6) {
3267             return getSubpathsAtLength(path, from).end;
3268         }
3269         var a = getSubpathsAtLength(path, to, 1);
3270         return from ? getSubpathsAtLength(a, from).end : a;
3271     };
3272     /*\
3273      * Element.getTotalLength
3274      [ method ]
3275      **
3276      * Returns length of the path in pixels. Only works for element of “path” type.
3277      = (number) length.
3278     \*/
3279     elproto.getTotalLength = function () {
3280         if (this.type != "path") {return;}
3281         if (this.node.getTotalLength) {
3282             return this.node.getTotalLength();
3283         }
3284         return getTotalLength(this.attrs.path);
3285     };
3286     /*\
3287      * Element.getPointAtLength
3288      [ method ]
3289      **
3290      * Return coordinates of the point located at the given length on the given path. Only works for element of “path” type.
3291      **
3292      > Parameters
3293      **
3294      - length (number)
3295      **
3296      = (object) representation of the point:
3297      o {
3298      o     x: (number) x coordinate
3299      o     y: (number) y coordinate
3300      o     alpha: (number) angle of derivative
3301      o }
3302     \*/
3303     elproto.getPointAtLength = function (length) {
3304         if (this.type != "path") {return;}
3305         return getPointAtLength(this.attrs.path, length);
3306     };
3307     /*\
3308      * Element.getSubpath
3309      [ method ]
3310      **
3311      * Return subpath of a given element from given length to given length. Only works for element of “path” type.
3312      **
3313      > Parameters
3314      **
3315      - from (number) position of the start of the segment
3316      - to (number) position of the end of the segment
3317      **
3318      = (string) pathstring for the segment
3319     \*/
3320     elproto.getSubpath = function (from, to) {
3321         if (this.type != "path") {return;}
3322         return R.getSubpath(this.attrs.path, from, to);
3323     };
3324     /*\
3325      * Raphael.easing_formulas
3326      [ property ]
3327      **
3328      * Object that contains easing formulas for animation. You could extend it with your own. By default it has following list of easing:
3329      # <ul>
3330      #     <li>“linear”</li>
3331      #     <li>“&lt;” or “easeIn” or “ease-in”</li>
3332      #     <li>“>” or “easeOut” or “ease-out”</li>
3333      #     <li>“&lt;>” or “easeInOut” or “ease-in-out”</li>
3334      #     <li>“backIn” or “back-in”</li>
3335      #     <li>“backOut” or “back-out”</li>
3336      #     <li>“elastic”</li>
3337      #     <li>“bounce”</li>
3338      # </ul>
3339      # <p>See also <a href="http://raphaeljs.com/easing.html">Easing demo</a>.</p>
3340     \*/
3341     var ef = R.easing_formulas = {
3342         linear: function (n) {
3343             return n;
3344         },
3345         "<": function (n) {
3346             return pow(n, 1.7);
3347         },
3348         ">": function (n) {
3349             return pow(n, .48);
3350         },
3351         "<>": function (n) {
3352             var q = .48 - n / 1.04,
3353                 Q = math.sqrt(.1734 + q * q),
3354                 x = Q - q,
3355                 X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
3356                 y = -Q - q,
3357                 Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
3358                 t = X + Y + .5;
3359             return (1 - t) * 3 * t * t + t * t * t;
3360         },
3361         backIn: function (n) {
3362             var s = 1.70158;
3363             return n * n * ((s + 1) * n - s);
3364         },
3365         backOut: function (n) {
3366             n = n - 1;
3367             var s = 1.70158;
3368             return n * n * ((s + 1) * n + s) + 1;
3369         },
3370         elastic: function (n) {
3371             if (n == !!n) {
3372                 return n;
3373             }
3374             return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
3375         },
3376         bounce: function (n) {
3377             var s = 7.5625,
3378                 p = 2.75,
3379                 l;
3380             if (n < (1 / p)) {
3381                 l = s * n * n;
3382             } else {
3383                 if (n < (2 / p)) {
3384                     n -= (1.5 / p);
3385                     l = s * n * n + .75;
3386                 } else {
3387                     if (n < (2.5 / p)) {
3388                         n -= (2.25 / p);
3389                         l = s * n * n + .9375;
3390                     } else {
3391                         n -= (2.625 / p);
3392                         l = s * n * n + .984375;
3393                     }
3394                 }
3395             }
3396             return l;
3397         }
3398     };
3399     ef.easeIn = ef["ease-in"] = ef["<"];
3400     ef.easeOut = ef["ease-out"] = ef[">"];
3401     ef.easeInOut = ef["ease-in-out"] = ef["<>"];
3402     ef["back-in"] = ef.backIn;
3403     ef["back-out"] = ef.backOut;
3404
3405     var animationElements = [],
3406         requestAnimFrame = window.requestAnimationFrame       ||
3407                            window.webkitRequestAnimationFrame ||
3408                            window.mozRequestAnimationFrame    ||
3409                            window.oRequestAnimationFrame      ||
3410                            window.msRequestAnimationFrame     ||
3411                            function (callback) {
3412                                setTimeout(callback, 16);
3413                            },
3414         animation = function () {
3415             var Now = +new Date,
3416                 l = 0;
3417             for (; l < animationElements.length; l++) {
3418                 var e = animationElements[l];
3419                 if (e.el.removed || e.paused) {
3420                     continue;
3421                 }
3422                 var time = Now - e.start,
3423                     ms = e.ms,
3424                     easing = e.easing,
3425                     from = e.from,
3426                     diff = e.diff,
3427                     to = e.to,
3428                     t = e.t,
3429                     that = e.el,
3430                     set = {},
3431                     now;
3432                 if (e.initstatus) {
3433                     time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
3434                     e.status = e.initstatus;
3435                     delete e.initstatus;
3436                     e.stop && animationElements.splice(l--, 1);
3437                 } else {
3438                     e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
3439                 }
3440                 if (time < 0) {
3441                     continue;
3442                 }
3443                 if (time < ms) {
3444                     var pos = easing(time / ms);
3445                     for (var attr in from) if (from[has](attr)) {
3446                         switch (availableAnimAttrs[attr]) {
3447                             case nu:
3448                                 now = +from[attr] + pos * ms * diff[attr];
3449                                 break;
3450                             case "colour":
3451                                 now = "rgb(" + [
3452                                     upto255(round(from[attr].r + pos * ms * diff[attr].r)),
3453                                     upto255(round(from[attr].g + pos * ms * diff[attr].g)),
3454                                     upto255(round(from[attr].b + pos * ms * diff[attr].b))
3455                                 ].join(",") + ")";
3456                                 break;
3457                             case "path":
3458                                 now = [];
3459                                 for (var i = 0, ii = from[attr].length; i < ii; i++) {
3460                                     now[i] = [from[attr][i][0]];
3461                                     for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
3462                                         now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
3463                                     }
3464                                     now[i] = now[i].join(S);
3465                                 }
3466                                 now = now.join(S);
3467                                 break;
3468                             case "transform":
3469                                 if (diff[attr].real) {
3470                                     now = [];
3471                                     for (i = 0, ii = from[attr].length; i < ii; i++) {
3472                                         now[i] = [from[attr][i][0]];
3473                                         for (j = 1, jj = from[attr][i].length; j < jj; j++) {
3474                                             now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
3475                                         }
3476                                     }
3477                                 } else {
3478                                     var get = function (i) {
3479                                         return +from[attr][i] + pos * ms * diff[attr][i];
3480                                     };
3481                                     // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
3482                                     now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
3483                                 }
3484                                 break;
3485                             case "csv":
3486                                 if (attr == "clip-rect") {
3487                                     now = [];
3488                                     i = 4;
3489                                     while (i--) {
3490                                         now[i] = +from[attr][i] + pos * ms * diff[attr][i];
3491                                     }
3492                                 }
3493                                 break;
3494                             default:
3495                                 var from2 = [].concat(from[attr]);
3496                                 now = [];
3497                                 i = that.paper.customAttributes[attr].length;
3498                                 while (i--) {
3499                                     now[i] = +from2[i] + pos * ms * diff[attr][i];
3500                                 }
3501                                 break;
3502                         }
3503                         set[attr] = now;
3504                     }
3505                     that.attr(set);
3506                     (function (id, that, anim) {
3507                         setTimeout(function () {
3508                             eve("anim.frame." + id, that, anim);
3509                         });
3510                     })(that.id, that, e.anim);
3511                 } else {
3512                     (function(f, el, a) {
3513                         setTimeout(function() {
3514                             eve("anim.frame." + el.id, el, a);
3515                             eve("anim.finish." + el.id, el, a);
3516                             R.is(f, "function") && f.call(el);
3517                         });
3518                     })(e.callback, that, e.anim);
3519                     that.attr(to);
3520                     animationElements.splice(l--, 1);
3521                     if (e.repeat > 1 && !e.next) {
3522                         runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
3523                     }
3524                     if (e.next && !e.stop) {
3525                         runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
3526                     }
3527                 }
3528             }
3529             R.svg && that && that.paper && that.paper.safari();
3530             animationElements.length && requestAnimFrame(animation);
3531         },
3532         upto255 = function (color) {
3533             return color > 255 ? 255 : color < 0 ? 0 : color;
3534         };
3535     /*\
3536      * Element.animateWith
3537      [ method ]
3538      **
3539      * Acts similar to @Element.animate, but ensure that given animation runs in sync with another given element.
3540      **
3541      > Parameters
3542      **
3543      - element (object) element to sync with
3544      - anim (object) animation to sync with
3545      - params (object) #optional final attributes for the element, see also @Element.attr
3546      - ms (number) #optional number of milliseconds for animation to run
3547      - easing (string) #optional easing type. Accept on of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
3548      - callback (function) #optional callback function. Will be called at the end of animation.
3549      * or
3550      - element (object) element to sync with
3551      - anim (object) animation to sync with
3552      - animation (object) #optional animation object, see @Raphael.animation
3553      **
3554      = (object) original element
3555     \*/
3556     elproto.animateWith = function (element, anim, params, ms, easing, callback) {
3557         var a = params ? R.animation(params, ms, easing, callback) : anim;
3558             status = element.status(anim);
3559         return this.animate(a).status(a, status * anim.ms / a.ms);
3560     };
3561     function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
3562         var cx = 3 * p1x,
3563             bx = 3 * (p2x - p1x) - cx,
3564             ax = 1 - cx - bx,
3565             cy = 3 * p1y,
3566             by = 3 * (p2y - p1y) - cy,
3567             ay = 1 - cy - by;
3568         function sampleCurveX(t) {
3569             return ((ax * t + bx) * t + cx) * t;
3570         }
3571         function solve(x, epsilon) {
3572             var t = solveCurveX(x, epsilon);
3573             return ((ay * t + by) * t + cy) * t;
3574         }
3575         function solveCurveX(x, epsilon) {
3576             var t0, t1, t2, x2, d2, i;
3577             for(t2 = x, i = 0; i < 8; i++) {
3578                 x2 = sampleCurveX(t2) - x;
3579                 if (abs(x2) < epsilon) {
3580                     return t2;
3581                 }
3582                 d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
3583                 if (abs(d2) < 1e-6) {
3584                     break;
3585                 }
3586                 t2 = t2 - x2 / d2;
3587             }
3588             t0 = 0;
3589             t1 = 1;
3590             t2 = x;
3591             if (t2 < t0) {
3592                 return t0;
3593             }
3594             if (t2 > t1) {
3595                 return t1;
3596             }
3597             while (t0 < t1) {
3598                 x2 = sampleCurveX(t2);
3599                 if (abs(x2 - x) < epsilon) {
3600                     return t2;
3601                 }
3602                 if (x > x2) {
3603                     t0 = t2;
3604                 } else {
3605                     t1 = t2;
3606                 }
3607                 t2 = (t1 - t0) / 2 + t0;
3608             }
3609             return t2;
3610         }
3611         return solve(t, 1 / (200 * duration));
3612     }
3613     elproto.onAnimation = function (f) {
3614         f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id);
3615         return this;
3616     };
3617     function Animation(anim, ms) {
3618         var percents = [],
3619             newAnim = {};
3620         this.ms = ms;
3621         this.times = 1;
3622         if (anim) {
3623             for (var attr in anim) if (anim[has](attr)) {
3624                 newAnim[toFloat(attr)] = anim[attr];
3625                 percents.push(toFloat(attr));
3626             }
3627             percents.sort(sortByNumber);
3628         }
3629         this.anim = newAnim;
3630         this.top = percents[percents.length - 1];
3631         this.percents = percents;
3632     }
3633     /*\
3634      * Animation.delay
3635      [ method ]
3636      **
3637      * Creates a copy of existing animation object with given delay.
3638      **
3639      > Parameters
3640      **
3641      - delay (number) number of ms to pass between animation start and actual animation
3642      **
3643      = (object) new altered Animation object
3644     \*/
3645     Animation.prototype.delay = function (delay) {
3646         var a = new Animation(this.anim, this.ms);
3647         a.times = this.times;
3648         a.del = +delay || 0;
3649         return a;
3650     };
3651     /*\
3652      * Animation.repeat
3653      [ method ]
3654      **
3655      * Creates a copy of existing animation object with given repetition.
3656      **
3657      > Parameters
3658      **
3659      - repeat (number) number iterations of animation. For infinite animation pass `Infinity`
3660      **
3661      = (object) new altered Animation object
3662     \*/
3663     Animation.prototype.repeat = function (times) { 
3664         var a = new Animation(this.anim, this.ms);
3665         a.del = this.del;
3666         a.times = math.floor(mmax(times, 0)) || 1;
3667         return a;
3668     };
3669     function runAnimation(anim, element, percent, status, totalOrigin, times) {
3670         percent = toFloat(percent);
3671         var params,
3672             isInAnim,
3673             isInAnimSet,
3674             percents = [],
3675             next,
3676             prev,
3677             timestamp,
3678             ms = anim.ms,
3679             from = {},
3680             to = {},
3681             diff = {};
3682         if (status) {
3683             for (i = 0, ii = animationElements.length; i < ii; i++) {
3684                 var e = animationElements[i];
3685                 if (e.el.id == element.id && e.anim == anim) {
3686                     if (e.percent != percent) {
3687                         animationElements.splice(i, 1);
3688                         isInAnimSet = 1;
3689                     } else {
3690                         isInAnim = e;
3691                     }
3692                     element.attr(e.totalOrigin);
3693                     break;
3694                 }
3695             }
3696         } else {
3697             status = +to; // NaN
3698         }
3699         for (var i = 0, ii = anim.percents.length; i < ii; i++) {
3700             if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
3701                 percent = anim.percents[i];
3702                 prev = anim.percents[i - 1] || 0;
3703                 ms = ms / anim.top * (percent - prev);
3704                 next = anim.percents[i + 1];
3705                 params = anim.anim[percent];
3706                 break;
3707             } else if (status) {
3708                 element.attr(anim.anim[anim.percents[i]]);
3709             }
3710         }
3711         if (!params) {
3712             return;
3713         }
3714         if (!isInAnim) {
3715             for (attr in params) if (params[has](attr)) {
3716                 if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
3717                     from[attr] = element.attr(attr);
3718                     (from[attr] == null) && (from[attr] = availableAttrs[attr]);
3719                     to[attr] = params[attr];
3720                     switch (availableAnimAttrs[attr]) {
3721                         case nu:
3722                             diff[attr] = (to[attr] - from[attr]) / ms;
3723                             break;
3724                         case "colour":
3725                             from[attr] = R.getRGB(from[attr]);
3726                             var toColour = R.getRGB(to[attr]);
3727                             diff[attr] = {
3728                                 r: (toColour.r - from[attr].r) / ms,
3729                                 g: (toColour.g - from[attr].g) / ms,
3730                                 b: (toColour.b - from[attr].b) / ms
3731                             };
3732                             break;
3733                         case "path":
3734                             var pathes = path2curve(from[attr], to[attr]),
3735                                 toPath = pathes[1];
3736                             from[attr] = pathes[0];
3737                             diff[attr] = [];
3738                             for (i = 0, ii = from[attr].length; i < ii; i++) {
3739                                 diff[attr][i] = [0];
3740                                 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
3741                                     diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
3742                                 }
3743                             }
3744                             break;
3745                         case "transform":
3746                             var _ = element._,
3747                                 eq = equaliseTransform(_[attr], to[attr]);
3748                             if (eq) {
3749                                 from[attr] = eq.from;
3750                                 to[attr] = eq.to;
3751                                 diff[attr] = [];
3752                                 diff[attr].real = true;
3753                                 for (i = 0, ii = from[attr].length; i < ii; i++) {
3754                                     diff[attr][i] = [from[attr][i][0]];
3755                                     for (j = 1, jj = from[attr][i].length; j < jj; j++) {
3756                                         diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
3757                                     }
3758                                 }
3759                             } else {
3760                                 var m = (element.matrix || new Matrix),
3761                                     to2 = {
3762                                         _: {transform: _.transform},
3763                                         getBBox: function () {
3764                                             return element.getBBox(1);
3765                                         }
3766                                     };
3767                                 from[attr] = [
3768                                     m.a,
3769                                     m.b,
3770                                     m.c,
3771                                     m.d,
3772                                     m.e,
3773                                     m.f
3774                                 ];
3775                                 extractTransform(to2, to[attr]);
3776                                 to[attr] = to2._.transform;
3777                                 diff[attr] = [
3778                                     (to2.matrix.a - m.a) / ms,
3779                                     (to2.matrix.b - m.b) / ms,
3780                                     (to2.matrix.c - m.c) / ms,
3781                                     (to2.matrix.d - m.d) / ms,
3782                                     (to2.matrix.e - m.e) / ms,
3783                                     (to2.matrix.e - m.f) / ms
3784                                 ];
3785                                 // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
3786                                 // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
3787                                 // extractTransform(to2, to[attr]);
3788                                 // diff[attr] = [
3789                                 //     (to2._.sx - _.sx) / ms,
3790                                 //     (to2._.sy - _.sy) / ms,
3791                                 //     (to2._.deg - _.deg) / ms,
3792                                 //     (to2._.dx - _.dx) / ms,
3793                                 //     (to2._.dy - _.dy) / ms
3794                                 // ];
3795                             }
3796                             break;
3797                         case "csv":
3798                             var values = Str(params[attr]).split(separator),
3799                                 from2 = Str(from[attr]).split(separator);
3800                             if (attr == "clip-rect") {
3801                                 from[attr] = from2;
3802                                 diff[attr] = [];
3803                                 i = from2.length;
3804                                 while (i--) {
3805                                     diff[attr][i] = (values[i] - from[attr][i]) / ms;
3806                                 }
3807                             }
3808                             to[attr] = values;
3809                             break;
3810                         default:
3811                             values = [].concat(params[attr]);
3812                             from2 = [].concat(from[attr]);
3813                             diff[attr] = [];
3814                             i = element.paper.customAttributes[attr].length;
3815                             while (i--) {
3816                                 diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
3817                             }
3818                             break;
3819                     }
3820                 }
3821             }
3822             var easing = params.easing,
3823                 easyeasy = R.easing_formulas[easing];
3824             if (!easyeasy) {
3825                 easyeasy = Str(easing).match(bezierrg);
3826                 if (easyeasy && easyeasy.length == 5) {
3827                     var curve = easyeasy;
3828                     easyeasy = function (t) {
3829                         return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
3830                     };
3831                 } else {
3832                     easyeasy = pipe;
3833                 }
3834             }
3835             timestamp = params.start || anim.start || +new Date;
3836             e = {
3837                 anim: anim,
3838                 percent: percent,
3839                 timestamp: timestamp,
3840                 start: timestamp + (anim.del || 0),
3841                 status: 0,
3842                 initstatus: status || 0,
3843                 stop: false,
3844                 ms: ms,
3845                 easing: easyeasy,
3846                 from: from,
3847                 diff: diff,
3848                 to: to,
3849                 el: element,
3850                 callback: params.callback,
3851                 prev: prev,
3852                 next: next,
3853                 repeat: times || anim.times,
3854                 origin: element.attr(),
3855                 totalOrigin: totalOrigin
3856             };
3857             animationElements.push(e);
3858             if (status && !isInAnim && !isInAnimSet) {
3859                 e.stop = true;
3860                 e.start = new Date - ms * status;
3861                 if (animationElements.length == 1) {
3862                     return animation();
3863                 }
3864             }
3865             if (isInAnimSet) {
3866                 e.start = new Date - e.ms * status;
3867             }
3868             animationElements.length == 1 && requestAnimFrame(animation);
3869         } else {
3870             isInAnim.initstatus = status;
3871             isInAnim.start = new Date - isInAnim.ms * status;
3872         }
3873         eve("anim.start." + element.id, element, anim);
3874     }
3875     /*\
3876      * Raphael.animation
3877      [ method ]
3878      **
3879      * Creates an animation object that can be passed to the @Element.animate or @Element.animateWith methods.
3880      * See also @Animation.delay and @Animation.repeat methods.
3881      **
3882      > Parameters
3883      **
3884      - params (object) final attributes for the element, see also @Element.attr
3885      - ms (number) number of milliseconds for animation to run
3886      - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
3887      - callback (function) #optional callback function. Will be called at the end of animation.
3888      **
3889      = (object) @Animation
3890     \*/
3891     R.animation = function (params, ms, easing, callback) {
3892         if (params instanceof Animation) {
3893             return params;
3894         }
3895         if (R.is(easing, "function") || !easing) {
3896             callback = callback || easing || null;
3897             easing = null;
3898         }
3899         params = Object(params);
3900         ms = +ms || 0;
3901         var p = {},
3902             json,
3903             attr;
3904         for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
3905             json = true;
3906             p[attr] = params[attr];
3907         }
3908         if (!json) {
3909             return new Animation(params, ms);
3910         } else {
3911             easing && (p.easing = easing);
3912             callback && (p.callback = callback);
3913             return new Animation({100: p}, ms);
3914         }
3915     };
3916     /*\
3917      * Element.animate
3918      [ method ]
3919      **
3920      * Creates and starts animation for given element.
3921      **
3922      > Parameters
3923      **
3924      - params (object) final attributes for the element, see also @Element.attr
3925      - ms (number) number of milliseconds for animation to run
3926      - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
3927      - callback (function) #optional callback function. Will be called at the end of animation.
3928      * or
3929      - animation (object) animation object, see @Raphael.animation
3930      **
3931      = (object) original element
3932     \*/
3933     elproto.animate = function (params, ms, easing, callback) {
3934         var element = this;
3935         if (element.removed) {
3936             callback && callback.call(element);
3937             return element;
3938         }
3939         var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
3940         runAnimation(anim, element, anim.percents[0], null, element.attr());
3941         return element;
3942     };
3943     /*\
3944      * Element.setTime
3945      [ method ]
3946      **
3947      * Sets the status of animation of the element in milliseconds. Similar to @Element.status method.
3948      **
3949      > Parameters
3950      **
3951      - anim (object) animation object
3952      - value (number) number of milliseconds from the beginning of the animation
3953      **
3954      = (object) original element if `value` is specified
3955      * Note, that during animation following events are triggered:
3956      *
3957      * On each animation frame event `anim.frame.<id>`, on start `anim.start.<id>` and on end `anim.finish.<id>`.
3958     \*/
3959     elproto.setTime = function (anim, value) {
3960         if (anim && value != null) {
3961             this.status(anim, mmin(value, anim.ms) / anim.ms);
3962         }
3963         return this;
3964     };
3965     /*\
3966      * Element.status
3967      [ method ]
3968      **
3969      * Gets or sets the status of animation of the element.
3970      **
3971      > Parameters
3972      **
3973      - anim (object) #optional animation object
3974      - 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.
3975      **
3976      = (number) status
3977      * or
3978      = (array) status if `anim` is not specified. Array of objects in format:
3979      o {
3980      o     anim: (object) animation object
3981      o     status: (number) status
3982      o }
3983      * or
3984      = (object) original element if `value` is specified
3985     \*/
3986     elproto.status = function (anim, value) {
3987         var out = [],
3988             i = 0,
3989             len,
3990             e;
3991         if (value != null) {
3992             runAnimation(anim, this, -1, mmin(value, 1));
3993             return this;
3994         } else {
3995             len = animationElements.length;
3996             for (; i < len; i++) {
3997                 e = animationElements[i];
3998                 if (e.el.id == this.id && (!anim || e.anim == anim)) {
3999                     if (anim) {
4000                         return e.status;
4001                     }
4002                     out.push({
4003                         anim: e.anim,
4004                         status: e.status
4005                     });
4006                 }
4007             }
4008             if (anim) {
4009                 return 0;
4010             }
4011             return out;
4012         }
4013     };
4014     /*\
4015      * Element.pause
4016      [ method ]
4017      **
4018      * Stops animation of the element with ability to resume it later on.
4019      **
4020      > Parameters
4021      **
4022      - anim (object) #optional animation object
4023      **
4024      = (object) original element
4025     \*/
4026     elproto.pause = function (anim) {
4027         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
4028             if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) {
4029                 animationElements[i].paused = true;
4030             }
4031         }
4032         return this;
4033     };
4034     /*\
4035      * Element.resume
4036      [ method ]
4037      **
4038      * Resumes animation if it was paused with @Element.pause method.
4039      **
4040      > Parameters
4041      **
4042      - anim (object) #optional animation object
4043      **
4044      = (object) original element
4045     \*/
4046     elproto.resume = function (anim) {
4047         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
4048             var e = animationElements[i];
4049             if (eve("anim.resume." + this.id, this, e.anim) !== false) {
4050                 delete e.paused;
4051                 this.status(e.anim, e.status);
4052             }
4053         }
4054         return this;
4055     };
4056     /*\
4057      * Element.stop
4058      [ method ]
4059      **
4060      * Stops animation of the element.
4061      **
4062      > Parameters
4063      **
4064      - anim (object) #optional animation object
4065      **
4066      = (object) original element
4067     \*/
4068     elproto.stop = function (anim) {
4069         for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
4070             if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) {
4071                 animationElements.splice(i--, 1);
4072             }
4073         }
4074         return this;
4075     };
4076     elproto.toString = function () {
4077         return "Rapha\xebl\u2019s object";
4078     };
4079
4080     // Set
4081     var Set = function (items) {
4082         this.items = [];
4083         this.length = 0;
4084         this.type = "set";
4085         if (items) {
4086             for (var i = 0, ii = items.length; i < ii; i++) {
4087                 if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
4088                     this[this.items.length] = this.items[this.items.length] = items[i];
4089                     this.length++;
4090                 }
4091             }
4092         }
4093     },
4094     setproto = Set.prototype;
4095     /*\
4096      * Set.push
4097      [ method ]
4098      **
4099      * Adds each argument to the current set.
4100      = (object) original element
4101     \*/
4102     setproto.push = function () {
4103         var item,
4104             len;
4105         for (var i = 0, ii = arguments.length; i < ii; i++) {
4106             item = arguments[i];
4107             if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
4108                 len = this.items.length;
4109                 this[len] = this.items[len] = item;
4110                 this.length++;
4111             }
4112         }
4113         return this;
4114     };
4115     /*\
4116      * Set.pop
4117      [ method ]
4118      **
4119      * Removes last element and returns it.
4120      = (object) element
4121     \*/
4122     setproto.pop = function () {
4123         this.length && delete this[this.length--];
4124         return this.items.pop();
4125     };
4126     /*\
4127      * Set.forEach
4128      [ method ]
4129      **
4130      * Executes given function for each element in the set.
4131      *
4132      * If function returns `false` it will stop loop running.
4133      **
4134      > Parameters
4135      **
4136      - callback (function) function to run
4137      - thisArg (object) context object for the callback
4138      = (object) Set object
4139     \*/
4140     setproto.forEach = function (callback, thisArg) {
4141         for (var i = 0, ii = this.items.length; i < ii; i++) {
4142             if (callback.call(thisArg, this.items[i]) === false) {
4143                 return this;
4144             }
4145         }
4146         return this;
4147     };
4148     for (var method in elproto) if (elproto[has](method)) {
4149         setproto[method] = (function (methodname) {
4150             return function () {
4151                 var arg = arguments;
4152                 return this.forEach(function (el) {
4153                     el[methodname][apply](el, arg);
4154                 });
4155             };
4156         })(method);
4157     }
4158     setproto.attr = function (name, value) {
4159         if (name && R.is(name, array) && R.is(name[0], "object")) {
4160             for (var j = 0, jj = name.length; j < jj; j++) {
4161                 this.items[j].attr(name[j]);
4162             }
4163         } else {
4164             for (var i = 0, ii = this.items.length; i < ii; i++) {
4165                 this.items[i].attr(name, value);
4166             }
4167         }
4168         return this;
4169     };
4170     /*\
4171      * Set.clear
4172      [ method ]
4173      **
4174      * Removeds all elements from the set
4175     \*/
4176     setproto.clear = function () {
4177         while (this.length) {
4178             this.pop();
4179         }
4180     };
4181     /*\
4182      * Set.splice
4183      [ method ]
4184      **
4185      * Removes given element from the set
4186      **
4187      > Parameters
4188      **
4189      - index (number) position of the deletion
4190      - count (number) number of element to remove
4191      - insertion… (object) #optional elements to insert
4192      = (object) set elements that were deleted
4193     \*/
4194     setproto.splice = function (index, count, insertion) {
4195         index = index < 0 ? mmax(this.length + index, 0) : index;
4196         count = mmax(0, mmin(this.length - index, count));
4197         var tail = [],
4198             todel = [],
4199             args = [],
4200             i;
4201         for (i = 2; i < arguments.length; i++) {
4202             args.push(arguments[i]);
4203         }
4204         for (i = 0; i < count; i++) {
4205             todel.push(this[index + i]);
4206         }
4207         for (; i < this.length - index; i++) {
4208             tail.push(this[index + i]);
4209         }
4210         var arglen = args.length;
4211         for (i = 0; i < arglen + tail.length; i++) {
4212             this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
4213         }
4214         i = this.items.length = this.length -= count - arglen;
4215         while (this[i]) {
4216             delete this[i++];
4217         }
4218         return new Set(todel);
4219     };
4220     /*\
4221      * Set.exclude
4222      [ method ]
4223      **
4224      * Removes given element from the set
4225      **
4226      > Parameters
4227      **
4228      - element (object) element to remove
4229      = (boolean) `true` if object was found & removed from the set
4230     \*/
4231     setproto.exclude = function (el) {
4232         for (var i = 0, ii = this.length, found; i < ii; i++) if (found || this[i] == el) {
4233             this[i] = this[i + 1];
4234             found = 1;
4235         }
4236         if (found) {
4237             this.length--;
4238             delete this[i];
4239             return true;
4240         }
4241     };
4242     setproto.animate = function (params, ms, easing, callback) {
4243         (R.is(easing, "function") || !easing) && (callback = easing || null);
4244         var len = this.items.length,
4245             i = len,
4246             item,
4247             set = this,
4248             collector;
4249         if (!len) {
4250             return this;
4251         }
4252         callback && (collector = function () {
4253             !--len && callback.call(set);
4254         });
4255         easing = R.is(easing, string) ? easing : collector;
4256         var anim = R.animation(params, ms, easing, collector);
4257         item = this.items[--i].animate(anim);
4258         while (i--) {
4259             this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim);
4260         }
4261         return this;
4262     };
4263     setproto.insertAfter = function (el) {
4264         var i = this.items.length;
4265         while (i--) {
4266             this.items[i].insertAfter(el);
4267         }
4268         return this;
4269     };
4270     setproto.getBBox = function () {
4271         var x = [],
4272             y = [],
4273             w = [],
4274             h = [];
4275         for (var i = this.items.length; i--;) if (!this.items[i].removed) {
4276             var box = this.items[i].getBBox();
4277             x.push(box.x);
4278             y.push(box.y);
4279             w.push(box.x + box.width);
4280             h.push(box.y + box.height);
4281         }
4282         x = mmin[apply](0, x);
4283         y = mmin[apply](0, y);
4284         return {
4285             x: x,
4286             y: y,
4287             width: mmax[apply](0, w) - x,
4288             height: mmax[apply](0, h) - y
4289         };
4290     };
4291     setproto.clone = function (s) {
4292         s = new Set;
4293         for (var i = 0, ii = this.items.length; i < ii; i++) {
4294             s.push(this.items[i].clone());
4295         }
4296         return s;
4297     };
4298     setproto.toString = function () {
4299         return "Rapha\xebl\u2018s set";
4300     };
4301
4302     /*\
4303      * Raphael.registerFont
4304      [ method ]
4305      **
4306      * Adds given font to the registered set of fonts for Raphaël. Should be used as an internal call from within Cufón’s font file.
4307      * Returns original parameter, so it could be used with chaining.
4308      # <a href="http://wiki.github.com/sorccu/cufon/about">More about Cufón and how to convert your font form TTF, OTF, etc to JavaScript file.</a>
4309      **
4310      > Parameters
4311      **
4312      - font (object) the font to register
4313      = (object) the font you passed in
4314      > Usage
4315      | Cufon.registerFont(Raphael.registerFont({…}));
4316     \*/
4317     R.registerFont = function (font) {
4318         if (!font.face) {
4319             return font;
4320         }
4321         this.fonts = this.fonts || {};
4322         var fontcopy = {
4323                 w: font.w,
4324                 face: {},
4325                 glyphs: {}
4326             },
4327             family = font.face["font-family"];
4328         for (var prop in font.face) if (font.face[has](prop)) {
4329             fontcopy.face[prop] = font.face[prop];
4330         }
4331         if (this.fonts[family]) {
4332             this.fonts[family].push(fontcopy);
4333         } else {
4334             this.fonts[family] = [fontcopy];
4335         }
4336         if (!font.svg) {
4337             fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
4338             for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
4339                 var path = font.glyphs[glyph];
4340                 fontcopy.glyphs[glyph] = {
4341                     w: path.w,
4342                     k: {},
4343                     d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
4344                             return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
4345                         }) + "z"
4346                 };
4347                 if (path.k) {
4348                     for (var k in path.k) if (path[has](k)) {
4349                         fontcopy.glyphs[glyph].k[k] = path.k[k];
4350                     }
4351                 }
4352             }
4353         }
4354         return font;
4355     };
4356     /*\
4357      * Paper.getFont
4358      [ method ]
4359      **
4360      * Finds font object in the registered fonts by given parameters. You could specify only one word from the font name, like “Myriad” for “Myriad Pro”.
4361      **
4362      > Parameters
4363      **
4364      - family (string) font family name or any word from it
4365      - weight (string) #optional font weight
4366      - style (string) #optional font style
4367      - stretch (string) #optional font stretch
4368      = (object) the font object
4369      > Usage
4370      | paper.print(100, 100, "Test string", paper.getFont("Times", 800), 30);
4371     \*/
4372     paperproto.getFont = function (family, weight, style, stretch) {
4373         stretch = stretch || "normal";
4374         style = style || "normal";
4375         weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
4376         if (!R.fonts) {
4377             return;
4378         }
4379         var font = R.fonts[family];
4380         if (!font) {
4381             var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
4382             for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
4383                 if (name.test(fontName)) {
4384                     font = R.fonts[fontName];
4385                     break;
4386                 }
4387             }
4388         }
4389         var thefont;
4390         if (font) {
4391             for (var i = 0, ii = font.length; i < ii; i++) {
4392                 thefont = font[i];
4393                 if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
4394                     break;
4395                 }
4396             }
4397         }
4398         return thefont;
4399     };
4400     /*\
4401      * Paper.print
4402      [ method ]
4403      **
4404      * Creates set of shapes to represent given font at given position with given size.
4405      * Result of the method is set object (see @Paper.set) which contains each letter as separate path object.
4406      **
4407      > Parameters
4408      **
4409      - x (number) x position of the text
4410      - y (number) y position of the text
4411      - text (string) text to print
4412      - font (object) font object, see @Paper.getFont
4413      - size (number) #optional size of the font, default is `16`
4414      - origin (string) #optional could be `"baseline"` or `"middle"`, default is `"middle"`
4415      - letter_spacing (number) #optional number in range `-1..1`, default is `0`
4416      = (object) resulting set of letters
4417      > Usage
4418      | var txt = r.print(10, 50, "print", r.getFont("Museo"), 30).attr({fill: "#fff"});
4419      | // following line will paint first letter in red
4420      | txt[0].attr({fill: "#f00"});
4421     \*/
4422     paperproto.print = function (x, y, string, font, size, origin, letter_spacing) {
4423         origin = origin || "middle"; // baseline|middle
4424         letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
4425         var out = this.set(),
4426             letters = Str(string).split(E),
4427             shift = 0,
4428             path = E,
4429             scale;
4430         R.is(font, string) && (font = this.getFont(font));
4431         if (font) {
4432             scale = (size || 16) / font.face["units-per-em"];
4433             var bb = font.face.bbox.split(separator),
4434                 top = +bb[0],
4435                 height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
4436             for (var i = 0, ii = letters.length; i < ii; i++) {
4437                 var prev = i && font.glyphs[letters[i - 1]] || {},
4438                     curr = font.glyphs[letters[i]];
4439                 shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
4440                 curr && curr.d && out.push(this.path(curr.d).attr({
4441                     fill: "#000",
4442                     stroke: "none",
4443                     transform: [["t", shift * scale, 0]]
4444                 }));
4445             }
4446             out.transform(["...s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
4447         }
4448         return out;
4449     };
4450
4451     /*\
4452      * Raphael.format
4453      [ method ]
4454      **
4455      * Simple format function. Replaces construction of type “`{<number>}`” to the corresponding argument.
4456      **
4457      > Parameters
4458      **
4459      - token (string) string to format
4460      - … (string) rest of arguments will be treated as parameters for replacement
4461      = (string) formated string
4462      > Usage
4463      | var x = 10,
4464      |     y = 20,
4465      |     width = 40,
4466      |     height = 50;
4467      | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
4468      | paper.path(Raphael.format("M{1},{2}h{3}v{4}h{5}z", x, y, width, height, -width));
4469     \*/
4470     R.format = function (token, params) {
4471         var args = R.is(params, array) ? [0][concat](params) : arguments;
4472         token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
4473             return args[++i] == null ? E : args[i];
4474         }));
4475         return token || E;
4476     };
4477     /*\
4478      * Raphael.fullfill
4479      [ method ]
4480      **
4481      * A little bit more advanced format function than @Raphael.format. Replaces construction of type “`{<name>}`” to the corresponding argument.
4482      **
4483      > Parameters
4484      **
4485      - token (string) string to format
4486      - json (object) object which properties will be used as a replacement
4487      = (string) formated string
4488      > Usage
4489      | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
4490      | paper.path(Raphael.format("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
4491      |     x: 10,
4492      |     y: 20,
4493      |     dim: {
4494      |         width: 40,
4495      |         height: 50,
4496      |         "negative width": -40
4497      |     }
4498      | }));
4499     \*/
4500     R.fullfill = (function () {
4501         var tokenRegex = /\{([^\}]+)\}/g,
4502             objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
4503             replacer = function (all, key, obj) {
4504                 var res = obj;
4505                 key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
4506                     name = name || quotedName;
4507                     if (res) {
4508                         if (name in res) {
4509                             res = res[name];
4510                         }
4511                         typeof res == "function" && isFunc && (res = res());
4512                     }
4513                 });
4514                 res = (res == null || res == obj ? all : res) + "";
4515                 return res;
4516             };
4517         return function (str, obj) {
4518             return String(str).replace(tokenRegex, function (all, key) {
4519                 return replacer(all, key, obj);
4520             });
4521         };
4522     })();
4523     /*\
4524      * Raphael.ninja
4525      [ method ]
4526      **
4527      * If you want to leave no trace of Raphaël (Well, Raphaël creates only one global variable `Raphael`, but anyway.) You can use `ninja` method.
4528      * Beware, that in this case plugins could stop working, because they are depending on global variable existance.
4529      **
4530      = (object) Raphael object
4531      > Usage
4532      | (function (local_raphael) {
4533      |     var paper = local_raphael(10, 10, 320, 200);
4534      |     …
4535      | })(Raphael.ninja());
4536     \*/
4537     R.ninja = function () {
4538         oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
4539         return R;
4540     };
4541     /*\
4542      * Raphael.st
4543      [ property (object) ]
4544      **
4545      * You can add your own method to elements and sets. It is wise to add a set method for each element method
4546      * you added, so you will be able to call the same method on sets too.
4547      **
4548      * See also @Raphael.el.
4549      > Usage
4550      | Raphael.el.red = function () {
4551      |     this.attr({fill: "#f00"});
4552      | };
4553      | Raphael.st.red = function () {
4554      |     this.forEach(function () {
4555      |         this.red();
4556      |     });
4557      | };
4558      | // then use it
4559      | paper.set(paper.circle(100, 100, 20), paper.circle(110, 100, 20)).red();
4560     \*/
4561     R.st = setproto;
4562     // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
4563     (function (doc, loaded, f) {
4564         if (doc.readyState == null && doc.addEventListener){
4565             doc.addEventListener(loaded, f = function () {
4566                 doc.removeEventListener(loaded, f, false);
4567                 doc.readyState = "complete";
4568             }, false);
4569             doc.readyState = "loading";
4570         }
4571         function isLoaded() {
4572             (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("DOMload");
4573         }
4574         isLoaded();
4575     })(document, "DOMContentLoaded");
4576
4577     oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);
4578     
4579     eve.on("DOMload", function () {
4580         loaded = true;
4581     });
4582 })();