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