1define(["./raphael.core"], function(R) { 2 if (R && !R.svg) { 3 return; 4 } 5 6 var has = "hasOwnProperty", 7 Str = String, 8 toFloat = parseFloat, 9 toInt = parseInt, 10 math = Math, 11 mmax = math.max, 12 abs = math.abs, 13 pow = math.pow, 14 separator = /[, ]+/, 15 eve = R.eve, 16 E = "", 17 S = " "; 18 var xlink = "http://www.w3.org/1999/xlink", 19 markers = { 20 block: "M5,0 0,2.5 5,5z", 21 classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z", 22 diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z", 23 open: "M6,1 1,3.5 6,6", 24 oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z" 25 }, 26 markerCounter = {}; 27 R.toString = function () { 28 return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version; 29 }; 30 var $ = function (el, attr) { 31 if (attr) { 32 if (typeof el == "string") { 33 el = $(el); 34 } 35 for (var key in attr) if (attr[has](key)) { 36 if (key.substring(0, 6) == "xlink:") { 37 el.setAttributeNS(xlink, key.substring(6), Str(attr[key])); 38 } else { 39 el.setAttribute(key, Str(attr[key])); 40 } 41 } 42 } else { 43 el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el); 44 el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)"); 45 } 46 return el; 47 }, 48 addGradientFill = function (element, gradient) { 49 var type = "linear", 50 id = element.id + gradient, 51 fx = .5, fy = .5, 52 o = element.node, 53 SVG = element.paper, 54 s = o.style, 55 el = R._g.doc.getElementById(id); 56 if (!el) { 57 gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) { 58 type = "radial"; 59 if (_fx && _fy) { 60 fx = toFloat(_fx); 61 fy = toFloat(_fy); 62 var dir = ((fy > .5) * 2 - 1); 63 pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && 64 (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) && 65 fy != .5 && 66 (fy = fy.toFixed(5) - 1e-5 * dir); 67 } 68 return E; 69 }); 70 gradient = gradient.split(/\s*\-\s*/); 71 if (type == "linear") { 72 var angle = gradient.shift(); 73 angle = -toFloat(angle); 74 if (isNaN(angle)) { 75 return null; 76 } 77 var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))], 78 max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1); 79 vector[2] *= max; 80 vector[3] *= max; 81 if (vector[2] < 0) { 82 vector[0] = -vector[2]; 83 vector[2] = 0; 84 } 85 if (vector[3] < 0) { 86 vector[1] = -vector[3]; 87 vector[3] = 0; 88 } 89 } 90 var dots = R._parseDots(gradient); 91 if (!dots) { 92 return null; 93 } 94 id = id.replace(/[\(\)\s,\xb0#]/g, "_"); 95 96 if (element.gradient && id != element.gradient.id) { 97 SVG.defs.removeChild(element.gradient); 98 delete element.gradient; 99 } 100 101 if (!element.gradient) { 102 el = $(type + "Gradient", {id: id}); 103 element.gradient = el; 104 $(el, type == "radial" ? { 105 fx: fx, 106 fy: fy 107 } : { 108 x1: vector[0], 109 y1: vector[1], 110 x2: vector[2], 111 y2: vector[3], 112 gradientTransform: element.matrix.invert() 113 }); 114 SVG.defs.appendChild(el); 115 for (var i = 0, ii = dots.length; i < ii; i++) { 116 el.appendChild($("stop", { 117 offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%", 118 "stop-color": dots[i].color || "#fff", 119 "stop-opacity": isFinite(dots[i].opacity) ? dots[i].opacity : 1 120 })); 121 } 122 } 123 } 124 $(o, { 125 fill: fillurl(id), 126 opacity: 1, 127 "fill-opacity": 1 128 }); 129 s.fill = E; 130 s.opacity = 1; 131 s.fillOpacity = 1; 132 return 1; 133 }, 134 isIE9or10 = function () { 135 var mode = document.documentMode; 136 return mode && (mode === 9 || mode === 10); 137 }, 138 fillurl = function (id) { 139 if (isIE9or10()) { 140 return "url('#" + id + "')"; 141 } 142 var location = document.location; 143 var locationString = ( 144 location.protocol + '//' + 145 location.host + 146 location.pathname + 147 location.search 148 ); 149 return "url('" + locationString + "#" + id + "')"; 150 }, 151 updatePosition = function (o) { 152 var bbox = o.getBBox(1); 153 $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"}); 154 }, 155 addArrow = function (o, value, isEnd) { 156 if (o.type == "path") { 157 var values = Str(value).toLowerCase().split("-"), 158 p = o.paper, 159 se = isEnd ? "end" : "start", 160 node = o.node, 161 attrs = o.attrs, 162 stroke = attrs["stroke-width"], 163 i = values.length, 164 type = "classic", 165 from, 166 to, 167 dx, 168 refX, 169 attr, 170 w = 3, 171 h = 3, 172 t = 5; 173 while (i--) { 174 switch (values[i]) { 175 case "block": 176 case "classic": 177 case "oval": 178 case "diamond": 179 case "open": 180 case "none": 181 type = values[i]; 182 break; 183 case "wide": h = 5; break; 184 case "narrow": h = 2; break; 185 case "long": w = 5; break; 186 case "short": w = 2; break; 187 } 188 } 189 if (type == "open") { 190 w += 2; 191 h += 2; 192 t += 2; 193 dx = 1; 194 refX = isEnd ? 4 : 1; 195 attr = { 196 fill: "none", 197 stroke: attrs.stroke 198 }; 199 } else { 200 refX = dx = w / 2; 201 attr = { 202 fill: attrs.stroke, 203 stroke: "none" 204 }; 205 } 206 if (o._.arrows) { 207 if (isEnd) { 208 o._.arrows.endPath && markerCounter[o._.arrows.endPath]--; 209 o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--; 210 } else { 211 o._.arrows.startPath && markerCounter[o._.arrows.startPath]--; 212 o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--; 213 } 214 } else { 215 o._.arrows = {}; 216 } 217 if (type != "none") { 218 var pathId = "raphael-marker-" + type, 219 markerId = "raphael-marker-" + se + type + w + h + "-obj" + o.id; 220 if (!R._g.doc.getElementById(pathId)) { 221 p.defs.appendChild($($("path"), { 222 "stroke-linecap": "round", 223 d: markers[type], 224 id: pathId 225 })); 226 markerCounter[pathId] = 1; 227 } else { 228 markerCounter[pathId]++; 229 } 230 var marker = R._g.doc.getElementById(markerId), 231 use; 232 if (!marker) { 233 marker = $($("marker"), { 234 id: markerId, 235 markerHeight: h, 236 markerWidth: w, 237 orient: "auto", 238 refX: refX, 239 refY: h / 2 240 }); 241 use = $($("use"), { 242 "xlink:href": "#" + pathId, 243 transform: (isEnd ? "rotate(180 " + w / 2 + " " + h / 2 + ") " : E) + "scale(" + w / t + "," + h / t + ")", 244 "stroke-width": (1 / ((w / t + h / t) / 2)).toFixed(4) 245 }); 246 marker.appendChild(use); 247 p.defs.appendChild(marker); 248 markerCounter[markerId] = 1; 249 } else { 250 markerCounter[markerId]++; 251 use = marker.getElementsByTagName("use")[0]; 252 } 253 $(use, attr); 254 var delta = dx * (type != "diamond" && type != "oval"); 255 if (isEnd) { 256 from = o._.arrows.startdx * stroke || 0; 257 to = R.getTotalLength(attrs.path) - delta * stroke; 258 } else { 259 from = delta * stroke; 260 to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0); 261 } 262 attr = {}; 263 attr["marker-" + se] = "url(#" + markerId + ")"; 264 if (to || from) { 265 attr.d = R.getSubpath(attrs.path, from, to); 266 } 267 $(node, attr); 268 o._.arrows[se + "Path"] = pathId; 269 o._.arrows[se + "Marker"] = markerId; 270 o._.arrows[se + "dx"] = delta; 271 o._.arrows[se + "Type"] = type; 272 o._.arrows[se + "String"] = value; 273 } else { 274 if (isEnd) { 275 from = o._.arrows.startdx * stroke || 0; 276 to = R.getTotalLength(attrs.path) - from; 277 } else { 278 from = 0; 279 to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0); 280 } 281 o._.arrows[se + "Path"] && $(node, {d: R.getSubpath(attrs.path, from, to)}); 282 delete o._.arrows[se + "Path"]; 283 delete o._.arrows[se + "Marker"]; 284 delete o._.arrows[se + "dx"]; 285 delete o._.arrows[se + "Type"]; 286 delete o._.arrows[se + "String"]; 287 } 288 for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) { 289 var item = R._g.doc.getElementById(attr); 290 item && item.parentNode.removeChild(item); 291 } 292 } 293 }, 294 dasharray = { 295 "-": [3, 1], 296 ".": [1, 1], 297 "-.": [3, 1, 1, 1], 298 "-..": [3, 1, 1, 1, 1, 1], 299 ". ": [1, 3], 300 "- ": [4, 3], 301 "--": [8, 3], 302 "- .": [4, 3, 1, 3], 303 "--.": [8, 3, 1, 3], 304 "--..": [8, 3, 1, 3, 1, 3] 305 }, 306 addDashes = function (o, value, params) { 307 value = dasharray[Str(value).toLowerCase()]; 308 if (value) { 309 var width = o.attrs["stroke-width"] || "1", 310 butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0, 311 dashes = [], 312 i = value.length; 313 while (i--) { 314 dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt; 315 } 316 $(o.node, {"stroke-dasharray": dashes.join(",")}); 317 } 318 else { 319 $(o.node, {"stroke-dasharray": "none"}); 320 } 321 }, 322 setFillAndStroke = function (o, params) { 323 var node = o.node, 324 attrs = o.attrs, 325 vis = node.style.visibility; 326 node.style.visibility = "hidden"; 327 for (var att in params) { 328 if (params[has](att)) { 329 if (!R._availableAttrs[has](att)) { 330 continue; 331 } 332 var value = params[att]; 333 attrs[att] = value; 334 switch (att) { 335 case "blur": 336 o.blur(value); 337 break; 338 case "title": 339 var title = node.getElementsByTagName("title"); 340 341 // Use the existing <title>. 342 if (title.length && (title = title[0])) { 343 title.firstChild.nodeValue = value; 344 } else { 345 title = $("title"); 346 var val = R._g.doc.createTextNode(value); 347 title.appendChild(val); 348 node.appendChild(title); 349 } 350 break; 351 case "href": 352 case "target": 353 var pn = node.parentNode; 354 if (pn.tagName.toLowerCase() != "a") { 355 var hl = $("a"); 356 pn.insertBefore(hl, node); 357 hl.appendChild(node); 358 pn = hl; 359 } 360 if (att == "target") { 361 pn.setAttributeNS(xlink, "show", value == "blank" ? "new" : value); 362 } else { 363 pn.setAttributeNS(xlink, att, value); 364 } 365 break; 366 case "cursor": 367 node.style.cursor = value; 368 break; 369 case "transform": 370 o.transform(value); 371 break; 372 case "arrow-start": 373 addArrow(o, value); 374 break; 375 case "arrow-end": 376 addArrow(o, value, 1); 377 break; 378 case "clip-rect": 379 var rect = Str(value).split(separator); 380 if (rect.length == 4) { 381 o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode); 382 var el = $("clipPath"), 383 rc = $("rect"); 384 el.id = R.createUUID(); 385 $(rc, { 386 x: rect[0], 387 y: rect[1], 388 width: rect[2], 389 height: rect[3] 390 }); 391 el.appendChild(rc); 392 o.paper.defs.appendChild(el); 393 $(node, {"clip-path": "url(#" + el.id + ")"}); 394 o.clip = rc; 395 } 396 if (!value) { 397 var path = node.getAttribute("clip-path"); 398 if (path) { 399 var clip = R._g.doc.getElementById(path.replace(/(^url\(#|\)$)/g, E)); 400 clip && clip.parentNode.removeChild(clip); 401 $(node, {"clip-path": E}); 402 delete o.clip; 403 } 404 } 405 break; 406 case "path": 407 if (o.type == "path") { 408 $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"}); 409 o._.dirty = 1; 410 if (o._.arrows) { 411 "startString" in o._.arrows && addArrow(o, o._.arrows.startString); 412 "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1); 413 } 414 } 415 break; 416 case "width": 417 node.setAttribute(att, value); 418 o._.dirty = 1; 419 if (attrs.fx) { 420 att = "x"; 421 value = attrs.x; 422 } else { 423 break; 424 } 425 case "x": 426 if (attrs.fx) { 427 value = -attrs.x - (attrs.width || 0); 428 } 429 case "rx": 430 if (att == "rx" && o.type == "rect") { 431 break; 432 } 433 case "cx": 434 node.setAttribute(att, value); 435 o.pattern && updatePosition(o); 436 o._.dirty = 1; 437 break; 438 case "height": 439 node.setAttribute(att, value); 440 o._.dirty = 1; 441 if (attrs.fy) { 442 att = "y"; 443 value = attrs.y; 444 } else { 445 break; 446 } 447 case "y": 448 if (attrs.fy) { 449 value = -attrs.y - (attrs.height || 0); 450 } 451 case "ry": 452 if (att == "ry" && o.type == "rect") { 453 break; 454 } 455 case "cy": 456 node.setAttribute(att, value); 457 o.pattern && updatePosition(o); 458 o._.dirty = 1; 459 break; 460 case "r": 461 if (o.type == "rect") { 462 $(node, {rx: value, ry: value}); 463 } else { 464 node.setAttribute(att, value); 465 } 466 o._.dirty = 1; 467 break; 468 case "src": 469 if (o.type == "image") { 470 node.setAttributeNS(xlink, "href", value); 471 } 472 break; 473 case "stroke-width": 474 if (o._.sx != 1 || o._.sy != 1) { 475 value /= mmax(abs(o._.sx), abs(o._.sy)) || 1; 476 } 477 node.setAttribute(att, value); 478 if (attrs["stroke-dasharray"]) { 479 addDashes(o, attrs["stroke-dasharray"], params); 480 } 481 if (o._.arrows) { 482 "startString" in o._.arrows && addArrow(o, o._.arrows.startString); 483 "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1); 484 } 485 break; 486 case "stroke-dasharray": 487 addDashes(o, value, params); 488 break; 489 case "fill": 490 var isURL = Str(value).match(R._ISURL); 491 if (isURL) { 492 el = $("pattern"); 493 var ig = $("image"); 494 el.id = R.createUUID(); 495 $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1}); 496 $(ig, {x: 0, y: 0, "xlink:href": isURL[1]}); 497 el.appendChild(ig); 498 499 (function (el) { 500 R._preload(isURL[1], function () { 501 var w = this.offsetWidth, 502 h = this.offsetHeight; 503 $(el, {width: w, height: h}); 504 $(ig, {width: w, height: h}); 505 }); 506 })(el); 507 o.paper.defs.appendChild(el); 508 $(node, {fill: "url(#" + el.id + ")"}); 509 o.pattern = el; 510 o.pattern && updatePosition(o); 511 break; 512 } 513 var clr = R.getRGB(value); 514 if (!clr.error) { 515 delete params.gradient; 516 delete attrs.gradient; 517 !R.is(attrs.opacity, "undefined") && 518 R.is(params.opacity, "undefined") && 519 $(node, {opacity: attrs.opacity}); 520 !R.is(attrs["fill-opacity"], "undefined") && 521 R.is(params["fill-opacity"], "undefined") && 522 $(node, {"fill-opacity": attrs["fill-opacity"]}); 523 } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) { 524 if ("opacity" in attrs || "fill-opacity" in attrs) { 525 var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E)); 526 if (gradient) { 527 var stops = gradient.getElementsByTagName("stop"); 528 $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)}); 529 } 530 } 531 attrs.gradient = value; 532 attrs.fill = "none"; 533 break; 534 } 535 clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity}); 536 case "stroke": 537 clr = R.getRGB(value); 538 node.setAttribute(att, clr.hex); 539 att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity}); 540 if (att == "stroke" && o._.arrows) { 541 "startString" in o._.arrows && addArrow(o, o._.arrows.startString); 542 "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1); 543 } 544 break; 545 case "gradient": 546 (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value); 547 break; 548 case "opacity": 549 if (attrs.gradient && !attrs[has]("stroke-opacity")) { 550 $(node, {"stroke-opacity": value > 1 ? value / 100 : value}); 551 } 552 // fall 553 case "fill-opacity": 554 if (attrs.gradient) { 555 gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E)); 556 if (gradient) { 557 stops = gradient.getElementsByTagName("stop"); 558 $(stops[stops.length - 1], {"stop-opacity": value}); 559 } 560 break; 561 } 562 default: 563 att == "font-size" && (value = toInt(value, 10) + "px"); 564 var cssrule = att.replace(/(\-.)/g, function (w) { 565 return w.substring(1).toUpperCase(); 566 }); 567 node.style[cssrule] = value; 568 o._.dirty = 1; 569 node.setAttribute(att, value); 570 break; 571 } 572 } 573 } 574 575 tuneText(o, params); 576 node.style.visibility = vis; 577 }, 578 leading = 1.2, 579 tuneText = function (el, params) { 580 if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) { 581 return; 582 } 583 var a = el.attrs, 584 node = el.node, 585 fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10; 586 587 if (params[has]("text")) { 588 a.text = params.text; 589 while (node.firstChild) { 590 node.removeChild(node.firstChild); 591 } 592 var texts = Str(params.text).split("\n"), 593 tspans = [], 594 tspan; 595 for (var i = 0, ii = texts.length; i < ii; i++) { 596 tspan = $("tspan"); 597 i && $(tspan, {dy: fontSize * leading, x: a.x}); 598 tspan.appendChild(R._g.doc.createTextNode(texts[i])); 599 node.appendChild(tspan); 600 tspans[i] = tspan; 601 } 602 } else { 603 tspans = node.getElementsByTagName("tspan"); 604 for (i = 0, ii = tspans.length; i < ii; i++) if (i) { 605 $(tspans[i], {dy: fontSize * leading, x: a.x}); 606 } else { 607 $(tspans[0], {dy: 0}); 608 } 609 } 610 $(node, {x: a.x, y: a.y}); 611 el._.dirty = 1; 612 var bb = el._getBBox(), 613 dif = a.y - (bb.y + bb.height / 2); 614 dif && R.is(dif, "finite") && $(tspans[0], {dy: dif}); 615 }, 616 getRealNode = function (node) { 617 if (node.parentNode && node.parentNode.tagName.toLowerCase() === "a") { 618 return node.parentNode; 619 } else { 620 return node; 621 } 622 }, 623 Element = function (node, svg) { 624 var X = 0, 625 Y = 0; 626 /*\ 627 * Element.node 628 [ property (object) ] 629 ** 630 * Gives you a reference to the DOM object, so you can assign event handlers or just mess around. 631 ** 632 * Note: Don’t mess with it. 633 > Usage 634 | // draw a circle at coordinate 10,10 with radius of 10 635 | var c = paper.circle(10, 10, 10); 636 | c.node.onclick = function () { 637 | c.attr("fill", "red"); 638 | }; 639 \*/ 640 this[0] = this.node = node; 641 /*\ 642 * Element.raphael 643 [ property (object) ] 644 ** 645 * Internal reference to @Raphael object. In case it is not available. 646 > Usage 647 | Raphael.el.red = function () { 648 | var hsb = this.paper.raphael.rgb2hsb(this.attr("fill")); 649 | hsb.h = 1; 650 | this.attr({fill: this.paper.raphael.hsb2rgb(hsb).hex}); 651 | } 652 \*/ 653 node.raphael = true; 654 /*\ 655 * Element.id 656 [ property (number) ] 657 ** 658 * Unique id of the element. Especially useful when you want to listen to events of the element, 659 * because all events are fired in format `<module>.<action>.<id>`. Also useful for @Paper.getById method. 660 \*/ 661 this.id = guid(); 662 node.raphaelid = this.id; 663 664 /** 665 * Method that returns a 5 letter/digit id, enough for 36^5 = 60466176 elements 666 * @returns {string} id 667 */ 668 function guid() { 669 return ("0000" + (Math.random()*Math.pow(36,5) << 0).toString(36)).slice(-5); 670 } 671 672 this.matrix = R.matrix(); 673 this.realPath = null; 674 /*\ 675 * Element.paper 676 [ property (object) ] 677 ** 678 * Internal reference to “paper” where object drawn. Mainly for use in plugins and element extensions. 679 > Usage 680 | Raphael.el.cross = function () { 681 | this.attr({fill: "red"}); 682 | this.paper.path("M10,10L50,50M50,10L10,50") 683 | .attr({stroke: "red"}); 684 | } 685 \*/ 686 this.paper = svg; 687 this.attrs = this.attrs || {}; 688 this._ = { 689 transform: [], 690 sx: 1, 691 sy: 1, 692 deg: 0, 693 dx: 0, 694 dy: 0, 695 dirty: 1 696 }; 697 !svg.bottom && (svg.bottom = this); 698 /*\ 699 * Element.prev 700 [ property (object) ] 701 ** 702 * Reference to the previous element in the hierarchy. 703 \*/ 704 this.prev = svg.top; 705 svg.top && (svg.top.next = this); 706 svg.top = this; 707 /*\ 708 * Element.next 709 [ property (object) ] 710 ** 711 * Reference to the next element in the hierarchy. 712 \*/ 713 this.next = null; 714 }, 715 elproto = R.el; 716 717 Element.prototype = elproto; 718 elproto.constructor = Element; 719 720 R._engine.path = function (pathString, SVG) { 721 var el = $("path"); 722 SVG.canvas && SVG.canvas.appendChild(el); 723 var p = new Element(el, SVG); 724 p.type = "path"; 725 setFillAndStroke(p, { 726 fill: "none", 727 stroke: "#000", 728 path: pathString 729 }); 730 return p; 731 }; 732 /*\ 733 * Element.rotate 734 [ method ] 735 ** 736 * Deprecated! Use @Element.transform instead. 737 * Adds rotation by given angle around given point to the list of 738 * transformations of the element. 739 > Parameters 740 - deg (number) angle in degrees 741 - cx (number) #optional x coordinate of the centre of rotation 742 - cy (number) #optional y coordinate of the centre of rotation 743 * If cx & cy aren’t specified centre of the shape is used as a point of rotation. 744 = (object) @Element 745 \*/ 746 elproto.rotate = function (deg, cx, cy) { 747 if (this.removed) { 748 return this; 749 } 750 deg = Str(deg).split(separator); 751 if (deg.length - 1) { 752 cx = toFloat(deg[1]); 753 cy = toFloat(deg[2]); 754 } 755 deg = toFloat(deg[0]); 756 (cy == null) && (cx = cy); 757 if (cx == null || cy == null) { 758 var bbox = this.getBBox(1); 759 cx = bbox.x + bbox.width / 2; 760 cy = bbox.y + bbox.height / 2; 761 } 762 this.transform(this._.transform.concat([["r", deg, cx, cy]])); 763 return this; 764 }; 765 /*\ 766 * Element.scale 767 [ method ] 768 ** 769 * Deprecated! Use @Element.transform instead. 770 * Adds scale by given amount relative to given point to the list of 771 * transformations of the element. 772 > Parameters 773 - sx (number) horisontal scale amount 774 - sy (number) vertical scale amount 775 - cx (number) #optional x coordinate of the centre of scale 776 - cy (number) #optional y coordinate of the centre of scale 777 * If cx & cy aren’t specified centre of the shape is used instead. 778 = (object) @Element 779 \*/ 780 elproto.scale = function (sx, sy, cx, cy) { 781 if (this.removed) { 782 return this; 783 } 784 sx = Str(sx).split(separator); 785 if (sx.length - 1) { 786 sy = toFloat(sx[1]); 787 cx = toFloat(sx[2]); 788 cy = toFloat(sx[3]); 789 } 790 sx = toFloat(sx[0]); 791 (sy == null) && (sy = sx); 792 (cy == null) && (cx = cy); 793 if (cx == null || cy == null) { 794 var bbox = this.getBBox(1); 795 } 796 cx = cx == null ? bbox.x + bbox.width / 2 : cx; 797 cy = cy == null ? bbox.y + bbox.height / 2 : cy; 798 this.transform(this._.transform.concat([["s", sx, sy, cx, cy]])); 799 return this; 800 }; 801 /*\ 802 * Element.translate 803 [ method ] 804 ** 805 * Deprecated! Use @Element.transform instead. 806 * Adds translation by given amount to the list of transformations of the element. 807 > Parameters 808 - dx (number) horisontal shift 809 - dy (number) vertical shift 810 = (object) @Element 811 \*/ 812 elproto.translate = function (dx, dy) { 813 if (this.removed) { 814 return this; 815 } 816 dx = Str(dx).split(separator); 817 if (dx.length - 1) { 818 dy = toFloat(dx[1]); 819 } 820 dx = toFloat(dx[0]) || 0; 821 dy = +dy || 0; 822 this.transform(this._.transform.concat([["t", dx, dy]])); 823 return this; 824 }; 825 /*\ 826 * Element.transform 827 [ method ] 828 ** 829 * Adds transformation to the element which is separate to other attributes, 830 * i.e. translation doesn’t change `x` or `y` of the rectange. The format 831 * of transformation string is similar to the path string syntax: 832 | "t100,100r30,100,100s2,2,100,100r45s1.5" 833 * Each letter is a command. There are four commands: `t` is for translate, `r` is for rotate, `s` is for 834 * scale and `m` is for matrix. 835 * 836 * There are also alternative “absolute” translation, rotation and scale: `T`, `R` and `S`. They will not take previous transformation into account. For example, `...T100,0` will always move element 100 px horisontally, while `...t100,0` could move it vertically if there is `r90` before. Just compare results of `r90t100,0` and `r90T100,0`. 837 * 838 * So, the example line above could be read like “translate by 100, 100; rotate 30° around 100, 100; scale twice around 100, 100; 839 * rotate 45° around centre; scale 1.5 times relative to centre”. As you can see rotate and scale commands have origin 840 * coordinates as optional parameters, the default is the centre point of the element. 841 * Matrix accepts six parameters. 842 > Usage 843 | var el = paper.rect(10, 20, 300, 200); 844 | // translate 100, 100, rotate 45°, translate -100, 0 845 | el.transform("t100,100r45t-100,0"); 846 | // if you want you can append or prepend transformations 847 | el.transform("...t50,50"); 848 | el.transform("s2..."); 849 | // or even wrap 850 | el.transform("t50,50...t-50-50"); 851 | // to reset transformation call method with empty string 852 | el.transform(""); 853 | // to get current value call it without parameters 854 | console.log(el.transform()); 855 > Parameters 856 - tstr (string) #optional transformation string 857 * If tstr isn’t specified 858 = (string) current transformation string 859 * else 860 = (object) @Element 861 \*/ 862 elproto.transform = function (tstr) { 863 var _ = this._; 864 if (tstr == null) { 865 return _.transform; 866 } 867 R._extractTransform(this, tstr); 868 869 this.clip && $(this.clip, {transform: this.matrix.invert()}); 870 this.pattern && updatePosition(this); 871 this.node && $(this.node, {transform: this.matrix}); 872 873 if (_.sx != 1 || _.sy != 1) { 874 var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1; 875 this.attr({"stroke-width": sw}); 876 } 877 878 return this; 879 }; 880 /*\ 881 * Element.hide 882 [ method ] 883 ** 884 * Makes element invisible. See @Element.show. 885 = (object) @Element 886 \*/ 887 elproto.hide = function () { 888 if(!this.removed) this.node.style.display = "none"; 889 return this; 890 }; 891 /*\ 892 * Element.show 893 [ method ] 894 ** 895 * Makes element visible. See @Element.hide. 896 = (object) @Element 897 \*/ 898 elproto.show = function () { 899 if(!this.removed) this.node.style.display = ""; 900 return this; 901 }; 902 /*\ 903 * Element.remove 904 [ method ] 905 ** 906 * Removes element from the paper. 907 \*/ 908 elproto.remove = function () { 909 var node = getRealNode(this.node); 910 if (this.removed || !node.parentNode) { 911 return; 912 } 913 var paper = this.paper; 914 paper.__set__ && paper.__set__.exclude(this); 915 eve.unbind("raphael.*.*." + this.id); 916 if (this.gradient) { 917 paper.defs.removeChild(this.gradient); 918 } 919 R._tear(this, paper); 920 921 node.parentNode.removeChild(node); 922 923 // Remove custom data for element 924 this.removeData(); 925 926 for (var i in this) { 927 this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null; 928 } 929 this.removed = true; 930 }; 931 elproto._getBBox = function () { 932 if (this.node.style.display == "none") { 933 this.show(); 934 var hide = true; 935 } 936 var canvasHidden = false, 937 containerStyle; 938 if (this.paper.canvas.parentElement) { 939 containerStyle = this.paper.canvas.parentElement.style; 940 } //IE10+ can't find parentElement 941 else if (this.paper.canvas.parentNode) { 942 containerStyle = this.paper.canvas.parentNode.style; 943 } 944 945 if(containerStyle && containerStyle.display == "none") { 946 canvasHidden = true; 947 containerStyle.display = ""; 948 } 949 var bbox = {}; 950 try { 951 bbox = this.node.getBBox(); 952 } catch(e) { 953 // Firefox 3.0.x, 25.0.1 (probably more versions affected) play badly here - possible fix 954 bbox = { 955 x: this.node.clientLeft, 956 y: this.node.clientTop, 957 width: this.node.clientWidth, 958 height: this.node.clientHeight 959 } 960 } finally { 961 bbox = bbox || {}; 962 if(canvasHidden){ 963 containerStyle.display = "none"; 964 } 965 } 966 hide && this.hide(); 967 return bbox; 968 }; 969 /*\ 970 * Element.attr 971 [ method ] 972 ** 973 * Sets the attributes of the element. 974 > Parameters 975 - attrName (string) attribute’s name 976 - value (string) value 977 * or 978 - params (object) object of name/value pairs 979 * or 980 - attrName (string) attribute’s name 981 * or 982 - attrNames (array) in this case method returns array of current values for given attribute names 983 = (object) @Element if attrsName & value or params are passed in. 984 = (...) value of the attribute if only attrsName is passed in. 985 = (array) array of values of the attribute if attrsNames is passed in. 986 = (object) object of attributes if nothing is passed in. 987 > Possible parameters 988 # <p>Please refer to the <a href="http://www.w3.org/TR/SVG/" title="The W3C Recommendation for the SVG language describes these properties in detail.">SVG specification</a> for an explanation of these parameters.</p> 989 o arrow-end (string) arrowhead on the end of the path. The format for string is `<type>[-<width>[-<length>]]`. Possible types: `classic`, `block`, `open`, `oval`, `diamond`, `none`, width: `wide`, `narrow`, `medium`, length: `long`, `short`, `midium`. 990 o clip-rect (string) comma or space separated values: x, y, width and height 991 o cursor (string) CSS type of the cursor 992 o cx (number) the x-axis coordinate of the center of the circle, or ellipse 993 o cy (number) the y-axis coordinate of the center of the circle, or ellipse 994 o fill (string) colour, gradient or image 995 o fill-opacity (number) 996 o font (string) 997 o font-family (string) 998 o font-size (number) font size in pixels 999 o font-weight (string) 1000 o height (number) 1001 o href (string) URL, if specified element behaves as hyperlink 1002 o opacity (number) 1003 o path (string) SVG path string format 1004 o r (number) radius of the circle, ellipse or rounded corner on the rect 1005 o rx (number) horisontal radius of the ellipse 1006 o ry (number) vertical radius of the ellipse 1007 o src (string) image URL, only works for @Element.image element 1008 o stroke (string) stroke colour 1009 o stroke-dasharray (string) [“”, “none”, “`-`”, “`.`”, “`-.`”, “`-..`”, “`. `”, “`- `”, “`--`”, “`- .`”, “`--.`”, “`--..`”] 1010 o stroke-linecap (string) [“`butt`”, “`square`”, “`round`”] 1011 o stroke-linejoin (string) [“`bevel`”, “`round`”, “`miter`”] 1012 o stroke-miterlimit (number) 1013 o stroke-opacity (number) 1014 o stroke-width (number) stroke width in pixels, default is '1' 1015 o target (string) used with href 1016 o text (string) contents of the text element. Use `\n` for multiline text 1017 o text-anchor (string) [“`start`”, “`middle`”, “`end`”], default is “`middle`” 1018 o title (string) will create tooltip with a given text 1019 o transform (string) see @Element.transform 1020 o width (number) 1021 o x (number) 1022 o y (number) 1023 > Gradients 1024 * Linear gradient format: “`‹angle›-‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`90-#fff-#000`” – 90° 1025 * gradient from white to black or “`0-#fff-#f00:20-#000`” – 0° gradient from white via red (at 20%) to black. 1026 * 1027 * radial gradient: “`r[(‹fx›, ‹fy›)]‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`r#fff-#000`” – 1028 * gradient from white to black or “`r(0.25, 0.75)#fff-#000`” – gradient from white to black with focus point 1029 * at 0.25, 0.75. Focus point coordinates are in 0..1 range. Radial gradients can only be applied to circles and ellipses. 1030 > Path String 1031 # <p>Please refer to <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path’s data attribute’s format are described in the SVG specification.">SVG documentation regarding path string</a>. Raphaël fully supports it.</p> 1032 > Colour Parsing 1033 # <ul> 1034 # <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li> 1035 # <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li> 1036 # <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li> 1037 # <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200, 100, 0)</code>”)</li> 1038 # <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%, 175%, 0%)</code>”)</li> 1039 # <li>rgba(•••, •••, •••, •••) — red, green and blue channels’ values: (“<code>rgba(200, 100, 0, .5)</code>”)</li> 1040 # <li>rgba(•••%, •••%, •••%, •••%) — same as above, but in %: (“<code>rgba(100%, 175%, 0%, 50%)</code>”)</li> 1041 # <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5, 0.25, 1)</code>”)</li> 1042 # <li>hsb(•••%, •••%, •••%) — same as above, but in %</li> 1043 # <li>hsba(•••, •••, •••, •••) — same as above, but with opacity</li> 1044 # <li>hsl(•••, •••, •••) — almost the same as hsb, see <a href="http://en.wikipedia.org/wiki/HSL_and_HSV" title="HSL and HSV - Wikipedia, the free encyclopedia">Wikipedia page</a></li> 1045 # <li>hsl(•••%, •••%, •••%) — same as above, but in %</li> 1046 # <li>hsla(•••, •••, •••, •••) — same as above, but with opacity</li> 1047 # <li>Optionally for hsb and hsl you could specify hue as a degree: “<code>hsl(240deg, 1, .5)</code>” or, if you want to go fancy, “<code>hsl(240°, 1, .5)</code>”</li> 1048 # </ul> 1049 \*/ 1050 elproto.attr = function (name, value) { 1051 if (this.removed) { 1052 return this; 1053 } 1054 if (name == null) { 1055 var res = {}; 1056 for (var a in this.attrs) if (this.attrs[has](a)) { 1057 res[a] = this.attrs[a]; 1058 } 1059 res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; 1060 res.transform = this._.transform; 1061 return res; 1062 } 1063 if (value == null && R.is(name, "string")) { 1064 if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) { 1065 return this.attrs.gradient; 1066 } 1067 if (name == "transform") { 1068 return this._.transform; 1069 } 1070 var names = name.split(separator), 1071 out = {}; 1072 for (var i = 0, ii = names.length; i < ii; i++) { 1073 name = names[i]; 1074 if (name in this.attrs) { 1075 out[name] = this.attrs[name]; 1076 } else if (R.is(this.paper.customAttributes[name], "function")) { 1077 out[name] = this.paper.customAttributes[name].def; 1078 } else { 1079 out[name] = R._availableAttrs[name]; 1080 } 1081 } 1082 return ii - 1 ? out : out[names[0]]; 1083 } 1084 if (value == null && R.is(name, "array")) { 1085 out = {}; 1086 for (i = 0, ii = name.length; i < ii; i++) { 1087 out[name[i]] = this.attr(name[i]); 1088 } 1089 return out; 1090 } 1091 if (value != null) { 1092 var params = {}; 1093 params[name] = value; 1094 } else if (name != null && R.is(name, "object")) { 1095 params = name; 1096 } 1097 for (var key in params) { 1098 eve("raphael.attr." + key + "." + this.id, this, params[key]); 1099 } 1100 for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) { 1101 var par = this.paper.customAttributes[key].apply(this, [].concat(params[key])); 1102 this.attrs[key] = params[key]; 1103 for (var subkey in par) if (par[has](subkey)) { 1104 params[subkey] = par[subkey]; 1105 } 1106 } 1107 setFillAndStroke(this, params); 1108 return this; 1109 }; 1110 /*\ 1111 * Element.toFront 1112 [ method ] 1113 ** 1114 * Moves the element so it is the closest to the viewer’s eyes, on top of other elements. 1115 = (object) @Element 1116 \*/ 1117 elproto.toFront = function () { 1118 if (this.removed) { 1119 return this; 1120 } 1121 var node = getRealNode(this.node); 1122 node.parentNode.appendChild(node); 1123 var svg = this.paper; 1124 svg.top != this && R._tofront(this, svg); 1125 return this; 1126 }; 1127 /*\ 1128 * Element.toBack 1129 [ method ] 1130 ** 1131 * Moves the element so it is the furthest from the viewer’s eyes, behind other elements. 1132 = (object) @Element 1133 \*/ 1134 elproto.toBack = function () { 1135 if (this.removed) { 1136 return this; 1137 } 1138 var node = getRealNode(this.node); 1139 var parentNode = node.parentNode; 1140 parentNode.insertBefore(node, parentNode.firstChild); 1141 R._toback(this, this.paper); 1142 var svg = this.paper; 1143 return this; 1144 }; 1145 /*\ 1146 * Element.insertAfter 1147 [ method ] 1148 ** 1149 * Inserts current object after the given one. 1150 = (object) @Element 1151 \*/ 1152 elproto.insertAfter = function (element) { 1153 if (this.removed || !element) { 1154 return this; 1155 } 1156 1157 var node = getRealNode(this.node); 1158 var afterNode = getRealNode(element.node || element[element.length - 1].node); 1159 if (afterNode.nextSibling) { 1160 afterNode.parentNode.insertBefore(node, afterNode.nextSibling); 1161 } else { 1162 afterNode.parentNode.appendChild(node); 1163 } 1164 R._insertafter(this, element, this.paper); 1165 return this; 1166 }; 1167 /*\ 1168 * Element.insertBefore 1169 [ method ] 1170 ** 1171 * Inserts current object before the given one. 1172 = (object) @Element 1173 \*/ 1174 elproto.insertBefore = function (element) { 1175 if (this.removed || !element) { 1176 return this; 1177 } 1178 1179 var node = getRealNode(this.node); 1180 var beforeNode = getRealNode(element.node || element[0].node); 1181 beforeNode.parentNode.insertBefore(node, beforeNode); 1182 R._insertbefore(this, element, this.paper); 1183 return this; 1184 }; 1185 elproto.blur = function (size) { 1186 // Experimental. No Safari support. Use it on your own risk. 1187 var t = this; 1188 if (+size !== 0) { 1189 var fltr = $("filter"), 1190 blur = $("feGaussianBlur"); 1191 t.attrs.blur = size; 1192 fltr.id = R.createUUID(); 1193 $(blur, {stdDeviation: +size || 1.5}); 1194 fltr.appendChild(blur); 1195 t.paper.defs.appendChild(fltr); 1196 t._blur = fltr; 1197 $(t.node, {filter: "url(#" + fltr.id + ")"}); 1198 } else { 1199 if (t._blur) { 1200 t._blur.parentNode.removeChild(t._blur); 1201 delete t._blur; 1202 delete t.attrs.blur; 1203 } 1204 t.node.removeAttribute("filter"); 1205 } 1206 return t; 1207 }; 1208 R._engine.circle = function (svg, x, y, r) { 1209 var el = $("circle"); 1210 svg.canvas && svg.canvas.appendChild(el); 1211 var res = new Element(el, svg); 1212 res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"}; 1213 res.type = "circle"; 1214 $(el, res.attrs); 1215 return res; 1216 }; 1217 R._engine.rect = function (svg, x, y, w, h, r) { 1218 var el = $("rect"); 1219 svg.canvas && svg.canvas.appendChild(el); 1220 var res = new Element(el, svg); 1221 res.attrs = {x: x, y: y, width: w, height: h, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"}; 1222 res.type = "rect"; 1223 $(el, res.attrs); 1224 return res; 1225 }; 1226 R._engine.ellipse = function (svg, x, y, rx, ry) { 1227 var el = $("ellipse"); 1228 svg.canvas && svg.canvas.appendChild(el); 1229 var res = new Element(el, svg); 1230 res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"}; 1231 res.type = "ellipse"; 1232 $(el, res.attrs); 1233 return res; 1234 }; 1235 R._engine.image = function (svg, src, x, y, w, h) { 1236 var el = $("image"); 1237 $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"}); 1238 el.setAttributeNS(xlink, "href", src); 1239 svg.canvas && svg.canvas.appendChild(el); 1240 var res = new Element(el, svg); 1241 res.attrs = {x: x, y: y, width: w, height: h, src: src}; 1242 res.type = "image"; 1243 return res; 1244 }; 1245 R._engine.text = function (svg, x, y, text) { 1246 var el = $("text"); 1247 svg.canvas && svg.canvas.appendChild(el); 1248 var res = new Element(el, svg); 1249 res.attrs = { 1250 x: x, 1251 y: y, 1252 "text-anchor": "middle", 1253 text: text, 1254 "font-family": R._availableAttrs["font-family"], 1255 "font-size": R._availableAttrs["font-size"], 1256 stroke: "none", 1257 fill: "#000" 1258 }; 1259 res.type = "text"; 1260 setFillAndStroke(res, res.attrs); 1261 return res; 1262 }; 1263 R._engine.setSize = function (width, height) { 1264 this.width = width || this.width; 1265 this.height = height || this.height; 1266 this.canvas.setAttribute("width", this.width); 1267 this.canvas.setAttribute("height", this.height); 1268 if (this._viewBox) { 1269 this.setViewBox.apply(this, this._viewBox); 1270 } 1271 return this; 1272 }; 1273 R._engine.create = function () { 1274 var con = R._getContainer.apply(0, arguments), 1275 container = con && con.container; 1276 if (!container) { 1277 throw new Error("SVG container not found."); 1278 } 1279 var x = con.x, 1280 y = con.y, 1281 width = con.width, 1282 height = con.height, 1283 cnvs = $("svg"), 1284 css = "overflow:hidden;", 1285 isFloating; 1286 x = x || 0; 1287 y = y || 0; 1288 width = width || 512; 1289 height = height || 342; 1290 $(cnvs, { 1291 height: height, 1292 version: 1.1, 1293 width: width, 1294 xmlns: "http://www.w3.org/2000/svg", 1295 "xmlns:xlink": "http://www.w3.org/1999/xlink" 1296 }); 1297 if (container == 1) { 1298 cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px"; 1299 R._g.doc.body.appendChild(cnvs); 1300 isFloating = 1; 1301 } else { 1302 cnvs.style.cssText = css + "position:relative"; 1303 if (container.firstChild) { 1304 container.insertBefore(cnvs, container.firstChild); 1305 } else { 1306 container.appendChild(cnvs); 1307 } 1308 } 1309 container = new R._Paper; 1310 container.width = width; 1311 container.height = height; 1312 container.canvas = cnvs; 1313 container.clear(); 1314 container._left = container._top = 0; 1315 isFloating && (container.renderfix = function () {}); 1316 container.renderfix(); 1317 return container; 1318 }; 1319 R._engine.setViewBox = function (x, y, w, h, fit) { 1320 eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]); 1321 var paperSize = this.getSize(), 1322 size = mmax(w / paperSize.width, h / paperSize.height), 1323 top = this.top, 1324 aspectRatio = fit ? "xMidYMid meet" : "xMinYMin", 1325 vb, 1326 sw; 1327 if (x == null) { 1328 if (this._vbSize) { 1329 size = 1; 1330 } 1331 delete this._vbSize; 1332 vb = "0 0 " + this.width + S + this.height; 1333 } else { 1334 this._vbSize = size; 1335 vb = x + S + y + S + w + S + h; 1336 } 1337 $(this.canvas, { 1338 viewBox: vb, 1339 preserveAspectRatio: aspectRatio 1340 }); 1341 while (size && top) { 1342 sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1; 1343 top.attr({"stroke-width": sw}); 1344 top._.dirty = 1; 1345 top._.dirtyT = 1; 1346 top = top.prev; 1347 } 1348 this._viewBox = [x, y, w, h, !!fit]; 1349 return this; 1350 }; 1351 /*\ 1352 * Paper.renderfix 1353 [ method ] 1354 ** 1355 * Fixes the issue of Firefox and IE9 regarding subpixel rendering. If paper is dependent 1356 * on other elements after reflow it could shift half pixel which cause for lines to lost their crispness. 1357 * This method fixes the issue. 1358 ** 1359 Special thanks to Mariusz Nowak (http://www.medikoo.com/) for this method. 1360 \*/ 1361 R.prototype.renderfix = function () { 1362 var cnvs = this.canvas, 1363 s = cnvs.style, 1364 pos; 1365 try { 1366 pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix(); 1367 } catch (e) { 1368 pos = cnvs.createSVGMatrix(); 1369 } 1370 var left = -pos.e % 1, 1371 top = -pos.f % 1; 1372 if (left || top) { 1373 if (left) { 1374 this._left = (this._left + left) % 1; 1375 s.left = this._left + "px"; 1376 } 1377 if (top) { 1378 this._top = (this._top + top) % 1; 1379 s.top = this._top + "px"; 1380 } 1381 } 1382 }; 1383 /*\ 1384 * Paper.clear 1385 [ method ] 1386 ** 1387 * Clears the paper, i.e. removes all the elements. 1388 \*/ 1389 R.prototype.clear = function () { 1390 R.eve("raphael.clear", this); 1391 var c = this.canvas; 1392 while (c.firstChild) { 1393 c.removeChild(c.firstChild); 1394 } 1395 this.bottom = this.top = null; 1396 (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version)); 1397 c.appendChild(this.desc); 1398 c.appendChild(this.defs = $("defs")); 1399 }; 1400 /*\ 1401 * Paper.remove 1402 [ method ] 1403 ** 1404 * Removes the paper from the DOM. 1405 \*/ 1406 R.prototype.remove = function () { 1407 eve("raphael.remove", this); 1408 this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); 1409 for (var i in this) { 1410 this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null; 1411 } 1412 }; 1413 var setproto = R.st; 1414 for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) { 1415 setproto[method] = (function (methodname) { 1416 return function () { 1417 var arg = arguments; 1418 return this.forEach(function (el) { 1419 el[methodname].apply(el, arg); 1420 }); 1421 }; 1422 })(method); 1423 } 1424}); 1425