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