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