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