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