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