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