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