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