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 isFloating; 2927 x = x || 0; 2928 y = y || 0; 2929 width = width || 512; 2930 height = height || 342; 2931 $(cnvs, { 2932 height: height, 2933 version: 1.1, 2934 width: width, 2935 xmlns: "http://www.w3.org/2000/svg" 2936 }); 2937 if (container == 1) { 2938 cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px"; 2939 g.doc.body.appendChild(cnvs); 2940 isFloating = 1; 2941 } else { 2942 cnvs.style.cssText = css + "position:relative"; 2943 if (container.firstChild) { 2944 container.insertBefore(cnvs, container.firstChild); 2945 } else { 2946 container.appendChild(cnvs); 2947 } 2948 } 2949 container = new Paper; 2950 container.width = width; 2951 container.height = height; 2952 container.canvas = cnvs; 2953 plugins.call(container, container, R.fn); 2954 container.clear(); 2955 container._left = container._top = 0; 2956 isFloating && (container.renderfix = fun); 2957 container.renderfix(); 2958 return container; 2959 }, 2960 setViewBox = function (x, y, w, h, fit) { 2961 eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]); 2962 var size = mmax(w / this.width, h / this.height), 2963 top = this.top, 2964 aspectRatio = fit ? "meet" : "xMinYMin", 2965 vb, 2966 sw; 2967 if (x == null) { 2968 if (this._vbSize) { 2969 size = 1; 2970 } 2971 delete this._vbSize; 2972 vb = "0 0 " + this.width + S + this.height; 2973 } else { 2974 this._vbSize = size; 2975 vb = x + S + y + S + w + S + h; 2976 } 2977 $(this.canvas, { 2978 viewBox: vb, 2979 preserveAspectRatio: aspectRatio 2980 }); 2981 while (size && top) { 2982 sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1; 2983 top.attr({"stroke-width": sw}); 2984 top._.dirty = 1; 2985 top._.dirtyT = 1; 2986 top = top.prev; 2987 } 2988 this._viewBox = [x, y, w, h, !!fit]; 2989 return this; 2990 }; 2991 /*\ 2992 * Paper.renderfix 2993 [ method ] 2994 ** 2995 * Fixes the issue of Firefox and IE9 regarding subpixel rendering. If paper is dependant 2996 * on other elements after reflow it could shift half pixel which cause for lines to lost their crispness. 2997 * This method fixes the issue. 2998 ** 2999 Special thanks to Mariusz Nowak (http://www.medikoo.com/) for this method. 3000 \*/ 3001 paperproto.renderfix = function () { 3002 var cnvs = this.canvas, 3003 s = cnvs.style, 3004 pos = cnvs.getScreenCTM(), 3005 left = -pos.e % 1, 3006 top = -pos.f % 1; 3007 if (left || top) { 3008 if (left) { 3009 this._left = (this._left + left) % 1; 3010 s.left = this._left + "px"; 3011 } 3012 if (top) { 3013 this._top = (this._top + top) % 1; 3014 s.top = this._top + "px"; 3015 } 3016 } 3017 }; 3018 /*\ 3019 * Paper.clear 3020 [ method ] 3021 ** 3022 * Clears the paper, i.e. removes all the elements. 3023 \*/ 3024 paperproto.clear = function () { 3025 eve("clear", this); 3026 var c = this.canvas; 3027 while (c.firstChild) { 3028 c.removeChild(c.firstChild); 3029 } 3030 this.bottom = this.top = null; 3031 (this.desc = $("desc")).appendChild(g.doc.createTextNode("Created with Rapha\xebl " + R.version)); 3032 c.appendChild(this.desc); 3033 c.appendChild(this.defs = $("defs")); 3034 }; 3035 /*\ 3036 * Paper.remove 3037 [ method ] 3038 ** 3039 * Removes the paper from the DOM. 3040 \*/ 3041 paperproto.remove = function () { 3042 eve("remove", this); 3043 this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); 3044 for (var i in this) { 3045 this[i] = removed(i); 3046 } 3047 }; 3048 } 3049 3050 // VML 3051 if (R.vml) { 3052 var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"}, 3053 bites = /([clmz]),?([^clmz]*)/gi, 3054 blurregexp = / progid:\S+Blur\([^\)]+\)/g, 3055 val = /-?[^,\s-]+/g, 3056 cssDot = "position:absolute;left:0;top:0;width:1px;height:1px", 3057 zoom = 21600, 3058 pathTypes = {path: 1, rect: 1}, 3059 ovalTypes = {circle: 1, ellipse: 1}, 3060 path2vml = function (path) { 3061 var total = /[ahqstv]/ig, 3062 command = pathToAbsolute; 3063 Str(path).match(total) && (command = path2curve); 3064 total = /[clmz]/g; 3065 if (command == pathToAbsolute && !Str(path).match(total)) { 3066 var res = Str(path).replace(bites, function (all, command, args) { 3067 var vals = [], 3068 isMove = lowerCase.call(command) == "m", 3069 res = map[command]; 3070 args.replace(val, function (value) { 3071 if (isMove && vals.length == 2) { 3072 res += vals + map[command == "m" ? "l" : "L"]; 3073 vals = []; 3074 } 3075 vals.push(round(value * zoom)); 3076 }); 3077 return res + vals; 3078 }); 3079 return res; 3080 } 3081 var pa = command(path), p, r; 3082 res = []; 3083 for (var i = 0, ii = pa.length; i < ii; i++) { 3084 p = pa[i]; 3085 r = lowerCase.call(pa[i][0]); 3086 r == "z" && (r = "x"); 3087 for (var j = 1, jj = p.length; j < jj; j++) { 3088 r += round(p[j] * zoom) + (j != jj - 1 ? "," : E); 3089 } 3090 res.push(r); 3091 } 3092 return res.join(S); 3093 }, 3094 compensation = function (deg, dx, dy) { 3095 var m = new Matrix; 3096 m.rotate(-deg, .5, .5); 3097 return { 3098 dx: m.x(dx, dy), 3099 dy: m.y(dx, dy) 3100 }; 3101 }, 3102 setCoords = function (p) { 3103 var _ = p._, 3104 sx = _.sx, 3105 sy = _.sy, 3106 deg = _.deg, 3107 dx = _.dx, 3108 dy = _.dy, 3109 fillpos = _.fillpos, 3110 o = p.node, 3111 s = o.style, 3112 y = 1, 3113 m = p.matrix, 3114 flip = "", 3115 dxdy, 3116 kx = zoom / sx, 3117 ky = zoom / sy; 3118 s.visibility = "hidden"; 3119 o.coordsize = abs(kx) + S + abs(ky); 3120 s.rotation = deg * (sx * sy < 0 ? -1 : 1); 3121 if (deg) { 3122 var c = compensation(deg, dx, dy); 3123 dx = c.dx; 3124 dy = c.dy; 3125 } 3126 sx < 0 && (flip += "x"); 3127 sy < 0 && (flip += " y") && (y = -1); 3128 s.flip = flip; 3129 o.coordorigin = (dx * -kx) + S + (dy * -ky); 3130 if (fillpos || _.fillsize) { 3131 var fill = o.getElementsByTagName(fillString); 3132 fill = fill && fill[0]; 3133 o.removeChild(fill); 3134 if (fillpos) { 3135 c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1])); 3136 fill.position = c.dx * y + S + c.dy * y; 3137 } 3138 if (_.fillsize) { 3139 fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy); 3140 } 3141 o.appendChild(fill); 3142 } 3143 s.visibility = "visible"; 3144 }; 3145 R.toString = function () { 3146 return "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version; 3147 }; 3148 addArrow = function (o, value, isEnd) { 3149 var values = Str(value).toLowerCase().split("-"), 3150 se = isEnd ? "end" : "start", 3151 i = values.length, 3152 type = "classic", 3153 w = "medium", 3154 h = "medium"; 3155 while (i--) { 3156 switch (values[i]) { 3157 case "block": 3158 case "classic": 3159 case "oval": 3160 case "diamond": 3161 case "open": 3162 case "none": 3163 type = values[i]; 3164 break; 3165 case "wide": 3166 case "narrow": h = values[i]; break; 3167 case "long": 3168 case "short": w = values[i]; break; 3169 } 3170 } 3171 var stroke = o.node.getElementsByTagName("stroke")[0]; 3172 stroke[se + "arrow"] = type; 3173 stroke[se + "arrowlength"] = w; 3174 stroke[se + "arrowwidth"] = h; 3175 }; 3176 setFillAndStroke = function (o, params) { 3177 o.paper.canvas.style.display = "none"; 3178 o.attrs = o.attrs || {}; 3179 var node = o.node, 3180 a = o.attrs, 3181 s = node.style, 3182 xy, 3183 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), 3184 isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry), 3185 res = o; 3186 3187 3188 for (var par in params) if (params[has](par)) { 3189 a[par] = params[par]; 3190 } 3191 if (newpath) { 3192 a.path = getPath[o.type](o); 3193 o._.dirty = 1; 3194 } 3195 params.href && (node.href = params.href); 3196 params.title && (node.title = params.title); 3197 params.target && (node.target = params.target); 3198 params.cursor && (s.cursor = params.cursor); 3199 "blur" in params && o.blur(params.blur); 3200 "transform" in params && o.transform(params.transform); 3201 if (params.path && o.type == "path" || newpath) { 3202 node.path = path2vml(a.path); 3203 } 3204 if (isOval) { 3205 var cx = a.cx, 3206 cy = a.cy, 3207 rx = a.rx || a.r || 0, 3208 ry = a.ry || a.r || 0; 3209 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)); 3210 } 3211 if ("clip-rect" in params) { 3212 var rect = Str(params["clip-rect"]).split(separator); 3213 if (rect.length == 4) { 3214 rect[2] = +rect[2] + (+rect[0]); 3215 rect[3] = +rect[3] + (+rect[1]); 3216 var div = node.clipRect || g.doc.createElement("div"), 3217 dstyle = div.style, 3218 group = node.parentNode; 3219 dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect); 3220 if (!node.clipRect) { 3221 dstyle.position = "absolute"; 3222 dstyle.top = 0; 3223 dstyle.left = 0; 3224 dstyle.width = o.paper.width + "px"; 3225 dstyle.height = o.paper.height + "px"; 3226 group.parentNode.insertBefore(div, group); 3227 div.appendChild(group); 3228 node.clipRect = div; 3229 } 3230 } 3231 if (!params["clip-rect"]) { 3232 node.clipRect && (node.clipRect.style.clip = E); 3233 } 3234 } 3235 if (o.textpath) { 3236 var textpathStyle = o.textpath.style; 3237 params.font && (textpathStyle.font = params.font); 3238 params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"'); 3239 params["font-size"] && (textpathStyle.fontSize = params["font-size"]); 3240 params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]); 3241 params["font-style"] && (textpathStyle.fontStyle = params["font-style"]); 3242 } 3243 if ("arrow-start" in params) { 3244 addArrow(res, params["arrow-start"]); 3245 } 3246 if ("arrow-end" in params) { 3247 addArrow(res, params["arrow-end"], 1); 3248 } 3249 if (params.opacity != null || 3250 params["stroke-width"] != null || 3251 params.fill != null || 3252 params.src != null || 3253 params.stroke != null || 3254 params["stroke-width"] != null || 3255 params["stroke-opacity"] != null || 3256 params["fill-opacity"] != null || 3257 params["stroke-dasharray"] != null || 3258 params["stroke-miterlimit"] != null || 3259 params["stroke-linejoin"] != null || 3260 params["stroke-linecap"] != null) { 3261 var fill = node.getElementsByTagName(fillString), 3262 newfill = false; 3263 fill = fill && fill[0]; 3264 !fill && (newfill = fill = createNode(fillString)); 3265 if (o.type == "image" && params.src) { 3266 fill.src = params.src; 3267 } 3268 if ("fill-opacity" in params || "opacity" in params) { 3269 var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1); 3270 opacity = mmin(mmax(opacity, 0), 1); 3271 fill.opacity = opacity; 3272 } 3273 params.fill && (fill.on = true); 3274 if (fill.on == null || params.fill == "none" || params.fill === null) { 3275 fill.on = false; 3276 } 3277 if (fill.on && params.fill) { 3278 var isURL = params.fill.match(ISURL); 3279 if (isURL) { 3280 fill.parentNode == node && node.removeChild(fill); 3281 fill.rotate = true; 3282 fill.src = isURL[1]; 3283 fill.type = "tile"; 3284 var bbox = o.getBBox(1); 3285 fill.position = bbox.x + S + bbox.y; 3286 o._.fillpos = [bbox.x, bbox.y]; 3287 3288 preload(isURL[1], function () { 3289 o._.fillsize = [this.offsetWidth, this.offsetHeight]; 3290 }); 3291 } else { 3292 fill.color = R.getRGB(params.fill).hex; 3293 fill.src = E; 3294 fill.type = "solid"; 3295 if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) { 3296 a.fill = "none"; 3297 a.gradient = params.fill; 3298 fill.rotate = false; 3299 } 3300 } 3301 } 3302 node.appendChild(fill); 3303 var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]), 3304 newstroke = false; 3305 !stroke && (newstroke = stroke = createNode("stroke")); 3306 if ((params.stroke && params.stroke != "none") || 3307 params["stroke-width"] || 3308 params["stroke-opacity"] != null || 3309 params["stroke-dasharray"] || 3310 params["stroke-miterlimit"] || 3311 params["stroke-linejoin"] || 3312 params["stroke-linecap"]) { 3313 stroke.on = true; 3314 } 3315 (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false); 3316 var strokeColor = R.getRGB(params.stroke); 3317 stroke.on && params.stroke && (stroke.color = strokeColor.hex); 3318 opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1); 3319 var width = (toFloat(params["stroke-width"]) || 1) * .75; 3320 opacity = mmin(mmax(opacity, 0), 1); 3321 params["stroke-width"] == null && (width = a["stroke-width"]); 3322 params["stroke-width"] && (stroke.weight = width); 3323 width && width < 1 && (opacity *= width) && (stroke.weight = 1); 3324 stroke.opacity = opacity; 3325 3326 params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter"); 3327 stroke.miterlimit = params["stroke-miterlimit"] || 8; 3328 params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round"); 3329 if (params["stroke-dasharray"]) { 3330 var dasharray = { 3331 "-": "shortdash", 3332 ".": "shortdot", 3333 "-.": "shortdashdot", 3334 "-..": "shortdashdotdot", 3335 ". ": "dot", 3336 "- ": "dash", 3337 "--": "longdash", 3338 "- .": "dashdot", 3339 "--.": "longdashdot", 3340 "--..": "longdashdotdot" 3341 }; 3342 stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E; 3343 } 3344 newstroke && node.appendChild(stroke); 3345 } 3346 if (res.type == "text") { 3347 res.paper.canvas.style.display = E; 3348 var span = res.paper.span, 3349 m = 100, 3350 fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/); 3351 s = span.style; 3352 a.font && (s.font = a.font); 3353 a["font-family"] && (s.fontFamily = a["font-family"]); 3354 a["font-weight"] && (s.fontWeight = a["font-weight"]); 3355 a["font-style"] && (s.fontStyle = a["font-style"]); 3356 fontSize = toFloat(fontSize ? fontSize[0] : a["font-size"]); 3357 s.fontSize = fontSize * m + "px"; 3358 res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>")); 3359 var brect = span.getBoundingClientRect(); 3360 res.W = a.w = (brect.right - brect.left) / m; 3361 res.H = a.h = (brect.bottom - brect.top) / m; 3362 res.paper.canvas.style.display = "none"; 3363 res.X = a.x; 3364 res.Y = a.y + res.H / 2; 3365 3366 ("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)); 3367 var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"]; 3368 for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) { 3369 res._.dirty = 1; 3370 break; 3371 } 3372 3373 // text-anchor emulation 3374 switch (a["text-anchor"]) { 3375 case "start": 3376 res.textpath.style["v-text-align"] = "left"; 3377 res.bbx = res.W / 2; 3378 break; 3379 case "end": 3380 res.textpath.style["v-text-align"] = "right"; 3381 res.bbx = -res.W / 2; 3382 break; 3383 default: 3384 res.textpath.style["v-text-align"] = "center"; 3385 res.bbx = 0; 3386 break; 3387 } 3388 res.textpath.style["v-text-kern"] = true; 3389 } 3390 res.paper.canvas.style.display = E; 3391 }; 3392 addGradientFill = function (o, gradient, fill) { 3393 o.attrs = o.attrs || {}; 3394 var attrs = o.attrs, 3395 type = "linear", 3396 fxfy = ".5 .5"; 3397 o.attrs.gradient = gradient; 3398 gradient = Str(gradient).replace(radial_gradient, function (all, fx, fy) { 3399 type = "radial"; 3400 if (fx && fy) { 3401 fx = toFloat(fx); 3402 fy = toFloat(fy); 3403 pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5); 3404 fxfy = fx + S + fy; 3405 } 3406 return E; 3407 }); 3408 gradient = gradient.split(/\s*\-\s*/); 3409 if (type == "linear") { 3410 var angle = gradient.shift(); 3411 angle = -toFloat(angle); 3412 if (isNaN(angle)) { 3413 return null; 3414 } 3415 } 3416 var dots = parseDots(gradient); 3417 if (!dots) { 3418 return null; 3419 } 3420 o = o.shape || o.node; 3421 if (dots.length) { 3422 o.removeChild(fill); 3423 fill.on = true; 3424 fill.method = "none"; 3425 fill.color = dots[0].color; 3426 fill.color2 = dots[dots.length - 1].color; 3427 var clrs = []; 3428 for (var i = 0, ii = dots.length; i < ii; i++) { 3429 dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color); 3430 } 3431 fill.colors && (fill.colors.value = clrs.length ? clrs.join() : "0% " + fill.color); 3432 if (type == "radial") { 3433 fill.type = "gradientTitle"; 3434 fill.focus = "100%"; 3435 fill.focussize = "0 0"; 3436 fill.focusposition = fxfy; 3437 fill.angle = 0; 3438 } else { 3439 // fill.rotate= true; 3440 fill.type = "gradient"; 3441 fill.angle = (270 - angle) % 360; 3442 } 3443 o.appendChild(fill); 3444 // alert(fill.outerHTML); 3445 } 3446 return 1; 3447 }; 3448 Element = function (node, vml) { 3449 this[0] = this.node = node; 3450 node.raphael = true; 3451 this.id = R._oid++; 3452 node.raphaelid = this.id; 3453 this.X = 0; 3454 this.Y = 0; 3455 this.attrs = {}; 3456 this.paper = vml; 3457 this.matrix = new Matrix; 3458 this._ = { 3459 transform: [], 3460 sx: 1, 3461 sy: 1, 3462 dx: 0, 3463 dy: 0, 3464 deg: 0, 3465 dirty: 1, 3466 dirtyT: 1 3467 }; 3468 !vml.bottom && (vml.bottom = this); 3469 this.prev = vml.top; 3470 vml.top && (vml.top.next = this); 3471 vml.top = this; 3472 this.next = null; 3473 }; 3474 elproto = Element.prototype; 3475 elproto.transform = function (tstr) { 3476 if (tstr == null) { 3477 return this._.transform; 3478 } 3479 extractTransform(this, tstr); 3480 var matrix = this.matrix.clone(), 3481 skew = this.skew; 3482 matrix.translate(-.5, -.5); 3483 if (this.type == "image") { 3484 if (Str(tstr).indexOf("m") + 1) { 3485 this.node.style.filter = matrix.toFilter(); 3486 var bb = this.getBBox(), 3487 bbt = this.getBBox(1), 3488 im = matrix.invert(), 3489 dx = im.x(bb.x, bb.y) - im.x(bbt.x, bbt.y), 3490 dy = im.y(bb.x, bb.y) - im.y(bbt.x, bbt.y); 3491 // skew.offset = dx + S + dy; 3492 // this.node.getElementsByTagName(fillString)[0].position = skew.offset; 3493 } else { 3494 this.node.style.filter = E; 3495 setCoords(this); 3496 } 3497 } else { 3498 // o = this.node, 3499 // _ = this._, 3500 // fillpos = _.fillpos, 3501 // deg, 3502 // matrix = this.matrix; 3503 // fill = o.getElementsByTagName(fillString)[0], 3504 // angle = fill.angle; 3505 3506 this.node.style.filter = E; 3507 skew.matrix = matrix; 3508 skew.offset = matrix.offset(); 3509 3510 // if (0&&angle) { 3511 // angle = R.rad(270 - angle); 3512 // var dx = 100 * math.cos(angle), 3513 // dy = 100 * math.sin(angle), 3514 // zx = matrix.x(0, 0), 3515 // zy = matrix.y(0, 0), 3516 // mx = matrix.x(dx, dy), 3517 // my = matrix.y(dx, dy); 3518 // angle = R.angle(zx, zy, mx, my); 3519 // fill.angle = (270 - angle) % 360; 3520 // } 3521 } 3522 return this; 3523 }; 3524 elproto.rotate = function (deg, cx, cy) { 3525 if (this.removed) { 3526 return this; 3527 } 3528 if (deg == null) { 3529 return; 3530 } 3531 deg = Str(deg).split(separator); 3532 if (deg.length - 1) { 3533 cx = toFloat(deg[1]); 3534 cy = toFloat(deg[2]); 3535 } 3536 deg = toFloat(deg[0]); 3537 (cy == null) && (cx = cy); 3538 if (cx == null || cy == null) { 3539 var bbox = this.getBBox(1); 3540 cx = bbox.x + bbox.width / 2; 3541 cy = bbox.y + bbox.height / 2; 3542 } 3543 this._.dirtyT = 1; 3544 this.transform(this._.transform.concat([["r", deg, cx, cy]])); 3545 return this; 3546 }; 3547 elproto.translate = function (dx, dy) { 3548 if (this.removed) { 3549 return this; 3550 } 3551 dx = Str(dx).split(separator); 3552 if (dx.length - 1) { 3553 dy = toFloat(dx[1]); 3554 } 3555 dx = toFloat(dx[0]) || 0; 3556 dy = +dy || 0; 3557 if (this._.bbox) { 3558 this._.bbox.x += dx; 3559 this._.bbox.y += dy; 3560 } 3561 this.transform(this._.transform.concat([["t", dx, dy]])); 3562 return this; 3563 }; 3564 elproto.scale = function (sx, sy, cx, cy) { 3565 if (this.removed) { 3566 return this; 3567 } 3568 sx = Str(sx).split(separator); 3569 if (sx.length - 1) { 3570 sy = toFloat(sx[1]); 3571 cx = toFloat(sx[2]); 3572 cy = toFloat(sx[3]); 3573 isNaN(cx) && (cx = null); 3574 isNaN(cy) && (cy = null); 3575 } 3576 sx = toFloat(sx[0]); 3577 (sy == null) && (sy = sx); 3578 (cy == null) && (cx = cy); 3579 if (cx == null || cy == null) { 3580 var bbox = this.getBBox(1); 3581 } 3582 cx = cx == null ? bbox.x + bbox.width / 2 : cx; 3583 cy = cy == null ? bbox.y + bbox.height / 2 : cy; 3584 3585 this.transform(this._.transform.concat([["s", sx, sy, cx, cy]])); 3586 this._.dirtyT = 1; 3587 return this; 3588 }; 3589 elproto.hide = function () { 3590 !this.removed && (this.node.style.display = "none"); 3591 return this; 3592 }; 3593 elproto.show = function () { 3594 !this.removed && (this.node.style.display = E); 3595 return this; 3596 }; 3597 elproto._getBBox = function () { 3598 if (this.removed) { 3599 return {}; 3600 } 3601 if (this.type == "text") { 3602 return { 3603 x: this.X + (this.bbx || 0) - this.W / 2, 3604 y: this.Y - this.H, 3605 width: this.W, 3606 height: this.H 3607 }; 3608 } else { 3609 return pathDimensions(this.attrs.path); 3610 } 3611 }; 3612 elproto.remove = function () { 3613 if (this.removed) { 3614 return; 3615 } 3616 eve.unbind("*.*." + this.id); 3617 tear(this, this.paper); 3618 this.node.parentNode.removeChild(this.node); 3619 this.shape && this.shape.parentNode.removeChild(this.shape); 3620 for (var i in this) { 3621 delete this[i]; 3622 } 3623 this.removed = true; 3624 }; 3625 elproto.attr = function (name, value) { 3626 if (this.removed) { 3627 return this; 3628 } 3629 if (name == null) { 3630 var res = {}; 3631 for (var i in this.attrs) if (this.attrs[has](i)) { 3632 res[i] = this.attrs[i]; 3633 } 3634 res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; 3635 return res; 3636 } 3637 if (value == null && R.is(name, "string")) { 3638 if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) { 3639 return this.attrs.gradient; 3640 } 3641 if (name in this.attrs) { 3642 return this.attrs[name]; 3643 } else if (R.is(this.paper.customAttributes[name], "function")) { 3644 return this.paper.customAttributes[name].def; 3645 } else { 3646 return availableAttrs[name]; 3647 } 3648 } 3649 if (this.attrs && value == null && R.is(name, array)) { 3650 var ii, values = {}; 3651 for (i = 0, ii = name.length; i < ii; i++) { 3652 values[name[i]] = this.attr(name[i]); 3653 } 3654 return values; 3655 } 3656 var params; 3657 if (value != null) { 3658 params = {}; 3659 params[name] = value; 3660 } 3661 value == null && R.is(name, "object") && (params = name); 3662 for (var key in params) { 3663 eve("attr." + key + "." + this.id, this, params[key]); 3664 } 3665 if (params) { 3666 for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) { 3667 var par = this.paper.customAttributes[key].apply(this, [][concat](params[key])); 3668 this.attrs[key] = params[key]; 3669 for (var subkey in par) if (par[has](subkey)) { 3670 params[subkey] = par[subkey]; 3671 } 3672 } 3673 // this.paper.canvas.style.display = "none"; 3674 if (params.text && this.type == "text") { 3675 this.textpath.string = params.text; 3676 } 3677 setFillAndStroke(this, params); 3678 // this.paper.canvas.style.display = E; 3679 } 3680 return this; 3681 }; 3682 elproto.toFront = function () { 3683 !this.removed && this.node.parentNode.appendChild(this.node); 3684 this.paper.top != this && tofront(this, this.paper); 3685 return this; 3686 }; 3687 elproto.toBack = function () { 3688 if (this.removed) { 3689 return this; 3690 } 3691 if (this.node.parentNode.firstChild != this.node) { 3692 this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild); 3693 toback(this, this.paper); 3694 } 3695 return this; 3696 }; 3697 elproto.insertAfter = function (element) { 3698 if (this.removed) { 3699 return this; 3700 } 3701 if (element.constructor == Set) { 3702 element = element[element.length - 1]; 3703 } 3704 if (element.node.nextSibling) { 3705 element.node.parentNode.insertBefore(this.node, element.node.nextSibling); 3706 } else { 3707 element.node.parentNode.appendChild(this.node); 3708 } 3709 insertafter(this, element, this.paper); 3710 return this; 3711 }; 3712 elproto.insertBefore = function (element) { 3713 if (this.removed) { 3714 return this; 3715 } 3716 if (element.constructor == Set) { 3717 element = element[0]; 3718 } 3719 element.node.parentNode.insertBefore(this.node, element.node); 3720 insertbefore(this, element, this.paper); 3721 return this; 3722 }; 3723 elproto.blur = function (size) { 3724 var s = this.node.runtimeStyle, 3725 f = s.filter; 3726 f = f.replace(blurregexp, E); 3727 if (+size !== 0) { 3728 this.attrs.blur = size; 3729 s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")"; 3730 s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5)); 3731 } else { 3732 s.filter = f; 3733 s.margin = 0; 3734 delete this.attrs.blur; 3735 } 3736 }; 3737 3738 thePath = function (pathString, vml) { 3739 var el = createNode("shape"); 3740 el.style.cssText = cssDot; 3741 el.coordsize = zoom + S + zoom; 3742 el.coordorigin = vml.coordorigin; 3743 var p = new Element(el, vml), 3744 attr = {fill: "none", stroke: "#000"}; 3745 pathString && (attr.path = pathString); 3746 p.type = "path"; 3747 p.path = []; 3748 p.Path = E; 3749 setFillAndStroke(p, attr); 3750 vml.canvas.appendChild(el); 3751 var skew = createNode("skew"); 3752 skew.on = true; 3753 el.appendChild(skew); 3754 p.skew = skew; 3755 p.transform(E); 3756 return p; 3757 }; 3758 theRect = function (vml, x, y, w, h, r) { 3759 var path = rectPath(x, y, w, h, r), 3760 res = vml.path(path), 3761 a = res.attrs; 3762 res.X = a.x = x; 3763 res.Y = a.y = y; 3764 res.W = a.width = w; 3765 res.H = a.height = h; 3766 a.r = r; 3767 a.path = path; 3768 res.type = "rect"; 3769 return res; 3770 }; 3771 theEllipse = function (vml, x, y, rx, ry) { 3772 var res = vml.path(), 3773 a = res.attrs; 3774 res.X = x - rx; 3775 res.Y = y - ry; 3776 res.W = rx * 2; 3777 res.H = ry * 2; 3778 res.type = "ellipse"; 3779 setFillAndStroke(res, { 3780 cx: x, 3781 cy: y, 3782 rx: rx, 3783 ry: ry 3784 }); 3785 return res; 3786 }; 3787 theCircle = function (vml, x, y, r) { 3788 var res = vml.path(), 3789 a = res.attrs; 3790 res.X = x - r; 3791 res.Y = y - r; 3792 res.W = res.H = r * 2; 3793 res.type = "circle"; 3794 setFillAndStroke(res, { 3795 cx: x, 3796 cy: y, 3797 r: r 3798 }); 3799 return res; 3800 }; 3801 theImage = function (vml, src, x, y, w, h) { 3802 var path = rectPath(x, y, w, h), 3803 res = vml.path(path).attr({stroke: "none"}), 3804 a = res.attrs, 3805 node = res.node, 3806 fill = node.getElementsByTagName(fillString)[0]; 3807 a.src = src; 3808 res.X = a.x = x; 3809 res.Y = a.y = y; 3810 res.W = a.width = w; 3811 res.H = a.height = h; 3812 a.path = path; 3813 res.type = "image"; 3814 fill.parentNode == node && node.removeChild(fill); 3815 fill.rotate = true; 3816 fill.src = src; 3817 fill.type = "tile"; 3818 res._.fillpos = [x, y]; 3819 res._.fillsize = [w, h]; 3820 node.appendChild(fill); 3821 setCoords(res); 3822 return res; 3823 }; 3824 theText = function (vml, x, y, text) { 3825 var el = createNode("shape"), 3826 path = createNode("path"), 3827 o = createNode("textpath"); 3828 x = x || 0; 3829 y = y || 0; 3830 text = text || ""; 3831 path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1); 3832 path.textpathok = true; 3833 o.string = Str(text); 3834 o.on = true; 3835 el.style.cssText = "position:absolute;left:0;top:0;width:1;height:1"; 3836 el.coordsize = zoom + S + zoom; 3837 el.coordorigin = "0 0"; 3838 var p = new Element(el, vml), 3839 attr = {fill: "#000", stroke: "none", font: availableAttrs.font, text: text}; 3840 p.shape = el; 3841 p.path = path; 3842 p.textpath = o; 3843 p.type = "text"; 3844 p.attrs.text = Str(text); 3845 p.attrs.x = x; 3846 p.attrs.y = y; 3847 p.attrs.w = 1; 3848 p.attrs.h = 1; 3849 setFillAndStroke(p, attr); 3850 el.appendChild(o); 3851 el.appendChild(path); 3852 vml.canvas.appendChild(el); 3853 var skew = createNode("skew"); 3854 skew.on = true; 3855 el.appendChild(skew); 3856 p.skew = skew; 3857 p.transform(E); 3858 return p; 3859 }; 3860 setSize = function (width, height) { 3861 var cs = this.canvas.style; 3862 this.width = width; 3863 this.height = height; 3864 width == +width && (width += "px"); 3865 height == +height && (height += "px"); 3866 cs.width = width; 3867 cs.height = height; 3868 cs.clip = "rect(0 " + width + " " + height + " 0)"; 3869 if (this._viewBox) { 3870 setViewBox.apply(this, this._viewBox); 3871 } 3872 return this; 3873 }; 3874 setViewBox = function (x, y, w, h, fit) { 3875 eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]); 3876 var width = this.width, 3877 height = this.height, 3878 size = 1e3 * mmax(w / width, h / height), 3879 H, W; 3880 if (fit) { 3881 H = height / h; 3882 W = width / w; 3883 if (w * H < width) { 3884 x -= (width - w * H) / 2 / H; 3885 } 3886 if (h * W < height) { 3887 y -= (height - h * W) / 2 / W; 3888 } 3889 } 3890 this._viewBox = [x, y, w, h, !!fit]; 3891 this.forEach(function (el) { 3892 el.transform("..."); 3893 }); 3894 return this; 3895 }; 3896 var createNode, 3897 initWin = function (win) { 3898 var doc = win.document; 3899 doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); 3900 try { 3901 !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); 3902 createNode = function (tagName) { 3903 return doc.createElement('<rvml:' + tagName + ' class="rvml">'); 3904 }; 3905 } catch (e) { 3906 createNode = function (tagName) { 3907 return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); 3908 }; 3909 } 3910 }; 3911 initWin(g.win); 3912 create = function () { 3913 var con = getContainer[apply](0, arguments), 3914 container = con.container, 3915 height = con.height, 3916 s, 3917 width = con.width, 3918 x = con.x, 3919 y = con.y; 3920 if (!container) { 3921 throw new Error("VML container not found."); 3922 } 3923 var res = new Paper, 3924 c = res.canvas = g.doc.createElement("div"), 3925 cs = c.style; 3926 x = x || 0; 3927 y = y || 0; 3928 width = width || 512; 3929 height = height || 342; 3930 res.width = width; 3931 res.height = height; 3932 width == +width && (width += "px"); 3933 height == +height && (height += "px"); 3934 res.coordsize = zoom * 1e3 + S + zoom * 1e3; 3935 res.coordorigin = "0 0"; 3936 res.span = g.doc.createElement("span"); 3937 res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;"; 3938 c.appendChild(res.span); 3939 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); 3940 if (container == 1) { 3941 g.doc.body.appendChild(c); 3942 cs.left = x + "px"; 3943 cs.top = y + "px"; 3944 cs.position = "absolute"; 3945 } else { 3946 if (container.firstChild) { 3947 container.insertBefore(c, container.firstChild); 3948 } else { 3949 container.appendChild(c); 3950 } 3951 } 3952 plugins.call(res, res, R.fn); 3953 res.renderfix = fun; 3954 return res; 3955 }; 3956 paperproto.clear = function () { 3957 eve("clear", this); 3958 this.canvas.innerHTML = E; 3959 this.span = g.doc.createElement("span"); 3960 this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;"; 3961 this.canvas.appendChild(this.span); 3962 this.bottom = this.top = null; 3963 }; 3964 paperproto.remove = function () { 3965 eve("remove", this); 3966 this.canvas.parentNode.removeChild(this.canvas); 3967 for (var i in this) { 3968 this[i] = removed(i); 3969 } 3970 return true; 3971 }; 3972 } 3973 3974 // WebKit rendering bug workaround method 3975 var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/); 3976 if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") || 3977 (navigator.vendor == "Google Inc." && version && version[1] < 8)) { 3978 /*\ 3979 * Paper.safari 3980 [ method ] 3981 ** 3982 * There is an inconvenient rendering bug in Safari (WebKit): 3983 * sometimes the rendering should be forced. 3984 * This method should help with dealing with this bug. 3985 \*/ 3986 paperproto.safari = function () { 3987 var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"}); 3988 setTimeout(function () {rect.remove();}); 3989 }; 3990 } else { 3991 paperproto.safari = fun; 3992 } 3993 3994 // Events 3995 var preventDefault = function () { 3996 this.returnValue = false; 3997 }, 3998 preventTouch = function () { 3999 return this.originalEvent.preventDefault(); 4000 }, 4001 stopPropagation = function () { 4002 this.cancelBubble = true; 4003 }, 4004 stopTouch = function () { 4005 return this.originalEvent.stopPropagation(); 4006 }, 4007 addEvent = (function () { 4008 if (g.doc.addEventListener) { 4009 return function (obj, type, fn, element) { 4010 var realName = supportsTouch && touchMap[type] ? touchMap[type] : type; 4011 var f = function (e) { 4012 if (supportsTouch && touchMap[has](type)) { 4013 for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) { 4014 if (e.targetTouches[i].target == obj) { 4015 var olde = e; 4016 e = e.targetTouches[i]; 4017 e.originalEvent = olde; 4018 e.preventDefault = preventTouch; 4019 e.stopPropagation = stopTouch; 4020 break; 4021 } 4022 } 4023 } 4024 return fn.call(element, e); 4025 }; 4026 obj.addEventListener(realName, f, false); 4027 return function () { 4028 obj.removeEventListener(realName, f, false); 4029 return true; 4030 }; 4031 }; 4032 } else if (g.doc.attachEvent) { 4033 return function (obj, type, fn, element) { 4034 var f = function (e) { 4035 e = e || g.win.event; 4036 e.preventDefault = e.preventDefault || preventDefault; 4037 e.stopPropagation = e.stopPropagation || stopPropagation; 4038 return fn.call(element, e); 4039 }; 4040 obj.attachEvent("on" + type, f); 4041 var detacher = function () { 4042 obj.detachEvent("on" + type, f); 4043 return true; 4044 }; 4045 return detacher; 4046 }; 4047 } 4048 })(), 4049 drag = [], 4050 dragMove = function (e) { 4051 var x = e.clientX, 4052 y = e.clientY, 4053 scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop, 4054 scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft, 4055 dragi, 4056 j = drag.length; 4057 while (j--) { 4058 dragi = drag[j]; 4059 if (supportsTouch) { 4060 var i = e.touches.length, 4061 touch; 4062 while (i--) { 4063 touch = e.touches[i]; 4064 if (touch.identifier == dragi.el._drag.id) { 4065 x = touch.clientX; 4066 y = touch.clientY; 4067 (e.originalEvent ? e.originalEvent : e).preventDefault(); 4068 break; 4069 } 4070 } 4071 } else { 4072 e.preventDefault(); 4073 } 4074 var node = dragi.el.node, 4075 o, 4076 next = node.nextSibling, 4077 parent = node.parentNode, 4078 display = node.style.display; 4079 g.win.opera && parent.removeChild(node); 4080 node.style.display = "none"; 4081 o = dragi.el.paper.getElementByPoint(x, y); 4082 node.style.display = display; 4083 g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node)); 4084 o && eve("drag.over." + dragi.el.id, dragi.el, o); 4085 x += scrollX; 4086 y += scrollY; 4087 eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e); 4088 } 4089 }, 4090 dragUp = function (e) { 4091 R.unmousemove(dragMove).unmouseup(dragUp); 4092 var i = drag.length, 4093 dragi; 4094 while (i--) { 4095 dragi = drag[i]; 4096 dragi.el._drag = {}; 4097 eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e); 4098 } 4099 drag = []; 4100 }; 4101 for (var i = events.length; i--;) { 4102 (function (eventName) { 4103 R[eventName] = Element.prototype[eventName] = function (fn, scope) { 4104 if (R.is(fn, "function")) { 4105 this.events = this.events || []; 4106 this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)}); 4107 } 4108 return this; 4109 }; 4110 R["un" + eventName] = Element.prototype["un" + eventName] = function (fn) { 4111 var events = this.events, 4112 l = events.length; 4113 while (l--) if (events[l].name == eventName && events[l].f == fn) { 4114 events[l].unbind(); 4115 events.splice(l, 1); 4116 !events.length && delete this.events; 4117 return this; 4118 } 4119 return this; 4120 }; 4121 })(events[i]); 4122 } 4123 /*\ 4124 * Element.hover 4125 [ method ] 4126 ** 4127 * Adds event handlers for hover for the element. 4128 > Parameters 4129 - f_in (function) handler for hover in 4130 - f_out (function) handler for hover out 4131 - icontext (object) #optional context for hover in handler 4132 - ocontext (object) #optional context for hover out handler 4133 = (object) @Element 4134 \*/ 4135 elproto.hover = function (f_in, f_out, scope_in, scope_out) { 4136 return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in); 4137 }; 4138 /*\ 4139 * Element.unhover 4140 [ method ] 4141 ** 4142 * Removes event handlers for hover for the element. 4143 > Parameters 4144 - f_in (function) handler for hover in 4145 - f_out (function) handler for hover out 4146 = (object) @Element 4147 \*/ 4148 elproto.unhover = function (f_in, f_out) { 4149 return this.unmouseover(f_in).unmouseout(f_out); 4150 }; 4151 /*\ 4152 * Element.drag 4153 [ method ] 4154 ** 4155 * Adds event handlers for drag of the element. 4156 > Parameters 4157 - onmove (function) handler for moving 4158 - onstart (function) handler for drag start 4159 - onend (function) handler for drag end 4160 - mcontext (object) #optional context for moving handler 4161 - scontext (object) #optional context for drag start handler 4162 - econtext (object) #optional context for drag end handler 4163 * Additionaly following `drag` events will be triggered: `drag.start.<id>` on start, 4164 * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element 4165 * `drag.over.<id>` will be fired as well. 4166 * 4167 * Start event and start handler will be called in specified context or in context of the element with following parameters: 4168 o x (number) x position of the mouse 4169 o y (number) y position of the mouse 4170 o event (object) DOM event object 4171 * Move event and move handler will be called in specified context or in context of the element with following parameters: 4172 o dx (number) shift by x from the start point 4173 o dy (number) shift by y from the start point 4174 o x (number) x position of the mouse 4175 o y (number) y position of the mouse 4176 o event (object) DOM event object 4177 * End event and end handler will be called in specified context or in context of the element with following parameters: 4178 o event (object) DOM event object 4179 = (object) @Element 4180 \*/ 4181 elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) { 4182 function start(e) { 4183 (e.originalEvent || e).preventDefault(); 4184 var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop, 4185 scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft; 4186 this._drag.x = e.clientX + scrollX; 4187 this._drag.y = e.clientY + scrollY; 4188 this._drag.id = e.identifier; 4189 !drag.length && R.mousemove(dragMove).mouseup(dragUp); 4190 drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope}); 4191 onstart && eve.on("drag.start." + this.id, onstart); 4192 onmove && eve.on("drag.move." + this.id, onmove); 4193 onend && eve.on("drag.end." + this.id, onend); 4194 eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e); 4195 } 4196 this._drag = {}; 4197 this.mousedown(start); 4198 return this; 4199 }; 4200 /*\ 4201 * Element.onDragOver 4202 [ method ] 4203 ** 4204 * Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id). 4205 > Parameters 4206 - f (function) handler for event 4207 \*/ 4208 elproto.onDragOver = function (f) { 4209 f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id); 4210 }; 4211 /*\ 4212 * Element.undrag 4213 [ method ] 4214 ** 4215 * Removes all drag event handlers from given element. 4216 \*/ 4217 elproto.undrag = function () { 4218 var i = drag.length; 4219 while (i--) if (drag[i].el == this) { 4220 R.unmousedown(drag[i].start); 4221 drag.splice(i++, 1); 4222 eve.unbind("drag.*." + this.id); 4223 } 4224 !drag.length && R.unmousemove(dragMove).unmouseup(dragUp); 4225 }; 4226 /*\ 4227 * Paper.circle 4228 [ method ] 4229 ** 4230 * Draws a circle. 4231 ** 4232 > Parameters 4233 ** 4234 - x (number) x coordinate of the centre 4235 - y (number) y coordinate of the centre 4236 - r (number) radius 4237 = (object) Raphaël element object with type “circle” 4238 ** 4239 > Usage 4240 | var c = paper.circle(50, 50, 40); 4241 \*/ 4242 paperproto.circle = function (x, y, r) { 4243 return theCircle(this, x || 0, y || 0, r || 0); 4244 }; 4245 /*\ 4246 * Paper.rect 4247 [ method ] 4248 * 4249 * Draws a rectangle. 4250 ** 4251 > Parameters 4252 ** 4253 - x (number) x coordinate of the top left corner 4254 - y (number) y coordinate of the top left corner 4255 - width (number) width 4256 - height (number) height 4257 - r (number) #optional radius for rounded corners, default is 0 4258 = (object) Raphaël element object with type “rect” 4259 ** 4260 > Usage 4261 | // regular rectangle 4262 | var c = paper.rect(10, 10, 50, 50); 4263 | // rectangle with rounded corners 4264 | var c = paper.rect(40, 40, 50, 50, 10); 4265 \*/ 4266 paperproto.rect = function (x, y, w, h, r) { 4267 return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0); 4268 }; 4269 /*\ 4270 * Paper.ellipse 4271 [ method ] 4272 ** 4273 * Draws an ellipse. 4274 ** 4275 > Parameters 4276 ** 4277 - x (number) x coordinate of the centre 4278 - y (number) y coordinate of the centre 4279 - rx (number) horizontal radius 4280 - ry (number) vertical radius 4281 = (object) Raphaël element object with type “ellipse” 4282 ** 4283 > Usage 4284 | var c = paper.ellipse(50, 50, 40, 20); 4285 \*/ 4286 paperproto.ellipse = function (x, y, rx, ry) { 4287 return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0); 4288 }; 4289 /*\ 4290 * Paper.path 4291 [ method ] 4292 ** 4293 * Creates a path element by given path data string. 4294 ** 4295 > Parameters 4296 ** 4297 - pathString (string) path data in SVG path string format. 4298 = (object) Raphaël element object with type “ellipse” 4299 # 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>. 4300 ** 4301 > Usage 4302 | var c = paper.path("M10 10L90 90"); 4303 | // draw a diagonal line: 4304 | // move to 10,10, line to 90,90 4305 \*/ 4306 paperproto.path = function (pathString) { 4307 pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E); 4308 return thePath(R.format[apply](R, arguments), this); 4309 }; 4310 /*\ 4311 * Paper.image 4312 [ method ] 4313 ** 4314 * Embeds an image into the surface. 4315 ** 4316 > Parameters 4317 ** 4318 - src (string) URI of the source image 4319 - x (number) x coordinate position 4320 - y (number) y coordinate position 4321 - width (number) width of the image 4322 - height (number) height of the image 4323 = (object) Raphaël element object with type “image” 4324 ** 4325 > Usage 4326 | var c = paper.image("apple.png", 10, 10, 80, 80); 4327 \*/ 4328 paperproto.image = function (src, x, y, w, h) { 4329 return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0); 4330 }; 4331 /*\ 4332 * Paper.text 4333 [ method ] 4334 ** 4335 * Draws a text string. If you need line breaks, put “\n” in the string. 4336 ** 4337 > Parameters 4338 ** 4339 - x (number) x coordinate position 4340 - y (number) y coordinate position 4341 - text (string) The text string to draw 4342 = (object) Raphaël element object with type “text” 4343 ** 4344 > Usage 4345 | var t = paper.text(50, 50, "Raphaël\nkicks\nbutt!"); 4346 \*/ 4347 paperproto.text = function (x, y, text) { 4348 return theText(this, x || 0, y || 0, Str(text)); 4349 }; 4350 /*\ 4351 * Paper.set 4352 [ method ] 4353 ** 4354 * Creates array-like object to keep and operate several elements at once. 4355 * Warning: it doesn’t create any elements for itself in the page, it just groups existing elements. 4356 * Sets act as pseudo elements — all methods available to an element can be used on a set. 4357 = (object) array-like object that represents set of elements 4358 ** 4359 > Usage 4360 | var st = paper.set(); 4361 | st.push( 4362 | paper.circle(10, 10, 5), 4363 | paper.circle(30, 10, 5) 4364 | ); 4365 | st.attr({fill: "red"}); 4366 \*/ 4367 paperproto.set = function (itemsArray) { 4368 arguments.length > 1 && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length)); 4369 return new Set(itemsArray); 4370 }; 4371 /*\ 4372 * Paper.setSize 4373 [ method ] 4374 ** 4375 * If you need to change dimensions of the canvas call this method 4376 ** 4377 > Parameters 4378 ** 4379 - width (number) new width of the canvas 4380 - height (number) new height of the canvas 4381 > Usage 4382 | var st = paper.set(); 4383 | st.push( 4384 | paper.circle(10, 10, 5), 4385 | paper.circle(30, 10, 5) 4386 | ); 4387 | st.attr({fill: "red"}); 4388 \*/ 4389 paperproto.setSize = setSize; 4390 /*\ 4391 * Paper.setViewBox 4392 [ method ] 4393 ** 4394 * Sets the view box of the paper. Practically it gives you ability to zoom and pan whole paper surface by 4395 * specifying new boundaries. 4396 ** 4397 > Parameters 4398 ** 4399 x, y, w, h, fit 4400 - x (number) new x position, default is `0` 4401 - y (number) new y position, default is `0` 4402 - w (number) new width of the canvas 4403 - h (number) new height of the canvas 4404 - fit (boolean) `true` if you want graphics to fit into new boundary box 4405 \*/ 4406 paperproto.setViewBox = setViewBox; 4407 /*\ 4408 * Paper.top 4409 [ property ] 4410 ** 4411 * Points to the topmost element on the paper 4412 \*/ 4413 /*\ 4414 * Paper.bottom 4415 [ property ] 4416 ** 4417 * Points to the bottom element on the paper 4418 \*/ 4419 paperproto.top = paperproto.bottom = null; 4420 /*\ 4421 * Paper.raphael 4422 [ property ] 4423 ** 4424 * Points to the @Raphael object/function 4425 \*/ 4426 paperproto.raphael = R; 4427 var getOffset = function (elem) { 4428 var box = elem.getBoundingClientRect(), 4429 doc = elem.ownerDocument, 4430 body = doc.body, 4431 docElem = doc.documentElement, 4432 clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, 4433 top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop, 4434 left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft; 4435 return { 4436 y: top, 4437 x: left 4438 }; 4439 }; 4440 /*\ 4441 * Paper.getElementByPoint 4442 [ method ] 4443 ** 4444 * Returns you topmost element under given point. 4445 ** 4446 = (object) Raphaël element object 4447 > Parameters 4448 ** 4449 - x (number) x coordinate from the top left corner of the window 4450 - y (number) y coordinate from the top left corner of the window 4451 > Usage 4452 | paper.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"}); 4453 \*/ 4454 paperproto.getElementByPoint = function (x, y) { 4455 var paper = this, 4456 svg = paper.canvas, 4457 target = g.doc.elementFromPoint(x, y); 4458 if (g.win.opera && target.tagName == "svg") { 4459 var so = getOffset(svg), 4460 sr = svg.createSVGRect(); 4461 sr.x = x - so.x; 4462 sr.y = y - so.y; 4463 sr.width = sr.height = 1; 4464 var hits = svg.getIntersectionList(sr, null); 4465 if (hits.length) { 4466 target = hits[hits.length - 1]; 4467 } 4468 } 4469 if (!target) { 4470 return null; 4471 } 4472 while (target.parentNode && target != svg.parentNode && !target.raphael) { 4473 target = target.parentNode; 4474 } 4475 target == paper.canvas.parentNode && (target = svg); 4476 target = target && target.raphael ? paper.getById(target.raphaelid) : null; 4477 return target; 4478 }; 4479 /*\ 4480 * Paper.getById 4481 [ method ] 4482 ** 4483 * Returns you element by its internal ID. 4484 ** 4485 > Parameters 4486 ** 4487 - id (number) id 4488 = (object) Raphaël element object 4489 \*/ 4490 paperproto.getById = function (id) { 4491 var bot = this.bottom; 4492 while (bot) { 4493 if (bot.id == id) { 4494 return bot; 4495 } 4496 bot = bot.next; 4497 } 4498 return null; 4499 }; 4500 /*\ 4501 * Paper.forEach 4502 [ method ] 4503 ** 4504 * Executes given function for each element on the paper 4505 * 4506 * If callback function returns `false` it will stop loop running. 4507 ** 4508 > Parameters 4509 ** 4510 - callback (function) function to run 4511 - thisArg (object) context object for the callback 4512 = (object) Paper object 4513 \*/ 4514 paperproto.forEach = function (callback, thisArg) { 4515 var bot = this.bottom; 4516 while (bot) { 4517 if (callback.call(thisArg, bot) === false) { 4518 return this; 4519 } 4520 bot = bot.next; 4521 } 4522 return this; 4523 }; 4524 function x_y() { 4525 return this.x + S + this.y; 4526 } 4527 function x_y_w_h() { 4528 return this.x + S + this.y + S + this.width + "\xd7" + this.height; 4529 } 4530 /*\ 4531 * Element.getBBox 4532 [ method ] 4533 ** 4534 * Return bounding box for a given element 4535 ** 4536 > Parameters 4537 ** 4538 - isWithoutTransform (boolean) flag, `true` if you want to have bounding box before transformations. Default is `false`. 4539 = (object) Bounding box object: 4540 o { 4541 o x: (number) top left corner x 4542 o y: (number) top left corner y 4543 o width: (number) width 4544 o height: (number) height 4545 o } 4546 \*/ 4547 elproto.getBBox = function (isWithoutTransform) { 4548 if (this.removed) { 4549 return {}; 4550 } 4551 var _ = this._; 4552 if (isWithoutTransform) { 4553 if (_.dirty || !_.bboxwt) { 4554 this.realPath = getPath[this.type](this); 4555 _.bboxwt = pathDimensions(this.realPath); 4556 _.bboxwt.toString = x_y_w_h; 4557 _.dirty = 0; 4558 } 4559 return _.bboxwt; 4560 } 4561 if (_.dirty || _.dirtyT || !_.bbox) { 4562 if (_.dirty || !this.realPath) { 4563 _.bboxwt = 0; 4564 this.realPath = getPath[this.type](this); 4565 } 4566 _.bbox = pathDimensions(mapPath(this.realPath, this.matrix)); 4567 _.bbox.toString = x_y_w_h; 4568 _.dirty = _.dirtyT = 0; 4569 } 4570 return _.bbox; 4571 }; 4572 /*\ 4573 * Element.clone 4574 [ method ] 4575 ** 4576 = (object) clone of a given element 4577 ** 4578 \*/ 4579 elproto.clone = function () { 4580 if (this.removed) { 4581 return null; 4582 } 4583 var attr = this.attr(); 4584 delete attr.scale; 4585 delete attr.translation; 4586 return this.paper[this.type]().attr(attr); 4587 }; 4588 /*\ 4589 * Element.glow 4590 [ method ] 4591 ** 4592 * Return set of elements that create glow-like effect around given element. See @Paper.set. 4593 * 4594 * Note: Glow is not connected to the element. If you change element attributes it won’t adjust itself. 4595 ** 4596 > Parameters 4597 ** 4598 - glow (object) #optional parameters object with all properties optional: 4599 o { 4600 o width (number) size of the glow, default is `10` 4601 o fill (boolean) will it be filled, default is `false` 4602 o opacity: opacity, default is `0.5` 4603 o offsetx: horizontal offset, default is `0` 4604 o offsety: vertical offset, default is `0` 4605 o color: glow colour, default is `black` 4606 o } 4607 = (object) @Paper.set of elements that represents glow 4608 \*/ 4609 elproto.glow = function (glow) { 4610 if (this.type == "text") { 4611 return null; 4612 } 4613 glow = glow || {}; 4614 var s = { 4615 width: (glow.width || 10) + (+this.attr("stroke-width") || 1), 4616 fill: glow.fill || false, 4617 opacity: glow.opacity || .5, 4618 offsetx: glow.offsetx || 0, 4619 offsety: glow.offsety || 0, 4620 color: glow.color || "#000" 4621 }, 4622 c = s.width / 2, 4623 r = this.paper, 4624 out = r.set(), 4625 path = this.realPath || getPath[this.type](this); 4626 path = this.matrix ? mapPath(path, this.matrix) : path; 4627 for (var i = 1; i < c + 1; i++) { 4628 out.push(r.path(path).attr({ 4629 stroke: s.color, 4630 fill: s.fill ? s.color : "none", 4631 "stroke-linejoin": "round", 4632 "stroke-linecap": "round", 4633 "stroke-width": +(s.width / c * i).toFixed(3), 4634 opacity: +(s.opacity / c).toFixed(3) 4635 })); 4636 } 4637 return out.insertBefore(this).translate(s.offsetx, s.offsety); 4638 }; 4639 var curveslengths = {}, 4640 getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { 4641 var len = 0, 4642 precision = 100, 4643 name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(), 4644 cache = curveslengths[name], 4645 old, dot; 4646 !cache && (curveslengths[name] = cache = {data: []}); 4647 cache.timer && clearTimeout(cache.timer); 4648 cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3); 4649 if (length != null && !cache.precision) { 4650 var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); 4651 cache.precision = ~~total * 10; 4652 cache.data = []; 4653 } 4654 precision = cache.precision || precision; 4655 for (var i = 0; i < precision + 1; i++) { 4656 if (cache.data[i * precision]) { 4657 dot = cache.data[i * precision]; 4658 } else { 4659 dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision); 4660 cache.data[i * precision] = dot; 4661 } 4662 i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5)); 4663 if (length != null && len >= length) { 4664 return dot; 4665 } 4666 old = dot; 4667 } 4668 if (length == null) { 4669 return len; 4670 } 4671 }, 4672 getLengthFactory = function (istotal, subpath) { 4673 return function (path, length, onlystart) { 4674 path = path2curve(path); 4675 var x, y, p, l, sp = "", subpaths = {}, point, 4676 len = 0; 4677 for (var i = 0, ii = path.length; i < ii; i++) { 4678 p = path[i]; 4679 if (p[0] == "M") { 4680 x = +p[1]; 4681 y = +p[2]; 4682 } else { 4683 l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); 4684 if (len + l > length) { 4685 if (subpath && !subpaths.start) { 4686 point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); 4687 sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y]; 4688 if (onlystart) {return sp;} 4689 subpaths.start = sp; 4690 sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join(); 4691 len += l; 4692 x = +p[5]; 4693 y = +p[6]; 4694 continue; 4695 } 4696 if (!istotal && !subpath) { 4697 point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); 4698 return {x: point.x, y: point.y, alpha: point.alpha}; 4699 } 4700 } 4701 len += l; 4702 x = +p[5]; 4703 y = +p[6]; 4704 } 4705 sp += p.shift() + p; 4706 } 4707 subpaths.end = sp; 4708 point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], 1); 4709 point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha}); 4710 return point; 4711 }; 4712 }; 4713 var getTotalLength = getLengthFactory(1), 4714 getPointAtLength = getLengthFactory(), 4715 getSubpathsAtLength = getLengthFactory(0, 1); 4716 /*\ 4717 * Raphael.getTotalLength 4718 [ method ] 4719 ** 4720 * Returns length of the given path in pixels. 4721 ** 4722 > Parameters 4723 ** 4724 - path (string) SVG path string. 4725 ** 4726 = (number) length. 4727 \*/ 4728 R.getTotalLength = getTotalLength; 4729 /*\ 4730 * Raphael.getPointAtLength 4731 [ method ] 4732 ** 4733 * Return coordinates of the point located at the given length on the given path. 4734 ** 4735 > Parameters 4736 ** 4737 - path (string) SVG path string 4738 - length (number) 4739 ** 4740 = (object) representation of the point: 4741 o { 4742 o x: (number) x coordinate 4743 o y: (number) y coordinate 4744 o alpha: (number) angle of derivative 4745 o } 4746 \*/ 4747 R.getPointAtLength = getPointAtLength; 4748 /*\ 4749 * Raphael.getSubpath 4750 [ method ] 4751 ** 4752 * Return subpath of a given path from given length to given length. 4753 ** 4754 > Parameters 4755 ** 4756 - path (string) SVG path string 4757 - from (number) position of the start of the segment 4758 - to (number) position of the end of the segment 4759 ** 4760 = (string) pathstring for the segment 4761 \*/ 4762 R.getSubpath = function (path, from, to) { 4763 if (abs(this.getTotalLength(path) - to) < 1e-6) { 4764 return getSubpathsAtLength(path, from).end; 4765 } 4766 var a = getSubpathsAtLength(path, to, 1); 4767 return from ? getSubpathsAtLength(a, from).end : a; 4768 }; 4769 /*\ 4770 * Element.getTotalLength 4771 [ method ] 4772 ** 4773 * Returns length of the path in pixels. Only works for element of “path” type. 4774 = (number) length. 4775 \*/ 4776 elproto.getTotalLength = function () { 4777 if (this.type != "path") {return;} 4778 if (this.node.getTotalLength) { 4779 return this.node.getTotalLength(); 4780 } 4781 return getTotalLength(this.attrs.path); 4782 }; 4783 /*\ 4784 * Element.getPointAtLength 4785 [ method ] 4786 ** 4787 * Return coordinates of the point located at the given length on the given path. Only works for element of “path” type. 4788 ** 4789 > Parameters 4790 ** 4791 - length (number) 4792 ** 4793 = (object) representation of the point: 4794 o { 4795 o x: (number) x coordinate 4796 o y: (number) y coordinate 4797 o alpha: (number) angle of derivative 4798 o } 4799 \*/ 4800 elproto.getPointAtLength = function (length) { 4801 if (this.type != "path") {return;} 4802 return getPointAtLength(this.attrs.path, length); 4803 }; 4804 /*\ 4805 * Element.getSubpath 4806 [ method ] 4807 ** 4808 * Return subpath of a given element from given length to given length. Only works for element of “path” type. 4809 ** 4810 > Parameters 4811 ** 4812 - from (number) position of the start of the segment 4813 - to (number) position of the end of the segment 4814 ** 4815 = (string) pathstring for the segment 4816 \*/ 4817 elproto.getSubpath = function (from, to) { 4818 if (this.type != "path") {return;} 4819 return R.getSubpath(this.attrs.path, from, to); 4820 }; 4821 /*\ 4822 * Raphael.easing_formulas 4823 [ property ] 4824 ** 4825 * Object that contains easing formulas for animation. You could extend it with your own. By default it has following list of easing: 4826 # <ul> 4827 # <li>“linear”</li> 4828 # <li>“<” or “easeIn” or “ease-in”</li> 4829 # <li>“>” or “easeOut” or “ease-out”</li> 4830 # <li>“<>” or “easeInOut” or “ease-in-out”</li> 4831 # <li>“backIn” or “back-in”</li> 4832 # <li>“backOut” or “back-out”</li> 4833 # <li>“elastic”</li> 4834 # <li>“bounce”</li> 4835 # </ul> 4836 # <p>See also <a href="http://raphaeljs.com/easing.html">Easing demo</a>.</p> 4837 \*/ 4838 var ef = R.easing_formulas = { 4839 linear: function (n) { 4840 return n; 4841 }, 4842 "<": function (n) { 4843 return pow(n, 1.7); 4844 }, 4845 ">": function (n) { 4846 return pow(n, .48); 4847 }, 4848 "<>": function (n) { 4849 var q = .48 - n / 1.04, 4850 Q = math.sqrt(.1734 + q * q), 4851 x = Q - q, 4852 X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1), 4853 y = -Q - q, 4854 Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1), 4855 t = X + Y + .5; 4856 return (1 - t) * 3 * t * t + t * t * t; 4857 }, 4858 backIn: function (n) { 4859 var s = 1.70158; 4860 return n * n * ((s + 1) * n - s); 4861 }, 4862 backOut: function (n) { 4863 n = n - 1; 4864 var s = 1.70158; 4865 return n * n * ((s + 1) * n + s) + 1; 4866 }, 4867 elastic: function (n) { 4868 if (n == !!n) { 4869 return n; 4870 } 4871 return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1; 4872 }, 4873 bounce: function (n) { 4874 var s = 7.5625, 4875 p = 2.75, 4876 l; 4877 if (n < (1 / p)) { 4878 l = s * n * n; 4879 } else { 4880 if (n < (2 / p)) { 4881 n -= (1.5 / p); 4882 l = s * n * n + .75; 4883 } else { 4884 if (n < (2.5 / p)) { 4885 n -= (2.25 / p); 4886 l = s * n * n + .9375; 4887 } else { 4888 n -= (2.625 / p); 4889 l = s * n * n + .984375; 4890 } 4891 } 4892 } 4893 return l; 4894 } 4895 }; 4896 ef.easeIn = ef["ease-in"] = ef["<"]; 4897 ef.easeOut = ef["ease-out"] = ef[">"]; 4898 ef.easeInOut = ef["ease-in-out"] = ef["<>"]; 4899 ef["back-in"] = ef.backIn; 4900 ef["back-out"] = ef.backOut; 4901 4902 var animationElements = [], 4903 requestAnimFrame = window.requestAnimationFrame || 4904 window.webkitRequestAnimationFrame || 4905 window.mozRequestAnimationFrame || 4906 window.oRequestAnimationFrame || 4907 window.msRequestAnimationFrame || 4908 function (callback) { 4909 setTimeout(callback, 16); 4910 }, 4911 animation = function () { 4912 var Now = +new Date, 4913 l = 0; 4914 for (; l < animationElements.length; l++) { 4915 var e = animationElements[l]; 4916 if (e.el.removed || e.paused) { 4917 continue; 4918 } 4919 var time = Now - e.start, 4920 ms = e.ms, 4921 easing = e.easing, 4922 from = e.from, 4923 diff = e.diff, 4924 to = e.to, 4925 t = e.t, 4926 that = e.el, 4927 set = {}, 4928 now; 4929 if (e.initstatus) { 4930 time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms; 4931 e.status = e.initstatus; 4932 delete e.initstatus; 4933 e.stop && animationElements.splice(l--, 1); 4934 } else { 4935 e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top; 4936 } 4937 if (time < 0) { 4938 continue; 4939 } 4940 if (time < ms) { 4941 var pos = easing(time / ms); 4942 for (var attr in from) if (from[has](attr)) { 4943 switch (availableAnimAttrs[attr]) { 4944 case nu: 4945 now = +from[attr] + pos * ms * diff[attr]; 4946 break; 4947 case "colour": 4948 now = "rgb(" + [ 4949 upto255(round(from[attr].r + pos * ms * diff[attr].r)), 4950 upto255(round(from[attr].g + pos * ms * diff[attr].g)), 4951 upto255(round(from[attr].b + pos * ms * diff[attr].b)) 4952 ].join(",") + ")"; 4953 break; 4954 case "path": 4955 now = []; 4956 for (var i = 0, ii = from[attr].length; i < ii; i++) { 4957 now[i] = [from[attr][i][0]]; 4958 for (var j = 1, jj = from[attr][i].length; j < jj; j++) { 4959 now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j]; 4960 } 4961 now[i] = now[i].join(S); 4962 } 4963 now = now.join(S); 4964 break; 4965 case "transform": 4966 if (diff[attr].real) { 4967 now = []; 4968 for (i = 0, ii = from[attr].length; i < ii; i++) { 4969 now[i] = [from[attr][i][0]]; 4970 for (j = 1, jj = from[attr][i].length; j < jj; j++) { 4971 now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j]; 4972 } 4973 } 4974 } else { 4975 var get = function (i) { 4976 return +from[attr][i] + pos * ms * diff[attr][i]; 4977 }; 4978 // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]]; 4979 now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]]; 4980 } 4981 break; 4982 case "csv": 4983 if (attr == "clip-rect") { 4984 now = []; 4985 i = 4; 4986 while (i--) { 4987 now[i] = +from[attr][i] + pos * ms * diff[attr][i]; 4988 } 4989 } 4990 break; 4991 default: 4992 var from2 = [].concat(from[attr]); 4993 now = []; 4994 i = that.paper.customAttributes[attr].length; 4995 while (i--) { 4996 now[i] = +from2[i] + pos * ms * diff[attr][i]; 4997 } 4998 break; 4999 } 5000 set[attr] = now; 5001 } 5002 that.attr(set); 5003 (function (id, that, anim) { 5004 setTimeout(function () { 5005 eve("anim.frame." + id, that, anim); 5006 }); 5007 })(that.id, that, e.anim); 5008 } else { 5009 (function(f, el, a) { 5010 setTimeout(function() { 5011 eve("anim.finish." + el.id, el, a); 5012 R.is(f, "function") && f.call(el); 5013 }); 5014 })(e.callback, that, e.anim); 5015 console.log(e.repeat); 5016 if (--e.repeat) { 5017 that.attr(e.origin); 5018 e.start = Now; 5019 } else { 5020 that.attr(to); 5021 animationElements.splice(l--, 1); 5022 } 5023 if (e.next && !e.stop) { 5024 runAnimation(e.anim, e.el, e.next, null, e.totalOrigin); 5025 } 5026 } 5027 } 5028 R.svg && that && that.paper && that.paper.safari(); 5029 animationElements.length && requestAnimFrame(animation); 5030 }, 5031 upto255 = function (color) { 5032 return mmax(mmin(color, 255), 0); 5033 }; 5034 /*\ 5035 * Element.animateWith 5036 [ method ] 5037 ** 5038 * Acts similar to @Element.animate, but ensure that given animation runs in sync with another given element. 5039 ** 5040 > Parameters 5041 ** 5042 - params (object) final attributes for the element, see also @Element.attr 5043 - ms (number) number of milliseconds for animation to run 5044 - easing (string) #optional easing type. Accept on of @Raphael.easing_formulas or CSS format: `cubic‐bezier(XX,&#160;XX,&#160;XX,&#160;XX)` 5045 - callback (function) #optional callback function. Will be called at the end of animation. 5046 * or 5047 - animation (object) animation object, see @Raphael.animation 5048 ** 5049 = (object) original element 5050 \*/ 5051 elproto.animateWith = function (element, params, ms, easing, callback) { 5052 this.animate(params, ms, easing, callback); 5053 var start, el; 5054 for (var i = 0, ii = animationElements.length; i < ii; i++) { 5055 el = animationElements[i]; 5056 if (el.el.id == element.id) { 5057 start = el.timestamp; 5058 } else if (el.el.id == this.id) { 5059 el.start = start; 5060 } 5061 } 5062 return this.animate(params, ms, easing, callback); 5063 }; 5064 function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) { 5065 var cx = 3 * p1x, 5066 bx = 3 * (p2x - p1x) - cx, 5067 ax = 1 - cx - bx, 5068 cy = 3 * p1y, 5069 by = 3 * (p2y - p1y) - cy, 5070 ay = 1 - cy - by; 5071 function sampleCurveX(t) { 5072 return ((ax * t + bx) * t + cx) * t; 5073 } 5074 function solve(x, epsilon) { 5075 var t = solveCurveX(x, epsilon); 5076 return ((ay * t + by) * t + cy) * t; 5077 } 5078 function solveCurveX(x, epsilon) { 5079 var t0, t1, t2, x2, d2, i; 5080 for(t2 = x, i = 0; i < 8; i++) { 5081 x2 = sampleCurveX(t2) - x; 5082 if (abs(x2) < epsilon) { 5083 return t2; 5084 } 5085 d2 = (3 * ax * t2 + 2 * bx) * t2 + cx; 5086 if (abs(d2) < 1e-6) { 5087 break; 5088 } 5089 t2 = t2 - x2 / d2; 5090 } 5091 t0 = 0; 5092 t1 = 1; 5093 t2 = x; 5094 if (t2 < t0) { 5095 return t0; 5096 } 5097 if (t2 > t1) { 5098 return t1; 5099 } 5100 while (t0 < t1) { 5101 x2 = sampleCurveX(t2); 5102 if (abs(x2 - x) < epsilon) { 5103 return t2; 5104 } 5105 if (x > x2) { 5106 t0 = t2; 5107 } else { 5108 t1 = t2; 5109 } 5110 t2 = (t1 - t0) / 2 + t0; 5111 } 5112 return t2; 5113 } 5114 return solve(t, 1 / (200 * duration)); 5115 } 5116 elproto.onAnimation = function (f) { 5117 f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id); 5118 return this; 5119 }; 5120 function Animation(anim, ms) { 5121 var percents = []; 5122 this.anim = anim; 5123 this.ms = ms; 5124 this.times = 1; 5125 if (this.anim) { 5126 for (var attr in this.anim) if (this.anim[has](attr)) { 5127 percents.push(+attr); 5128 } 5129 percents.sort(sortByNumber); 5130 } 5131 this.top = percents[percents.length - 1]; 5132 this.percents = percents; 5133 } 5134 /*\ 5135 * Animation.delay 5136 [ method ] 5137 ** 5138 * Creates a copy of existing animation object with given delay. 5139 ** 5140 > Parameters 5141 ** 5142 - delay (number) number of ms to pass between animation start and actual animation 5143 ** 5144 = (object) new altered Animation object 5145 \*/ 5146 Animation.prototype.delay = function (delay) { 5147 var a = new Animation(this.anim, this.ms); 5148 a.times = this.times; 5149 a.del = +delay || 0; 5150 return a; 5151 }; 5152 /*\ 5153 * Animation.repeat 5154 [ method ] 5155 ** 5156 * Creates a copy of existing animation object with given repetition. 5157 ** 5158 > Parameters 5159 ** 5160 - repeat (number) number iterations of animation. For infinite animation pass `Infinity` 5161 ** 5162 = (object) new altered Animation object 5163 \*/ 5164 Animation.prototype.repeat = function (times) { 5165 var a = new Animation(this.anim, this.ms); 5166 a.del = this.del; 5167 a.times = math.floor(mmax(times, 0)) || 1; 5168 return a; 5169 }; 5170 function runAnimation(anim, element, percent, status, totalOrigin) { 5171 percent = toFloat(percent); 5172 var params, 5173 isInAnim, 5174 isInAnimSet, 5175 percents = [], 5176 next, 5177 prev, 5178 timestamp, 5179 ms = anim.ms, 5180 from = {}, 5181 to = {}, 5182 diff = {}; 5183 if (status) { 5184 for (i = 0, ii = animationElements.length; i < ii; i++) { 5185 var e = animationElements[i]; 5186 if (e.el.id == element.id && e.anim == anim) { 5187 if (e.percent != percent) { 5188 animationElements.splice(i, 1); 5189 isInAnimSet = 1; 5190 } else { 5191 isInAnim = e; 5192 } 5193 element.attr(e.totalOrigin); 5194 break; 5195 } 5196 } 5197 } else { 5198 status = +to; // NaN 5199 } 5200 for (var i = 0, ii = anim.percents.length; i < ii; i++) { 5201 if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) { 5202 percent = anim.percents[i]; 5203 prev = anim.percents[i - 1] || 0; 5204 ms = ms / anim.top * (percent - prev); 5205 next = anim.percents[i + 1]; 5206 params = anim.anim[percent]; 5207 break; 5208 } else if (status) { 5209 element.attr(anim.anim[anim.percents[i]]); 5210 } 5211 } 5212 if (!params) { 5213 return; 5214 } 5215 if (!isInAnim) { 5216 for (attr in params) if (params[has](attr)) { 5217 if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) { 5218 from[attr] = element.attr(attr); 5219 (from[attr] == null) && (from[attr] = availableAttrs[attr]); 5220 to[attr] = params[attr]; 5221 switch (availableAnimAttrs[attr]) { 5222 case nu: 5223 diff[attr] = (to[attr] - from[attr]) / ms; 5224 break; 5225 case "colour": 5226 from[attr] = R.getRGB(from[attr]); 5227 var toColour = R.getRGB(to[attr]); 5228 diff[attr] = { 5229 r: (toColour.r - from[attr].r) / ms, 5230 g: (toColour.g - from[attr].g) / ms, 5231 b: (toColour.b - from[attr].b) / ms 5232 }; 5233 break; 5234 case "path": 5235 var pathes = path2curve(from[attr], to[attr]), 5236 toPath = pathes[1]; 5237 from[attr] = pathes[0]; 5238 diff[attr] = []; 5239 for (i = 0, ii = from[attr].length; i < ii; i++) { 5240 diff[attr][i] = [0]; 5241 for (var j = 1, jj = from[attr][i].length; j < jj; j++) { 5242 diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms; 5243 } 5244 } 5245 break; 5246 case "transform": 5247 var _ = element._, 5248 eq = equaliseTransform(_[attr], to[attr]); 5249 if (eq) { 5250 from[attr] = eq.from; 5251 to[attr] = eq.to; 5252 diff[attr] = []; 5253 diff[attr].real = true; 5254 for (i = 0, ii = from[attr].length; i < ii; i++) { 5255 diff[attr][i] = [from[attr][i][0]]; 5256 for (j = 1, jj = from[attr][i].length; j < jj; j++) { 5257 diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms; 5258 } 5259 } 5260 } else { 5261 var m = (element.matrix || new Matrix).m, 5262 to2 = {_:{transform: _.transform}, getBBox: function () { return element.getBBox(); }}; 5263 from[attr] = [ 5264 m[0][0], 5265 m[1][0], 5266 m[0][1], 5267 m[1][1], 5268 m[0][2], 5269 m[1][2] 5270 ]; 5271 extractTransform(to2, to[attr]); 5272 to[attr] = to2._.transform; 5273 diff[attr] = [ 5274 (to2.matrix.m[0][0] - m[0][0]) / ms, 5275 (to2.matrix.m[1][0] - m[1][0]) / ms, 5276 (to2.matrix.m[0][1] - m[0][1]) / ms, 5277 (to2.matrix.m[1][1] - m[1][1]) / ms, 5278 (to2.matrix.m[0][2] - m[0][2]) / ms, 5279 (to2.matrix.m[1][2] - m[1][2]) / ms 5280 ]; 5281 // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy]; 5282 // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }}; 5283 // extractTransform(to2, to[attr]); 5284 // diff[attr] = [ 5285 // (to2._.sx - _.sx) / ms, 5286 // (to2._.sy - _.sy) / ms, 5287 // (to2._.deg - _.deg) / ms, 5288 // (to2._.dx - _.dx) / ms, 5289 // (to2._.dy - _.dy) / ms 5290 // ]; 5291 } 5292 break; 5293 case "csv": 5294 var values = Str(params[attr]).split(separator), 5295 from2 = Str(from[attr]).split(separator); 5296 if (attr == "clip-rect") { 5297 from[attr] = from2; 5298 diff[attr] = []; 5299 i = from2.length; 5300 while (i--) { 5301 diff[attr][i] = (values[i] - from[attr][i]) / ms; 5302 } 5303 } 5304 to[attr] = values; 5305 break; 5306 default: 5307 values = [].concat(params[attr]); 5308 from2 = [].concat(from[attr]); 5309 diff[attr] = []; 5310 i = element.paper.customAttributes[attr].length; 5311 while (i--) { 5312 diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms; 5313 } 5314 break; 5315 } 5316 } 5317 } 5318 var easing = params.easing, 5319 easyeasy = R.easing_formulas[easing]; 5320 if (!easyeasy) { 5321 easyeasy = Str(easing).match(bezierrg); 5322 if (easyeasy && easyeasy.length == 5) { 5323 var curve = easyeasy; 5324 easyeasy = function (t) { 5325 return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms); 5326 }; 5327 } else { 5328 easyeasy = pipe; 5329 } 5330 } 5331 timestamp = params.start || anim.start || +new Date; 5332 e = { 5333 anim: anim, 5334 percent: percent, 5335 timestamp: timestamp, 5336 start: timestamp + (anim.del || 0), 5337 status: 0, 5338 initstatus: status || 0, 5339 stop: false, 5340 ms: ms, 5341 easing: easyeasy, 5342 from: from, 5343 diff: diff, 5344 to: to, 5345 el: element, 5346 callback: params.callback, 5347 prev: prev, 5348 next: next, 5349 repeat: anim.times, 5350 origin: element.attr(), 5351 totalOrigin: totalOrigin 5352 }; 5353 animationElements.push(e); 5354 if (status && !isInAnim) { 5355 e.stop = true; 5356 e.start = new Date - ms * status; 5357 if (animationElements.length == 1) { 5358 return animation(); 5359 } 5360 } 5361 animationElements.length == 1 && requestAnimFrame(animation); 5362 } else { 5363 isInAnim.initstatus = status; 5364 isInAnim.start = new Date - isInAnim.ms * status; 5365 } 5366 eve("anim.start." + element.id, element, anim); 5367 } 5368 /*\ 5369 * Raphael.animation 5370 [ method ] 5371 ** 5372 * Creates an animation object that can be passed to the @Element.animate or @Element.animateWith methods. 5373 * See also @Animation.delay and @Animation.repeat methods. 5374 ** 5375 > Parameters 5376 ** 5377 - params (object) final attributes for the element, see also @Element.attr 5378 - ms (number) number of milliseconds for animation to run 5379 - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic‐bezier(XX,&#160;XX,&#160;XX,&#160;XX)` 5380 - callback (function) #optional callback function. Will be called at the end of animation. 5381 ** 5382 = (object) @Animation 5383 \*/ 5384 R.animation = function (params, ms, easing, callback) { 5385 if (R.is(easing, "function") || !easing) { 5386 callback = callback || easing || null; 5387 easing = null; 5388 } 5389 params = Object(params); 5390 ms = +ms || 0; 5391 var p = {}, 5392 json, 5393 attr; 5394 for (attr in params) if (params[has](attr) && toFloat(attr) != attr) { 5395 json = true; 5396 p[attr] = params[attr]; 5397 } 5398 if (!json) { 5399 return new Animation(params, ms); 5400 } else { 5401 easing && (p.easing = easing); 5402 callback && (p.callback = callback); 5403 return new Animation({100: p}, ms); 5404 } 5405 }; 5406 /*\ 5407 * Element.animate 5408 [ method ] 5409 ** 5410 * Creates and starts animation for given element. 5411 ** 5412 > Parameters 5413 ** 5414 - params (object) final attributes for the element, see also @Element.attr 5415 - ms (number) number of milliseconds for animation to run 5416 - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic‐bezier(XX,&#160;XX,&#160;XX,&#160;XX)` 5417 - callback (function) #optional callback function. Will be called at the end of animation. 5418 * or 5419 - animation (object) animation object, see @Raphael.animation 5420 ** 5421 = (object) original element 5422 \*/ 5423 elproto.animate = function (params, ms, easing, callback) { 5424 var element = this; 5425 if (element.removed) { 5426 callback && callback.call(element); 5427 return element; 5428 } 5429 var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback); 5430 runAnimation(anim, element, anim.percents[0], null, element.attr()); 5431 return element; 5432 }; 5433 /*\ 5434 * Element.setTime 5435 [ method ] 5436 ** 5437 * Sets the status of animation of the element in milliseconds. Similar to @Element.status method. 5438 ** 5439 > Parameters 5440 ** 5441 - anim (object) animation object 5442 - value (number) number of milliseconds from the beginning of the animation 5443 ** 5444 = (object) original element if `value` is specified 5445 * Note, that during animation following events are triggered: 5446 * 5447 * On each animation frame event `anim.frame.<id>`, on start `anim.start.<id>` and on end `anim.finish.<id>`. 5448 \*/ 5449 elproto.setTime = function (anim, value) { 5450 if (anim && value != null) { 5451 this.status(anim, mmin(value, anim.ms) / anim.ms); 5452 } 5453 return this; 5454 }; 5455 /*\ 5456 * Element.status 5457 [ method ] 5458 ** 5459 * Gets or sets the status of animation of the element. 5460 ** 5461 > Parameters 5462 ** 5463 - anim (object) #optional animation object 5464 - 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. 5465 ** 5466 = (number) status 5467 * or 5468 = (array) status if `anim` is not specified. Array of objects in format: 5469 o { 5470 o anim: (object) animation object 5471 o status: (number) status 5472 o } 5473 * or 5474 = (object) original element if `value` is specified 5475 \*/ 5476 elproto.status = function (anim, value) { 5477 var out = [], 5478 i = 0, 5479 len, 5480 e; 5481 if (value != null) { 5482 runAnimation(anim, this, -1, mmin(value, 1)); 5483 return this; 5484 } else { 5485 len = animationElements.length; 5486 for (; i < len; i++) { 5487 e = animationElements[i]; 5488 if (e.el.id == this.id && (!anim || e.anim == anim)) { 5489 if (anim) { 5490 return e.status; 5491 } 5492 out.push({anim: e.anim, status: e.status}); 5493 } 5494 } 5495 if (anim) { 5496 return 0; 5497 } 5498 return out; 5499 } 5500 }; 5501 /*\ 5502 * Element.pause 5503 [ method ] 5504 ** 5505 * Stops animation of the element with ability to resume it later on. 5506 ** 5507 > Parameters 5508 ** 5509 - anim (object) #optional animation object 5510 ** 5511 = (object) original element 5512 \*/ 5513 elproto.pause = function (anim) { 5514 for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) { 5515 if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) { 5516 animationElements[i].paused = true; 5517 } 5518 } 5519 return this; 5520 }; 5521 /*\ 5522 * Element.resume 5523 [ method ] 5524 ** 5525 * Resumes animation if it was paused with @Element.pause method. 5526 ** 5527 > Parameters 5528 ** 5529 - anim (object) #optional animation object 5530 ** 5531 = (object) original element 5532 \*/ 5533 elproto.resume = function (anim) { 5534 for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) { 5535 var e = animationElements[i]; 5536 if (eve("anim.resume." + this.id, this, e.anim) !== false) { 5537 delete e.paused; 5538 this.status(e.anim, e.status); 5539 } 5540 } 5541 return this; 5542 }; 5543 /*\ 5544 * Element.stop 5545 [ method ] 5546 ** 5547 * Stops animation of the element. 5548 ** 5549 > Parameters 5550 ** 5551 - anim (object) #optional animation object 5552 ** 5553 = (object) original element 5554 \*/ 5555 elproto.stop = function (anim) { 5556 for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) { 5557 if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) { 5558 animationElements.splice(i--, 1); 5559 } 5560 } 5561 return this; 5562 }; 5563 elproto.toString = function () { 5564 return "Rapha\xebl\u2019s object"; 5565 }; 5566 5567 // Set 5568 var Set = function (items) { 5569 this.items = []; 5570 this.length = 0; 5571 this.type = "set"; 5572 if (items) { 5573 for (var i = 0, ii = items.length; i < ii; i++) { 5574 if (items[i] && (items[i].constructor == Element || items[i].constructor == Set)) { 5575 this[this.items.length] = this.items[this.items.length] = items[i]; 5576 this.length++; 5577 } 5578 } 5579 } 5580 }, 5581 setproto = Set.prototype; 5582 /*\ 5583 * Set.push 5584 [ method ] 5585 ** 5586 * Adds each argument to the current set. 5587 = (object) original element 5588 \*/ 5589 setproto.push = function () { 5590 var item, 5591 len; 5592 for (var i = 0, ii = arguments.length; i < ii; i++) { 5593 item = arguments[i]; 5594 if (item && (item.constructor == Element || item.constructor == Set)) { 5595 len = this.items.length; 5596 this[len] = this.items[len] = item; 5597 this.length++; 5598 } 5599 } 5600 return this; 5601 }; 5602 /*\ 5603 * Set.pop 5604 [ method ] 5605 ** 5606 * Removes last element and returns it. 5607 = (object) element 5608 \*/ 5609 setproto.pop = function () { 5610 this.length && delete this[this.length--]; 5611 return this.items.pop(); 5612 }; 5613 /*\ 5614 * Set.forEach 5615 [ method ] 5616 ** 5617 * Executes given function for each element in the set. 5618 * 5619 * If function returns `false` it will stop loop running. 5620 ** 5621 > Parameters 5622 ** 5623 - callback (function) function to run 5624 - thisArg (object) context object for the callback 5625 = (object) Set object 5626 \*/ 5627 setproto.forEach = function (callback, thisArg) { 5628 for (var i = 0, ii = this.items.length; i < ii; i++) { 5629 if (callback.call(thisArg, this.items[i]) === false) { 5630 return this; 5631 } 5632 } 5633 return this; 5634 }; 5635 for (var method in elproto) if (elproto[has](method)) { 5636 setproto[method] = (function (methodname) { 5637 return function () { 5638 var arg = arguments; 5639 return this.forEach(function (el) { 5640 el[methodname][apply](el, arg); 5641 }); 5642 }; 5643 })(method); 5644 } 5645 setproto.attr = function (name, value) { 5646 if (name && R.is(name, array) && R.is(name[0], "object")) { 5647 for (var j = 0, jj = name.length; j < jj; j++) { 5648 this.items[j].attr(name[j]); 5649 } 5650 } else { 5651 for (var i = 0, ii = this.items.length; i < ii; i++) { 5652 this.items[i].attr(name, value); 5653 } 5654 } 5655 return this; 5656 }; 5657 setproto.animate = function (params, ms, easing, callback) { 5658 (R.is(easing, "function") || !easing) && (callback = easing || null); 5659 var len = this.items.length, 5660 i = len, 5661 item, 5662 set = this, 5663 collector; 5664 callback && (collector = function () { 5665 !--len && callback.call(set); 5666 }); 5667 easing = R.is(easing, string) ? easing : collector; 5668 var anim = params instanceof Animation ? params : R.animation(params, ms, easing, collector); 5669 item = this.items[--i].animate(anim); 5670 while (i--) { 5671 this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim); 5672 } 5673 return this; 5674 }; 5675 setproto.insertAfter = function (el) { 5676 var i = this.items.length; 5677 while (i--) { 5678 this.items[i].insertAfter(el); 5679 } 5680 return this; 5681 }; 5682 setproto.getBBox = function () { 5683 var x = [], 5684 y = [], 5685 w = [], 5686 h = []; 5687 for (var i = this.items.length; i--;) if (!this.items[i].removed) { 5688 var box = this.items[i].getBBox(); 5689 x.push(box.x); 5690 y.push(box.y); 5691 w.push(box.x + box.width); 5692 h.push(box.y + box.height); 5693 } 5694 x = mmin[apply](0, x); 5695 y = mmin[apply](0, y); 5696 return { 5697 x: x, 5698 y: y, 5699 width: mmax[apply](0, w) - x, 5700 height: mmax[apply](0, h) - y 5701 }; 5702 }; 5703 setproto.clone = function (s) { 5704 s = new Set; 5705 for (var i = 0, ii = this.items.length; i < ii; i++) { 5706 s.push(this.items[i].clone()); 5707 } 5708 return s; 5709 }; 5710 setproto.toString = function () { 5711 return "Rapha\xebl\u2018s set"; 5712 }; 5713 5714 R.registerFont = function (font) { 5715 if (!font.face) { 5716 return font; 5717 } 5718 this.fonts = this.fonts || {}; 5719 var fontcopy = { 5720 w: font.w, 5721 face: {}, 5722 glyphs: {} 5723 }, 5724 family = font.face["font-family"]; 5725 for (var prop in font.face) if (font.face[has](prop)) { 5726 fontcopy.face[prop] = font.face[prop]; 5727 } 5728 if (this.fonts[family]) { 5729 this.fonts[family].push(fontcopy); 5730 } else { 5731 this.fonts[family] = [fontcopy]; 5732 } 5733 if (!font.svg) { 5734 fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10); 5735 for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) { 5736 var path = font.glyphs[glyph]; 5737 fontcopy.glyphs[glyph] = { 5738 w: path.w, 5739 k: {}, 5740 d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) { 5741 return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M"; 5742 }) + "z" 5743 }; 5744 if (path.k) { 5745 for (var k in path.k) if (path[has](k)) { 5746 fontcopy.glyphs[glyph].k[k] = path.k[k]; 5747 } 5748 } 5749 } 5750 } 5751 return font; 5752 }; 5753 paperproto.getFont = function (family, weight, style, stretch) { 5754 stretch = stretch || "normal"; 5755 style = style || "normal"; 5756 weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400; 5757 if (!R.fonts) { 5758 return; 5759 } 5760 var font = R.fonts[family]; 5761 if (!font) { 5762 var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i"); 5763 for (var fontName in R.fonts) if (R.fonts[has](fontName)) { 5764 if (name.test(fontName)) { 5765 font = R.fonts[fontName]; 5766 break; 5767 } 5768 } 5769 } 5770 var thefont; 5771 if (font) { 5772 for (var i = 0, ii = font.length; i < ii; i++) { 5773 thefont = font[i]; 5774 if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) { 5775 break; 5776 } 5777 } 5778 } 5779 return thefont; 5780 }; 5781 paperproto.print = function (x, y, string, font, size, origin, letter_spacing) { 5782 origin = origin || "middle"; // baseline|middle 5783 letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1); 5784 var out = this.set(), 5785 letters = Str(string).split(E), 5786 shift = 0, 5787 path = E, 5788 scale; 5789 R.is(font, string) && (font = this.getFont(font)); 5790 if (font) { 5791 scale = (size || 16) / font.face["units-per-em"]; 5792 var bb = font.face.bbox.split(separator), 5793 top = +bb[0], 5794 height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2); 5795 for (var i = 0, ii = letters.length; i < ii; i++) { 5796 var prev = i && font.glyphs[letters[i - 1]] || {}, 5797 curr = font.glyphs[letters[i]]; 5798 shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0; 5799 curr && curr.d && out.push(this.path(curr.d).attr({fill: "#000", stroke: "none", transform: [["t", shift, 0]]})); 5800 } 5801 out.scale(scale, scale, top, height).translate(x - top, y - height); 5802 } 5803 return out; 5804 }; 5805 5806 R.format = function (token, params) { 5807 var args = R.is(params, array) ? [0][concat](params) : arguments; 5808 token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) { 5809 return args[++i] == null ? E : args[i]; 5810 })); 5811 return token || E; 5812 }; 5813 R.ninja = function () { 5814 oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael; 5815 return R; 5816 }; 5817 /*\ 5818 * Raphael.el 5819 [ property (object) ] 5820 ** 5821 * You can add your own method to elements. This is usefull when you want to hack default functionality or 5822 * want to wrap some common transformation or attributes in one method. In difference to canvas methods, 5823 * you can redefine element method at any time. Expending element methods wouldn’t affect set. 5824 > Usage 5825 | Raphael.el.red = function () { 5826 | this.attr({fill: "#f00"}); 5827 | }; 5828 | // then use it 5829 | paper.circle(100, 100, 20).red(); 5830 \*/ 5831 R.el = elproto; 5832 R.st = setproto; 5833 // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html 5834 (function (doc, loaded, f) { 5835 if (doc.readyState == null && doc.addEventListener){ 5836 doc.addEventListener(loaded, f = function () { 5837 doc.removeEventListener(loaded, f, false); 5838 doc.readyState = "complete"; 5839 }, false); 5840 doc.readyState = "loading"; 5841 } 5842 function isLoaded() { 5843 (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : eve("DOMload"); 5844 } 5845 isLoaded(); 5846 })(document, "DOMContentLoaded"); 5847 5848 oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R); 5849 5850 /* 5851 * Eve 0.2.1 - JavaScript Events Library 5852 * 5853 * Copyright (c) 2010 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) 5854 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. 5855 */ 5856 5857 var eve = R.eve = (function () { 5858 var version = "0.2.1", 5859 has = "hasOwnProperty", 5860 separator = /[\.\/]/, 5861 wildcard = "*", 5862 events = {n: {}}, 5863 eve = function (name, scope) { 5864 var e = events, 5865 args = Array.prototype.slice.call(arguments, 2), 5866 listeners = eve.listeners(name), 5867 errors = []; 5868 for (var i = 0, ii = listeners.length; i < ii; i++) { 5869 try { 5870 listeners[i].apply(scope, args); 5871 } catch (ex) { 5872 errors.push({error: ex && ex.message || ex, func: listeners[i]}); 5873 } 5874 } 5875 if (errors.length) { 5876 return errors; 5877 } 5878 }; 5879 eve.listeners = function (name) { 5880 var names = name.split(separator), 5881 e = events, 5882 item, 5883 items, 5884 k, 5885 i, 5886 ii, 5887 j, 5888 jj, 5889 nes, 5890 es = [e], 5891 out = []; 5892 for (i = 0, ii = names.length; i < ii; i++) { 5893 nes = []; 5894 for (j = 0, jj = es.length; j < jj; j++) { 5895 e = es[j].n; 5896 items = [e[names[i]], e[wildcard]]; 5897 k = 2; 5898 while (k--) { 5899 item = items[k]; 5900 if (item) { 5901 nes.push(item); 5902 out = out.concat(item.f || []); 5903 } 5904 } 5905 } 5906 es = nes; 5907 } 5908 return out; 5909 }; 5910 eve.on = function (name, f) { 5911 var names = name.split(separator), 5912 e = events; 5913 for (var i = 0, ii = names.length; i < ii; i++) { 5914 e = e.n; 5915 !e[names[i]] && (e[names[i]] = {n: {}}); 5916 e = e[names[i]]; 5917 } 5918 e.f = e.f || []; 5919 for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) { 5920 return false; 5921 } 5922 e.f.push(f); 5923 }; 5924 eve.unbind = function (name, f) { 5925 var names = name.split(separator), 5926 e, 5927 key, 5928 splice, 5929 cur = [events]; 5930 for (var i = 0, ii = names.length; i < ii; i++) { 5931 for (var j = 0; j < cur.length; j += splice.length - 2) { 5932 splice = [j, 1]; 5933 e = cur[j].n; 5934 if (names[i] != wildcard) { 5935 if (e[names[i]]) { 5936 splice.push(e[names[i]]); 5937 } 5938 } else { 5939 for (key in e) if (e[has](key)) { 5940 splice.push(e[key]); 5941 } 5942 } 5943 cur.splice.apply(cur, splice); 5944 } 5945 } 5946 for (i = 0, ii = cur.length; i < ii; i++) { 5947 e = cur[i]; 5948 while (e.n) { 5949 if (f) { 5950 if (e.f) { 5951 for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) { 5952 e.f.splice(i, 1); 5953 break; 5954 } 5955 !e.f.length && delete e.f; 5956 } 5957 for (key in e.n) if (e.n[has](key) && e.n[key].f) { 5958 var funcs = e.n[key].f; 5959 for (i = 0, ii = funcs.length; i < ii; i++) if (funcs[i] == f) { 5960 funcs.splice(i, 1); 5961 break; 5962 } 5963 !funcs.length && delete e.n[key].f; 5964 } 5965 } else { 5966 delete e.f; 5967 for (key in e.n) if (e.n[has](key) && e.n[key].f) { 5968 delete e.n[key].f; 5969 } 5970 } 5971 e = e.n; 5972 } 5973 } 5974 return true; 5975 }; 5976 eve.version = version; 5977 eve.toString = function () { 5978 return "You are running Eve " + version; 5979 }; 5980 return eve; 5981 })(); 5982})();