1// Snap.svg 0.4.1 2// 3// Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved. 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16// 17// build: 2015-04-13 18 19// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 20// 21// Licensed under the Apache License, Version 2.0 (the "License"); 22// you may not use this file except in compliance with the License. 23// You may obtain a copy of the License at 24// 25// http://www.apache.org/licenses/LICENSE-2.0 26// 27// Unless required by applicable law or agreed to in writing, software 28// distributed under the License is distributed on an "AS IS" BASIS, 29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30// See the License for the specific language governing permissions and 31// limitations under the License. 32// ┌────────────────────────────────────────────────────────────┐ \\ 33// │ Eve 0.4.2 - JavaScript Events Library │ \\ 34// ├────────────────────────────────────────────────────────────┤ \\ 35// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\ 36// └────────────────────────────────────────────────────────────┘ \\ 37 38(function (glob) { 39 var version = "0.4.2", 40 has = "hasOwnProperty", 41 separator = /[\.\/]/, 42 comaseparator = /\s*,\s*/, 43 wildcard = "*", 44 fun = function () {}, 45 numsort = function (a, b) { 46 return a - b; 47 }, 48 current_event, 49 stop, 50 events = {n: {}}, 51 firstDefined = function () { 52 for (var i = 0, ii = this.length; i < ii; i++) { 53 if (typeof this[i] != "undefined") { 54 return this[i]; 55 } 56 } 57 }, 58 lastDefined = function () { 59 var i = this.length; 60 while (--i) { 61 if (typeof this[i] != "undefined") { 62 return this[i]; 63 } 64 } 65 }, 66 /*\ 67 * eve 68 [ method ] 69 70 * Fires event with given `name`, given scope and other parameters. 71 72 > Arguments 73 74 - name (string) name of the *event*, dot (`.`) or slash (`/`) separated 75 - scope (object) context for the event handlers 76 - varargs (...) the rest of arguments will be sent to event handlers 77 78 = (object) array of returned values from the listeners. Array has two methods `.firstDefined()` and `.lastDefined()` to get first or last not `undefined` value. 79 \*/ 80 eve = function (name, scope) { 81 name = String(name); 82 var e = events, 83 oldstop = stop, 84 args = Array.prototype.slice.call(arguments, 2), 85 listeners = eve.listeners(name), 86 z = 0, 87 f = false, 88 l, 89 indexed = [], 90 queue = {}, 91 out = [], 92 ce = current_event, 93 errors = []; 94 out.firstDefined = firstDefined; 95 out.lastDefined = lastDefined; 96 current_event = name; 97 stop = 0; 98 for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) { 99 indexed.push(listeners[i].zIndex); 100 if (listeners[i].zIndex < 0) { 101 queue[listeners[i].zIndex] = listeners[i]; 102 } 103 } 104 indexed.sort(numsort); 105 while (indexed[z] < 0) { 106 l = queue[indexed[z++]]; 107 out.push(l.apply(scope, args)); 108 if (stop) { 109 stop = oldstop; 110 return out; 111 } 112 } 113 for (i = 0; i < ii; i++) { 114 l = listeners[i]; 115 if ("zIndex" in l) { 116 if (l.zIndex == indexed[z]) { 117 out.push(l.apply(scope, args)); 118 if (stop) { 119 break; 120 } 121 do { 122 z++; 123 l = queue[indexed[z]]; 124 l && out.push(l.apply(scope, args)); 125 if (stop) { 126 break; 127 } 128 } while (l) 129 } else { 130 queue[l.zIndex] = l; 131 } 132 } else { 133 out.push(l.apply(scope, args)); 134 if (stop) { 135 break; 136 } 137 } 138 } 139 stop = oldstop; 140 current_event = ce; 141 return out; 142 }; 143 // Undocumented. Debug only. 144 eve._events = events; 145 /*\ 146 * eve.listeners 147 [ method ] 148 149 * Internal method which gives you array of all event handlers that will be triggered by the given `name`. 150 151 > Arguments 152 153 - name (string) name of the event, dot (`.`) or slash (`/`) separated 154 155 = (array) array of event handlers 156 \*/ 157 eve.listeners = function (name) { 158 var names = name.split(separator), 159 e = events, 160 item, 161 items, 162 k, 163 i, 164 ii, 165 j, 166 jj, 167 nes, 168 es = [e], 169 out = []; 170 for (i = 0, ii = names.length; i < ii; i++) { 171 nes = []; 172 for (j = 0, jj = es.length; j < jj; j++) { 173 e = es[j].n; 174 items = [e[names[i]], e[wildcard]]; 175 k = 2; 176 while (k--) { 177 item = items[k]; 178 if (item) { 179 nes.push(item); 180 out = out.concat(item.f || []); 181 } 182 } 183 } 184 es = nes; 185 } 186 return out; 187 }; 188 189 /*\ 190 * eve.on 191 [ method ] 192 ** 193 * Binds given event handler with a given name. You can use wildcards “`*`” for the names: 194 | eve.on("*.under.*", f); 195 | eve("mouse.under.floor"); // triggers f 196 * Use @eve to trigger the listener. 197 ** 198 > Arguments 199 ** 200 - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards 201 - f (function) event handler function 202 ** 203 = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. 204 > Example: 205 | eve.on("mouse", eatIt)(2); 206 | eve.on("mouse", scream); 207 | eve.on("mouse", catchIt)(1); 208 * This will ensure that `catchIt` function will be called before `eatIt`. 209 * 210 * If you want to put your handler before non-indexed handlers, specify a negative value. 211 * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”. 212 \*/ 213 eve.on = function (name, f) { 214 name = String(name); 215 if (typeof f != "function") { 216 return function () {}; 217 } 218 var names = name.split(comaseparator); 219 for (var i = 0, ii = names.length; i < ii; i++) { 220 (function (name) { 221 var names = name.split(separator), 222 e = events, 223 exist; 224 for (var i = 0, ii = names.length; i < ii; i++) { 225 e = e.n; 226 e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}}); 227 } 228 e.f = e.f || []; 229 for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) { 230 exist = true; 231 break; 232 } 233 !exist && e.f.push(f); 234 }(names[i])); 235 } 236 return function (zIndex) { 237 if (+zIndex == +zIndex) { 238 f.zIndex = +zIndex; 239 } 240 }; 241 }; 242 /*\ 243 * eve.f 244 [ method ] 245 ** 246 * Returns function that will fire given event with optional arguments. 247 * Arguments that will be passed to the result function will be also 248 * concated to the list of final arguments. 249 | el.onclick = eve.f("click", 1, 2); 250 | eve.on("click", function (a, b, c) { 251 | console.log(a, b, c); // 1, 2, [event object] 252 | }); 253 > Arguments 254 - event (string) event name 255 - varargs (…) and any other arguments 256 = (function) possible event handler function 257 \*/ 258 eve.f = function (event) { 259 var attrs = [].slice.call(arguments, 1); 260 return function () { 261 eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0))); 262 }; 263 }; 264 /*\ 265 * eve.stop 266 [ method ] 267 ** 268 * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing. 269 \*/ 270 eve.stop = function () { 271 stop = 1; 272 }; 273 /*\ 274 * eve.nt 275 [ method ] 276 ** 277 * Could be used inside event handler to figure out actual name of the event. 278 ** 279 > Arguments 280 ** 281 - subname (string) #optional subname of the event 282 ** 283 = (string) name of the event, if `subname` is not specified 284 * or 285 = (boolean) `true`, if current event’s name contains `subname` 286 \*/ 287 eve.nt = function (subname) { 288 if (subname) { 289 return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event); 290 } 291 return current_event; 292 }; 293 /*\ 294 * eve.nts 295 [ method ] 296 ** 297 * Could be used inside event handler to figure out actual name of the event. 298 ** 299 ** 300 = (array) names of the event 301 \*/ 302 eve.nts = function () { 303 return current_event.split(separator); 304 }; 305 /*\ 306 * eve.off 307 [ method ] 308 ** 309 * Removes given function from the list of event listeners assigned to given name. 310 * If no arguments specified all the events will be cleared. 311 ** 312 > Arguments 313 ** 314 - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards 315 - f (function) event handler function 316 \*/ 317 /*\ 318 * eve.unbind 319 [ method ] 320 ** 321 * See @eve.off 322 \*/ 323 eve.off = eve.unbind = function (name, f) { 324 if (!name) { 325 eve._events = events = {n: {}}; 326 return; 327 } 328 var names = name.split(comaseparator); 329 if (names.length > 1) { 330 for (var i = 0, ii = names.length; i < ii; i++) { 331 eve.off(names[i], f); 332 } 333 return; 334 } 335 names = name.split(separator); 336 var e, 337 key, 338 splice, 339 i, ii, j, jj, 340 cur = [events]; 341 for (i = 0, ii = names.length; i < ii; i++) { 342 for (j = 0; j < cur.length; j += splice.length - 2) { 343 splice = [j, 1]; 344 e = cur[j].n; 345 if (names[i] != wildcard) { 346 if (e[names[i]]) { 347 splice.push(e[names[i]]); 348 } 349 } else { 350 for (key in e) if (e[has](key)) { 351 splice.push(e[key]); 352 } 353 } 354 cur.splice.apply(cur, splice); 355 } 356 } 357 for (i = 0, ii = cur.length; i < ii; i++) { 358 e = cur[i]; 359 while (e.n) { 360 if (f) { 361 if (e.f) { 362 for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) { 363 e.f.splice(j, 1); 364 break; 365 } 366 !e.f.length && delete e.f; 367 } 368 for (key in e.n) if (e.n[has](key) && e.n[key].f) { 369 var funcs = e.n[key].f; 370 for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) { 371 funcs.splice(j, 1); 372 break; 373 } 374 !funcs.length && delete e.n[key].f; 375 } 376 } else { 377 delete e.f; 378 for (key in e.n) if (e.n[has](key) && e.n[key].f) { 379 delete e.n[key].f; 380 } 381 } 382 e = e.n; 383 } 384 } 385 }; 386 /*\ 387 * eve.once 388 [ method ] 389 ** 390 * Binds given event handler with a given name to only run once then unbind itself. 391 | eve.once("login", f); 392 | eve("login"); // triggers f 393 | eve("login"); // no listeners 394 * Use @eve to trigger the listener. 395 ** 396 > Arguments 397 ** 398 - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards 399 - f (function) event handler function 400 ** 401 = (function) same return function as @eve.on 402 \*/ 403 eve.once = function (name, f) { 404 var f2 = function () { 405 eve.unbind(name, f2); 406 return f.apply(this, arguments); 407 }; 408 return eve.on(name, f2); 409 }; 410 /*\ 411 * eve.version 412 [ property (string) ] 413 ** 414 * Current version of the library. 415 \*/ 416 eve.version = version; 417 eve.toString = function () { 418 return "You are running Eve " + version; 419 }; 420 (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define === "function" && define.amd ? (define("eve", [], function() { return eve; })) : (glob.eve = eve)); 421})(this); 422 423(function (glob, factory) { 424 // AMD support 425 if (typeof define == "function" && define.amd) { 426 // Define as an anonymous module 427 define(["eve"], function (eve) { 428 return factory(glob, eve); 429 }); 430 } else if (typeof exports != 'undefined') { 431 // Next for Node.js or CommonJS 432 var eve = require('eve'); 433 module.exports = factory(glob, eve); 434 } else { 435 // Browser globals (glob is window) 436 // Snap adds itself to window 437 factory(glob, glob.eve); 438 } 439}(window || this, function (window, eve) { 440 441// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 442// 443// Licensed under the Apache License, Version 2.0 (the "License"); 444// you may not use this file except in compliance with the License. 445// You may obtain a copy of the License at 446// 447// http://www.apache.org/licenses/LICENSE-2.0 448// 449// Unless required by applicable law or agreed to in writing, software 450// distributed under the License is distributed on an "AS IS" BASIS, 451// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 452// See the License for the specific language governing permissions and 453// limitations under the License. 454var mina = (function (eve) { 455 var animations = {}, 456 requestAnimFrame = window.requestAnimationFrame || 457 window.webkitRequestAnimationFrame || 458 window.mozRequestAnimationFrame || 459 window.oRequestAnimationFrame || 460 window.msRequestAnimationFrame || 461 function (callback) { 462 setTimeout(callback, 16); 463 }, 464 isArray = Array.isArray || function (a) { 465 return a instanceof Array || 466 Object.prototype.toString.call(a) == "[object Array]"; 467 }, 468 idgen = 0, 469 idprefix = "M" + (+new Date).toString(36), 470 ID = function () { 471 return idprefix + (idgen++).toString(36); 472 }, 473 diff = function (a, b, A, B) { 474 if (isArray(a)) { 475 res = []; 476 for (var i = 0, ii = a.length; i < ii; i++) { 477 res[i] = diff(a[i], b, A[i], B); 478 } 479 return res; 480 } 481 var dif = (A - a) / (B - b); 482 return function (bb) { 483 return a + dif * (bb - b); 484 }; 485 }, 486 timer = Date.now || function () { 487 return +new Date; 488 }, 489 sta = function (val) { 490 var a = this; 491 if (val == null) { 492 return a.s; 493 } 494 var ds = a.s - val; 495 a.b += a.dur * ds; 496 a.B += a.dur * ds; 497 a.s = val; 498 }, 499 speed = function (val) { 500 var a = this; 501 if (val == null) { 502 return a.spd; 503 } 504 a.spd = val; 505 }, 506 duration = function (val) { 507 var a = this; 508 if (val == null) { 509 return a.dur; 510 } 511 a.s = a.s * val / a.dur; 512 a.dur = val; 513 }, 514 stopit = function () { 515 var a = this; 516 delete animations[a.id]; 517 a.update(); 518 eve("mina.stop." + a.id, a); 519 }, 520 pause = function () { 521 var a = this; 522 if (a.pdif) { 523 return; 524 } 525 delete animations[a.id]; 526 a.update(); 527 a.pdif = a.get() - a.b; 528 }, 529 resume = function () { 530 var a = this; 531 if (!a.pdif) { 532 return; 533 } 534 a.b = a.get() - a.pdif; 535 delete a.pdif; 536 animations[a.id] = a; 537 }, 538 update = function () { 539 var a = this, 540 res; 541 if (isArray(a.start)) { 542 res = []; 543 for (var j = 0, jj = a.start.length; j < jj; j++) { 544 res[j] = +a.start[j] + 545 (a.end[j] - a.start[j]) * a.easing(a.s); 546 } 547 } else { 548 res = +a.start + (a.end - a.start) * a.easing(a.s); 549 } 550 a.set(res); 551 }, 552 frame = function () { 553 var len = 0; 554 for (var i in animations) if (animations.hasOwnProperty(i)) { 555 var a = animations[i], 556 b = a.get(), 557 res; 558 len++; 559 a.s = (b - a.b) / (a.dur / a.spd); 560 if (a.s >= 1) { 561 delete animations[i]; 562 a.s = 1; 563 len--; 564 (function (a) { 565 setTimeout(function () { 566 eve("mina.finish." + a.id, a); 567 }); 568 }(a)); 569 } 570 a.update(); 571 } 572 len && requestAnimFrame(frame); 573 }, 574 /*\ 575 * mina 576 [ method ] 577 ** 578 * Generic animation of numbers 579 ** 580 - a (number) start _slave_ number 581 - A (number) end _slave_ number 582 - b (number) start _master_ number (start time in general case) 583 - B (number) end _master_ number (end time in gereal case) 584 - get (function) getter of _master_ number (see @mina.time) 585 - set (function) setter of _slave_ number 586 - easing (function) #optional easing function, default is @mina.linear 587 = (object) animation descriptor 588 o { 589 o id (string) animation id, 590 o start (number) start _slave_ number, 591 o end (number) end _slave_ number, 592 o b (number) start _master_ number, 593 o s (number) animation status (0..1), 594 o dur (number) animation duration, 595 o spd (number) animation speed, 596 o get (function) getter of _master_ number (see @mina.time), 597 o set (function) setter of _slave_ number, 598 o easing (function) easing function, default is @mina.linear, 599 o status (function) status getter/setter, 600 o speed (function) speed getter/setter, 601 o duration (function) duration getter/setter, 602 o stop (function) animation stopper 603 o pause (function) pauses the animation 604 o resume (function) resumes the animation 605 o update (function) calles setter with the right value of the animation 606 o } 607 \*/ 608 mina = function (a, A, b, B, get, set, easing) { 609 var anim = { 610 id: ID(), 611 start: a, 612 end: A, 613 b: b, 614 s: 0, 615 dur: B - b, 616 spd: 1, 617 get: get, 618 set: set, 619 easing: easing || mina.linear, 620 status: sta, 621 speed: speed, 622 duration: duration, 623 stop: stopit, 624 pause: pause, 625 resume: resume, 626 update: update 627 }; 628 animations[anim.id] = anim; 629 var len = 0, i; 630 for (i in animations) if (animations.hasOwnProperty(i)) { 631 len++; 632 if (len == 2) { 633 break; 634 } 635 } 636 len == 1 && requestAnimFrame(frame); 637 return anim; 638 }; 639 /*\ 640 * mina.time 641 [ method ] 642 ** 643 * Returns the current time. Equivalent to: 644 | function () { 645 | return (new Date).getTime(); 646 | } 647 \*/ 648 mina.time = timer; 649 /*\ 650 * mina.getById 651 [ method ] 652 ** 653 * Returns an animation by its id 654 - id (string) animation's id 655 = (object) See @mina 656 \*/ 657 mina.getById = function (id) { 658 return animations[id] || null; 659 }; 660 661 /*\ 662 * mina.linear 663 [ method ] 664 ** 665 * Default linear easing 666 - n (number) input 0..1 667 = (number) output 0..1 668 \*/ 669 mina.linear = function (n) { 670 return n; 671 }; 672 /*\ 673 * mina.easeout 674 [ method ] 675 ** 676 * Easeout easing 677 - n (number) input 0..1 678 = (number) output 0..1 679 \*/ 680 mina.easeout = function (n) { 681 return Math.pow(n, 1.7); 682 }; 683 /*\ 684 * mina.easein 685 [ method ] 686 ** 687 * Easein easing 688 - n (number) input 0..1 689 = (number) output 0..1 690 \*/ 691 mina.easein = function (n) { 692 return Math.pow(n, .48); 693 }; 694 /*\ 695 * mina.easeinout 696 [ method ] 697 ** 698 * Easeinout easing 699 - n (number) input 0..1 700 = (number) output 0..1 701 \*/ 702 mina.easeinout = function (n) { 703 if (n == 1) { 704 return 1; 705 } 706 if (n == 0) { 707 return 0; 708 } 709 var q = .48 - n / 1.04, 710 Q = Math.sqrt(.1734 + q * q), 711 x = Q - q, 712 X = Math.pow(Math.abs(x), 1 / 3) * (x < 0 ? -1 : 1), 713 y = -Q - q, 714 Y = Math.pow(Math.abs(y), 1 / 3) * (y < 0 ? -1 : 1), 715 t = X + Y + .5; 716 return (1 - t) * 3 * t * t + t * t * t; 717 }; 718 /*\ 719 * mina.backin 720 [ method ] 721 ** 722 * Backin easing 723 - n (number) input 0..1 724 = (number) output 0..1 725 \*/ 726 mina.backin = function (n) { 727 if (n == 1) { 728 return 1; 729 } 730 var s = 1.70158; 731 return n * n * ((s + 1) * n - s); 732 }; 733 /*\ 734 * mina.backout 735 [ method ] 736 ** 737 * Backout easing 738 - n (number) input 0..1 739 = (number) output 0..1 740 \*/ 741 mina.backout = function (n) { 742 if (n == 0) { 743 return 0; 744 } 745 n = n - 1; 746 var s = 1.70158; 747 return n * n * ((s + 1) * n + s) + 1; 748 }; 749 /*\ 750 * mina.elastic 751 [ method ] 752 ** 753 * Elastic easing 754 - n (number) input 0..1 755 = (number) output 0..1 756 \*/ 757 mina.elastic = function (n) { 758 if (n == !!n) { 759 return n; 760 } 761 return Math.pow(2, -10 * n) * Math.sin((n - .075) * 762 (2 * Math.PI) / .3) + 1; 763 }; 764 /*\ 765 * mina.bounce 766 [ method ] 767 ** 768 * Bounce easing 769 - n (number) input 0..1 770 = (number) output 0..1 771 \*/ 772 mina.bounce = function (n) { 773 var s = 7.5625, 774 p = 2.75, 775 l; 776 if (n < (1 / p)) { 777 l = s * n * n; 778 } else { 779 if (n < (2 / p)) { 780 n -= (1.5 / p); 781 l = s * n * n + .75; 782 } else { 783 if (n < (2.5 / p)) { 784 n -= (2.25 / p); 785 l = s * n * n + .9375; 786 } else { 787 n -= (2.625 / p); 788 l = s * n * n + .984375; 789 } 790 } 791 } 792 return l; 793 }; 794 window.mina = mina; 795 return mina; 796})(typeof eve == "undefined" ? function () {} : eve); 797// Copyright (c) 2013 - 2015 Adobe Systems Incorporated. All rights reserved. 798// 799// Licensed under the Apache License, Version 2.0 (the "License"); 800// you may not use this file except in compliance with the License. 801// You may obtain a copy of the License at 802// 803// http://www.apache.org/licenses/LICENSE-2.0 804// 805// Unless required by applicable law or agreed to in writing, software 806// distributed under the License is distributed on an "AS IS" BASIS, 807// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 808// See the License for the specific language governing permissions and 809// limitations under the License. 810 811var Snap = (function(root) { 812Snap.version = "0.4.0"; 813/*\ 814 * Snap 815 [ method ] 816 ** 817 * Creates a drawing surface or wraps existing SVG element. 818 ** 819 - width (number|string) width of surface 820 - height (number|string) height of surface 821 * or 822 - DOM (SVGElement) element to be wrapped into Snap structure 823 * or 824 - array (array) array of elements (will return set of elements) 825 * or 826 - query (string) CSS query selector 827 = (object) @Element 828\*/ 829function Snap(w, h) { 830 if (w) { 831 if (w.nodeType) { 832 return wrap(w); 833 } 834 if (is(w, "array") && Snap.set) { 835 return Snap.set.apply(Snap, w); 836 } 837 if (w instanceof Element) { 838 return w; 839 } 840 if (h == null) { 841 w = glob.doc.querySelector(String(w)); 842 return wrap(w); 843 } 844 } 845 w = w == null ? "100%" : w; 846 h = h == null ? "100%" : h; 847 return new Paper(w, h); 848} 849Snap.toString = function () { 850 return "Snap v" + this.version; 851}; 852Snap._ = {}; 853var glob = { 854 win: root.window, 855 doc: root.window.document 856}; 857Snap._.glob = glob; 858var has = "hasOwnProperty", 859 Str = String, 860 toFloat = parseFloat, 861 toInt = parseInt, 862 math = Math, 863 mmax = math.max, 864 mmin = math.min, 865 abs = math.abs, 866 pow = math.pow, 867 PI = math.PI, 868 round = math.round, 869 E = "", 870 S = " ", 871 objectToString = Object.prototype.toString, 872 ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i, 873 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, 874 bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/, 875 reURLValue = /^url\(#?([^)]+)\)$/, 876 separator = Snap._.separator = /[,\s]+/, 877 whitespace = /[\s]/g, 878 commaSpaces = /[\s]*,[\s]*/, 879 hsrg = {hs: 1, rg: 1}, 880 pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig, 881 tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig, 882 pathValues = /(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/ig, 883 idgen = 0, 884 idprefix = "S" + (+new Date).toString(36), 885 ID = function (el) { 886 return (el && el.type ? el.type : E) + idprefix + (idgen++).toString(36); 887 }, 888 xlink = "http://www.w3.org/1999/xlink", 889 xmlns = "http://www.w3.org/2000/svg", 890 hub = {}, 891 URL = Snap.url = function (url) { 892 return "url('#" + url + "')"; 893 }; 894 895function $(el, attr) { 896 if (attr) { 897 if (el == "#text") { 898 el = glob.doc.createTextNode(attr.text || attr["#text"] || ""); 899 } 900 if (el == "#comment") { 901 el = glob.doc.createComment(attr.text || attr["#text"] || ""); 902 } 903 if (typeof el == "string") { 904 el = $(el); 905 } 906 if (typeof attr == "string") { 907 if (el.nodeType == 1) { 908 if (attr.substring(0, 6) == "xlink:") { 909 return el.getAttributeNS(xlink, attr.substring(6)); 910 } 911 if (attr.substring(0, 4) == "xml:") { 912 return el.getAttributeNS(xmlns, attr.substring(4)); 913 } 914 return el.getAttribute(attr); 915 } else if (attr == "text") { 916 return el.nodeValue; 917 } else { 918 return null; 919 } 920 } 921 if (el.nodeType == 1) { 922 for (var key in attr) if (attr[has](key)) { 923 var val = Str(attr[key]); 924 if (val) { 925 if (key.substring(0, 6) == "xlink:") { 926 el.setAttributeNS(xlink, key.substring(6), val); 927 } else if (key.substring(0, 4) == "xml:") { 928 el.setAttributeNS(xmlns, key.substring(4), val); 929 } else { 930 el.setAttribute(key, val); 931 } 932 } else { 933 el.removeAttribute(key); 934 } 935 } 936 } else if ("text" in attr) { 937 el.nodeValue = attr.text; 938 } 939 } else { 940 el = glob.doc.createElementNS(xmlns, el); 941 } 942 return el; 943} 944Snap._.$ = $; 945Snap._.id = ID; 946function getAttrs(el) { 947 var attrs = el.attributes, 948 name, 949 out = {}; 950 for (var i = 0; i < attrs.length; i++) { 951 if (attrs[i].namespaceURI == xlink) { 952 name = "xlink:"; 953 } else { 954 name = ""; 955 } 956 name += attrs[i].name; 957 out[name] = attrs[i].textContent; 958 } 959 return out; 960} 961function is(o, type) { 962 type = Str.prototype.toLowerCase.call(type); 963 if (type == "finite") { 964 return isFinite(o); 965 } 966 if (type == "array" && 967 (o instanceof Array || Array.isArray && Array.isArray(o))) { 968 return true; 969 } 970 return (type == "null" && o === null) || 971 (type == typeof o && o !== null) || 972 (type == "object" && o === Object(o)) || 973 objectToString.call(o).slice(8, -1).toLowerCase() == type; 974} 975/*\ 976 * Snap.format 977 [ method ] 978 ** 979 * Replaces construction of type `{<name>}` to the corresponding argument 980 ** 981 - token (string) string to format 982 - json (object) object which properties are used as a replacement 983 = (string) formatted string 984 > Usage 985 | // this draws a rectangular shape equivalent to "M10,20h40v50h-40z" 986 | paper.path(Snap.format("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", { 987 | x: 10, 988 | y: 20, 989 | dim: { 990 | width: 40, 991 | height: 50, 992 | "negative width": -40 993 | } 994 | })); 995\*/ 996Snap.format = (function () { 997 var tokenRegex = /\{([^\}]+)\}/g, 998 objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties 999 replacer = function (all, key, obj) { 1000 var res = obj; 1001 key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) { 1002 name = name || quotedName; 1003 if (res) { 1004 if (name in res) { 1005 res = res[name]; 1006 } 1007 typeof res == "function" && isFunc && (res = res()); 1008 } 1009 }); 1010 res = (res == null || res == obj ? all : res) + ""; 1011 return res; 1012 }; 1013 return function (str, obj) { 1014 return Str(str).replace(tokenRegex, function (all, key) { 1015 return replacer(all, key, obj); 1016 }); 1017 }; 1018})(); 1019function clone(obj) { 1020 if (typeof obj == "function" || Object(obj) !== obj) { 1021 return obj; 1022 } 1023 var res = new obj.constructor; 1024 for (var key in obj) if (obj[has](key)) { 1025 res[key] = clone(obj[key]); 1026 } 1027 return res; 1028} 1029Snap._.clone = clone; 1030function repush(array, item) { 1031 for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) { 1032 return array.push(array.splice(i, 1)[0]); 1033 } 1034} 1035function cacher(f, scope, postprocessor) { 1036 function newf() { 1037 var arg = Array.prototype.slice.call(arguments, 0), 1038 args = arg.join("\u2400"), 1039 cache = newf.cache = newf.cache || {}, 1040 count = newf.count = newf.count || []; 1041 if (cache[has](args)) { 1042 repush(count, args); 1043 return postprocessor ? postprocessor(cache[args]) : cache[args]; 1044 } 1045 count.length >= 1e3 && delete cache[count.shift()]; 1046 count.push(args); 1047 cache[args] = f.apply(scope, arg); 1048 return postprocessor ? postprocessor(cache[args]) : cache[args]; 1049 } 1050 return newf; 1051} 1052Snap._.cacher = cacher; 1053function angle(x1, y1, x2, y2, x3, y3) { 1054 if (x3 == null) { 1055 var x = x1 - x2, 1056 y = y1 - y2; 1057 if (!x && !y) { 1058 return 0; 1059 } 1060 return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360; 1061 } else { 1062 return angle(x1, y1, x3, y3) - angle(x2, y2, x3, y3); 1063 } 1064} 1065function rad(deg) { 1066 return deg % 360 * PI / 180; 1067} 1068function deg(rad) { 1069 return rad * 180 / PI % 360; 1070} 1071function x_y() { 1072 return this.x + S + this.y; 1073} 1074function x_y_w_h() { 1075 return this.x + S + this.y + S + this.width + " \xd7 " + this.height; 1076} 1077 1078/*\ 1079 * Snap.rad 1080 [ method ] 1081 ** 1082 * Transform angle to radians 1083 - deg (number) angle in degrees 1084 = (number) angle in radians 1085\*/ 1086Snap.rad = rad; 1087/*\ 1088 * Snap.deg 1089 [ method ] 1090 ** 1091 * Transform angle to degrees 1092 - rad (number) angle in radians 1093 = (number) angle in degrees 1094\*/ 1095Snap.deg = deg; 1096/*\ 1097 * Snap.sin 1098 [ method ] 1099 ** 1100 * Equivalent to `Math.sin()` only works with degrees, not radians. 1101 - angle (number) angle in degrees 1102 = (number) sin 1103\*/ 1104Snap.sin = function (angle) { 1105 return math.sin(Snap.rad(angle)); 1106}; 1107/*\ 1108 * Snap.tan 1109 [ method ] 1110 ** 1111 * Equivalent to `Math.tan()` only works with degrees, not radians. 1112 - angle (number) angle in degrees 1113 = (number) tan 1114\*/ 1115Snap.tan = function (angle) { 1116 return math.tan(Snap.rad(angle)); 1117}; 1118/*\ 1119 * Snap.cos 1120 [ method ] 1121 ** 1122 * Equivalent to `Math.cos()` only works with degrees, not radians. 1123 - angle (number) angle in degrees 1124 = (number) cos 1125\*/ 1126Snap.cos = function (angle) { 1127 return math.cos(Snap.rad(angle)); 1128}; 1129/*\ 1130 * Snap.asin 1131 [ method ] 1132 ** 1133 * Equivalent to `Math.asin()` only works with degrees, not radians. 1134 - num (number) value 1135 = (number) asin in degrees 1136\*/ 1137Snap.asin = function (num) { 1138 return Snap.deg(math.asin(num)); 1139}; 1140/*\ 1141 * Snap.acos 1142 [ method ] 1143 ** 1144 * Equivalent to `Math.acos()` only works with degrees, not radians. 1145 - num (number) value 1146 = (number) acos in degrees 1147\*/ 1148Snap.acos = function (num) { 1149 return Snap.deg(math.acos(num)); 1150}; 1151/*\ 1152 * Snap.atan 1153 [ method ] 1154 ** 1155 * Equivalent to `Math.atan()` only works with degrees, not radians. 1156 - num (number) value 1157 = (number) atan in degrees 1158\*/ 1159Snap.atan = function (num) { 1160 return Snap.deg(math.atan(num)); 1161}; 1162/*\ 1163 * Snap.atan2 1164 [ method ] 1165 ** 1166 * Equivalent to `Math.atan2()` only works with degrees, not radians. 1167 - num (number) value 1168 = (number) atan2 in degrees 1169\*/ 1170Snap.atan2 = function (num) { 1171 return Snap.deg(math.atan2(num)); 1172}; 1173/*\ 1174 * Snap.angle 1175 [ method ] 1176 ** 1177 * Returns an angle between two or three points 1178 > Parameters 1179 - x1 (number) x coord of first point 1180 - y1 (number) y coord of first point 1181 - x2 (number) x coord of second point 1182 - y2 (number) y coord of second point 1183 - x3 (number) #optional x coord of third point 1184 - y3 (number) #optional y coord of third point 1185 = (number) angle in degrees 1186\*/ 1187Snap.angle = angle; 1188/*\ 1189 * Snap.len 1190 [ method ] 1191 ** 1192 * Returns distance between two points 1193 > Parameters 1194 - x1 (number) x coord of first point 1195 - y1 (number) y coord of first point 1196 - x2 (number) x coord of second point 1197 - y2 (number) y coord of second point 1198 = (number) distance 1199\*/ 1200Snap.len = function (x1, y1, x2, y2) { 1201 return Math.sqrt(Snap.len2(x1, y1, x2, y2)); 1202}; 1203/*\ 1204 * Snap.len2 1205 [ method ] 1206 ** 1207 * Returns squared distance between two points 1208 > Parameters 1209 - x1 (number) x coord of first point 1210 - y1 (number) y coord of first point 1211 - x2 (number) x coord of second point 1212 - y2 (number) y coord of second point 1213 = (number) distance 1214\*/ 1215Snap.len2 = function (x1, y1, x2, y2) { 1216 return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); 1217}; 1218/*\ 1219 * Snap.closestPoint 1220 [ method ] 1221 ** 1222 * Returns closest point to a given one on a given path. 1223 > Parameters 1224 - path (Element) path element 1225 - x (number) x coord of a point 1226 - y (number) y coord of a point 1227 = (object) in format 1228 { 1229 x (number) x coord of the point on the path 1230 y (number) y coord of the point on the path 1231 length (number) length of the path to the point 1232 distance (number) distance from the given point to the path 1233 } 1234\*/ 1235// Copied from http://bl.ocks.org/mbostock/8027637 1236Snap.closestPoint = function (path, x, y) { 1237 function distance2(p) { 1238 var dx = p.x - x, 1239 dy = p.y - y; 1240 return dx * dx + dy * dy; 1241 } 1242 var pathNode = path.node, 1243 pathLength = pathNode.getTotalLength(), 1244 precision = pathLength / pathNode.pathSegList.numberOfItems * .125, 1245 best, 1246 bestLength, 1247 bestDistance = Infinity; 1248 1249 // linear scan for coarse approximation 1250 for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { 1251 if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { 1252 best = scan, bestLength = scanLength, bestDistance = scanDistance; 1253 } 1254 } 1255 1256 // binary search for precise estimate 1257 precision *= .5; 1258 while (precision > .5) { 1259 var before, 1260 after, 1261 beforeLength, 1262 afterLength, 1263 beforeDistance, 1264 afterDistance; 1265 if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) { 1266 best = before, bestLength = beforeLength, bestDistance = beforeDistance; 1267 } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) { 1268 best = after, bestLength = afterLength, bestDistance = afterDistance; 1269 } else { 1270 precision *= .5; 1271 } 1272 } 1273 1274 best = { 1275 x: best.x, 1276 y: best.y, 1277 length: bestLength, 1278 distance: Math.sqrt(bestDistance) 1279 }; 1280 return best; 1281} 1282/*\ 1283 * Snap.is 1284 [ method ] 1285 ** 1286 * Handy replacement for the `typeof` operator 1287 - o (…) any object or primitive 1288 - type (string) name of the type, e.g., `string`, `function`, `number`, etc. 1289 = (boolean) `true` if given value is of given type 1290\*/ 1291Snap.is = is; 1292/*\ 1293 * Snap.snapTo 1294 [ method ] 1295 ** 1296 * Snaps given value to given grid 1297 - values (array|number) given array of values or step of the grid 1298 - value (number) value to adjust 1299 - tolerance (number) #optional maximum distance to the target value that would trigger the snap. Default is `10`. 1300 = (number) adjusted value 1301\*/ 1302Snap.snapTo = function (values, value, tolerance) { 1303 tolerance = is(tolerance, "finite") ? tolerance : 10; 1304 if (is(values, "array")) { 1305 var i = values.length; 1306 while (i--) if (abs(values[i] - value) <= tolerance) { 1307 return values[i]; 1308 } 1309 } else { 1310 values = +values; 1311 var rem = value % values; 1312 if (rem < tolerance) { 1313 return value - rem; 1314 } 1315 if (rem > values - tolerance) { 1316 return value - rem + values; 1317 } 1318 } 1319 return value; 1320}; 1321// Colour 1322/*\ 1323 * Snap.getRGB 1324 [ method ] 1325 ** 1326 * Parses color string as RGB object 1327 - color (string) color string in one of the following formats: 1328 # <ul> 1329 # <li>Color name (<code>red</code>, <code>green</code>, <code>cornflowerblue</code>, etc)</li> 1330 # <li>#••• — shortened HTML color: (<code>#000</code>, <code>#fc0</code>, etc.)</li> 1331 # <li>#•••••• — full length HTML color: (<code>#000000</code>, <code>#bd2300</code>)</li> 1332 # <li>rgb(•••, •••, •••) — red, green and blue channels values: (<code>rgb(200, 100, 0)</code>)</li> 1333 # <li>rgba(•••, •••, •••, •••) — also with opacity</li> 1334 # <li>rgb(•••%, •••%, •••%) — same as above, but in %: (<code>rgb(100%, 175%, 0%)</code>)</li> 1335 # <li>rgba(•••%, •••%, •••%, •••%) — also with opacity</li> 1336 # <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (<code>hsb(0.5, 0.25, 1)</code>)</li> 1337 # <li>hsba(•••, •••, •••, •••) — also with opacity</li> 1338 # <li>hsb(•••%, •••%, •••%) — same as above, but in %</li> 1339 # <li>hsba(•••%, •••%, •••%, •••%) — also with opacity</li> 1340 # <li>hsl(•••, •••, •••) — hue, saturation and luminosity values: (<code>hsb(0.5, 0.25, 0.5)</code>)</li> 1341 # <li>hsla(•••, •••, •••, •••) — also with opacity</li> 1342 # <li>hsl(•••%, •••%, •••%) — same as above, but in %</li> 1343 # <li>hsla(•••%, •••%, •••%, •••%) — also with opacity</li> 1344 # </ul> 1345 * Note that `%` can be used any time: `rgb(20%, 255, 50%)`. 1346 = (object) RGB object in the following format: 1347 o { 1348 o r (number) red, 1349 o g (number) green, 1350 o b (number) blue, 1351 o hex (string) color in HTML/CSS format: #••••••, 1352 o error (boolean) true if string can't be parsed 1353 o } 1354\*/ 1355Snap.getRGB = cacher(function (colour) { 1356 if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) { 1357 return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString}; 1358 } 1359 if (colour == "none") { 1360 return {r: -1, g: -1, b: -1, hex: "none", toString: rgbtoString}; 1361 } 1362 !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour)); 1363 if (!colour) { 1364 return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString}; 1365 } 1366 var res, 1367 red, 1368 green, 1369 blue, 1370 opacity, 1371 t, 1372 values, 1373 rgb = colour.match(colourRegExp); 1374 if (rgb) { 1375 if (rgb[2]) { 1376 blue = toInt(rgb[2].substring(5), 16); 1377 green = toInt(rgb[2].substring(3, 5), 16); 1378 red = toInt(rgb[2].substring(1, 3), 16); 1379 } 1380 if (rgb[3]) { 1381 blue = toInt((t = rgb[3].charAt(3)) + t, 16); 1382 green = toInt((t = rgb[3].charAt(2)) + t, 16); 1383 red = toInt((t = rgb[3].charAt(1)) + t, 16); 1384 } 1385 if (rgb[4]) { 1386 values = rgb[4].split(commaSpaces); 1387 red = toFloat(values[0]); 1388 values[0].slice(-1) == "%" && (red *= 2.55); 1389 green = toFloat(values[1]); 1390 values[1].slice(-1) == "%" && (green *= 2.55); 1391 blue = toFloat(values[2]); 1392 values[2].slice(-1) == "%" && (blue *= 2.55); 1393 rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3])); 1394 values[3] && values[3].slice(-1) == "%" && (opacity /= 100); 1395 } 1396 if (rgb[5]) { 1397 values = rgb[5].split(commaSpaces); 1398 red = toFloat(values[0]); 1399 values[0].slice(-1) == "%" && (red /= 100); 1400 green = toFloat(values[1]); 1401 values[1].slice(-1) == "%" && (green /= 100); 1402 blue = toFloat(values[2]); 1403 values[2].slice(-1) == "%" && (blue /= 100); 1404 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360); 1405 rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3])); 1406 values[3] && values[3].slice(-1) == "%" && (opacity /= 100); 1407 return Snap.hsb2rgb(red, green, blue, opacity); 1408 } 1409 if (rgb[6]) { 1410 values = rgb[6].split(commaSpaces); 1411 red = toFloat(values[0]); 1412 values[0].slice(-1) == "%" && (red /= 100); 1413 green = toFloat(values[1]); 1414 values[1].slice(-1) == "%" && (green /= 100); 1415 blue = toFloat(values[2]); 1416 values[2].slice(-1) == "%" && (blue /= 100); 1417 (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360); 1418 rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3])); 1419 values[3] && values[3].slice(-1) == "%" && (opacity /= 100); 1420 return Snap.hsl2rgb(red, green, blue, opacity); 1421 } 1422 red = mmin(math.round(red), 255); 1423 green = mmin(math.round(green), 255); 1424 blue = mmin(math.round(blue), 255); 1425 opacity = mmin(mmax(opacity, 0), 1); 1426 rgb = {r: red, g: green, b: blue, toString: rgbtoString}; 1427 rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1); 1428 rgb.opacity = is(opacity, "finite") ? opacity : 1; 1429 return rgb; 1430 } 1431 return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString}; 1432}, Snap); 1433/*\ 1434 * Snap.hsb 1435 [ method ] 1436 ** 1437 * Converts HSB values to a hex representation of the color 1438 - h (number) hue 1439 - s (number) saturation 1440 - b (number) value or brightness 1441 = (string) hex representation of the color 1442\*/ 1443Snap.hsb = cacher(function (h, s, b) { 1444 return Snap.hsb2rgb(h, s, b).hex; 1445}); 1446/*\ 1447 * Snap.hsl 1448 [ method ] 1449 ** 1450 * Converts HSL values to a hex representation of the color 1451 - h (number) hue 1452 - s (number) saturation 1453 - l (number) luminosity 1454 = (string) hex representation of the color 1455\*/ 1456Snap.hsl = cacher(function (h, s, l) { 1457 return Snap.hsl2rgb(h, s, l).hex; 1458}); 1459/*\ 1460 * Snap.rgb 1461 [ method ] 1462 ** 1463 * Converts RGB values to a hex representation of the color 1464 - r (number) red 1465 - g (number) green 1466 - b (number) blue 1467 = (string) hex representation of the color 1468\*/ 1469Snap.rgb = cacher(function (r, g, b, o) { 1470 if (is(o, "finite")) { 1471 var round = math.round; 1472 return "rgba(" + [round(r), round(g), round(b), +o.toFixed(2)] + ")"; 1473 } 1474 return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1); 1475}); 1476var toHex = function (color) { 1477 var i = glob.doc.getElementsByTagName("head")[0] || glob.doc.getElementsByTagName("svg")[0], 1478 red = "rgb(255, 0, 0)"; 1479 toHex = cacher(function (color) { 1480 if (color.toLowerCase() == "red") { 1481 return red; 1482 } 1483 i.style.color = red; 1484 i.style.color = color; 1485 var out = glob.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color"); 1486 return out == red ? null : out; 1487 }); 1488 return toHex(color); 1489}, 1490hsbtoString = function () { 1491 return "hsb(" + [this.h, this.s, this.b] + ")"; 1492}, 1493hsltoString = function () { 1494 return "hsl(" + [this.h, this.s, this.l] + ")"; 1495}, 1496rgbtoString = function () { 1497 return this.opacity == 1 || this.opacity == null ? 1498 this.hex : 1499 "rgba(" + [this.r, this.g, this.b, this.opacity] + ")"; 1500}, 1501prepareRGB = function (r, g, b) { 1502 if (g == null && is(r, "object") && "r" in r && "g" in r && "b" in r) { 1503 b = r.b; 1504 g = r.g; 1505 r = r.r; 1506 } 1507 if (g == null && is(r, string)) { 1508 var clr = Snap.getRGB(r); 1509 r = clr.r; 1510 g = clr.g; 1511 b = clr.b; 1512 } 1513 if (r > 1 || g > 1 || b > 1) { 1514 r /= 255; 1515 g /= 255; 1516 b /= 255; 1517 } 1518 1519 return [r, g, b]; 1520}, 1521packageRGB = function (r, g, b, o) { 1522 r = math.round(r * 255); 1523 g = math.round(g * 255); 1524 b = math.round(b * 255); 1525 var rgb = { 1526 r: r, 1527 g: g, 1528 b: b, 1529 opacity: is(o, "finite") ? o : 1, 1530 hex: Snap.rgb(r, g, b), 1531 toString: rgbtoString 1532 }; 1533 is(o, "finite") && (rgb.opacity = o); 1534 return rgb; 1535}; 1536/*\ 1537 * Snap.color 1538 [ method ] 1539 ** 1540 * Parses the color string and returns an object featuring the color's component values 1541 - clr (string) color string in one of the supported formats (see @Snap.getRGB) 1542 = (object) Combined RGB/HSB object in the following format: 1543 o { 1544 o r (number) red, 1545 o g (number) green, 1546 o b (number) blue, 1547 o hex (string) color in HTML/CSS format: #••••••, 1548 o error (boolean) `true` if string can't be parsed, 1549 o h (number) hue, 1550 o s (number) saturation, 1551 o v (number) value (brightness), 1552 o l (number) lightness 1553 o } 1554\*/ 1555Snap.color = function (clr) { 1556 var rgb; 1557 if (is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) { 1558 rgb = Snap.hsb2rgb(clr); 1559 clr.r = rgb.r; 1560 clr.g = rgb.g; 1561 clr.b = rgb.b; 1562 clr.opacity = 1; 1563 clr.hex = rgb.hex; 1564 } else if (is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) { 1565 rgb = Snap.hsl2rgb(clr); 1566 clr.r = rgb.r; 1567 clr.g = rgb.g; 1568 clr.b = rgb.b; 1569 clr.opacity = 1; 1570 clr.hex = rgb.hex; 1571 } else { 1572 if (is(clr, "string")) { 1573 clr = Snap.getRGB(clr); 1574 } 1575 if (is(clr, "object") && "r" in clr && "g" in clr && "b" in clr && !("error" in clr)) { 1576 rgb = Snap.rgb2hsl(clr); 1577 clr.h = rgb.h; 1578 clr.s = rgb.s; 1579 clr.l = rgb.l; 1580 rgb = Snap.rgb2hsb(clr); 1581 clr.v = rgb.b; 1582 } else { 1583 clr = {hex: "none"}; 1584 clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1; 1585 clr.error = 1; 1586 } 1587 } 1588 clr.toString = rgbtoString; 1589 return clr; 1590}; 1591/*\ 1592 * Snap.hsb2rgb 1593 [ method ] 1594 ** 1595 * Converts HSB values to an RGB object 1596 - h (number) hue 1597 - s (number) saturation 1598 - v (number) value or brightness 1599 = (object) RGB object in the following format: 1600 o { 1601 o r (number) red, 1602 o g (number) green, 1603 o b (number) blue, 1604 o hex (string) color in HTML/CSS format: #•••••• 1605 o } 1606\*/ 1607Snap.hsb2rgb = function (h, s, v, o) { 1608 if (is(h, "object") && "h" in h && "s" in h && "b" in h) { 1609 v = h.b; 1610 s = h.s; 1611 o = h.o; 1612 h = h.h; 1613 } 1614 h *= 360; 1615 var R, G, B, X, C; 1616 h = (h % 360) / 60; 1617 C = v * s; 1618 X = C * (1 - abs(h % 2 - 1)); 1619 R = G = B = v - C; 1620 1621 h = ~~h; 1622 R += [C, X, 0, 0, X, C][h]; 1623 G += [X, C, C, X, 0, 0][h]; 1624 B += [0, 0, X, C, C, X][h]; 1625 return packageRGB(R, G, B, o); 1626}; 1627/*\ 1628 * Snap.hsl2rgb 1629 [ method ] 1630 ** 1631 * Converts HSL values to an RGB object 1632 - h (number) hue 1633 - s (number) saturation 1634 - l (number) luminosity 1635 = (object) RGB object in the following format: 1636 o { 1637 o r (number) red, 1638 o g (number) green, 1639 o b (number) blue, 1640 o hex (string) color in HTML/CSS format: #•••••• 1641 o } 1642\*/ 1643Snap.hsl2rgb = function (h, s, l, o) { 1644 if (is(h, "object") && "h" in h && "s" in h && "l" in h) { 1645 l = h.l; 1646 s = h.s; 1647 h = h.h; 1648 } 1649 if (h > 1 || s > 1 || l > 1) { 1650 h /= 360; 1651 s /= 100; 1652 l /= 100; 1653 } 1654 h *= 360; 1655 var R, G, B, X, C; 1656 h = (h % 360) / 60; 1657 C = 2 * s * (l < .5 ? l : 1 - l); 1658 X = C * (1 - abs(h % 2 - 1)); 1659 R = G = B = l - C / 2; 1660 1661 h = ~~h; 1662 R += [C, X, 0, 0, X, C][h]; 1663 G += [X, C, C, X, 0, 0][h]; 1664 B += [0, 0, X, C, C, X][h]; 1665 return packageRGB(R, G, B, o); 1666}; 1667/*\ 1668 * Snap.rgb2hsb 1669 [ method ] 1670 ** 1671 * Converts RGB values to an HSB object 1672 - r (number) red 1673 - g (number) green 1674 - b (number) blue 1675 = (object) HSB object in the following format: 1676 o { 1677 o h (number) hue, 1678 o s (number) saturation, 1679 o b (number) brightness 1680 o } 1681\*/ 1682Snap.rgb2hsb = function (r, g, b) { 1683 b = prepareRGB(r, g, b); 1684 r = b[0]; 1685 g = b[1]; 1686 b = b[2]; 1687 1688 var H, S, V, C; 1689 V = mmax(r, g, b); 1690 C = V - mmin(r, g, b); 1691 H = (C == 0 ? null : 1692 V == r ? (g - b) / C : 1693 V == g ? (b - r) / C + 2 : 1694 (r - g) / C + 4 1695 ); 1696 H = ((H + 360) % 6) * 60 / 360; 1697 S = C == 0 ? 0 : C / V; 1698 return {h: H, s: S, b: V, toString: hsbtoString}; 1699}; 1700/*\ 1701 * Snap.rgb2hsl 1702 [ method ] 1703 ** 1704 * Converts RGB values to an HSL object 1705 - r (number) red 1706 - g (number) green 1707 - b (number) blue 1708 = (object) HSL object in the following format: 1709 o { 1710 o h (number) hue, 1711 o s (number) saturation, 1712 o l (number) luminosity 1713 o } 1714\*/ 1715Snap.rgb2hsl = function (r, g, b) { 1716 b = prepareRGB(r, g, b); 1717 r = b[0]; 1718 g = b[1]; 1719 b = b[2]; 1720 1721 var H, S, L, M, m, C; 1722 M = mmax(r, g, b); 1723 m = mmin(r, g, b); 1724 C = M - m; 1725 H = (C == 0 ? null : 1726 M == r ? (g - b) / C : 1727 M == g ? (b - r) / C + 2 : 1728 (r - g) / C + 4); 1729 H = ((H + 360) % 6) * 60 / 360; 1730 L = (M + m) / 2; 1731 S = (C == 0 ? 0 : 1732 L < .5 ? C / (2 * L) : 1733 C / (2 - 2 * L)); 1734 return {h: H, s: S, l: L, toString: hsltoString}; 1735}; 1736 1737// Transformations 1738/*\ 1739 * Snap.parsePathString 1740 [ method ] 1741 ** 1742 * Utility method 1743 ** 1744 * Parses given path string into an array of arrays of path segments 1745 - pathString (string|array) path string or array of segments (in the last case it is returned straight away) 1746 = (array) array of segments 1747\*/ 1748Snap.parsePathString = function (pathString) { 1749 if (!pathString) { 1750 return null; 1751 } 1752 var pth = Snap.path(pathString); 1753 if (pth.arr) { 1754 return Snap.path.clone(pth.arr); 1755 } 1756 1757 var paramCounts = {a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0}, 1758 data = []; 1759 if (is(pathString, "array") && is(pathString[0], "array")) { // rough assumption 1760 data = Snap.path.clone(pathString); 1761 } 1762 if (!data.length) { 1763 Str(pathString).replace(pathCommand, function (a, b, c) { 1764 var params = [], 1765 name = b.toLowerCase(); 1766 c.replace(pathValues, function (a, b) { 1767 b && params.push(+b); 1768 }); 1769 if (name == "m" && params.length > 2) { 1770 data.push([b].concat(params.splice(0, 2))); 1771 name = "l"; 1772 b = b == "m" ? "l" : "L"; 1773 } 1774 if (name == "o" && params.length == 1) { 1775 data.push([b, params[0]]); 1776 } 1777 if (name == "r") { 1778 data.push([b].concat(params)); 1779 } else while (params.length >= paramCounts[name]) { 1780 data.push([b].concat(params.splice(0, paramCounts[name]))); 1781 if (!paramCounts[name]) { 1782 break; 1783 } 1784 } 1785 }); 1786 } 1787 data.toString = Snap.path.toString; 1788 pth.arr = Snap.path.clone(data); 1789 return data; 1790}; 1791/*\ 1792 * Snap.parseTransformString 1793 [ method ] 1794 ** 1795 * Utility method 1796 ** 1797 * Parses given transform string into an array of transformations 1798 - TString (string|array) transform string or array of transformations (in the last case it is returned straight away) 1799 = (array) array of transformations 1800\*/ 1801var parseTransformString = Snap.parseTransformString = function (TString) { 1802 if (!TString) { 1803 return null; 1804 } 1805 var paramCounts = {r: 3, s: 4, t: 2, m: 6}, 1806 data = []; 1807 if (is(TString, "array") && is(TString[0], "array")) { // rough assumption 1808 data = Snap.path.clone(TString); 1809 } 1810 if (!data.length) { 1811 Str(TString).replace(tCommand, function (a, b, c) { 1812 var params = [], 1813 name = b.toLowerCase(); 1814 c.replace(pathValues, function (a, b) { 1815 b && params.push(+b); 1816 }); 1817 data.push([b].concat(params)); 1818 }); 1819 } 1820 data.toString = Snap.path.toString; 1821 return data; 1822}; 1823function svgTransform2string(tstr) { 1824 var res = []; 1825 tstr = tstr.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g, function (all, name, params) { 1826 params = params.split(/\s*,\s*|\s+/); 1827 if (name == "rotate" && params.length == 1) { 1828 params.push(0, 0); 1829 } 1830 if (name == "scale") { 1831 if (params.length > 2) { 1832 params = params.slice(0, 2); 1833 } else if (params.length == 2) { 1834 params.push(0, 0); 1835 } 1836 if (params.length == 1) { 1837 params.push(params[0], 0, 0); 1838 } 1839 } 1840 if (name == "skewX") { 1841 res.push(["m", 1, 0, math.tan(rad(params[0])), 1, 0, 0]); 1842 } else if (name == "skewY") { 1843 res.push(["m", 1, math.tan(rad(params[0])), 0, 1, 0, 0]); 1844 } else { 1845 res.push([name.charAt(0)].concat(params)); 1846 } 1847 return all; 1848 }); 1849 return res; 1850} 1851Snap._.svgTransform2string = svgTransform2string; 1852Snap._.rgTransform = /^[a-z][\s]*-?\.?\d/i; 1853function transform2matrix(tstr, bbox) { 1854 var tdata = parseTransformString(tstr), 1855 m = new Snap.Matrix; 1856 if (tdata) { 1857 for (var i = 0, ii = tdata.length; i < ii; i++) { 1858 var t = tdata[i], 1859 tlen = t.length, 1860 command = Str(t[0]).toLowerCase(), 1861 absolute = t[0] != command, 1862 inver = absolute ? m.invert() : 0, 1863 x1, 1864 y1, 1865 x2, 1866 y2, 1867 bb; 1868 if (command == "t" && tlen == 2){ 1869 m.translate(t[1], 0); 1870 } else if (command == "t" && tlen == 3) { 1871 if (absolute) { 1872 x1 = inver.x(0, 0); 1873 y1 = inver.y(0, 0); 1874 x2 = inver.x(t[1], t[2]); 1875 y2 = inver.y(t[1], t[2]); 1876 m.translate(x2 - x1, y2 - y1); 1877 } else { 1878 m.translate(t[1], t[2]); 1879 } 1880 } else if (command == "r") { 1881 if (tlen == 2) { 1882 bb = bb || bbox; 1883 m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2); 1884 } else if (tlen == 4) { 1885 if (absolute) { 1886 x2 = inver.x(t[2], t[3]); 1887 y2 = inver.y(t[2], t[3]); 1888 m.rotate(t[1], x2, y2); 1889 } else { 1890 m.rotate(t[1], t[2], t[3]); 1891 } 1892 } 1893 } else if (command == "s") { 1894 if (tlen == 2 || tlen == 3) { 1895 bb = bb || bbox; 1896 m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2); 1897 } else if (tlen == 4) { 1898 if (absolute) { 1899 x2 = inver.x(t[2], t[3]); 1900 y2 = inver.y(t[2], t[3]); 1901 m.scale(t[1], t[1], x2, y2); 1902 } else { 1903 m.scale(t[1], t[1], t[2], t[3]); 1904 } 1905 } else if (tlen == 5) { 1906 if (absolute) { 1907 x2 = inver.x(t[3], t[4]); 1908 y2 = inver.y(t[3], t[4]); 1909 m.scale(t[1], t[2], x2, y2); 1910 } else { 1911 m.scale(t[1], t[2], t[3], t[4]); 1912 } 1913 } 1914 } else if (command == "m" && tlen == 7) { 1915 m.add(t[1], t[2], t[3], t[4], t[5], t[6]); 1916 } 1917 } 1918 } 1919 return m; 1920} 1921Snap._.transform2matrix = transform2matrix; 1922Snap._unit2px = unit2px; 1923var contains = glob.doc.contains || glob.doc.compareDocumentPosition ? 1924 function (a, b) { 1925 var adown = a.nodeType == 9 ? a.documentElement : a, 1926 bup = b && b.parentNode; 1927 return a == bup || !!(bup && bup.nodeType == 1 && ( 1928 adown.contains ? 1929 adown.contains(bup) : 1930 a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 1931 )); 1932 } : 1933 function (a, b) { 1934 if (b) { 1935 while (b) { 1936 b = b.parentNode; 1937 if (b == a) { 1938 return true; 1939 } 1940 } 1941 } 1942 return false; 1943 }; 1944function getSomeDefs(el) { 1945 var p = (el.node.ownerSVGElement && wrap(el.node.ownerSVGElement)) || 1946 (el.node.parentNode && wrap(el.node.parentNode)) || 1947 Snap.select("svg") || 1948 Snap(0, 0), 1949 pdefs = p.select("defs"), 1950 defs = pdefs == null ? false : pdefs.node; 1951 if (!defs) { 1952 defs = make("defs", p.node).node; 1953 } 1954 return defs; 1955} 1956function getSomeSVG(el) { 1957 return el.node.ownerSVGElement && wrap(el.node.ownerSVGElement) || Snap.select("svg"); 1958} 1959Snap._.getSomeDefs = getSomeDefs; 1960Snap._.getSomeSVG = getSomeSVG; 1961function unit2px(el, name, value) { 1962 var svg = getSomeSVG(el).node, 1963 out = {}, 1964 mgr = svg.querySelector(".svg---mgr"); 1965 if (!mgr) { 1966 mgr = $("rect"); 1967 $(mgr, {x: -9e9, y: -9e9, width: 10, height: 10, "class": "svg---mgr", fill: "none"}); 1968 svg.appendChild(mgr); 1969 } 1970 function getW(val) { 1971 if (val == null) { 1972 return E; 1973 } 1974 if (val == +val) { 1975 return val; 1976 } 1977 $(mgr, {width: val}); 1978 try { 1979 return mgr.getBBox().width; 1980 } catch (e) { 1981 return 0; 1982 } 1983 } 1984 function getH(val) { 1985 if (val == null) { 1986 return E; 1987 } 1988 if (val == +val) { 1989 return val; 1990 } 1991 $(mgr, {height: val}); 1992 try { 1993 return mgr.getBBox().height; 1994 } catch (e) { 1995 return 0; 1996 } 1997 } 1998 function set(nam, f) { 1999 if (name == null) { 2000 out[nam] = f(el.attr(nam) || 0); 2001 } else if (nam == name) { 2002 out = f(value == null ? el.attr(nam) || 0 : value); 2003 } 2004 } 2005 switch (el.type) { 2006 case "rect": 2007 set("rx", getW); 2008 set("ry", getH); 2009 case "image": 2010 set("width", getW); 2011 set("height", getH); 2012 case "text": 2013 set("x", getW); 2014 set("y", getH); 2015 break; 2016 case "circle": 2017 set("cx", getW); 2018 set("cy", getH); 2019 set("r", getW); 2020 break; 2021 case "ellipse": 2022 set("cx", getW); 2023 set("cy", getH); 2024 set("rx", getW); 2025 set("ry", getH); 2026 break; 2027 case "line": 2028 set("x1", getW); 2029 set("x2", getW); 2030 set("y1", getH); 2031 set("y2", getH); 2032 break; 2033 case "marker": 2034 set("refX", getW); 2035 set("markerWidth", getW); 2036 set("refY", getH); 2037 set("markerHeight", getH); 2038 break; 2039 case "radialGradient": 2040 set("fx", getW); 2041 set("fy", getH); 2042 break; 2043 case "tspan": 2044 set("dx", getW); 2045 set("dy", getH); 2046 break; 2047 default: 2048 set(name, getW); 2049 } 2050 svg.removeChild(mgr); 2051 return out; 2052} 2053/*\ 2054 * Snap.select 2055 [ method ] 2056 ** 2057 * Wraps a DOM element specified by CSS selector as @Element 2058 - query (string) CSS selector of the element 2059 = (Element) the current element 2060\*/ 2061Snap.select = function (query) { 2062 query = Str(query).replace(/([^\\]):/g, "$1\\:"); 2063 return wrap(glob.doc.querySelector(query)); 2064}; 2065/*\ 2066 * Snap.selectAll 2067 [ method ] 2068 ** 2069 * Wraps DOM elements specified by CSS selector as set or array of @Element 2070 - query (string) CSS selector of the element 2071 = (Element) the current element 2072\*/ 2073Snap.selectAll = function (query) { 2074 var nodelist = glob.doc.querySelectorAll(query), 2075 set = (Snap.set || Array)(); 2076 for (var i = 0; i < nodelist.length; i++) { 2077 set.push(wrap(nodelist[i])); 2078 } 2079 return set; 2080}; 2081 2082function add2group(list) { 2083 if (!is(list, "array")) { 2084 list = Array.prototype.slice.call(arguments, 0); 2085 } 2086 var i = 0, 2087 j = 0, 2088 node = this.node; 2089 while (this[i]) delete this[i++]; 2090 for (i = 0; i < list.length; i++) { 2091 if (list[i].type == "set") { 2092 list[i].forEach(function (el) { 2093 node.appendChild(el.node); 2094 }); 2095 } else { 2096 node.appendChild(list[i].node); 2097 } 2098 } 2099 var children = node.childNodes; 2100 for (i = 0; i < children.length; i++) { 2101 this[j++] = wrap(children[i]); 2102 } 2103 return this; 2104} 2105// Hub garbage collector every 10s 2106setInterval(function () { 2107 for (var key in hub) if (hub[has](key)) { 2108 var el = hub[key], 2109 node = el.node; 2110 if (el.type != "svg" && !node.ownerSVGElement || el.type == "svg" && (!node.parentNode || "ownerSVGElement" in node.parentNode && !node.ownerSVGElement)) { 2111 delete hub[key]; 2112 } 2113 } 2114}, 1e4); 2115function Element(el) { 2116 if (el.snap in hub) { 2117 return hub[el.snap]; 2118 } 2119 var svg; 2120 try { 2121 svg = el.ownerSVGElement; 2122 } catch(e) {} 2123 /*\ 2124 * Element.node 2125 [ property (object) ] 2126 ** 2127 * Gives you a reference to the DOM object, so you can assign event handlers or just mess around. 2128 > Usage 2129 | // draw a circle at coordinate 10,10 with radius of 10 2130 | var c = paper.circle(10, 10, 10); 2131 | c.node.onclick = function () { 2132 | c.attr("fill", "red"); 2133 | }; 2134 \*/ 2135 this.node = el; 2136 if (svg) { 2137 this.paper = new Paper(svg); 2138 } 2139 /*\ 2140 * Element.type 2141 [ property (string) ] 2142 ** 2143 * SVG tag name of the given element. 2144 \*/ 2145 this.type = el.tagName || el.nodeName; 2146 var id = this.id = ID(this); 2147 this.anims = {}; 2148 this._ = { 2149 transform: [] 2150 }; 2151 el.snap = id; 2152 hub[id] = this; 2153 if (this.type == "g") { 2154 this.add = add2group; 2155 } 2156 if (this.type in {g: 1, mask: 1, pattern: 1, symbol: 1}) { 2157 for (var method in Paper.prototype) if (Paper.prototype[has](method)) { 2158 this[method] = Paper.prototype[method]; 2159 } 2160 } 2161} 2162 /*\ 2163 * Element.attr 2164 [ method ] 2165 ** 2166 * Gets or sets given attributes of the element. 2167 ** 2168 - params (object) contains key-value pairs of attributes you want to set 2169 * or 2170 - param (string) name of the attribute 2171 = (Element) the current element 2172 * or 2173 = (string) value of attribute 2174 > Usage 2175 | el.attr({ 2176 | fill: "#fc0", 2177 | stroke: "#000", 2178 | strokeWidth: 2, // CamelCase... 2179 | "fill-opacity": 0.5, // or dash-separated names 2180 | width: "*=2" // prefixed values 2181 | }); 2182 | console.log(el.attr("fill")); // #fc0 2183 * Prefixed values in format `"+=10"` supported. All four operations 2184 * (`+`, `-`, `*` and `/`) could be used. Optionally you can use units for `+` 2185 * and `-`: `"+=2em"`. 2186 \*/ 2187 Element.prototype.attr = function (params, value) { 2188 var el = this, 2189 node = el.node; 2190 if (!params) { 2191 if (node.nodeType != 1) { 2192 return { 2193 text: node.nodeValue 2194 }; 2195 } 2196 var attr = node.attributes, 2197 out = {}; 2198 for (var i = 0, ii = attr.length; i < ii; i++) { 2199 out[attr[i].nodeName] = attr[i].nodeValue; 2200 } 2201 return out; 2202 } 2203 if (is(params, "string")) { 2204 if (arguments.length > 1) { 2205 var json = {}; 2206 json[params] = value; 2207 params = json; 2208 } else { 2209 return eve("snap.util.getattr." + params, el).firstDefined(); 2210 } 2211 } 2212 for (var att in params) { 2213 if (params[has](att)) { 2214 eve("snap.util.attr." + att, el, params[att]); 2215 } 2216 } 2217 return el; 2218 }; 2219/*\ 2220 * Snap.parse 2221 [ method ] 2222 ** 2223 * Parses SVG fragment and converts it into a @Fragment 2224 ** 2225 - svg (string) SVG string 2226 = (Fragment) the @Fragment 2227\*/ 2228Snap.parse = function (svg) { 2229 var f = glob.doc.createDocumentFragment(), 2230 full = true, 2231 div = glob.doc.createElement("div"); 2232 svg = Str(svg); 2233 if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) { 2234 svg = "<svg>" + svg + "</svg>"; 2235 full = false; 2236 } 2237 div.innerHTML = svg; 2238 svg = div.getElementsByTagName("svg")[0]; 2239 if (svg) { 2240 if (full) { 2241 f = svg; 2242 } else { 2243 while (svg.firstChild) { 2244 f.appendChild(svg.firstChild); 2245 } 2246 } 2247 } 2248 return new Fragment(f); 2249}; 2250function Fragment(frag) { 2251 this.node = frag; 2252} 2253/*\ 2254 * Snap.fragment 2255 [ method ] 2256 ** 2257 * Creates a DOM fragment from a given list of elements or strings 2258 ** 2259 - varargs (…) SVG string 2260 = (Fragment) the @Fragment 2261\*/ 2262Snap.fragment = function () { 2263 var args = Array.prototype.slice.call(arguments, 0), 2264 f = glob.doc.createDocumentFragment(); 2265 for (var i = 0, ii = args.length; i < ii; i++) { 2266 var item = args[i]; 2267 if (item.node && item.node.nodeType) { 2268 f.appendChild(item.node); 2269 } 2270 if (item.nodeType) { 2271 f.appendChild(item); 2272 } 2273 if (typeof item == "string") { 2274 f.appendChild(Snap.parse(item).node); 2275 } 2276 } 2277 return new Fragment(f); 2278}; 2279 2280function make(name, parent) { 2281 var res = $(name); 2282 parent.appendChild(res); 2283 var el = wrap(res); 2284 return el; 2285} 2286function Paper(w, h) { 2287 var res, 2288 desc, 2289 defs, 2290 proto = Paper.prototype; 2291 if (w && w.tagName == "svg") { 2292 if (w.snap in hub) { 2293 return hub[w.snap]; 2294 } 2295 var doc = w.ownerDocument; 2296 res = new Element(w); 2297 desc = w.getElementsByTagName("desc")[0]; 2298 defs = w.getElementsByTagName("defs")[0]; 2299 if (!desc) { 2300 desc = $("desc"); 2301 desc.appendChild(doc.createTextNode("Created with Snap")); 2302 res.node.appendChild(desc); 2303 } 2304 if (!defs) { 2305 defs = $("defs"); 2306 res.node.appendChild(defs); 2307 } 2308 res.defs = defs; 2309 for (var key in proto) if (proto[has](key)) { 2310 res[key] = proto[key]; 2311 } 2312 res.paper = res.root = res; 2313 } else { 2314 res = make("svg", glob.doc.body); 2315 $(res.node, { 2316 height: h, 2317 version: 1.1, 2318 width: w, 2319 xmlns: xmlns 2320 }); 2321 } 2322 return res; 2323} 2324function wrap(dom) { 2325 if (!dom) { 2326 return dom; 2327 } 2328 if (dom instanceof Element || dom instanceof Fragment) { 2329 return dom; 2330 } 2331 if (dom.tagName && dom.tagName.toLowerCase() == "svg") { 2332 return new Paper(dom); 2333 } 2334 if (dom.tagName && dom.tagName.toLowerCase() == "object" && dom.type == "image/svg+xml") { 2335 return new Paper(dom.contentDocument.getElementsByTagName("svg")[0]); 2336 } 2337 return new Element(dom); 2338} 2339 2340Snap._.make = make; 2341Snap._.wrap = wrap; 2342/*\ 2343 * Paper.el 2344 [ method ] 2345 ** 2346 * Creates an element on paper with a given name and no attributes 2347 ** 2348 - name (string) tag name 2349 - attr (object) attributes 2350 = (Element) the current element 2351 > Usage 2352 | var c = paper.circle(10, 10, 10); // is the same as... 2353 | var c = paper.el("circle").attr({ 2354 | cx: 10, 2355 | cy: 10, 2356 | r: 10 2357 | }); 2358 | // and the same as 2359 | var c = paper.el("circle", { 2360 | cx: 10, 2361 | cy: 10, 2362 | r: 10 2363 | }); 2364\*/ 2365Paper.prototype.el = function (name, attr) { 2366 var el = make(name, this.node); 2367 attr && el.attr(attr); 2368 return el; 2369}; 2370/*\ 2371 * Element.children 2372 [ method ] 2373 ** 2374 * Returns array of all the children of the element. 2375 = (array) array of Elements 2376\*/ 2377Element.prototype.children = function () { 2378 var out = [], 2379 ch = this.node.childNodes; 2380 for (var i = 0, ii = ch.length; i < ii; i++) { 2381 out[i] = Snap(ch[i]); 2382 } 2383 return out; 2384}; 2385function jsonFiller(root, o) { 2386 for (var i = 0, ii = root.length; i < ii; i++) { 2387 var item = { 2388 type: root[i].type, 2389 attr: root[i].attr() 2390 }, 2391 children = root[i].children(); 2392 o.push(item); 2393 if (children.length) { 2394 jsonFiller(children, item.childNodes = []); 2395 } 2396 } 2397} 2398/*\ 2399 * Element.toJSON 2400 [ method ] 2401 ** 2402 * Returns object representation of the given element and all its children. 2403 = (object) in format 2404 o { 2405 o type (string) this.type, 2406 o attr (object) attributes map, 2407 o childNodes (array) optional array of children in the same format 2408 o } 2409\*/ 2410Element.prototype.toJSON = function () { 2411 var out = []; 2412 jsonFiller([this], out); 2413 return out[0]; 2414}; 2415// default 2416eve.on("snap.util.getattr", function () { 2417 var att = eve.nt(); 2418 att = att.substring(att.lastIndexOf(".") + 1); 2419 var css = att.replace(/[A-Z]/g, function (letter) { 2420 return "-" + letter.toLowerCase(); 2421 }); 2422 if (cssAttr[has](css)) { 2423 return this.node.ownerDocument.defaultView.getComputedStyle(this.node, null).getPropertyValue(css); 2424 } else { 2425 return $(this.node, att); 2426 } 2427}); 2428var cssAttr = { 2429 "alignment-baseline": 0, 2430 "baseline-shift": 0, 2431 "clip": 0, 2432 "clip-path": 0, 2433 "clip-rule": 0, 2434 "color": 0, 2435 "color-interpolation": 0, 2436 "color-interpolation-filters": 0, 2437 "color-profile": 0, 2438 "color-rendering": 0, 2439 "cursor": 0, 2440 "direction": 0, 2441 "display": 0, 2442 "dominant-baseline": 0, 2443 "enable-background": 0, 2444 "fill": 0, 2445 "fill-opacity": 0, 2446 "fill-rule": 0, 2447 "filter": 0, 2448 "flood-color": 0, 2449 "flood-opacity": 0, 2450 "font": 0, 2451 "font-family": 0, 2452 "font-size": 0, 2453 "font-size-adjust": 0, 2454 "font-stretch": 0, 2455 "font-style": 0, 2456 "font-variant": 0, 2457 "font-weight": 0, 2458 "glyph-orientation-horizontal": 0, 2459 "glyph-orientation-vertical": 0, 2460 "image-rendering": 0, 2461 "kerning": 0, 2462 "letter-spacing": 0, 2463 "lighting-color": 0, 2464 "marker": 0, 2465 "marker-end": 0, 2466 "marker-mid": 0, 2467 "marker-start": 0, 2468 "mask": 0, 2469 "opacity": 0, 2470 "overflow": 0, 2471 "pointer-events": 0, 2472 "shape-rendering": 0, 2473 "stop-color": 0, 2474 "stop-opacity": 0, 2475 "stroke": 0, 2476 "stroke-dasharray": 0, 2477 "stroke-dashoffset": 0, 2478 "stroke-linecap": 0, 2479 "stroke-linejoin": 0, 2480 "stroke-miterlimit": 0, 2481 "stroke-opacity": 0, 2482 "stroke-width": 0, 2483 "text-anchor": 0, 2484 "text-decoration": 0, 2485 "text-rendering": 0, 2486 "unicode-bidi": 0, 2487 "visibility": 0, 2488 "word-spacing": 0, 2489 "writing-mode": 0 2490}; 2491 2492eve.on("snap.util.attr", function (value) { 2493 var att = eve.nt(), 2494 attr = {}; 2495 att = att.substring(att.lastIndexOf(".") + 1); 2496 attr[att] = value; 2497 var style = att.replace(/-(\w)/gi, function (all, letter) { 2498 return letter.toUpperCase(); 2499 }), 2500 css = att.replace(/[A-Z]/g, function (letter) { 2501 return "-" + letter.toLowerCase(); 2502 }); 2503 if (cssAttr[has](css)) { 2504 this.node.style[style] = value == null ? E : value; 2505 } else { 2506 $(this.node, attr); 2507 } 2508}); 2509(function (proto) {}(Paper.prototype)); 2510 2511// simple ajax 2512/*\ 2513 * Snap.ajax 2514 [ method ] 2515 ** 2516 * Simple implementation of Ajax 2517 ** 2518 - url (string) URL 2519 - postData (object|string) data for post request 2520 - callback (function) callback 2521 - scope (object) #optional scope of callback 2522 * or 2523 - url (string) URL 2524 - callback (function) callback 2525 - scope (object) #optional scope of callback 2526 = (XMLHttpRequest) the XMLHttpRequest object, just in case 2527\*/ 2528Snap.ajax = function (url, postData, callback, scope){ 2529 var req = new XMLHttpRequest, 2530 id = ID(); 2531 if (req) { 2532 if (is(postData, "function")) { 2533 scope = callback; 2534 callback = postData; 2535 postData = null; 2536 } else if (is(postData, "object")) { 2537 var pd = []; 2538 for (var key in postData) if (postData.hasOwnProperty(key)) { 2539 pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key])); 2540 } 2541 postData = pd.join("&"); 2542 } 2543 req.open((postData ? "POST" : "GET"), url, true); 2544 if (postData) { 2545 req.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 2546 req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 2547 } 2548 if (callback) { 2549 eve.once("snap.ajax." + id + ".0", callback); 2550 eve.once("snap.ajax." + id + ".200", callback); 2551 eve.once("snap.ajax." + id + ".304", callback); 2552 } 2553 req.onreadystatechange = function() { 2554 if (req.readyState != 4) return; 2555 eve("snap.ajax." + id + "." + req.status, scope, req); 2556 }; 2557 if (req.readyState == 4) { 2558 return req; 2559 } 2560 req.send(postData); 2561 return req; 2562 } 2563}; 2564/*\ 2565 * Snap.load 2566 [ method ] 2567 ** 2568 * Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX) 2569 ** 2570 - url (string) URL 2571 - callback (function) callback 2572 - scope (object) #optional scope of callback 2573\*/ 2574Snap.load = function (url, callback, scope) { 2575 Snap.ajax(url, function (req) { 2576 var f = Snap.parse(req.responseText); 2577 scope ? callback.call(scope, f) : callback(f); 2578 }); 2579}; 2580var getOffset = function (elem) { 2581 var box = elem.getBoundingClientRect(), 2582 doc = elem.ownerDocument, 2583 body = doc.body, 2584 docElem = doc.documentElement, 2585 clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, 2586 top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop, 2587 left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft; 2588 return { 2589 y: top, 2590 x: left 2591 }; 2592}; 2593/*\ 2594 * Snap.getElementByPoint 2595 [ method ] 2596 ** 2597 * Returns you topmost element under given point. 2598 ** 2599 = (object) Snap element object 2600 - x (number) x coordinate from the top left corner of the window 2601 - y (number) y coordinate from the top left corner of the window 2602 > Usage 2603 | Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"}); 2604\*/ 2605Snap.getElementByPoint = function (x, y) { 2606 var paper = this, 2607 svg = paper.canvas, 2608 target = glob.doc.elementFromPoint(x, y); 2609 if (glob.win.opera && target.tagName == "svg") { 2610 var so = getOffset(target), 2611 sr = target.createSVGRect(); 2612 sr.x = x - so.x; 2613 sr.y = y - so.y; 2614 sr.width = sr.height = 1; 2615 var hits = target.getIntersectionList(sr, null); 2616 if (hits.length) { 2617 target = hits[hits.length - 1]; 2618 } 2619 } 2620 if (!target) { 2621 return null; 2622 } 2623 return wrap(target); 2624}; 2625/*\ 2626 * Snap.plugin 2627 [ method ] 2628 ** 2629 * Let you write plugins. You pass in a function with five arguments, like this: 2630 | Snap.plugin(function (Snap, Element, Paper, global, Fragment) { 2631 | Snap.newmethod = function () {}; 2632 | Element.prototype.newmethod = function () {}; 2633 | Paper.prototype.newmethod = function () {}; 2634 | }); 2635 * Inside the function you have access to all main objects (and their 2636 * prototypes). This allow you to extend anything you want. 2637 ** 2638 - f (function) your plugin body 2639\*/ 2640Snap.plugin = function (f) { 2641 f(Snap, Element, Paper, glob, Fragment); 2642}; 2643glob.win.Snap = Snap; 2644return Snap; 2645}(window || this)); 2646 2647// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 2648// 2649// Licensed under the Apache License, Version 2.0 (the "License"); 2650// you may not use this file except in compliance with the License. 2651// You may obtain a copy of the License at 2652// 2653// http://www.apache.org/licenses/LICENSE-2.0 2654// 2655// Unless required by applicable law or agreed to in writing, software 2656// distributed under the License is distributed on an "AS IS" BASIS, 2657// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2658// See the License for the specific language governing permissions and 2659// limitations under the License. 2660Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 2661 var elproto = Element.prototype, 2662 is = Snap.is, 2663 Str = String, 2664 unit2px = Snap._unit2px, 2665 $ = Snap._.$, 2666 make = Snap._.make, 2667 getSomeDefs = Snap._.getSomeDefs, 2668 has = "hasOwnProperty", 2669 wrap = Snap._.wrap; 2670 /*\ 2671 * Element.getBBox 2672 [ method ] 2673 ** 2674 * Returns the bounding box descriptor for the given element 2675 ** 2676 = (object) bounding box descriptor: 2677 o { 2678 o cx: (number) x of the center, 2679 o cy: (number) x of the center, 2680 o h: (number) height, 2681 o height: (number) height, 2682 o path: (string) path command for the box, 2683 o r0: (number) radius of a circle that fully encloses the box, 2684 o r1: (number) radius of the smallest circle that can be enclosed, 2685 o r2: (number) radius of the largest circle that can be enclosed, 2686 o vb: (string) box as a viewbox command, 2687 o w: (number) width, 2688 o width: (number) width, 2689 o x2: (number) x of the right side, 2690 o x: (number) x of the left side, 2691 o y2: (number) y of the bottom edge, 2692 o y: (number) y of the top edge 2693 o } 2694 \*/ 2695 elproto.getBBox = function (isWithoutTransform) { 2696 if (!Snap.Matrix || !Snap.path) { 2697 return this.node.getBBox(); 2698 } 2699 var el = this, 2700 m = new Snap.Matrix; 2701 if (el.removed) { 2702 return Snap._.box(); 2703 } 2704 while (el.type == "use") { 2705 if (!isWithoutTransform) { 2706 m = m.add(el.transform().localMatrix.translate(el.attr("x") || 0, el.attr("y") || 0)); 2707 } 2708 if (el.original) { 2709 el = el.original; 2710 } else { 2711 var href = el.attr("xlink:href"); 2712 el = el.original = el.node.ownerDocument.getElementById(href.substring(href.indexOf("#") + 1)); 2713 } 2714 } 2715 var _ = el._, 2716 pathfinder = Snap.path.get[el.type] || Snap.path.get.deflt; 2717 try { 2718 if (isWithoutTransform) { 2719 _.bboxwt = pathfinder ? Snap.path.getBBox(el.realPath = pathfinder(el)) : Snap._.box(el.node.getBBox()); 2720 return Snap._.box(_.bboxwt); 2721 } else { 2722 el.realPath = pathfinder(el); 2723 el.matrix = el.transform().localMatrix; 2724 _.bbox = Snap.path.getBBox(Snap.path.map(el.realPath, m.add(el.matrix))); 2725 return Snap._.box(_.bbox); 2726 } 2727 } catch (e) { 2728 // Firefox doesn’t give you bbox of hidden element 2729 return Snap._.box(); 2730 } 2731 }; 2732 var propString = function () { 2733 return this.string; 2734 }; 2735 function extractTransform(el, tstr) { 2736 if (tstr == null) { 2737 var doReturn = true; 2738 if (el.type == "linearGradient" || el.type == "radialGradient") { 2739 tstr = el.node.getAttribute("gradientTransform"); 2740 } else if (el.type == "pattern") { 2741 tstr = el.node.getAttribute("patternTransform"); 2742 } else { 2743 tstr = el.node.getAttribute("transform"); 2744 } 2745 if (!tstr) { 2746 return new Snap.Matrix; 2747 } 2748 tstr = Snap._.svgTransform2string(tstr); 2749 } else { 2750 if (!Snap._.rgTransform.test(tstr)) { 2751 tstr = Snap._.svgTransform2string(tstr); 2752 } else { 2753 tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || ""); 2754 } 2755 if (is(tstr, "array")) { 2756 tstr = Snap.path ? Snap.path.toString.call(tstr) : Str(tstr); 2757 } 2758 el._.transform = tstr; 2759 } 2760 var m = Snap._.transform2matrix(tstr, el.getBBox(1)); 2761 if (doReturn) { 2762 return m; 2763 } else { 2764 el.matrix = m; 2765 } 2766 } 2767 /*\ 2768 * Element.transform 2769 [ method ] 2770 ** 2771 * Gets or sets transformation of the element 2772 ** 2773 - tstr (string) transform string in Snap or SVG format 2774 = (Element) the current element 2775 * or 2776 = (object) transformation descriptor: 2777 o { 2778 o string (string) transform string, 2779 o globalMatrix (Matrix) matrix of all transformations applied to element or its parents, 2780 o localMatrix (Matrix) matrix of transformations applied only to the element, 2781 o diffMatrix (Matrix) matrix of difference between global and local transformations, 2782 o global (string) global transformation as string, 2783 o local (string) local transformation as string, 2784 o toString (function) returns `string` property 2785 o } 2786 \*/ 2787 elproto.transform = function (tstr) { 2788 var _ = this._; 2789 if (tstr == null) { 2790 var papa = this, 2791 global = new Snap.Matrix(this.node.getCTM()), 2792 local = extractTransform(this), 2793 ms = [local], 2794 m = new Snap.Matrix, 2795 i, 2796 localString = local.toTransformString(), 2797 string = Str(local) == Str(this.matrix) ? 2798 Str(_.transform) : localString; 2799 while (papa.type != "svg" && (papa = papa.parent())) { 2800 ms.push(extractTransform(papa)); 2801 } 2802 i = ms.length; 2803 while (i--) { 2804 m.add(ms[i]); 2805 } 2806 return { 2807 string: string, 2808 globalMatrix: global, 2809 totalMatrix: m, 2810 localMatrix: local, 2811 diffMatrix: global.clone().add(local.invert()), 2812 global: global.toTransformString(), 2813 total: m.toTransformString(), 2814 local: localString, 2815 toString: propString 2816 }; 2817 } 2818 if (tstr instanceof Snap.Matrix) { 2819 this.matrix = tstr; 2820 this._.transform = tstr.toTransformString(); 2821 } else { 2822 extractTransform(this, tstr); 2823 } 2824 2825 if (this.node) { 2826 if (this.type == "linearGradient" || this.type == "radialGradient") { 2827 $(this.node, {gradientTransform: this.matrix}); 2828 } else if (this.type == "pattern") { 2829 $(this.node, {patternTransform: this.matrix}); 2830 } else { 2831 $(this.node, {transform: this.matrix}); 2832 } 2833 } 2834 2835 return this; 2836 }; 2837 /*\ 2838 * Element.parent 2839 [ method ] 2840 ** 2841 * Returns the element's parent 2842 ** 2843 = (Element) the parent element 2844 \*/ 2845 elproto.parent = function () { 2846 return wrap(this.node.parentNode); 2847 }; 2848 /*\ 2849 * Element.append 2850 [ method ] 2851 ** 2852 * Appends the given element to current one 2853 ** 2854 - el (Element|Set) element to append 2855 = (Element) the parent element 2856 \*/ 2857 /*\ 2858 * Element.add 2859 [ method ] 2860 ** 2861 * See @Element.append 2862 \*/ 2863 elproto.append = elproto.add = function (el) { 2864 if (el) { 2865 if (el.type == "set") { 2866 var it = this; 2867 el.forEach(function (el) { 2868 it.add(el); 2869 }); 2870 return this; 2871 } 2872 el = wrap(el); 2873 this.node.appendChild(el.node); 2874 el.paper = this.paper; 2875 } 2876 return this; 2877 }; 2878 /*\ 2879 * Element.appendTo 2880 [ method ] 2881 ** 2882 * Appends the current element to the given one 2883 ** 2884 - el (Element) parent element to append to 2885 = (Element) the child element 2886 \*/ 2887 elproto.appendTo = function (el) { 2888 if (el) { 2889 el = wrap(el); 2890 el.append(this); 2891 } 2892 return this; 2893 }; 2894 /*\ 2895 * Element.prepend 2896 [ method ] 2897 ** 2898 * Prepends the given element to the current one 2899 ** 2900 - el (Element) element to prepend 2901 = (Element) the parent element 2902 \*/ 2903 elproto.prepend = function (el) { 2904 if (el) { 2905 if (el.type == "set") { 2906 var it = this, 2907 first; 2908 el.forEach(function (el) { 2909 if (first) { 2910 first.after(el); 2911 } else { 2912 it.prepend(el); 2913 } 2914 first = el; 2915 }); 2916 return this; 2917 } 2918 el = wrap(el); 2919 var parent = el.parent(); 2920 this.node.insertBefore(el.node, this.node.firstChild); 2921 this.add && this.add(); 2922 el.paper = this.paper; 2923 this.parent() && this.parent().add(); 2924 parent && parent.add(); 2925 } 2926 return this; 2927 }; 2928 /*\ 2929 * Element.prependTo 2930 [ method ] 2931 ** 2932 * Prepends the current element to the given one 2933 ** 2934 - el (Element) parent element to prepend to 2935 = (Element) the child element 2936 \*/ 2937 elproto.prependTo = function (el) { 2938 el = wrap(el); 2939 el.prepend(this); 2940 return this; 2941 }; 2942 /*\ 2943 * Element.before 2944 [ method ] 2945 ** 2946 * Inserts given element before the current one 2947 ** 2948 - el (Element) element to insert 2949 = (Element) the parent element 2950 \*/ 2951 elproto.before = function (el) { 2952 if (el.type == "set") { 2953 var it = this; 2954 el.forEach(function (el) { 2955 var parent = el.parent(); 2956 it.node.parentNode.insertBefore(el.node, it.node); 2957 parent && parent.add(); 2958 }); 2959 this.parent().add(); 2960 return this; 2961 } 2962 el = wrap(el); 2963 var parent = el.parent(); 2964 this.node.parentNode.insertBefore(el.node, this.node); 2965 this.parent() && this.parent().add(); 2966 parent && parent.add(); 2967 el.paper = this.paper; 2968 return this; 2969 }; 2970 /*\ 2971 * Element.after 2972 [ method ] 2973 ** 2974 * Inserts given element after the current one 2975 ** 2976 - el (Element) element to insert 2977 = (Element) the parent element 2978 \*/ 2979 elproto.after = function (el) { 2980 el = wrap(el); 2981 var parent = el.parent(); 2982 if (this.node.nextSibling) { 2983 this.node.parentNode.insertBefore(el.node, this.node.nextSibling); 2984 } else { 2985 this.node.parentNode.appendChild(el.node); 2986 } 2987 this.parent() && this.parent().add(); 2988 parent && parent.add(); 2989 el.paper = this.paper; 2990 return this; 2991 }; 2992 /*\ 2993 * Element.insertBefore 2994 [ method ] 2995 ** 2996 * Inserts the element after the given one 2997 ** 2998 - el (Element) element next to whom insert to 2999 = (Element) the parent element 3000 \*/ 3001 elproto.insertBefore = function (el) { 3002 el = wrap(el); 3003 var parent = this.parent(); 3004 el.node.parentNode.insertBefore(this.node, el.node); 3005 this.paper = el.paper; 3006 parent && parent.add(); 3007 el.parent() && el.parent().add(); 3008 return this; 3009 }; 3010 /*\ 3011 * Element.insertAfter 3012 [ method ] 3013 ** 3014 * Inserts the element after the given one 3015 ** 3016 - el (Element) element next to whom insert to 3017 = (Element) the parent element 3018 \*/ 3019 elproto.insertAfter = function (el) { 3020 el = wrap(el); 3021 var parent = this.parent(); 3022 el.node.parentNode.insertBefore(this.node, el.node.nextSibling); 3023 this.paper = el.paper; 3024 parent && parent.add(); 3025 el.parent() && el.parent().add(); 3026 return this; 3027 }; 3028 /*\ 3029 * Element.remove 3030 [ method ] 3031 ** 3032 * Removes element from the DOM 3033 = (Element) the detached element 3034 \*/ 3035 elproto.remove = function () { 3036 var parent = this.parent(); 3037 this.node.parentNode && this.node.parentNode.removeChild(this.node); 3038 delete this.paper; 3039 this.removed = true; 3040 parent && parent.add(); 3041 return this; 3042 }; 3043 /*\ 3044 * Element.select 3045 [ method ] 3046 ** 3047 * Gathers the nested @Element matching the given set of CSS selectors 3048 ** 3049 - query (string) CSS selector 3050 = (Element) result of query selection 3051 \*/ 3052 elproto.select = function (query) { 3053 return wrap(this.node.querySelector(query)); 3054 }; 3055 /*\ 3056 * Element.selectAll 3057 [ method ] 3058 ** 3059 * Gathers nested @Element objects matching the given set of CSS selectors 3060 ** 3061 - query (string) CSS selector 3062 = (Set|array) result of query selection 3063 \*/ 3064 elproto.selectAll = function (query) { 3065 var nodelist = this.node.querySelectorAll(query), 3066 set = (Snap.set || Array)(); 3067 for (var i = 0; i < nodelist.length; i++) { 3068 set.push(wrap(nodelist[i])); 3069 } 3070 return set; 3071 }; 3072 /*\ 3073 * Element.asPX 3074 [ method ] 3075 ** 3076 * Returns given attribute of the element as a `px` value (not %, em, etc.) 3077 ** 3078 - attr (string) attribute name 3079 - value (string) #optional attribute value 3080 = (Element) result of query selection 3081 \*/ 3082 elproto.asPX = function (attr, value) { 3083 if (value == null) { 3084 value = this.attr(attr); 3085 } 3086 return +unit2px(this, attr, value); 3087 }; 3088 // SIERRA Element.use(): I suggest adding a note about how to access the original element the returned <use> instantiates. It's a part of SVG with which ordinary web developers may be least familiar. 3089 /*\ 3090 * Element.use 3091 [ method ] 3092 ** 3093 * Creates a `<use>` element linked to the current element 3094 ** 3095 = (Element) the `<use>` element 3096 \*/ 3097 elproto.use = function () { 3098 var use, 3099 id = this.node.id; 3100 if (!id) { 3101 id = this.id; 3102 $(this.node, { 3103 id: id 3104 }); 3105 } 3106 if (this.type == "linearGradient" || this.type == "radialGradient" || 3107 this.type == "pattern") { 3108 use = make(this.type, this.node.parentNode); 3109 } else { 3110 use = make("use", this.node.parentNode); 3111 } 3112 $(use.node, { 3113 "xlink:href": "#" + id 3114 }); 3115 use.original = this; 3116 return use; 3117 }; 3118 function fixids(el) { 3119 var els = el.selectAll("*"), 3120 it, 3121 url = /^\s*url\(("|'|)(.*)\1\)\s*$/, 3122 ids = [], 3123 uses = {}; 3124 function urltest(it, name) { 3125 var val = $(it.node, name); 3126 val = val && val.match(url); 3127 val = val && val[2]; 3128 if (val && val.charAt() == "#") { 3129 val = val.substring(1); 3130 } else { 3131 return; 3132 } 3133 if (val) { 3134 uses[val] = (uses[val] || []).concat(function (id) { 3135 var attr = {}; 3136 attr[name] = URL(id); 3137 $(it.node, attr); 3138 }); 3139 } 3140 } 3141 function linktest(it) { 3142 var val = $(it.node, "xlink:href"); 3143 if (val && val.charAt() == "#") { 3144 val = val.substring(1); 3145 } else { 3146 return; 3147 } 3148 if (val) { 3149 uses[val] = (uses[val] || []).concat(function (id) { 3150 it.attr("xlink:href", "#" + id); 3151 }); 3152 } 3153 } 3154 for (var i = 0, ii = els.length; i < ii; i++) { 3155 it = els[i]; 3156 urltest(it, "fill"); 3157 urltest(it, "stroke"); 3158 urltest(it, "filter"); 3159 urltest(it, "mask"); 3160 urltest(it, "clip-path"); 3161 linktest(it); 3162 var oldid = $(it.node, "id"); 3163 if (oldid) { 3164 $(it.node, {id: it.id}); 3165 ids.push({ 3166 old: oldid, 3167 id: it.id 3168 }); 3169 } 3170 } 3171 for (i = 0, ii = ids.length; i < ii; i++) { 3172 var fs = uses[ids[i].old]; 3173 if (fs) { 3174 for (var j = 0, jj = fs.length; j < jj; j++) { 3175 fs[j](ids[i].id); 3176 } 3177 } 3178 } 3179 } 3180 /*\ 3181 * Element.clone 3182 [ method ] 3183 ** 3184 * Creates a clone of the element and inserts it after the element 3185 ** 3186 = (Element) the clone 3187 \*/ 3188 elproto.clone = function () { 3189 var clone = wrap(this.node.cloneNode(true)); 3190 if ($(clone.node, "id")) { 3191 $(clone.node, {id: clone.id}); 3192 } 3193 fixids(clone); 3194 clone.insertAfter(this); 3195 return clone; 3196 }; 3197 /*\ 3198 * Element.toDefs 3199 [ method ] 3200 ** 3201 * Moves element to the shared `<defs>` area 3202 ** 3203 = (Element) the element 3204 \*/ 3205 elproto.toDefs = function () { 3206 var defs = getSomeDefs(this); 3207 defs.appendChild(this.node); 3208 return this; 3209 }; 3210 /*\ 3211 * Element.toPattern 3212 [ method ] 3213 ** 3214 * Creates a `<pattern>` element from the current element 3215 ** 3216 * To create a pattern you have to specify the pattern rect: 3217 - x (string|number) 3218 - y (string|number) 3219 - width (string|number) 3220 - height (string|number) 3221 = (Element) the `<pattern>` element 3222 * You can use pattern later on as an argument for `fill` attribute: 3223 | var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({ 3224 | fill: "none", 3225 | stroke: "#bada55", 3226 | strokeWidth: 5 3227 | }).pattern(0, 0, 10, 10), 3228 | c = paper.circle(200, 200, 100); 3229 | c.attr({ 3230 | fill: p 3231 | }); 3232 \*/ 3233 elproto.pattern = elproto.toPattern = function (x, y, width, height) { 3234 var p = make("pattern", getSomeDefs(this)); 3235 if (x == null) { 3236 x = this.getBBox(); 3237 } 3238 if (is(x, "object") && "x" in x) { 3239 y = x.y; 3240 width = x.width; 3241 height = x.height; 3242 x = x.x; 3243 } 3244 $(p.node, { 3245 x: x, 3246 y: y, 3247 width: width, 3248 height: height, 3249 patternUnits: "userSpaceOnUse", 3250 id: p.id, 3251 viewBox: [x, y, width, height].join(" ") 3252 }); 3253 p.node.appendChild(this.node); 3254 return p; 3255 }; 3256// SIERRA Element.marker(): clarify what a reference point is. E.g., helps you offset the object from its edge such as when centering it over a path. 3257// SIERRA Element.marker(): I suggest the method should accept default reference point values. Perhaps centered with (refX = width/2) and (refY = height/2)? Also, couldn't it assume the element's current _width_ and _height_? And please specify what _x_ and _y_ mean: offsets? If so, from where? Couldn't they also be assigned default values? 3258 /*\ 3259 * Element.marker 3260 [ method ] 3261 ** 3262 * Creates a `<marker>` element from the current element 3263 ** 3264 * To create a marker you have to specify the bounding rect and reference point: 3265 - x (number) 3266 - y (number) 3267 - width (number) 3268 - height (number) 3269 - refX (number) 3270 - refY (number) 3271 = (Element) the `<marker>` element 3272 * You can specify the marker later as an argument for `marker-start`, `marker-end`, `marker-mid`, and `marker` attributes. The `marker` attribute places the marker at every point along the path, and `marker-mid` places them at every point except the start and end. 3273 \*/ 3274 // TODO add usage for markers 3275 elproto.marker = function (x, y, width, height, refX, refY) { 3276 var p = make("marker", getSomeDefs(this)); 3277 if (x == null) { 3278 x = this.getBBox(); 3279 } 3280 if (is(x, "object") && "x" in x) { 3281 y = x.y; 3282 width = x.width; 3283 height = x.height; 3284 refX = x.refX || x.cx; 3285 refY = x.refY || x.cy; 3286 x = x.x; 3287 } 3288 $(p.node, { 3289 viewBox: [x, y, width, height].join(" "), 3290 markerWidth: width, 3291 markerHeight: height, 3292 orient: "auto", 3293 refX: refX || 0, 3294 refY: refY || 0, 3295 id: p.id 3296 }); 3297 p.node.appendChild(this.node); 3298 return p; 3299 }; 3300 // animation 3301 function slice(from, to, f) { 3302 return function (arr) { 3303 var res = arr.slice(from, to); 3304 if (res.length == 1) { 3305 res = res[0]; 3306 } 3307 return f ? f(res) : res; 3308 }; 3309 } 3310 var Animation = function (attr, ms, easing, callback) { 3311 if (typeof easing == "function" && !easing.length) { 3312 callback = easing; 3313 easing = mina.linear; 3314 } 3315 this.attr = attr; 3316 this.dur = ms; 3317 easing && (this.easing = easing); 3318 callback && (this.callback = callback); 3319 }; 3320 Snap._.Animation = Animation; 3321 /*\ 3322 * Snap.animation 3323 [ method ] 3324 ** 3325 * Creates an animation object 3326 ** 3327 - attr (object) attributes of final destination 3328 - duration (number) duration of the animation, in milliseconds 3329 - easing (function) #optional one of easing functions of @mina or custom one 3330 - callback (function) #optional callback function that fires when animation ends 3331 = (object) animation object 3332 \*/ 3333 Snap.animation = function (attr, ms, easing, callback) { 3334 return new Animation(attr, ms, easing, callback); 3335 }; 3336 /*\ 3337 * Element.inAnim 3338 [ method ] 3339 ** 3340 * Returns a set of animations that may be able to manipulate the current element 3341 ** 3342 = (object) in format: 3343 o { 3344 o anim (object) animation object, 3345 o mina (object) @mina object, 3346 o curStatus (number) 0..1 — status of the animation: 0 — just started, 1 — just finished, 3347 o status (function) gets or sets the status of the animation, 3348 o stop (function) stops the animation 3349 o } 3350 \*/ 3351 elproto.inAnim = function () { 3352 var el = this, 3353 res = []; 3354 for (var id in el.anims) if (el.anims[has](id)) { 3355 (function (a) { 3356 res.push({ 3357 anim: new Animation(a._attrs, a.dur, a.easing, a._callback), 3358 mina: a, 3359 curStatus: a.status(), 3360 status: function (val) { 3361 return a.status(val); 3362 }, 3363 stop: function () { 3364 a.stop(); 3365 } 3366 }); 3367 }(el.anims[id])); 3368 } 3369 return res; 3370 }; 3371 /*\ 3372 * Snap.animate 3373 [ method ] 3374 ** 3375 * Runs generic animation of one number into another with a caring function 3376 ** 3377 - from (number|array) number or array of numbers 3378 - to (number|array) number or array of numbers 3379 - setter (function) caring function that accepts one number argument 3380 - duration (number) duration, in milliseconds 3381 - easing (function) #optional easing function from @mina or custom 3382 - callback (function) #optional callback function to execute when animation ends 3383 = (object) animation object in @mina format 3384 o { 3385 o id (string) animation id, consider it read-only, 3386 o duration (function) gets or sets the duration of the animation, 3387 o easing (function) easing, 3388 o speed (function) gets or sets the speed of the animation, 3389 o status (function) gets or sets the status of the animation, 3390 o stop (function) stops the animation 3391 o } 3392 | var rect = Snap().rect(0, 0, 10, 10); 3393 | Snap.animate(0, 10, function (val) { 3394 | rect.attr({ 3395 | x: val 3396 | }); 3397 | }, 1000); 3398 | // in given context is equivalent to 3399 | rect.animate({x: 10}, 1000); 3400 \*/ 3401 Snap.animate = function (from, to, setter, ms, easing, callback) { 3402 if (typeof easing == "function" && !easing.length) { 3403 callback = easing; 3404 easing = mina.linear; 3405 } 3406 var now = mina.time(), 3407 anim = mina(from, to, now, now + ms, mina.time, setter, easing); 3408 callback && eve.once("mina.finish." + anim.id, callback); 3409 return anim; 3410 }; 3411 /*\ 3412 * Element.stop 3413 [ method ] 3414 ** 3415 * Stops all the animations for the current element 3416 ** 3417 = (Element) the current element 3418 \*/ 3419 elproto.stop = function () { 3420 var anims = this.inAnim(); 3421 for (var i = 0, ii = anims.length; i < ii; i++) { 3422 anims[i].stop(); 3423 } 3424 return this; 3425 }; 3426 /*\ 3427 * Element.animate 3428 [ method ] 3429 ** 3430 * Animates the given attributes of the element 3431 ** 3432 - attrs (object) key-value pairs of destination attributes 3433 - duration (number) duration of the animation in milliseconds 3434 - easing (function) #optional easing function from @mina or custom 3435 - callback (function) #optional callback function that executes when the animation ends 3436 = (Element) the current element 3437 \*/ 3438 elproto.animate = function (attrs, ms, easing, callback) { 3439 if (typeof easing == "function" && !easing.length) { 3440 callback = easing; 3441 easing = mina.linear; 3442 } 3443 if (attrs instanceof Animation) { 3444 callback = attrs.callback; 3445 easing = attrs.easing; 3446 ms = attrs.dur; 3447 attrs = attrs.attr; 3448 } 3449 var fkeys = [], tkeys = [], keys = {}, from, to, f, eq, 3450 el = this; 3451 for (var key in attrs) if (attrs[has](key)) { 3452 if (el.equal) { 3453 eq = el.equal(key, Str(attrs[key])); 3454 from = eq.from; 3455 to = eq.to; 3456 f = eq.f; 3457 } else { 3458 from = +el.attr(key); 3459 to = +attrs[key]; 3460 } 3461 var len = is(from, "array") ? from.length : 1; 3462 keys[key] = slice(fkeys.length, fkeys.length + len, f); 3463 fkeys = fkeys.concat(from); 3464 tkeys = tkeys.concat(to); 3465 } 3466 var now = mina.time(), 3467 anim = mina(fkeys, tkeys, now, now + ms, mina.time, function (val) { 3468 var attr = {}; 3469 for (var key in keys) if (keys[has](key)) { 3470 attr[key] = keys[key](val); 3471 } 3472 el.attr(attr); 3473 }, easing); 3474 el.anims[anim.id] = anim; 3475 anim._attrs = attrs; 3476 anim._callback = callback; 3477 eve("snap.animcreated." + el.id, anim); 3478 eve.once("mina.finish." + anim.id, function () { 3479 delete el.anims[anim.id]; 3480 callback && callback.call(el); 3481 }); 3482 eve.once("mina.stop." + anim.id, function () { 3483 delete el.anims[anim.id]; 3484 }); 3485 return el; 3486 }; 3487 var eldata = {}; 3488 /*\ 3489 * Element.data 3490 [ method ] 3491 ** 3492 * Adds or retrieves given value associated with given key. (Don’t confuse 3493 * with `data-` attributes) 3494 * 3495 * See also @Element.removeData 3496 - key (string) key to store data 3497 - value (any) #optional value to store 3498 = (object) @Element 3499 * or, if value is not specified: 3500 = (any) value 3501 > Usage 3502 | for (var i = 0, i < 5, i++) { 3503 | paper.circle(10 + 15 * i, 10, 10) 3504 | .attr({fill: "#000"}) 3505 | .data("i", i) 3506 | .click(function () { 3507 | alert(this.data("i")); 3508 | }); 3509 | } 3510 \*/ 3511 elproto.data = function (key, value) { 3512 var data = eldata[this.id] = eldata[this.id] || {}; 3513 if (arguments.length == 0){ 3514 eve("snap.data.get." + this.id, this, data, null); 3515 return data; 3516 } 3517 if (arguments.length == 1) { 3518 if (Snap.is(key, "object")) { 3519 for (var i in key) if (key[has](i)) { 3520 this.data(i, key[i]); 3521 } 3522 return this; 3523 } 3524 eve("snap.data.get." + this.id, this, data[key], key); 3525 return data[key]; 3526 } 3527 data[key] = value; 3528 eve("snap.data.set." + this.id, this, value, key); 3529 return this; 3530 }; 3531 /*\ 3532 * Element.removeData 3533 [ method ] 3534 ** 3535 * Removes value associated with an element by given key. 3536 * If key is not provided, removes all the data of the element. 3537 - key (string) #optional key 3538 = (object) @Element 3539 \*/ 3540 elproto.removeData = function (key) { 3541 if (key == null) { 3542 eldata[this.id] = {}; 3543 } else { 3544 eldata[this.id] && delete eldata[this.id][key]; 3545 } 3546 return this; 3547 }; 3548 /*\ 3549 * Element.outerSVG 3550 [ method ] 3551 ** 3552 * Returns SVG code for the element, equivalent to HTML's `outerHTML`. 3553 * 3554 * See also @Element.innerSVG 3555 = (string) SVG code for the element 3556 \*/ 3557 /*\ 3558 * Element.toString 3559 [ method ] 3560 ** 3561 * See @Element.outerSVG 3562 \*/ 3563 elproto.outerSVG = elproto.toString = toString(1); 3564 /*\ 3565 * Element.innerSVG 3566 [ method ] 3567 ** 3568 * Returns SVG code for the element's contents, equivalent to HTML's `innerHTML` 3569 = (string) SVG code for the element 3570 \*/ 3571 elproto.innerSVG = toString(); 3572 function toString(type) { 3573 return function () { 3574 var res = type ? "<" + this.type : "", 3575 attr = this.node.attributes, 3576 chld = this.node.childNodes; 3577 if (type) { 3578 for (var i = 0, ii = attr.length; i < ii; i++) { 3579 res += " " + attr[i].name + '="' + 3580 attr[i].value.replace(/"/g, '\\"') + '"'; 3581 } 3582 } 3583 if (chld.length) { 3584 type && (res += ">"); 3585 for (i = 0, ii = chld.length; i < ii; i++) { 3586 if (chld[i].nodeType == 3) { 3587 res += chld[i].nodeValue; 3588 } else if (chld[i].nodeType == 1) { 3589 res += wrap(chld[i]).toString(); 3590 } 3591 } 3592 type && (res += "</" + this.type + ">"); 3593 } else { 3594 type && (res += "/>"); 3595 } 3596 return res; 3597 }; 3598 } 3599 elproto.toDataURL = function () { 3600 if (window && window.btoa) { 3601 var bb = this.getBBox(), 3602 svg = Snap.format('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}" viewBox="{x} {y} {width} {height}">{contents}</svg>', { 3603 x: +bb.x.toFixed(3), 3604 y: +bb.y.toFixed(3), 3605 width: +bb.width.toFixed(3), 3606 height: +bb.height.toFixed(3), 3607 contents: this.outerSVG() 3608 }); 3609 return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svg))); 3610 } 3611 }; 3612 /*\ 3613 * Fragment.select 3614 [ method ] 3615 ** 3616 * See @Element.select 3617 \*/ 3618 Fragment.prototype.select = elproto.select; 3619 /*\ 3620 * Fragment.selectAll 3621 [ method ] 3622 ** 3623 * See @Element.selectAll 3624 \*/ 3625 Fragment.prototype.selectAll = elproto.selectAll; 3626}); 3627 3628// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 3629// 3630// Licensed under the Apache License, Version 2.0 (the "License"); 3631// you may not use this file except in compliance with the License. 3632// You may obtain a copy of the License at 3633// 3634// http://www.apache.org/licenses/LICENSE-2.0 3635// 3636// Unless required by applicable law or agreed to in writing, software 3637// distributed under the License is distributed on an "AS IS" BASIS, 3638// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3639// See the License for the specific language governing permissions and 3640// limitations under the License. 3641Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 3642 var objectToString = Object.prototype.toString, 3643 Str = String, 3644 math = Math, 3645 E = ""; 3646 function Matrix(a, b, c, d, e, f) { 3647 if (b == null && objectToString.call(a) == "[object SVGMatrix]") { 3648 this.a = a.a; 3649 this.b = a.b; 3650 this.c = a.c; 3651 this.d = a.d; 3652 this.e = a.e; 3653 this.f = a.f; 3654 return; 3655 } 3656 if (a != null) { 3657 this.a = +a; 3658 this.b = +b; 3659 this.c = +c; 3660 this.d = +d; 3661 this.e = +e; 3662 this.f = +f; 3663 } else { 3664 this.a = 1; 3665 this.b = 0; 3666 this.c = 0; 3667 this.d = 1; 3668 this.e = 0; 3669 this.f = 0; 3670 } 3671 } 3672 (function (matrixproto) { 3673 /*\ 3674 * Matrix.add 3675 [ method ] 3676 ** 3677 * Adds the given matrix to existing one 3678 - a (number) 3679 - b (number) 3680 - c (number) 3681 - d (number) 3682 - e (number) 3683 - f (number) 3684 * or 3685 - matrix (object) @Matrix 3686 \*/ 3687 matrixproto.add = function (a, b, c, d, e, f) { 3688 var out = [[], [], []], 3689 m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], 3690 matrix = [[a, c, e], [b, d, f], [0, 0, 1]], 3691 x, y, z, res; 3692 3693 if (a && a instanceof Matrix) { 3694 matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]; 3695 } 3696 3697 for (x = 0; x < 3; x++) { 3698 for (y = 0; y < 3; y++) { 3699 res = 0; 3700 for (z = 0; z < 3; z++) { 3701 res += m[x][z] * matrix[z][y]; 3702 } 3703 out[x][y] = res; 3704 } 3705 } 3706 this.a = out[0][0]; 3707 this.b = out[1][0]; 3708 this.c = out[0][1]; 3709 this.d = out[1][1]; 3710 this.e = out[0][2]; 3711 this.f = out[1][2]; 3712 return this; 3713 }; 3714 /*\ 3715 * Matrix.invert 3716 [ method ] 3717 ** 3718 * Returns an inverted version of the matrix 3719 = (object) @Matrix 3720 \*/ 3721 matrixproto.invert = function () { 3722 var me = this, 3723 x = me.a * me.d - me.b * me.c; 3724 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); 3725 }; 3726 /*\ 3727 * Matrix.clone 3728 [ method ] 3729 ** 3730 * Returns a copy of the matrix 3731 = (object) @Matrix 3732 \*/ 3733 matrixproto.clone = function () { 3734 return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); 3735 }; 3736 /*\ 3737 * Matrix.translate 3738 [ method ] 3739 ** 3740 * Translate the matrix 3741 - x (number) horizontal offset distance 3742 - y (number) vertical offset distance 3743 \*/ 3744 matrixproto.translate = function (x, y) { 3745 return this.add(1, 0, 0, 1, x, y); 3746 }; 3747 /*\ 3748 * Matrix.scale 3749 [ method ] 3750 ** 3751 * Scales the matrix 3752 - x (number) amount to be scaled, with `1` resulting in no change 3753 - y (number) #optional amount to scale along the vertical axis. (Otherwise `x` applies to both axes.) 3754 - cx (number) #optional horizontal origin point from which to scale 3755 - cy (number) #optional vertical origin point from which to scale 3756 * Default cx, cy is the middle point of the element. 3757 \*/ 3758 matrixproto.scale = function (x, y, cx, cy) { 3759 y == null && (y = x); 3760 (cx || cy) && this.add(1, 0, 0, 1, cx, cy); 3761 this.add(x, 0, 0, y, 0, 0); 3762 (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy); 3763 return this; 3764 }; 3765 /*\ 3766 * Matrix.rotate 3767 [ method ] 3768 ** 3769 * Rotates the matrix 3770 - a (number) angle of rotation, in degrees 3771 - x (number) horizontal origin point from which to rotate 3772 - y (number) vertical origin point from which to rotate 3773 \*/ 3774 matrixproto.rotate = function (a, x, y) { 3775 a = Snap.rad(a); 3776 x = x || 0; 3777 y = y || 0; 3778 var cos = +math.cos(a).toFixed(9), 3779 sin = +math.sin(a).toFixed(9); 3780 this.add(cos, sin, -sin, cos, x, y); 3781 return this.add(1, 0, 0, 1, -x, -y); 3782 }; 3783 /*\ 3784 * Matrix.x 3785 [ method ] 3786 ** 3787 * Returns x coordinate for given point after transformation described by the matrix. See also @Matrix.y 3788 - x (number) 3789 - y (number) 3790 = (number) x 3791 \*/ 3792 matrixproto.x = function (x, y) { 3793 return x * this.a + y * this.c + this.e; 3794 }; 3795 /*\ 3796 * Matrix.y 3797 [ method ] 3798 ** 3799 * Returns y coordinate for given point after transformation described by the matrix. See also @Matrix.x 3800 - x (number) 3801 - y (number) 3802 = (number) y 3803 \*/ 3804 matrixproto.y = function (x, y) { 3805 return x * this.b + y * this.d + this.f; 3806 }; 3807 matrixproto.get = function (i) { 3808 return +this[Str.fromCharCode(97 + i)].toFixed(4); 3809 }; 3810 matrixproto.toString = function () { 3811 return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")"; 3812 }; 3813 matrixproto.offset = function () { 3814 return [this.e.toFixed(4), this.f.toFixed(4)]; 3815 }; 3816 function norm(a) { 3817 return a[0] * a[0] + a[1] * a[1]; 3818 } 3819 function normalize(a) { 3820 var mag = math.sqrt(norm(a)); 3821 a[0] && (a[0] /= mag); 3822 a[1] && (a[1] /= mag); 3823 } 3824 /*\ 3825 * Matrix.determinant 3826 [ method ] 3827 ** 3828 * Finds determinant of the given matrix. 3829 = (number) determinant 3830 \*/ 3831 matrixproto.determinant = function () { 3832 return this.a * this.d - this.b * this.c; 3833 }; 3834 /*\ 3835 * Matrix.split 3836 [ method ] 3837 ** 3838 * Splits matrix into primitive transformations 3839 = (object) in format: 3840 o dx (number) translation by x 3841 o dy (number) translation by y 3842 o scalex (number) scale by x 3843 o scaley (number) scale by y 3844 o shear (number) shear 3845 o rotate (number) rotation in deg 3846 o isSimple (boolean) could it be represented via simple transformations 3847 \*/ 3848 matrixproto.split = function () { 3849 var out = {}; 3850 // translation 3851 out.dx = this.e; 3852 out.dy = this.f; 3853 3854 // scale and shear 3855 var row = [[this.a, this.c], [this.b, this.d]]; 3856 out.scalex = math.sqrt(norm(row[0])); 3857 normalize(row[0]); 3858 3859 out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1]; 3860 row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear]; 3861 3862 out.scaley = math.sqrt(norm(row[1])); 3863 normalize(row[1]); 3864 out.shear /= out.scaley; 3865 3866 if (this.determinant() < 0) { 3867 out.scalex = -out.scalex; 3868 } 3869 3870 // rotation 3871 var sin = -row[0][1], 3872 cos = row[1][1]; 3873 if (cos < 0) { 3874 out.rotate = Snap.deg(math.acos(cos)); 3875 if (sin < 0) { 3876 out.rotate = 360 - out.rotate; 3877 } 3878 } else { 3879 out.rotate = Snap.deg(math.asin(sin)); 3880 } 3881 3882 out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate); 3883 out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate; 3884 out.noRotation = !+out.shear.toFixed(9) && !out.rotate; 3885 return out; 3886 }; 3887 /*\ 3888 * Matrix.toTransformString 3889 [ method ] 3890 ** 3891 * Returns transform string that represents given matrix 3892 = (string) transform string 3893 \*/ 3894 matrixproto.toTransformString = function (shorter) { 3895 var s = shorter || this.split(); 3896 if (!+s.shear.toFixed(9)) { 3897 s.scalex = +s.scalex.toFixed(4); 3898 s.scaley = +s.scaley.toFixed(4); 3899 s.rotate = +s.rotate.toFixed(4); 3900 return (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) + 3901 (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) + 3902 (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E); 3903 } else { 3904 return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)]; 3905 } 3906 }; 3907 })(Matrix.prototype); 3908 /*\ 3909 * Snap.Matrix 3910 [ method ] 3911 ** 3912 * Matrix constructor, extend on your own risk. 3913 * To create matrices use @Snap.matrix. 3914 \*/ 3915 Snap.Matrix = Matrix; 3916 /*\ 3917 * Snap.matrix 3918 [ method ] 3919 ** 3920 * Utility method 3921 ** 3922 * Returns a matrix based on the given parameters 3923 - a (number) 3924 - b (number) 3925 - c (number) 3926 - d (number) 3927 - e (number) 3928 - f (number) 3929 * or 3930 - svgMatrix (SVGMatrix) 3931 = (object) @Matrix 3932 \*/ 3933 Snap.matrix = function (a, b, c, d, e, f) { 3934 return new Matrix(a, b, c, d, e, f); 3935 }; 3936}); 3937// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 3938// 3939// Licensed under the Apache License, Version 2.0 (the "License"); 3940// you may not use this file except in compliance with the License. 3941// You may obtain a copy of the License at 3942// 3943// http://www.apache.org/licenses/LICENSE-2.0 3944// 3945// Unless required by applicable law or agreed to in writing, software 3946// distributed under the License is distributed on an "AS IS" BASIS, 3947// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3948// See the License for the specific language governing permissions and 3949// limitations under the License. 3950Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 3951 var has = "hasOwnProperty", 3952 make = Snap._.make, 3953 wrap = Snap._.wrap, 3954 is = Snap.is, 3955 getSomeDefs = Snap._.getSomeDefs, 3956 reURLValue = /^url\(#?([^)]+)\)$/, 3957 $ = Snap._.$, 3958 URL = Snap.url, 3959 Str = String, 3960 separator = Snap._.separator, 3961 E = ""; 3962 // Attributes event handlers 3963 eve.on("snap.util.attr.mask", function (value) { 3964 if (value instanceof Element || value instanceof Fragment) { 3965 eve.stop(); 3966 if (value instanceof Fragment && value.node.childNodes.length == 1) { 3967 value = value.node.firstChild; 3968 getSomeDefs(this).appendChild(value); 3969 value = wrap(value); 3970 } 3971 if (value.type == "mask") { 3972 var mask = value; 3973 } else { 3974 mask = make("mask", getSomeDefs(this)); 3975 mask.node.appendChild(value.node); 3976 } 3977 !mask.node.id && $(mask.node, { 3978 id: mask.id 3979 }); 3980 $(this.node, { 3981 mask: URL(mask.id) 3982 }); 3983 } 3984 }); 3985 (function (clipIt) { 3986 eve.on("snap.util.attr.clip", clipIt); 3987 eve.on("snap.util.attr.clip-path", clipIt); 3988 eve.on("snap.util.attr.clipPath", clipIt); 3989 }(function (value) { 3990 if (value instanceof Element || value instanceof Fragment) { 3991 eve.stop(); 3992 if (value.type == "clipPath") { 3993 var clip = value; 3994 } else { 3995 clip = make("clipPath", getSomeDefs(this)); 3996 clip.node.appendChild(value.node); 3997 !clip.node.id && $(clip.node, { 3998 id: clip.id 3999 }); 4000 } 4001 $(this.node, { 4002 "clip-path": URL(clip.node.id || clip.id) 4003 }); 4004 } 4005 })); 4006 function fillStroke(name) { 4007 return function (value) { 4008 eve.stop(); 4009 if (value instanceof Fragment && value.node.childNodes.length == 1 && 4010 (value.node.firstChild.tagName == "radialGradient" || 4011 value.node.firstChild.tagName == "linearGradient" || 4012 value.node.firstChild.tagName == "pattern")) { 4013 value = value.node.firstChild; 4014 getSomeDefs(this).appendChild(value); 4015 value = wrap(value); 4016 } 4017 if (value instanceof Element) { 4018 if (value.type == "radialGradient" || value.type == "linearGradient" 4019 || value.type == "pattern") { 4020 if (!value.node.id) { 4021 $(value.node, { 4022 id: value.id 4023 }); 4024 } 4025 var fill = URL(value.node.id); 4026 } else { 4027 fill = value.attr(name); 4028 } 4029 } else { 4030 fill = Snap.color(value); 4031 if (fill.error) { 4032 var grad = Snap(getSomeDefs(this).ownerSVGElement).gradient(value); 4033 if (grad) { 4034 if (!grad.node.id) { 4035 $(grad.node, { 4036 id: grad.id 4037 }); 4038 } 4039 fill = URL(grad.node.id); 4040 } else { 4041 fill = value; 4042 } 4043 } else { 4044 fill = Str(fill); 4045 } 4046 } 4047 var attrs = {}; 4048 attrs[name] = fill; 4049 $(this.node, attrs); 4050 this.node.style[name] = E; 4051 }; 4052 } 4053 eve.on("snap.util.attr.fill", fillStroke("fill")); 4054 eve.on("snap.util.attr.stroke", fillStroke("stroke")); 4055 var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i; 4056 eve.on("snap.util.grad.parse", function parseGrad(string) { 4057 string = Str(string); 4058 var tokens = string.match(gradrg); 4059 if (!tokens) { 4060 return null; 4061 } 4062 var type = tokens[1], 4063 params = tokens[2], 4064 stops = tokens[3]; 4065 params = params.split(/\s*,\s*/).map(function (el) { 4066 return +el == el ? +el : el; 4067 }); 4068 if (params.length == 1 && params[0] == 0) { 4069 params = []; 4070 } 4071 stops = stops.split("-"); 4072 stops = stops.map(function (el) { 4073 el = el.split(":"); 4074 var out = { 4075 color: el[0] 4076 }; 4077 if (el[1]) { 4078 out.offset = parseFloat(el[1]); 4079 } 4080 return out; 4081 }); 4082 return { 4083 type: type, 4084 params: params, 4085 stops: stops 4086 }; 4087 }); 4088 4089 eve.on("snap.util.attr.d", function (value) { 4090 eve.stop(); 4091 if (is(value, "array") && is(value[0], "array")) { 4092 value = Snap.path.toString.call(value); 4093 } 4094 value = Str(value); 4095 if (value.match(/[ruo]/i)) { 4096 value = Snap.path.toAbsolute(value); 4097 } 4098 $(this.node, {d: value}); 4099 })(-1); 4100 eve.on("snap.util.attr.#text", function (value) { 4101 eve.stop(); 4102 value = Str(value); 4103 var txt = glob.doc.createTextNode(value); 4104 while (this.node.firstChild) { 4105 this.node.removeChild(this.node.firstChild); 4106 } 4107 this.node.appendChild(txt); 4108 })(-1); 4109 eve.on("snap.util.attr.path", function (value) { 4110 eve.stop(); 4111 this.attr({d: value}); 4112 })(-1); 4113 eve.on("snap.util.attr.class", function (value) { 4114 eve.stop(); 4115 this.node.className.baseVal = value; 4116 })(-1); 4117 eve.on("snap.util.attr.viewBox", function (value) { 4118 var vb; 4119 if (is(value, "object") && "x" in value) { 4120 vb = [value.x, value.y, value.width, value.height].join(" "); 4121 } else if (is(value, "array")) { 4122 vb = value.join(" "); 4123 } else { 4124 vb = value; 4125 } 4126 $(this.node, { 4127 viewBox: vb 4128 }); 4129 eve.stop(); 4130 })(-1); 4131 eve.on("snap.util.attr.transform", function (value) { 4132 this.transform(value); 4133 eve.stop(); 4134 })(-1); 4135 eve.on("snap.util.attr.r", function (value) { 4136 if (this.type == "rect") { 4137 eve.stop(); 4138 $(this.node, { 4139 rx: value, 4140 ry: value 4141 }); 4142 } 4143 })(-1); 4144 eve.on("snap.util.attr.textpath", function (value) { 4145 eve.stop(); 4146 if (this.type == "text") { 4147 var id, tp, node; 4148 if (!value && this.textPath) { 4149 tp = this.textPath; 4150 while (tp.node.firstChild) { 4151 this.node.appendChild(tp.node.firstChild); 4152 } 4153 tp.remove(); 4154 delete this.textPath; 4155 return; 4156 } 4157 if (is(value, "string")) { 4158 var defs = getSomeDefs(this), 4159 path = wrap(defs.parentNode).path(value); 4160 defs.appendChild(path.node); 4161 id = path.id; 4162 path.attr({id: id}); 4163 } else { 4164 value = wrap(value); 4165 if (value instanceof Element) { 4166 id = value.attr("id"); 4167 if (!id) { 4168 id = value.id; 4169 value.attr({id: id}); 4170 } 4171 } 4172 } 4173 if (id) { 4174 tp = this.textPath; 4175 node = this.node; 4176 if (tp) { 4177 tp.attr({"xlink:href": "#" + id}); 4178 } else { 4179 tp = $("textPath", { 4180 "xlink:href": "#" + id 4181 }); 4182 while (node.firstChild) { 4183 tp.appendChild(node.firstChild); 4184 } 4185 node.appendChild(tp); 4186 this.textPath = wrap(tp); 4187 } 4188 } 4189 } 4190 })(-1); 4191 eve.on("snap.util.attr.text", function (value) { 4192 if (this.type == "text") { 4193 var i = 0, 4194 node = this.node, 4195 tuner = function (chunk) { 4196 var out = $("tspan"); 4197 if (is(chunk, "array")) { 4198 for (var i = 0; i < chunk.length; i++) { 4199 out.appendChild(tuner(chunk[i])); 4200 } 4201 } else { 4202 out.appendChild(glob.doc.createTextNode(chunk)); 4203 } 4204 out.normalize && out.normalize(); 4205 return out; 4206 }; 4207 while (node.firstChild) { 4208 node.removeChild(node.firstChild); 4209 } 4210 var tuned = tuner(value); 4211 while (tuned.firstChild) { 4212 node.appendChild(tuned.firstChild); 4213 } 4214 } 4215 eve.stop(); 4216 })(-1); 4217 function setFontSize(value) { 4218 eve.stop(); 4219 if (value == +value) { 4220 value += "px"; 4221 } 4222 this.node.style.fontSize = value; 4223 } 4224 eve.on("snap.util.attr.fontSize", setFontSize)(-1); 4225 eve.on("snap.util.attr.font-size", setFontSize)(-1); 4226 4227 4228 eve.on("snap.util.getattr.transform", function () { 4229 eve.stop(); 4230 return this.transform(); 4231 })(-1); 4232 eve.on("snap.util.getattr.textpath", function () { 4233 eve.stop(); 4234 return this.textPath; 4235 })(-1); 4236 // Markers 4237 (function () { 4238 function getter(end) { 4239 return function () { 4240 eve.stop(); 4241 var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end); 4242 if (style == "none") { 4243 return style; 4244 } else { 4245 return Snap(glob.doc.getElementById(style.match(reURLValue)[1])); 4246 } 4247 }; 4248 } 4249 function setter(end) { 4250 return function (value) { 4251 eve.stop(); 4252 var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1); 4253 if (value == "" || !value) { 4254 this.node.style[name] = "none"; 4255 return; 4256 } 4257 if (value.type == "marker") { 4258 var id = value.node.id; 4259 if (!id) { 4260 $(value.node, {id: value.id}); 4261 } 4262 this.node.style[name] = URL(id); 4263 return; 4264 } 4265 }; 4266 } 4267 eve.on("snap.util.getattr.marker-end", getter("end"))(-1); 4268 eve.on("snap.util.getattr.markerEnd", getter("end"))(-1); 4269 eve.on("snap.util.getattr.marker-start", getter("start"))(-1); 4270 eve.on("snap.util.getattr.markerStart", getter("start"))(-1); 4271 eve.on("snap.util.getattr.marker-mid", getter("mid"))(-1); 4272 eve.on("snap.util.getattr.markerMid", getter("mid"))(-1); 4273 eve.on("snap.util.attr.marker-end", setter("end"))(-1); 4274 eve.on("snap.util.attr.markerEnd", setter("end"))(-1); 4275 eve.on("snap.util.attr.marker-start", setter("start"))(-1); 4276 eve.on("snap.util.attr.markerStart", setter("start"))(-1); 4277 eve.on("snap.util.attr.marker-mid", setter("mid"))(-1); 4278 eve.on("snap.util.attr.markerMid", setter("mid"))(-1); 4279 }()); 4280 eve.on("snap.util.getattr.r", function () { 4281 if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) { 4282 eve.stop(); 4283 return $(this.node, "rx"); 4284 } 4285 })(-1); 4286 function textExtract(node) { 4287 var out = []; 4288 var children = node.childNodes; 4289 for (var i = 0, ii = children.length; i < ii; i++) { 4290 var chi = children[i]; 4291 if (chi.nodeType == 3) { 4292 out.push(chi.nodeValue); 4293 } 4294 if (chi.tagName == "tspan") { 4295 if (chi.childNodes.length == 1 && chi.firstChild.nodeType == 3) { 4296 out.push(chi.firstChild.nodeValue); 4297 } else { 4298 out.push(textExtract(chi)); 4299 } 4300 } 4301 } 4302 return out; 4303 } 4304 eve.on("snap.util.getattr.text", function () { 4305 if (this.type == "text" || this.type == "tspan") { 4306 eve.stop(); 4307 var out = textExtract(this.node); 4308 return out.length == 1 ? out[0] : out; 4309 } 4310 })(-1); 4311 eve.on("snap.util.getattr.#text", function () { 4312 return this.node.textContent; 4313 })(-1); 4314 eve.on("snap.util.getattr.viewBox", function () { 4315 eve.stop(); 4316 var vb = $(this.node, "viewBox"); 4317 if (vb) { 4318 vb = vb.split(separator); 4319 return Snap._.box(+vb[0], +vb[1], +vb[2], +vb[3]); 4320 } else { 4321 return; 4322 } 4323 })(-1); 4324 eve.on("snap.util.getattr.points", function () { 4325 var p = $(this.node, "points"); 4326 eve.stop(); 4327 if (p) { 4328 return p.split(separator); 4329 } else { 4330 return; 4331 } 4332 })(-1); 4333 eve.on("snap.util.getattr.path", function () { 4334 var p = $(this.node, "d"); 4335 eve.stop(); 4336 return p; 4337 })(-1); 4338 eve.on("snap.util.getattr.class", function () { 4339 return this.node.className.baseVal; 4340 })(-1); 4341 function getFontSize() { 4342 eve.stop(); 4343 return this.node.style.fontSize; 4344 } 4345 eve.on("snap.util.getattr.fontSize", getFontSize)(-1); 4346 eve.on("snap.util.getattr.font-size", getFontSize)(-1); 4347}); 4348 4349// Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved. 4350// 4351// Licensed under the Apache License, Version 2.0 (the "License"); 4352// you may not use this file except in compliance with the License. 4353// You may obtain a copy of the License at 4354// 4355// http://www.apache.org/licenses/LICENSE-2.0 4356// 4357// Unless required by applicable law or agreed to in writing, software 4358// distributed under the License is distributed on an "AS IS" BASIS, 4359// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4360// See the License for the specific language governing permissions and 4361// limitations under the License. 4362Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 4363 var rgNotSpace = /\S+/g, 4364 rgBadSpace = /[\t\r\n\f]/g, 4365 rgTrim = /(^\s+|\s+$)/g, 4366 Str = String, 4367 elproto = Element.prototype; 4368 /*\ 4369 * Element.addClass 4370 [ method ] 4371 ** 4372 * Adds given class name or list of class names to the element. 4373 - value (string) class name or space separated list of class names 4374 ** 4375 = (Element) original element. 4376 \*/ 4377 elproto.addClass = function (value) { 4378 var classes = Str(value || "").match(rgNotSpace) || [], 4379 elem = this.node, 4380 className = elem.className.baseVal, 4381 curClasses = className.match(rgNotSpace) || [], 4382 j, 4383 pos, 4384 clazz, 4385 finalValue; 4386 4387 if (classes.length) { 4388 j = 0; 4389 while ((clazz = classes[j++])) { 4390 pos = curClasses.indexOf(clazz); 4391 if (!~pos) { 4392 curClasses.push(clazz); 4393 } 4394 } 4395 4396 finalValue = curClasses.join(" "); 4397 if (className != finalValue) { 4398 elem.className.baseVal = finalValue; 4399 } 4400 } 4401 return this; 4402 }; 4403 /*\ 4404 * Element.removeClass 4405 [ method ] 4406 ** 4407 * Removes given class name or list of class names from the element. 4408 - value (string) class name or space separated list of class names 4409 ** 4410 = (Element) original element. 4411 \*/ 4412 elproto.removeClass = function (value) { 4413 var classes = Str(value || "").match(rgNotSpace) || [], 4414 elem = this.node, 4415 className = elem.className.baseVal, 4416 curClasses = className.match(rgNotSpace) || [], 4417 j, 4418 pos, 4419 clazz, 4420 finalValue; 4421 if (curClasses.length) { 4422 j = 0; 4423 while ((clazz = classes[j++])) { 4424 pos = curClasses.indexOf(clazz); 4425 if (~pos) { 4426 curClasses.splice(pos, 1); 4427 } 4428 } 4429 4430 finalValue = curClasses.join(" "); 4431 if (className != finalValue) { 4432 elem.className.baseVal = finalValue; 4433 } 4434 } 4435 return this; 4436 }; 4437 /*\ 4438 * Element.hasClass 4439 [ method ] 4440 ** 4441 * Checks if the element has a given class name in the list of class names applied to it. 4442 - value (string) class name 4443 ** 4444 = (boolean) `true` if the element has given class 4445 \*/ 4446 elproto.hasClass = function (value) { 4447 var elem = this.node, 4448 className = elem.className.baseVal, 4449 curClasses = className.match(rgNotSpace) || []; 4450 return !!~curClasses.indexOf(value); 4451 }; 4452 /*\ 4453 * Element.toggleClass 4454 [ method ] 4455 ** 4456 * Add or remove one or more classes from the element, depending on either 4457 * the class’s presence or the value of the `flag` argument. 4458 - value (string) class name or space separated list of class names 4459 - flag (boolean) value to determine whether the class should be added or removed 4460 ** 4461 = (Element) original element. 4462 \*/ 4463 elproto.toggleClass = function (value, flag) { 4464 if (flag != null) { 4465 if (flag) { 4466 return this.addClass(value); 4467 } else { 4468 return this.removeClass(value); 4469 } 4470 } 4471 var classes = (value || "").match(rgNotSpace) || [], 4472 elem = this.node, 4473 className = elem.className.baseVal, 4474 curClasses = className.match(rgNotSpace) || [], 4475 j, 4476 pos, 4477 clazz, 4478 finalValue; 4479 j = 0; 4480 while ((clazz = classes[j++])) { 4481 pos = curClasses.indexOf(clazz); 4482 if (~pos) { 4483 curClasses.splice(pos, 1); 4484 } else { 4485 curClasses.push(clazz); 4486 } 4487 } 4488 4489 finalValue = curClasses.join(" "); 4490 if (className != finalValue) { 4491 elem.className.baseVal = finalValue; 4492 } 4493 return this; 4494 }; 4495}); 4496 4497// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 4498// 4499// Licensed under the Apache License, Version 2.0 (the "License"); 4500// you may not use this file except in compliance with the License. 4501// You may obtain a copy of the License at 4502// 4503// http://www.apache.org/licenses/LICENSE-2.0 4504// 4505// Unless required by applicable law or agreed to in writing, software 4506// distributed under the License is distributed on an "AS IS" BASIS, 4507// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4508// See the License for the specific language governing permissions and 4509// limitations under the License. 4510Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 4511 var operators = { 4512 "+": function (x, y) { 4513 return x + y; 4514 }, 4515 "-": function (x, y) { 4516 return x - y; 4517 }, 4518 "/": function (x, y) { 4519 return x / y; 4520 }, 4521 "*": function (x, y) { 4522 return x * y; 4523 } 4524 }, 4525 Str = String, 4526 reUnit = /[a-z]+$/i, 4527 reAddon = /^\s*([+\-\/*])\s*=\s*([\d.eE+\-]+)\s*([^\d\s]+)?\s*$/; 4528 function getNumber(val) { 4529 return val; 4530 } 4531 function getUnit(unit) { 4532 return function (val) { 4533 return +val.toFixed(3) + unit; 4534 }; 4535 } 4536 eve.on("snap.util.attr", function (val) { 4537 var plus = Str(val).match(reAddon); 4538 if (plus) { 4539 var evnt = eve.nt(), 4540 name = evnt.substring(evnt.lastIndexOf(".") + 1), 4541 a = this.attr(name), 4542 atr = {}; 4543 eve.stop(); 4544 var unit = plus[3] || "", 4545 aUnit = a.match(reUnit), 4546 op = operators[plus[1]]; 4547 if (aUnit && aUnit == unit) { 4548 val = op(parseFloat(a), +plus[2]); 4549 } else { 4550 a = this.asPX(name); 4551 val = op(this.asPX(name), this.asPX(name, plus[2] + unit)); 4552 } 4553 if (isNaN(a) || isNaN(val)) { 4554 return; 4555 } 4556 atr[name] = val; 4557 this.attr(atr); 4558 } 4559 })(-10); 4560 eve.on("snap.util.equal", function (name, b) { 4561 var A, B, a = Str(this.attr(name) || ""), 4562 el = this, 4563 bplus = Str(b).match(reAddon); 4564 if (bplus) { 4565 eve.stop(); 4566 var unit = bplus[3] || "", 4567 aUnit = a.match(reUnit), 4568 op = operators[bplus[1]]; 4569 if (aUnit && aUnit == unit) { 4570 return { 4571 from: parseFloat(a), 4572 to: op(parseFloat(a), +bplus[2]), 4573 f: getUnit(aUnit) 4574 }; 4575 } else { 4576 a = this.asPX(name); 4577 return { 4578 from: a, 4579 to: op(a, this.asPX(name, bplus[2] + unit)), 4580 f: getNumber 4581 }; 4582 } 4583 } 4584 })(-10); 4585}); 4586// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 4587// 4588// Licensed under the Apache License, Version 2.0 (the "License"); 4589// you may not use this file except in compliance with the License. 4590// You may obtain a copy of the License at 4591// 4592// http://www.apache.org/licenses/LICENSE-2.0 4593// 4594// Unless required by applicable law or agreed to in writing, software 4595// distributed under the License is distributed on an "AS IS" BASIS, 4596// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4597// See the License for the specific language governing permissions and 4598// limitations under the License. 4599Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 4600 var proto = Paper.prototype, 4601 is = Snap.is; 4602 /*\ 4603 * Paper.rect 4604 [ method ] 4605 * 4606 * Draws a rectangle 4607 ** 4608 - x (number) x coordinate of the top left corner 4609 - y (number) y coordinate of the top left corner 4610 - width (number) width 4611 - height (number) height 4612 - rx (number) #optional horizontal radius for rounded corners, default is 0 4613 - ry (number) #optional vertical radius for rounded corners, default is rx or 0 4614 = (object) the `rect` element 4615 ** 4616 > Usage 4617 | // regular rectangle 4618 | var c = paper.rect(10, 10, 50, 50); 4619 | // rectangle with rounded corners 4620 | var c = paper.rect(40, 40, 50, 50, 10); 4621 \*/ 4622 proto.rect = function (x, y, w, h, rx, ry) { 4623 var attr; 4624 if (ry == null) { 4625 ry = rx; 4626 } 4627 if (is(x, "object") && x == "[object Object]") { 4628 attr = x; 4629 } else if (x != null) { 4630 attr = { 4631 x: x, 4632 y: y, 4633 width: w, 4634 height: h 4635 }; 4636 if (rx != null) { 4637 attr.rx = rx; 4638 attr.ry = ry; 4639 } 4640 } 4641 return this.el("rect", attr); 4642 }; 4643 /*\ 4644 * Paper.circle 4645 [ method ] 4646 ** 4647 * Draws a circle 4648 ** 4649 - x (number) x coordinate of the centre 4650 - y (number) y coordinate of the centre 4651 - r (number) radius 4652 = (object) the `circle` element 4653 ** 4654 > Usage 4655 | var c = paper.circle(50, 50, 40); 4656 \*/ 4657 proto.circle = function (cx, cy, r) { 4658 var attr; 4659 if (is(cx, "object") && cx == "[object Object]") { 4660 attr = cx; 4661 } else if (cx != null) { 4662 attr = { 4663 cx: cx, 4664 cy: cy, 4665 r: r 4666 }; 4667 } 4668 return this.el("circle", attr); 4669 }; 4670 4671 var preload = (function () { 4672 function onerror() { 4673 this.parentNode.removeChild(this); 4674 } 4675 return function (src, f) { 4676 var img = glob.doc.createElement("img"), 4677 body = glob.doc.body; 4678 img.style.cssText = "position:absolute;left:-9999em;top:-9999em"; 4679 img.onload = function () { 4680 f.call(img); 4681 img.onload = img.onerror = null; 4682 body.removeChild(img); 4683 }; 4684 img.onerror = onerror; 4685 body.appendChild(img); 4686 img.src = src; 4687 }; 4688 }()); 4689 4690 /*\ 4691 * Paper.image 4692 [ method ] 4693 ** 4694 * Places an image on the surface 4695 ** 4696 - src (string) URI of the source image 4697 - x (number) x offset position 4698 - y (number) y offset position 4699 - width (number) width of the image 4700 - height (number) height of the image 4701 = (object) the `image` element 4702 * or 4703 = (object) Snap element object with type `image` 4704 ** 4705 > Usage 4706 | var c = paper.image("apple.png", 10, 10, 80, 80); 4707 \*/ 4708 proto.image = function (src, x, y, width, height) { 4709 var el = this.el("image"); 4710 if (is(src, "object") && "src" in src) { 4711 el.attr(src); 4712 } else if (src != null) { 4713 var set = { 4714 "xlink:href": src, 4715 preserveAspectRatio: "none" 4716 }; 4717 if (x != null && y != null) { 4718 set.x = x; 4719 set.y = y; 4720 } 4721 if (width != null && height != null) { 4722 set.width = width; 4723 set.height = height; 4724 } else { 4725 preload(src, function () { 4726 Snap._.$(el.node, { 4727 width: this.offsetWidth, 4728 height: this.offsetHeight 4729 }); 4730 }); 4731 } 4732 Snap._.$(el.node, set); 4733 } 4734 return el; 4735 }; 4736 /*\ 4737 * Paper.ellipse 4738 [ method ] 4739 ** 4740 * Draws an ellipse 4741 ** 4742 - x (number) x coordinate of the centre 4743 - y (number) y coordinate of the centre 4744 - rx (number) horizontal radius 4745 - ry (number) vertical radius 4746 = (object) the `ellipse` element 4747 ** 4748 > Usage 4749 | var c = paper.ellipse(50, 50, 40, 20); 4750 \*/ 4751 proto.ellipse = function (cx, cy, rx, ry) { 4752 var attr; 4753 if (is(cx, "object") && cx == "[object Object]") { 4754 attr = cx; 4755 } else if (cx != null) { 4756 attr ={ 4757 cx: cx, 4758 cy: cy, 4759 rx: rx, 4760 ry: ry 4761 }; 4762 } 4763 return this.el("ellipse", attr); 4764 }; 4765 // SIERRA Paper.path(): Unclear from the link what a Catmull-Rom curveto is, and why it would make life any easier. 4766 /*\ 4767 * Paper.path 4768 [ method ] 4769 ** 4770 * Creates a `<path>` element using the given string as the path's definition 4771 - pathString (string) #optional path string in SVG format 4772 * Path string consists of one-letter commands, followed by comma seprarated arguments in numerical form. Example: 4773 | "M10,20L30,40" 4774 * This example features two commands: `M`, with arguments `(10, 20)` and `L` with arguments `(30, 40)`. Uppercase letter commands express coordinates in absolute terms, while lowercase commands express them in relative terms from the most recently declared coordinates. 4775 * 4776 # <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> or <a href="https://developer.mozilla.org/en/SVG/Tutorial/Paths">article about path strings at MDN</a>.</p> 4777 # <table><thead><tr><th>Command</th><th>Name</th><th>Parameters</th></tr></thead><tbody> 4778 # <tr><td>M</td><td>moveto</td><td>(x y)+</td></tr> 4779 # <tr><td>Z</td><td>closepath</td><td>(none)</td></tr> 4780 # <tr><td>L</td><td>lineto</td><td>(x y)+</td></tr> 4781 # <tr><td>H</td><td>horizontal lineto</td><td>x+</td></tr> 4782 # <tr><td>V</td><td>vertical lineto</td><td>y+</td></tr> 4783 # <tr><td>C</td><td>curveto</td><td>(x1 y1 x2 y2 x y)+</td></tr> 4784 # <tr><td>S</td><td>smooth curveto</td><td>(x2 y2 x y)+</td></tr> 4785 # <tr><td>Q</td><td>quadratic Bézier curveto</td><td>(x1 y1 x y)+</td></tr> 4786 # <tr><td>T</td><td>smooth quadratic Bézier curveto</td><td>(x y)+</td></tr> 4787 # <tr><td>A</td><td>elliptical arc</td><td>(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+</td></tr> 4788 # <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> 4789 * * _Catmull-Rom curveto_ is a not standard SVG command and added to make life easier. 4790 * Note: there is a special case when a path consists of only three commands: `M10,10R…z`. In this case the path connects back to its starting point. 4791 > Usage 4792 | var c = paper.path("M10 10L90 90"); 4793 | // draw a diagonal line: 4794 | // move to 10,10, line to 90,90 4795 \*/ 4796 proto.path = function (d) { 4797 var attr; 4798 if (is(d, "object") && !is(d, "array")) { 4799 attr = d; 4800 } else if (d) { 4801 attr = {d: d}; 4802 } 4803 return this.el("path", attr); 4804 }; 4805 /*\ 4806 * Paper.g 4807 [ method ] 4808 ** 4809 * Creates a group element 4810 ** 4811 - varargs (…) #optional elements to nest within the group 4812 = (object) the `g` element 4813 ** 4814 > Usage 4815 | var c1 = paper.circle(), 4816 | c2 = paper.rect(), 4817 | g = paper.g(c2, c1); // note that the order of elements is different 4818 * or 4819 | var c1 = paper.circle(), 4820 | c2 = paper.rect(), 4821 | g = paper.g(); 4822 | g.add(c2, c1); 4823 \*/ 4824 /*\ 4825 * Paper.group 4826 [ method ] 4827 ** 4828 * See @Paper.g 4829 \*/ 4830 proto.group = proto.g = function (first) { 4831 var attr, 4832 el = this.el("g"); 4833 if (arguments.length == 1 && first && !first.type) { 4834 el.attr(first); 4835 } else if (arguments.length) { 4836 el.add(Array.prototype.slice.call(arguments, 0)); 4837 } 4838 return el; 4839 }; 4840 /*\ 4841 * Paper.svg 4842 [ method ] 4843 ** 4844 * Creates a nested SVG element. 4845 - x (number) @optional X of the element 4846 - y (number) @optional Y of the element 4847 - width (number) @optional width of the element 4848 - height (number) @optional height of the element 4849 - vbx (number) @optional viewbox X 4850 - vby (number) @optional viewbox Y 4851 - vbw (number) @optional viewbox width 4852 - vbh (number) @optional viewbox height 4853 ** 4854 = (object) the `svg` element 4855 ** 4856 \*/ 4857 proto.svg = function (x, y, width, height, vbx, vby, vbw, vbh) { 4858 var attrs = {}; 4859 if (is(x, "object") && y == null) { 4860 attrs = x; 4861 } else { 4862 if (x != null) { 4863 attrs.x = x; 4864 } 4865 if (y != null) { 4866 attrs.y = y; 4867 } 4868 if (width != null) { 4869 attrs.width = width; 4870 } 4871 if (height != null) { 4872 attrs.height = height; 4873 } 4874 if (vbx != null && vby != null && vbw != null && vbh != null) { 4875 attrs.viewBox = [vbx, vby, vbw, vbh]; 4876 } 4877 } 4878 return this.el("svg", attrs); 4879 }; 4880 /*\ 4881 * Paper.mask 4882 [ method ] 4883 ** 4884 * Equivalent in behaviour to @Paper.g, except it’s a mask. 4885 ** 4886 = (object) the `mask` element 4887 ** 4888 \*/ 4889 proto.mask = function (first) { 4890 var attr, 4891 el = this.el("mask"); 4892 if (arguments.length == 1 && first && !first.type) { 4893 el.attr(first); 4894 } else if (arguments.length) { 4895 el.add(Array.prototype.slice.call(arguments, 0)); 4896 } 4897 return el; 4898 }; 4899 /*\ 4900 * Paper.ptrn 4901 [ method ] 4902 ** 4903 * Equivalent in behaviour to @Paper.g, except it’s a pattern. 4904 - x (number) @optional X of the element 4905 - y (number) @optional Y of the element 4906 - width (number) @optional width of the element 4907 - height (number) @optional height of the element 4908 - vbx (number) @optional viewbox X 4909 - vby (number) @optional viewbox Y 4910 - vbw (number) @optional viewbox width 4911 - vbh (number) @optional viewbox height 4912 ** 4913 = (object) the `pattern` element 4914 ** 4915 \*/ 4916 proto.ptrn = function (x, y, width, height, vx, vy, vw, vh) { 4917 if (is(x, "object")) { 4918 var attr = x; 4919 } else { 4920 attr = {patternUnits: "userSpaceOnUse"}; 4921 if (x) { 4922 attr.x = x; 4923 } 4924 if (y) { 4925 attr.y = y; 4926 } 4927 if (width != null) { 4928 attr.width = width; 4929 } 4930 if (height != null) { 4931 attr.height = height; 4932 } 4933 if (vx != null && vy != null && vw != null && vh != null) { 4934 attr.viewBox = [vx, vy, vw, vh]; 4935 } else { 4936 attr.viewBox = [x || 0, y || 0, width || 0, height || 0]; 4937 } 4938 } 4939 return this.el("pattern", attr); 4940 }; 4941 /*\ 4942 * Paper.use 4943 [ method ] 4944 ** 4945 * Creates a <use> element. 4946 - id (string) @optional id of element to link 4947 * or 4948 - id (Element) @optional element to link 4949 ** 4950 = (object) the `use` element 4951 ** 4952 \*/ 4953 proto.use = function (id) { 4954 if (id != null) { 4955 if (id instanceof Element) { 4956 if (!id.attr("id")) { 4957 id.attr({id: Snap._.id(id)}); 4958 } 4959 id = id.attr("id"); 4960 } 4961 if (String(id).charAt() == "#") { 4962 id = id.substring(1); 4963 } 4964 return this.el("use", {"xlink:href": "#" + id}); 4965 } else { 4966 return Element.prototype.use.call(this); 4967 } 4968 }; 4969 /*\ 4970 * Paper.symbol 4971 [ method ] 4972 ** 4973 * Creates a <symbol> element. 4974 - vbx (number) @optional viewbox X 4975 - vby (number) @optional viewbox Y 4976 - vbw (number) @optional viewbox width 4977 - vbh (number) @optional viewbox height 4978 = (object) the `symbol` element 4979 ** 4980 \*/ 4981 proto.symbol = function (vx, vy, vw, vh) { 4982 var attr = {}; 4983 if (vx != null && vy != null && vw != null && vh != null) { 4984 attr.viewBox = [vx, vy, vw, vh]; 4985 } 4986 4987 return this.el("symbol", attr); 4988 }; 4989 /*\ 4990 * Paper.text 4991 [ method ] 4992 ** 4993 * Draws a text string 4994 ** 4995 - x (number) x coordinate position 4996 - y (number) y coordinate position 4997 - text (string|array) The text string to draw or array of strings to nest within separate `<tspan>` elements 4998 = (object) the `text` element 4999 ** 5000 > Usage 5001 | var t1 = paper.text(50, 50, "Snap"); 5002 | var t2 = paper.text(50, 50, ["S","n","a","p"]); 5003 | // Text path usage 5004 | t1.attr({textpath: "M10,10L100,100"}); 5005 | // or 5006 | var pth = paper.path("M10,10L100,100"); 5007 | t1.attr({textpath: pth}); 5008 \*/ 5009 proto.text = function (x, y, text) { 5010 var attr = {}; 5011 if (is(x, "object")) { 5012 attr = x; 5013 } else if (x != null) { 5014 attr = { 5015 x: x, 5016 y: y, 5017 text: text || "" 5018 }; 5019 } 5020 return this.el("text", attr); 5021 }; 5022 /*\ 5023 * Paper.line 5024 [ method ] 5025 ** 5026 * Draws a line 5027 ** 5028 - x1 (number) x coordinate position of the start 5029 - y1 (number) y coordinate position of the start 5030 - x2 (number) x coordinate position of the end 5031 - y2 (number) y coordinate position of the end 5032 = (object) the `line` element 5033 ** 5034 > Usage 5035 | var t1 = paper.line(50, 50, 100, 100); 5036 \*/ 5037 proto.line = function (x1, y1, x2, y2) { 5038 var attr = {}; 5039 if (is(x1, "object")) { 5040 attr = x1; 5041 } else if (x1 != null) { 5042 attr = { 5043 x1: x1, 5044 x2: x2, 5045 y1: y1, 5046 y2: y2 5047 }; 5048 } 5049 return this.el("line", attr); 5050 }; 5051 /*\ 5052 * Paper.polyline 5053 [ method ] 5054 ** 5055 * Draws a polyline 5056 ** 5057 - points (array) array of points 5058 * or 5059 - varargs (…) points 5060 = (object) the `polyline` element 5061 ** 5062 > Usage 5063 | var p1 = paper.polyline([10, 10, 100, 100]); 5064 | var p2 = paper.polyline(10, 10, 100, 100); 5065 \*/ 5066 proto.polyline = function (points) { 5067 if (arguments.length > 1) { 5068 points = Array.prototype.slice.call(arguments, 0); 5069 } 5070 var attr = {}; 5071 if (is(points, "object") && !is(points, "array")) { 5072 attr = points; 5073 } else if (points != null) { 5074 attr = {points: points}; 5075 } 5076 return this.el("polyline", attr); 5077 }; 5078 /*\ 5079 * Paper.polygon 5080 [ method ] 5081 ** 5082 * Draws a polygon. See @Paper.polyline 5083 \*/ 5084 proto.polygon = function (points) { 5085 if (arguments.length > 1) { 5086 points = Array.prototype.slice.call(arguments, 0); 5087 } 5088 var attr = {}; 5089 if (is(points, "object") && !is(points, "array")) { 5090 attr = points; 5091 } else if (points != null) { 5092 attr = {points: points}; 5093 } 5094 return this.el("polygon", attr); 5095 }; 5096 // gradients 5097 (function () { 5098 var $ = Snap._.$; 5099 // gradients' helpers 5100 function Gstops() { 5101 return this.selectAll("stop"); 5102 } 5103 function GaddStop(color, offset) { 5104 var stop = $("stop"), 5105 attr = { 5106 offset: +offset + "%" 5107 }; 5108 color = Snap.color(color); 5109 attr["stop-color"] = color.hex; 5110 if (color.opacity < 1) { 5111 attr["stop-opacity"] = color.opacity; 5112 } 5113 $(stop, attr); 5114 this.node.appendChild(stop); 5115 return this; 5116 } 5117 function GgetBBox() { 5118 if (this.type == "linearGradient") { 5119 var x1 = $(this.node, "x1") || 0, 5120 x2 = $(this.node, "x2") || 1, 5121 y1 = $(this.node, "y1") || 0, 5122 y2 = $(this.node, "y2") || 0; 5123 return Snap._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1)); 5124 } else { 5125 var cx = this.node.cx || .5, 5126 cy = this.node.cy || .5, 5127 r = this.node.r || 0; 5128 return Snap._.box(cx - r, cy - r, r * 2, r * 2); 5129 } 5130 } 5131 function gradient(defs, str) { 5132 var grad = eve("snap.util.grad.parse", null, str).firstDefined(), 5133 el; 5134 if (!grad) { 5135 return null; 5136 } 5137 grad.params.unshift(defs); 5138 if (grad.type.toLowerCase() == "l") { 5139 el = gradientLinear.apply(0, grad.params); 5140 } else { 5141 el = gradientRadial.apply(0, grad.params); 5142 } 5143 if (grad.type != grad.type.toLowerCase()) { 5144 $(el.node, { 5145 gradientUnits: "userSpaceOnUse" 5146 }); 5147 } 5148 var stops = grad.stops, 5149 len = stops.length, 5150 start = 0, 5151 j = 0; 5152 function seed(i, end) { 5153 var step = (end - start) / (i - j); 5154 for (var k = j; k < i; k++) { 5155 stops[k].offset = +(+start + step * (k - j)).toFixed(2); 5156 } 5157 j = i; 5158 start = end; 5159 } 5160 len--; 5161 for (var i = 0; i < len; i++) if ("offset" in stops[i]) { 5162 seed(i, stops[i].offset); 5163 } 5164 stops[len].offset = stops[len].offset || 100; 5165 seed(len, stops[len].offset); 5166 for (i = 0; i <= len; i++) { 5167 var stop = stops[i]; 5168 el.addStop(stop.color, stop.offset); 5169 } 5170 return el; 5171 } 5172 function gradientLinear(defs, x1, y1, x2, y2) { 5173 var el = Snap._.make("linearGradient", defs); 5174 el.stops = Gstops; 5175 el.addStop = GaddStop; 5176 el.getBBox = GgetBBox; 5177 if (x1 != null) { 5178 $(el.node, { 5179 x1: x1, 5180 y1: y1, 5181 x2: x2, 5182 y2: y2 5183 }); 5184 } 5185 return el; 5186 } 5187 function gradientRadial(defs, cx, cy, r, fx, fy) { 5188 var el = Snap._.make("radialGradient", defs); 5189 el.stops = Gstops; 5190 el.addStop = GaddStop; 5191 el.getBBox = GgetBBox; 5192 if (cx != null) { 5193 $(el.node, { 5194 cx: cx, 5195 cy: cy, 5196 r: r 5197 }); 5198 } 5199 if (fx != null && fy != null) { 5200 $(el.node, { 5201 fx: fx, 5202 fy: fy 5203 }); 5204 } 5205 return el; 5206 } 5207 /*\ 5208 * Paper.gradient 5209 [ method ] 5210 ** 5211 * Creates a gradient element 5212 ** 5213 - gradient (string) gradient descriptor 5214 > Gradient Descriptor 5215 * The gradient descriptor is an expression formatted as 5216 * follows: `<type>(<coords>)<colors>`. The `<type>` can be 5217 * either linear or radial. The uppercase `L` or `R` letters 5218 * indicate absolute coordinates offset from the SVG surface. 5219 * Lowercase `l` or `r` letters indicate coordinates 5220 * calculated relative to the element to which the gradient is 5221 * applied. Coordinates specify a linear gradient vector as 5222 * `x1`, `y1`, `x2`, `y2`, or a radial gradient as `cx`, `cy`, 5223 * `r` and optional `fx`, `fy` specifying a focal point away 5224 * from the center of the circle. Specify `<colors>` as a list 5225 * of dash-separated CSS color values. Each color may be 5226 * followed by a custom offset value, separated with a colon 5227 * character. 5228 > Examples 5229 * Linear gradient, relative from top-left corner to bottom-right 5230 * corner, from black through red to white: 5231 | var g = paper.gradient("l(0, 0, 1, 1)#000-#f00-#fff"); 5232 * Linear gradient, absolute from (0, 0) to (100, 100), from black 5233 * through red at 25% to white: 5234 | var g = paper.gradient("L(0, 0, 100, 100)#000-#f00:25-#fff"); 5235 * Radial gradient, relative from the center of the element with radius 5236 * half the width, from black to white: 5237 | var g = paper.gradient("r(0.5, 0.5, 0.5)#000-#fff"); 5238 * To apply the gradient: 5239 | paper.circle(50, 50, 40).attr({ 5240 | fill: g 5241 | }); 5242 = (object) the `gradient` element 5243 \*/ 5244 proto.gradient = function (str) { 5245 return gradient(this.defs, str); 5246 }; 5247 proto.gradientLinear = function (x1, y1, x2, y2) { 5248 return gradientLinear(this.defs, x1, y1, x2, y2); 5249 }; 5250 proto.gradientRadial = function (cx, cy, r, fx, fy) { 5251 return gradientRadial(this.defs, cx, cy, r, fx, fy); 5252 }; 5253 /*\ 5254 * Paper.toString 5255 [ method ] 5256 ** 5257 * Returns SVG code for the @Paper 5258 = (string) SVG code for the @Paper 5259 \*/ 5260 proto.toString = function () { 5261 var doc = this.node.ownerDocument, 5262 f = doc.createDocumentFragment(), 5263 d = doc.createElement("div"), 5264 svg = this.node.cloneNode(true), 5265 res; 5266 f.appendChild(d); 5267 d.appendChild(svg); 5268 Snap._.$(svg, {xmlns: "http://www.w3.org/2000/svg"}); 5269 res = d.innerHTML; 5270 f.removeChild(f.firstChild); 5271 return res; 5272 }; 5273 /*\ 5274 * Paper.toDataURL 5275 [ method ] 5276 ** 5277 * Returns SVG code for the @Paper as Data URI string. 5278 = (string) Data URI string 5279 \*/ 5280 proto.toDataURL = function () { 5281 if (window && window.btoa) { 5282 return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(this))); 5283 } 5284 }; 5285 /*\ 5286 * Paper.clear 5287 [ method ] 5288 ** 5289 * Removes all child nodes of the paper, except <defs>. 5290 \*/ 5291 proto.clear = function () { 5292 var node = this.node.firstChild, 5293 next; 5294 while (node) { 5295 next = node.nextSibling; 5296 if (node.tagName != "defs") { 5297 node.parentNode.removeChild(node); 5298 } else { 5299 proto.clear.call({node: node}); 5300 } 5301 node = next; 5302 } 5303 }; 5304 }()); 5305}); 5306 5307// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 5308// 5309// Licensed under the Apache License, Version 2.0 (the "License"); 5310// you may not use this file except in compliance with the License. 5311// You may obtain a copy of the License at 5312// 5313// http://www.apache.org/licenses/LICENSE-2.0 5314// 5315// Unless required by applicable law or agreed to in writing, software 5316// distributed under the License is distributed on an "AS IS" BASIS, 5317// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5318// See the License for the specific language governing permissions and 5319// limitations under the License. 5320Snap.plugin(function (Snap, Element, Paper, glob) { 5321 var elproto = Element.prototype, 5322 is = Snap.is, 5323 clone = Snap._.clone, 5324 has = "hasOwnProperty", 5325 p2s = /,?([a-z]),?/gi, 5326 toFloat = parseFloat, 5327 math = Math, 5328 PI = math.PI, 5329 mmin = math.min, 5330 mmax = math.max, 5331 pow = math.pow, 5332 abs = math.abs; 5333 function paths(ps) { 5334 var p = paths.ps = paths.ps || {}; 5335 if (p[ps]) { 5336 p[ps].sleep = 100; 5337 } else { 5338 p[ps] = { 5339 sleep: 100 5340 }; 5341 } 5342 setTimeout(function () { 5343 for (var key in p) if (p[has](key) && key != ps) { 5344 p[key].sleep--; 5345 !p[key].sleep && delete p[key]; 5346 } 5347 }); 5348 return p[ps]; 5349 } 5350 function box(x, y, width, height) { 5351 if (x == null) { 5352 x = y = width = height = 0; 5353 } 5354 if (y == null) { 5355 y = x.y; 5356 width = x.width; 5357 height = x.height; 5358 x = x.x; 5359 } 5360 return { 5361 x: x, 5362 y: y, 5363 width: width, 5364 w: width, 5365 height: height, 5366 h: height, 5367 x2: x + width, 5368 y2: y + height, 5369 cx: x + width / 2, 5370 cy: y + height / 2, 5371 r1: math.min(width, height) / 2, 5372 r2: math.max(width, height) / 2, 5373 r0: math.sqrt(width * width + height * height) / 2, 5374 path: rectPath(x, y, width, height), 5375 vb: [x, y, width, height].join(" ") 5376 }; 5377 } 5378 function toString() { 5379 return this.join(",").replace(p2s, "$1"); 5380 } 5381 function pathClone(pathArray) { 5382 var res = clone(pathArray); 5383 res.toString = toString; 5384 return res; 5385 } 5386 function getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { 5387 if (length == null) { 5388 return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); 5389 } else { 5390 return findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, 5391 getTotLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length)); 5392 } 5393 } 5394 function getLengthFactory(istotal, subpath) { 5395 function O(val) { 5396 return +(+val).toFixed(3); 5397 } 5398 return Snap._.cacher(function (path, length, onlystart) { 5399 if (path instanceof Element) { 5400 path = path.attr("d"); 5401 } 5402 path = path2curve(path); 5403 var x, y, p, l, sp = "", subpaths = {}, point, 5404 len = 0; 5405 for (var i = 0, ii = path.length; i < ii; i++) { 5406 p = path[i]; 5407 if (p[0] == "M") { 5408 x = +p[1]; 5409 y = +p[2]; 5410 } else { 5411 l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); 5412 if (len + l > length) { 5413 if (subpath && !subpaths.start) { 5414 point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); 5415 sp += [ 5416 "C" + O(point.start.x), 5417 O(point.start.y), 5418 O(point.m.x), 5419 O(point.m.y), 5420 O(point.x), 5421 O(point.y) 5422 ]; 5423 if (onlystart) {return sp;} 5424 subpaths.start = sp; 5425 sp = [ 5426 "M" + O(point.x), 5427 O(point.y) + "C" + O(point.n.x), 5428 O(point.n.y), 5429 O(point.end.x), 5430 O(point.end.y), 5431 O(p[5]), 5432 O(p[6]) 5433 ].join(); 5434 len += l; 5435 x = +p[5]; 5436 y = +p[6]; 5437 continue; 5438 } 5439 if (!istotal && !subpath) { 5440 point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); 5441 return point; 5442 } 5443 } 5444 len += l; 5445 x = +p[5]; 5446 y = +p[6]; 5447 } 5448 sp += p.shift() + p; 5449 } 5450 subpaths.end = sp; 5451 point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); 5452 return point; 5453 }, null, Snap._.clone); 5454 } 5455 var getTotalLength = getLengthFactory(1), 5456 getPointAtLength = getLengthFactory(), 5457 getSubpathsAtLength = getLengthFactory(0, 1); 5458 function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { 5459 var t1 = 1 - t, 5460 t13 = pow(t1, 3), 5461 t12 = pow(t1, 2), 5462 t2 = t * t, 5463 t3 = t2 * t, 5464 x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, 5465 y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y, 5466 mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), 5467 my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), 5468 nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), 5469 ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), 5470 ax = t1 * p1x + t * c1x, 5471 ay = t1 * p1y + t * c1y, 5472 cx = t1 * c2x + t * p2x, 5473 cy = t1 * c2y + t * p2y, 5474 alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI); 5475 // (mx > nx || my < ny) && (alpha += 180); 5476 return { 5477 x: x, 5478 y: y, 5479 m: {x: mx, y: my}, 5480 n: {x: nx, y: ny}, 5481 start: {x: ax, y: ay}, 5482 end: {x: cx, y: cy}, 5483 alpha: alpha 5484 }; 5485 } 5486 function bezierBBox(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { 5487 if (!Snap.is(p1x, "array")) { 5488 p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y]; 5489 } 5490 var bbox = curveDim.apply(null, p1x); 5491 return box( 5492 bbox.min.x, 5493 bbox.min.y, 5494 bbox.max.x - bbox.min.x, 5495 bbox.max.y - bbox.min.y 5496 ); 5497 } 5498 function isPointInsideBBox(bbox, x, y) { 5499 return x >= bbox.x && 5500 x <= bbox.x + bbox.width && 5501 y >= bbox.y && 5502 y <= bbox.y + bbox.height; 5503 } 5504 function isBBoxIntersect(bbox1, bbox2) { 5505 bbox1 = box(bbox1); 5506 bbox2 = box(bbox2); 5507 return isPointInsideBBox(bbox2, bbox1.x, bbox1.y) 5508 || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y) 5509 || isPointInsideBBox(bbox2, bbox1.x, bbox1.y2) 5510 || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y2) 5511 || isPointInsideBBox(bbox1, bbox2.x, bbox2.y) 5512 || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y) 5513 || isPointInsideBBox(bbox1, bbox2.x, bbox2.y2) 5514 || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y2) 5515 || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x 5516 || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) 5517 && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y 5518 || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y); 5519 } 5520 function base3(t, p1, p2, p3, p4) { 5521 var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, 5522 t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; 5523 return t * t2 - 3 * p1 + 3 * p2; 5524 } 5525 function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { 5526 if (z == null) { 5527 z = 1; 5528 } 5529 z = z > 1 ? 1 : z < 0 ? 0 : z; 5530 var z2 = z / 2, 5531 n = 12, 5532 Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816], 5533 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], 5534 sum = 0; 5535 for (var i = 0; i < n; i++) { 5536 var ct = z2 * Tvalues[i] + z2, 5537 xbase = base3(ct, x1, x2, x3, x4), 5538 ybase = base3(ct, y1, y2, y3, y4), 5539 comb = xbase * xbase + ybase * ybase; 5540 sum += Cvalues[i] * math.sqrt(comb); 5541 } 5542 return z2 * sum; 5543 } 5544 function getTotLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) { 5545 if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) { 5546 return; 5547 } 5548 var t = 1, 5549 step = t / 2, 5550 t2 = t - step, 5551 l, 5552 e = .01; 5553 l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); 5554 while (abs(l - ll) > e) { 5555 step /= 2; 5556 t2 += (l < ll ? 1 : -1) * step; 5557 l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); 5558 } 5559 return t2; 5560 } 5561 function intersect(x1, y1, x2, y2, x3, y3, x4, y4) { 5562 if ( 5563 mmax(x1, x2) < mmin(x3, x4) || 5564 mmin(x1, x2) > mmax(x3, x4) || 5565 mmax(y1, y2) < mmin(y3, y4) || 5566 mmin(y1, y2) > mmax(y3, y4) 5567 ) { 5568 return; 5569 } 5570 var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), 5571 ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), 5572 denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); 5573 5574 if (!denominator) { 5575 return; 5576 } 5577 var px = nx / denominator, 5578 py = ny / denominator, 5579 px2 = +px.toFixed(2), 5580 py2 = +py.toFixed(2); 5581 if ( 5582 px2 < +mmin(x1, x2).toFixed(2) || 5583 px2 > +mmax(x1, x2).toFixed(2) || 5584 px2 < +mmin(x3, x4).toFixed(2) || 5585 px2 > +mmax(x3, x4).toFixed(2) || 5586 py2 < +mmin(y1, y2).toFixed(2) || 5587 py2 > +mmax(y1, y2).toFixed(2) || 5588 py2 < +mmin(y3, y4).toFixed(2) || 5589 py2 > +mmax(y3, y4).toFixed(2) 5590 ) { 5591 return; 5592 } 5593 return {x: px, y: py}; 5594 } 5595 function inter(bez1, bez2) { 5596 return interHelper(bez1, bez2); 5597 } 5598 function interCount(bez1, bez2) { 5599 return interHelper(bez1, bez2, 1); 5600 } 5601 function interHelper(bez1, bez2, justCount) { 5602 var bbox1 = bezierBBox(bez1), 5603 bbox2 = bezierBBox(bez2); 5604 if (!isBBoxIntersect(bbox1, bbox2)) { 5605 return justCount ? 0 : []; 5606 } 5607 var l1 = bezlen.apply(0, bez1), 5608 l2 = bezlen.apply(0, bez2), 5609 n1 = ~~(l1 / 8), 5610 n2 = ~~(l2 / 8), 5611 dots1 = [], 5612 dots2 = [], 5613 xy = {}, 5614 res = justCount ? 0 : []; 5615 for (var i = 0; i < n1 + 1; i++) { 5616 var p = findDotsAtSegment.apply(0, bez1.concat(i / n1)); 5617 dots1.push({x: p.x, y: p.y, t: i / n1}); 5618 } 5619 for (i = 0; i < n2 + 1; i++) { 5620 p = findDotsAtSegment.apply(0, bez2.concat(i / n2)); 5621 dots2.push({x: p.x, y: p.y, t: i / n2}); 5622 } 5623 for (i = 0; i < n1; i++) { 5624 for (var j = 0; j < n2; j++) { 5625 var di = dots1[i], 5626 di1 = dots1[i + 1], 5627 dj = dots2[j], 5628 dj1 = dots2[j + 1], 5629 ci = abs(di1.x - di.x) < .001 ? "y" : "x", 5630 cj = abs(dj1.x - dj.x) < .001 ? "y" : "x", 5631 is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y); 5632 if (is) { 5633 if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) { 5634 continue; 5635 } 5636 xy[is.x.toFixed(4)] = is.y.toFixed(4); 5637 var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), 5638 t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t); 5639 if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { 5640 if (justCount) { 5641 res++; 5642 } else { 5643 res.push({ 5644 x: is.x, 5645 y: is.y, 5646 t1: t1, 5647 t2: t2 5648 }); 5649 } 5650 } 5651 } 5652 } 5653 } 5654 return res; 5655 } 5656 function pathIntersection(path1, path2) { 5657 return interPathHelper(path1, path2); 5658 } 5659 function pathIntersectionNumber(path1, path2) { 5660 return interPathHelper(path1, path2, 1); 5661 } 5662 function interPathHelper(path1, path2, justCount) { 5663 path1 = path2curve(path1); 5664 path2 = path2curve(path2); 5665 var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, 5666 res = justCount ? 0 : []; 5667 for (var i = 0, ii = path1.length; i < ii; i++) { 5668 var pi = path1[i]; 5669 if (pi[0] == "M") { 5670 x1 = x1m = pi[1]; 5671 y1 = y1m = pi[2]; 5672 } else { 5673 if (pi[0] == "C") { 5674 bez1 = [x1, y1].concat(pi.slice(1)); 5675 x1 = bez1[6]; 5676 y1 = bez1[7]; 5677 } else { 5678 bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m]; 5679 x1 = x1m; 5680 y1 = y1m; 5681 } 5682 for (var j = 0, jj = path2.length; j < jj; j++) { 5683 var pj = path2[j]; 5684 if (pj[0] == "M") { 5685 x2 = x2m = pj[1]; 5686 y2 = y2m = pj[2]; 5687 } else { 5688 if (pj[0] == "C") { 5689 bez2 = [x2, y2].concat(pj.slice(1)); 5690 x2 = bez2[6]; 5691 y2 = bez2[7]; 5692 } else { 5693 bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m]; 5694 x2 = x2m; 5695 y2 = y2m; 5696 } 5697 var intr = interHelper(bez1, bez2, justCount); 5698 if (justCount) { 5699 res += intr; 5700 } else { 5701 for (var k = 0, kk = intr.length; k < kk; k++) { 5702 intr[k].segment1 = i; 5703 intr[k].segment2 = j; 5704 intr[k].bez1 = bez1; 5705 intr[k].bez2 = bez2; 5706 } 5707 res = res.concat(intr); 5708 } 5709 } 5710 } 5711 } 5712 } 5713 return res; 5714 } 5715 function isPointInsidePath(path, x, y) { 5716 var bbox = pathBBox(path); 5717 return isPointInsideBBox(bbox, x, y) && 5718 interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1; 5719 } 5720 function pathBBox(path) { 5721 var pth = paths(path); 5722 if (pth.bbox) { 5723 return clone(pth.bbox); 5724 } 5725 if (!path) { 5726 return box(); 5727 } 5728 path = path2curve(path); 5729 var x = 0, 5730 y = 0, 5731 X = [], 5732 Y = [], 5733 p; 5734 for (var i = 0, ii = path.length; i < ii; i++) { 5735 p = path[i]; 5736 if (p[0] == "M") { 5737 x = p[1]; 5738 y = p[2]; 5739 X.push(x); 5740 Y.push(y); 5741 } else { 5742 var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); 5743 X = X.concat(dim.min.x, dim.max.x); 5744 Y = Y.concat(dim.min.y, dim.max.y); 5745 x = p[5]; 5746 y = p[6]; 5747 } 5748 } 5749 var xmin = mmin.apply(0, X), 5750 ymin = mmin.apply(0, Y), 5751 xmax = mmax.apply(0, X), 5752 ymax = mmax.apply(0, Y), 5753 bb = box(xmin, ymin, xmax - xmin, ymax - ymin); 5754 pth.bbox = clone(bb); 5755 return bb; 5756 } 5757 function rectPath(x, y, w, h, r) { 5758 if (r) { 5759 return [ 5760 ["M", +x + (+r), y], 5761 ["l", w - r * 2, 0], 5762 ["a", r, r, 0, 0, 1, r, r], 5763 ["l", 0, h - r * 2], 5764 ["a", r, r, 0, 0, 1, -r, r], 5765 ["l", r * 2 - w, 0], 5766 ["a", r, r, 0, 0, 1, -r, -r], 5767 ["l", 0, r * 2 - h], 5768 ["a", r, r, 0, 0, 1, r, -r], 5769 ["z"] 5770 ]; 5771 } 5772 var res = [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]]; 5773 res.toString = toString; 5774 return res; 5775 } 5776 function ellipsePath(x, y, rx, ry, a) { 5777 if (a == null && ry == null) { 5778 ry = rx; 5779 } 5780 x = +x; 5781 y = +y; 5782 rx = +rx; 5783 ry = +ry; 5784 if (a != null) { 5785 var rad = Math.PI / 180, 5786 x1 = x + rx * Math.cos(-ry * rad), 5787 x2 = x + rx * Math.cos(-a * rad), 5788 y1 = y + rx * Math.sin(-ry * rad), 5789 y2 = y + rx * Math.sin(-a * rad), 5790 res = [["M", x1, y1], ["A", rx, rx, 0, +(a - ry > 180), 0, x2, y2]]; 5791 } else { 5792 res = [ 5793 ["M", x, y], 5794 ["m", 0, -ry], 5795 ["a", rx, ry, 0, 1, 1, 0, 2 * ry], 5796 ["a", rx, ry, 0, 1, 1, 0, -2 * ry], 5797 ["z"] 5798 ]; 5799 } 5800 res.toString = toString; 5801 return res; 5802 } 5803 var unit2px = Snap._unit2px, 5804 getPath = { 5805 path: function (el) { 5806 return el.attr("path"); 5807 }, 5808 circle: function (el) { 5809 var attr = unit2px(el); 5810 return ellipsePath(attr.cx, attr.cy, attr.r); 5811 }, 5812 ellipse: function (el) { 5813 var attr = unit2px(el); 5814 return ellipsePath(attr.cx || 0, attr.cy || 0, attr.rx, attr.ry); 5815 }, 5816 rect: function (el) { 5817 var attr = unit2px(el); 5818 return rectPath(attr.x || 0, attr.y || 0, attr.width, attr.height, attr.rx, attr.ry); 5819 }, 5820 image: function (el) { 5821 var attr = unit2px(el); 5822 return rectPath(attr.x || 0, attr.y || 0, attr.width, attr.height); 5823 }, 5824 line: function (el) { 5825 return "M" + [el.attr("x1") || 0, el.attr("y1") || 0, el.attr("x2"), el.attr("y2")]; 5826 }, 5827 polyline: function (el) { 5828 return "M" + el.attr("points"); 5829 }, 5830 polygon: function (el) { 5831 return "M" + el.attr("points") + "z"; 5832 }, 5833 deflt: function (el) { 5834 var bbox = el.node.getBBox(); 5835 return rectPath(bbox.x, bbox.y, bbox.width, bbox.height); 5836 } 5837 }; 5838 function pathToRelative(pathArray) { 5839 var pth = paths(pathArray), 5840 lowerCase = String.prototype.toLowerCase; 5841 if (pth.rel) { 5842 return pathClone(pth.rel); 5843 } 5844 if (!Snap.is(pathArray, "array") || !Snap.is(pathArray && pathArray[0], "array")) { 5845 pathArray = Snap.parsePathString(pathArray); 5846 } 5847 var res = [], 5848 x = 0, 5849 y = 0, 5850 mx = 0, 5851 my = 0, 5852 start = 0; 5853 if (pathArray[0][0] == "M") { 5854 x = pathArray[0][1]; 5855 y = pathArray[0][2]; 5856 mx = x; 5857 my = y; 5858 start++; 5859 res.push(["M", x, y]); 5860 } 5861 for (var i = start, ii = pathArray.length; i < ii; i++) { 5862 var r = res[i] = [], 5863 pa = pathArray[i]; 5864 if (pa[0] != lowerCase.call(pa[0])) { 5865 r[0] = lowerCase.call(pa[0]); 5866 switch (r[0]) { 5867 case "a": 5868 r[1] = pa[1]; 5869 r[2] = pa[2]; 5870 r[3] = pa[3]; 5871 r[4] = pa[4]; 5872 r[5] = pa[5]; 5873 r[6] = +(pa[6] - x).toFixed(3); 5874 r[7] = +(pa[7] - y).toFixed(3); 5875 break; 5876 case "v": 5877 r[1] = +(pa[1] - y).toFixed(3); 5878 break; 5879 case "m": 5880 mx = pa[1]; 5881 my = pa[2]; 5882 default: 5883 for (var j = 1, jj = pa.length; j < jj; j++) { 5884 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); 5885 } 5886 } 5887 } else { 5888 r = res[i] = []; 5889 if (pa[0] == "m") { 5890 mx = pa[1] + x; 5891 my = pa[2] + y; 5892 } 5893 for (var k = 0, kk = pa.length; k < kk; k++) { 5894 res[i][k] = pa[k]; 5895 } 5896 } 5897 var len = res[i].length; 5898 switch (res[i][0]) { 5899 case "z": 5900 x = mx; 5901 y = my; 5902 break; 5903 case "h": 5904 x += +res[i][len - 1]; 5905 break; 5906 case "v": 5907 y += +res[i][len - 1]; 5908 break; 5909 default: 5910 x += +res[i][len - 2]; 5911 y += +res[i][len - 1]; 5912 } 5913 } 5914 res.toString = toString; 5915 pth.rel = pathClone(res); 5916 return res; 5917 } 5918 function pathToAbsolute(pathArray) { 5919 var pth = paths(pathArray); 5920 if (pth.abs) { 5921 return pathClone(pth.abs); 5922 } 5923 if (!is(pathArray, "array") || !is(pathArray && pathArray[0], "array")) { // rough assumption 5924 pathArray = Snap.parsePathString(pathArray); 5925 } 5926 if (!pathArray || !pathArray.length) { 5927 return [["M", 0, 0]]; 5928 } 5929 var res = [], 5930 x = 0, 5931 y = 0, 5932 mx = 0, 5933 my = 0, 5934 start = 0, 5935 pa0; 5936 if (pathArray[0][0] == "M") { 5937 x = +pathArray[0][1]; 5938 y = +pathArray[0][2]; 5939 mx = x; 5940 my = y; 5941 start++; 5942 res[0] = ["M", x, y]; 5943 } 5944 var crz = pathArray.length == 3 && 5945 pathArray[0][0] == "M" && 5946 pathArray[1][0].toUpperCase() == "R" && 5947 pathArray[2][0].toUpperCase() == "Z"; 5948 for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { 5949 res.push(r = []); 5950 pa = pathArray[i]; 5951 pa0 = pa[0]; 5952 if (pa0 != pa0.toUpperCase()) { 5953 r[0] = pa0.toUpperCase(); 5954 switch (r[0]) { 5955 case "A": 5956 r[1] = pa[1]; 5957 r[2] = pa[2]; 5958 r[3] = pa[3]; 5959 r[4] = pa[4]; 5960 r[5] = pa[5]; 5961 r[6] = +pa[6] + x; 5962 r[7] = +pa[7] + y; 5963 break; 5964 case "V": 5965 r[1] = +pa[1] + y; 5966 break; 5967 case "H": 5968 r[1] = +pa[1] + x; 5969 break; 5970 case "R": 5971 var dots = [x, y].concat(pa.slice(1)); 5972 for (var j = 2, jj = dots.length; j < jj; j++) { 5973 dots[j] = +dots[j] + x; 5974 dots[++j] = +dots[j] + y; 5975 } 5976 res.pop(); 5977 res = res.concat(catmullRom2bezier(dots, crz)); 5978 break; 5979 case "O": 5980 res.pop(); 5981 dots = ellipsePath(x, y, pa[1], pa[2]); 5982 dots.push(dots[0]); 5983 res = res.concat(dots); 5984 break; 5985 case "U": 5986 res.pop(); 5987 res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); 5988 r = ["U"].concat(res[res.length - 1].slice(-2)); 5989 break; 5990 case "M": 5991 mx = +pa[1] + x; 5992 my = +pa[2] + y; 5993 default: 5994 for (j = 1, jj = pa.length; j < jj; j++) { 5995 r[j] = +pa[j] + ((j % 2) ? x : y); 5996 } 5997 } 5998 } else if (pa0 == "R") { 5999 dots = [x, y].concat(pa.slice(1)); 6000 res.pop(); 6001 res = res.concat(catmullRom2bezier(dots, crz)); 6002 r = ["R"].concat(pa.slice(-2)); 6003 } else if (pa0 == "O") { 6004 res.pop(); 6005 dots = ellipsePath(x, y, pa[1], pa[2]); 6006 dots.push(dots[0]); 6007 res = res.concat(dots); 6008 } else if (pa0 == "U") { 6009 res.pop(); 6010 res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3])); 6011 r = ["U"].concat(res[res.length - 1].slice(-2)); 6012 } else { 6013 for (var k = 0, kk = pa.length; k < kk; k++) { 6014 r[k] = pa[k]; 6015 } 6016 } 6017 pa0 = pa0.toUpperCase(); 6018 if (pa0 != "O") { 6019 switch (r[0]) { 6020 case "Z": 6021 x = +mx; 6022 y = +my; 6023 break; 6024 case "H": 6025 x = r[1]; 6026 break; 6027 case "V": 6028 y = r[1]; 6029 break; 6030 case "M": 6031 mx = r[r.length - 2]; 6032 my = r[r.length - 1]; 6033 default: 6034 x = r[r.length - 2]; 6035 y = r[r.length - 1]; 6036 } 6037 } 6038 } 6039 res.toString = toString; 6040 pth.abs = pathClone(res); 6041 return res; 6042 } 6043 function l2c(x1, y1, x2, y2) { 6044 return [x1, y1, x2, y2, x2, y2]; 6045 } 6046 function q2c(x1, y1, ax, ay, x2, y2) { 6047 var _13 = 1 / 3, 6048 _23 = 2 / 3; 6049 return [ 6050 _13 * x1 + _23 * ax, 6051 _13 * y1 + _23 * ay, 6052 _13 * x2 + _23 * ax, 6053 _13 * y2 + _23 * ay, 6054 x2, 6055 y2 6056 ]; 6057 } 6058 function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { 6059 // for more information of where this math came from visit: 6060 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes 6061 var _120 = PI * 120 / 180, 6062 rad = PI / 180 * (+angle || 0), 6063 res = [], 6064 xy, 6065 rotate = Snap._.cacher(function (x, y, rad) { 6066 var X = x * math.cos(rad) - y * math.sin(rad), 6067 Y = x * math.sin(rad) + y * math.cos(rad); 6068 return {x: X, y: Y}; 6069 }); 6070 if (!recursive) { 6071 xy = rotate(x1, y1, -rad); 6072 x1 = xy.x; 6073 y1 = xy.y; 6074 xy = rotate(x2, y2, -rad); 6075 x2 = xy.x; 6076 y2 = xy.y; 6077 var cos = math.cos(PI / 180 * angle), 6078 sin = math.sin(PI / 180 * angle), 6079 x = (x1 - x2) / 2, 6080 y = (y1 - y2) / 2; 6081 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); 6082 if (h > 1) { 6083 h = math.sqrt(h); 6084 rx = h * rx; 6085 ry = h * ry; 6086 } 6087 var rx2 = rx * rx, 6088 ry2 = ry * ry, 6089 k = (large_arc_flag == sweep_flag ? -1 : 1) * 6090 math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), 6091 cx = k * rx * y / ry + (x1 + x2) / 2, 6092 cy = k * -ry * x / rx + (y1 + y2) / 2, 6093 f1 = math.asin(((y1 - cy) / ry).toFixed(9)), 6094 f2 = math.asin(((y2 - cy) / ry).toFixed(9)); 6095 6096 f1 = x1 < cx ? PI - f1 : f1; 6097 f2 = x2 < cx ? PI - f2 : f2; 6098 f1 < 0 && (f1 = PI * 2 + f1); 6099 f2 < 0 && (f2 = PI * 2 + f2); 6100 if (sweep_flag && f1 > f2) { 6101 f1 = f1 - PI * 2; 6102 } 6103 if (!sweep_flag && f2 > f1) { 6104 f2 = f2 - PI * 2; 6105 } 6106 } else { 6107 f1 = recursive[0]; 6108 f2 = recursive[1]; 6109 cx = recursive[2]; 6110 cy = recursive[3]; 6111 } 6112 var df = f2 - f1; 6113 if (abs(df) > _120) { 6114 var f2old = f2, 6115 x2old = x2, 6116 y2old = y2; 6117 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); 6118 x2 = cx + rx * math.cos(f2); 6119 y2 = cy + ry * math.sin(f2); 6120 res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); 6121 } 6122 df = f2 - f1; 6123 var c1 = math.cos(f1), 6124 s1 = math.sin(f1), 6125 c2 = math.cos(f2), 6126 s2 = math.sin(f2), 6127 t = math.tan(df / 4), 6128 hx = 4 / 3 * rx * t, 6129 hy = 4 / 3 * ry * t, 6130 m1 = [x1, y1], 6131 m2 = [x1 + hx * s1, y1 - hy * c1], 6132 m3 = [x2 + hx * s2, y2 - hy * c2], 6133 m4 = [x2, y2]; 6134 m2[0] = 2 * m1[0] - m2[0]; 6135 m2[1] = 2 * m1[1] - m2[1]; 6136 if (recursive) { 6137 return [m2, m3, m4].concat(res); 6138 } else { 6139 res = [m2, m3, m4].concat(res).join().split(","); 6140 var newres = []; 6141 for (var i = 0, ii = res.length; i < ii; i++) { 6142 newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; 6143 } 6144 return newres; 6145 } 6146 } 6147 function findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { 6148 var t1 = 1 - t; 6149 return { 6150 x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, 6151 y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y 6152 }; 6153 } 6154 6155 // Returns bounding box of cubic bezier curve. 6156 // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html 6157 // Original version: NISHIO Hirokazu 6158 // Modifications: https://github.com/timo22345 6159 function curveDim(x0, y0, x1, y1, x2, y2, x3, y3) { 6160 var tvalues = [], 6161 bounds = [[], []], 6162 a, b, c, t, t1, t2, b2ac, sqrtb2ac; 6163 for (var i = 0; i < 2; ++i) { 6164 if (i == 0) { 6165 b = 6 * x0 - 12 * x1 + 6 * x2; 6166 a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3; 6167 c = 3 * x1 - 3 * x0; 6168 } else { 6169 b = 6 * y0 - 12 * y1 + 6 * y2; 6170 a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3; 6171 c = 3 * y1 - 3 * y0; 6172 } 6173 if (abs(a) < 1e-12) { 6174 if (abs(b) < 1e-12) { 6175 continue; 6176 } 6177 t = -c / b; 6178 if (0 < t && t < 1) { 6179 tvalues.push(t); 6180 } 6181 continue; 6182 } 6183 b2ac = b * b - 4 * c * a; 6184 sqrtb2ac = math.sqrt(b2ac); 6185 if (b2ac < 0) { 6186 continue; 6187 } 6188 t1 = (-b + sqrtb2ac) / (2 * a); 6189 if (0 < t1 && t1 < 1) { 6190 tvalues.push(t1); 6191 } 6192 t2 = (-b - sqrtb2ac) / (2 * a); 6193 if (0 < t2 && t2 < 1) { 6194 tvalues.push(t2); 6195 } 6196 } 6197 6198 var x, y, j = tvalues.length, 6199 jlen = j, 6200 mt; 6201 while (j--) { 6202 t = tvalues[j]; 6203 mt = 1 - t; 6204 bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3); 6205 bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3); 6206 } 6207 6208 bounds[0][jlen] = x0; 6209 bounds[1][jlen] = y0; 6210 bounds[0][jlen + 1] = x3; 6211 bounds[1][jlen + 1] = y3; 6212 bounds[0].length = bounds[1].length = jlen + 2; 6213 6214 6215 return { 6216 min: {x: mmin.apply(0, bounds[0]), y: mmin.apply(0, bounds[1])}, 6217 max: {x: mmax.apply(0, bounds[0]), y: mmax.apply(0, bounds[1])} 6218 }; 6219 } 6220 6221 function path2curve(path, path2) { 6222 var pth = !path2 && paths(path); 6223 if (!path2 && pth.curve) { 6224 return pathClone(pth.curve); 6225 } 6226 var p = pathToAbsolute(path), 6227 p2 = path2 && pathToAbsolute(path2), 6228 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, 6229 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, 6230 processPath = function (path, d, pcom) { 6231 var nx, ny; 6232 if (!path) { 6233 return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; 6234 } 6235 !(path[0] in {T: 1, Q: 1}) && (d.qx = d.qy = null); 6236 switch (path[0]) { 6237 case "M": 6238 d.X = path[1]; 6239 d.Y = path[2]; 6240 break; 6241 case "A": 6242 path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1)))); 6243 break; 6244 case "S": 6245 if (pcom == "C" || pcom == "S") { // In "S" case we have to take into account, if the previous command is C/S. 6246 nx = d.x * 2 - d.bx; // And reflect the previous 6247 ny = d.y * 2 - d.by; // command's control point relative to the current point. 6248 } 6249 else { // or some else or nothing 6250 nx = d.x; 6251 ny = d.y; 6252 } 6253 path = ["C", nx, ny].concat(path.slice(1)); 6254 break; 6255 case "T": 6256 if (pcom == "Q" || pcom == "T") { // In "T" case we have to take into account, if the previous command is Q/T. 6257 d.qx = d.x * 2 - d.qx; // And make a reflection similar 6258 d.qy = d.y * 2 - d.qy; // to case "S". 6259 } 6260 else { // or something else or nothing 6261 d.qx = d.x; 6262 d.qy = d.y; 6263 } 6264 path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); 6265 break; 6266 case "Q": 6267 d.qx = path[1]; 6268 d.qy = path[2]; 6269 path = ["C"].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4])); 6270 break; 6271 case "L": 6272 path = ["C"].concat(l2c(d.x, d.y, path[1], path[2])); 6273 break; 6274 case "H": 6275 path = ["C"].concat(l2c(d.x, d.y, path[1], d.y)); 6276 break; 6277 case "V": 6278 path = ["C"].concat(l2c(d.x, d.y, d.x, path[1])); 6279 break; 6280 case "Z": 6281 path = ["C"].concat(l2c(d.x, d.y, d.X, d.Y)); 6282 break; 6283 } 6284 return path; 6285 }, 6286 fixArc = function (pp, i) { 6287 if (pp[i].length > 7) { 6288 pp[i].shift(); 6289 var pi = pp[i]; 6290 while (pi.length) { 6291 pcoms1[i] = "A"; // if created multiple C:s, their original seg is saved 6292 p2 && (pcoms2[i] = "A"); // the same as above 6293 pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6))); 6294 } 6295 pp.splice(i, 1); 6296 ii = mmax(p.length, p2 && p2.length || 0); 6297 } 6298 }, 6299 fixM = function (path1, path2, a1, a2, i) { 6300 if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { 6301 path2.splice(i, 0, ["M", a2.x, a2.y]); 6302 a1.bx = 0; 6303 a1.by = 0; 6304 a1.x = path1[i][1]; 6305 a1.y = path1[i][2]; 6306 ii = mmax(p.length, p2 && p2.length || 0); 6307 } 6308 }, 6309 pcoms1 = [], // path commands of original path p 6310 pcoms2 = [], // path commands of original path p2 6311 pfirst = "", // temporary holder for original path command 6312 pcom = ""; // holder for previous path command of original path 6313 for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) { 6314 p[i] && (pfirst = p[i][0]); // save current path command 6315 6316 if (pfirst != "C") // C is not saved yet, because it may be result of conversion 6317 { 6318 pcoms1[i] = pfirst; // Save current path command 6319 i && ( pcom = pcoms1[i - 1]); // Get previous path command pcom 6320 } 6321 p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath 6322 6323 if (pcoms1[i] != "A" && pfirst == "C") pcoms1[i] = "C"; // A is the only command 6324 // which may produce multiple C:s 6325 // so we have to make sure that C is also C in original path 6326 6327 fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1 6328 6329 if (p2) { // the same procedures is done to p2 6330 p2[i] && (pfirst = p2[i][0]); 6331 if (pfirst != "C") { 6332 pcoms2[i] = pfirst; 6333 i && (pcom = pcoms2[i - 1]); 6334 } 6335 p2[i] = processPath(p2[i], attrs2, pcom); 6336 6337 if (pcoms2[i] != "A" && pfirst == "C") { 6338 pcoms2[i] = "C"; 6339 } 6340 6341 fixArc(p2, i); 6342 } 6343 fixM(p, p2, attrs, attrs2, i); 6344 fixM(p2, p, attrs2, attrs, i); 6345 var seg = p[i], 6346 seg2 = p2 && p2[i], 6347 seglen = seg.length, 6348 seg2len = p2 && seg2.length; 6349 attrs.x = seg[seglen - 2]; 6350 attrs.y = seg[seglen - 1]; 6351 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; 6352 attrs.by = toFloat(seg[seglen - 3]) || attrs.y; 6353 attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); 6354 attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); 6355 attrs2.x = p2 && seg2[seg2len - 2]; 6356 attrs2.y = p2 && seg2[seg2len - 1]; 6357 } 6358 if (!p2) { 6359 pth.curve = pathClone(p); 6360 } 6361 return p2 ? [p, p2] : p; 6362 } 6363 function mapPath(path, matrix) { 6364 if (!matrix) { 6365 return path; 6366 } 6367 var x, y, i, j, ii, jj, pathi; 6368 path = path2curve(path); 6369 for (i = 0, ii = path.length; i < ii; i++) { 6370 pathi = path[i]; 6371 for (j = 1, jj = pathi.length; j < jj; j += 2) { 6372 x = matrix.x(pathi[j], pathi[j + 1]); 6373 y = matrix.y(pathi[j], pathi[j + 1]); 6374 pathi[j] = x; 6375 pathi[j + 1] = y; 6376 } 6377 } 6378 return path; 6379 } 6380 6381 // http://schepers.cc/getting-to-the-point 6382 function catmullRom2bezier(crp, z) { 6383 var d = []; 6384 for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { 6385 var p = [ 6386 {x: +crp[i - 2], y: +crp[i - 1]}, 6387 {x: +crp[i], y: +crp[i + 1]}, 6388 {x: +crp[i + 2], y: +crp[i + 3]}, 6389 {x: +crp[i + 4], y: +crp[i + 5]} 6390 ]; 6391 if (z) { 6392 if (!i) { 6393 p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]}; 6394 } else if (iLen - 4 == i) { 6395 p[3] = {x: +crp[0], y: +crp[1]}; 6396 } else if (iLen - 2 == i) { 6397 p[2] = {x: +crp[0], y: +crp[1]}; 6398 p[3] = {x: +crp[2], y: +crp[3]}; 6399 } 6400 } else { 6401 if (iLen - 4 == i) { 6402 p[3] = p[2]; 6403 } else if (!i) { 6404 p[0] = {x: +crp[i], y: +crp[i + 1]}; 6405 } 6406 } 6407 d.push(["C", 6408 (-p[0].x + 6 * p[1].x + p[2].x) / 6, 6409 (-p[0].y + 6 * p[1].y + p[2].y) / 6, 6410 (p[1].x + 6 * p[2].x - p[3].x) / 6, 6411 (p[1].y + 6*p[2].y - p[3].y) / 6, 6412 p[2].x, 6413 p[2].y 6414 ]); 6415 } 6416 6417 return d; 6418 } 6419 6420 // export 6421 Snap.path = paths; 6422 6423 /*\ 6424 * Snap.path.getTotalLength 6425 [ method ] 6426 ** 6427 * Returns the length of the given path in pixels 6428 ** 6429 - path (string) SVG path string 6430 ** 6431 = (number) length 6432 \*/ 6433 Snap.path.getTotalLength = getTotalLength; 6434 /*\ 6435 * Snap.path.getPointAtLength 6436 [ method ] 6437 ** 6438 * Returns the coordinates of the point located at the given length along the given path 6439 ** 6440 - path (string) SVG path string 6441 - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps 6442 ** 6443 = (object) representation of the point: 6444 o { 6445 o x: (number) x coordinate, 6446 o y: (number) y coordinate, 6447 o alpha: (number) angle of derivative 6448 o } 6449 \*/ 6450 Snap.path.getPointAtLength = getPointAtLength; 6451 /*\ 6452 * Snap.path.getSubpath 6453 [ method ] 6454 ** 6455 * Returns the subpath of a given path between given start and end lengths 6456 ** 6457 - path (string) SVG path string 6458 - from (number) length, in pixels, from the start of the path to the start of the segment 6459 - to (number) length, in pixels, from the start of the path to the end of the segment 6460 ** 6461 = (string) path string definition for the segment 6462 \*/ 6463 Snap.path.getSubpath = function (path, from, to) { 6464 if (this.getTotalLength(path) - to < 1e-6) { 6465 return getSubpathsAtLength(path, from).end; 6466 } 6467 var a = getSubpathsAtLength(path, to, 1); 6468 return from ? getSubpathsAtLength(a, from).end : a; 6469 }; 6470 /*\ 6471 * Element.getTotalLength 6472 [ method ] 6473 ** 6474 * Returns the length of the path in pixels (only works for `path` elements) 6475 = (number) length 6476 \*/ 6477 elproto.getTotalLength = function () { 6478 if (this.node.getTotalLength) { 6479 return this.node.getTotalLength(); 6480 } 6481 }; 6482 // SIERRA Element.getPointAtLength()/Element.getTotalLength(): If a <path> is broken into different segments, is the jump distance to the new coordinates set by the _M_ or _m_ commands calculated as part of the path's total length? 6483 /*\ 6484 * Element.getPointAtLength 6485 [ method ] 6486 ** 6487 * Returns coordinates of the point located at the given length on the given path (only works for `path` elements) 6488 ** 6489 - length (number) length, in pixels, from the start of the path, excluding non-rendering jumps 6490 ** 6491 = (object) representation of the point: 6492 o { 6493 o x: (number) x coordinate, 6494 o y: (number) y coordinate, 6495 o alpha: (number) angle of derivative 6496 o } 6497 \*/ 6498 elproto.getPointAtLength = function (length) { 6499 return getPointAtLength(this.attr("d"), length); 6500 }; 6501 // SIERRA Element.getSubpath(): Similar to the problem for Element.getPointAtLength(). Unclear how this would work for a segmented path. Overall, the concept of _subpath_ and what I'm calling a _segment_ (series of non-_M_ or _Z_ commands) is unclear. 6502 /*\ 6503 * Element.getSubpath 6504 [ method ] 6505 ** 6506 * Returns subpath of a given element from given start and end lengths (only works for `path` elements) 6507 ** 6508 - from (number) length, in pixels, from the start of the path to the start of the segment 6509 - to (number) length, in pixels, from the start of the path to the end of the segment 6510 ** 6511 = (string) path string definition for the segment 6512 \*/ 6513 elproto.getSubpath = function (from, to) { 6514 return Snap.path.getSubpath(this.attr("d"), from, to); 6515 }; 6516 Snap._.box = box; 6517 /*\ 6518 * Snap.path.findDotsAtSegment 6519 [ method ] 6520 ** 6521 * Utility method 6522 ** 6523 * Finds dot coordinates on the given cubic beziér curve at the given t 6524 - p1x (number) x of the first point of the curve 6525 - p1y (number) y of the first point of the curve 6526 - c1x (number) x of the first anchor of the curve 6527 - c1y (number) y of the first anchor of the curve 6528 - c2x (number) x of the second anchor of the curve 6529 - c2y (number) y of the second anchor of the curve 6530 - p2x (number) x of the second point of the curve 6531 - p2y (number) y of the second point of the curve 6532 - t (number) position on the curve (0..1) 6533 = (object) point information in format: 6534 o { 6535 o x: (number) x coordinate of the point, 6536 o y: (number) y coordinate of the point, 6537 o m: { 6538 o x: (number) x coordinate of the left anchor, 6539 o y: (number) y coordinate of the left anchor 6540 o }, 6541 o n: { 6542 o x: (number) x coordinate of the right anchor, 6543 o y: (number) y coordinate of the right anchor 6544 o }, 6545 o start: { 6546 o x: (number) x coordinate of the start of the curve, 6547 o y: (number) y coordinate of the start of the curve 6548 o }, 6549 o end: { 6550 o x: (number) x coordinate of the end of the curve, 6551 o y: (number) y coordinate of the end of the curve 6552 o }, 6553 o alpha: (number) angle of the curve derivative at the point 6554 o } 6555 \*/ 6556 Snap.path.findDotsAtSegment = findDotsAtSegment; 6557 /*\ 6558 * Snap.path.bezierBBox 6559 [ method ] 6560 ** 6561 * Utility method 6562 ** 6563 * Returns the bounding box of a given cubic beziér curve 6564 - p1x (number) x of the first point of the curve 6565 - p1y (number) y of the first point of the curve 6566 - c1x (number) x of the first anchor of the curve 6567 - c1y (number) y of the first anchor of the curve 6568 - c2x (number) x of the second anchor of the curve 6569 - c2y (number) y of the second anchor of the curve 6570 - p2x (number) x of the second point of the curve 6571 - p2y (number) y of the second point of the curve 6572 * or 6573 - bez (array) array of six points for beziér curve 6574 = (object) bounding box 6575 o { 6576 o x: (number) x coordinate of the left top point of the box, 6577 o y: (number) y coordinate of the left top point of the box, 6578 o x2: (number) x coordinate of the right bottom point of the box, 6579 o y2: (number) y coordinate of the right bottom point of the box, 6580 o width: (number) width of the box, 6581 o height: (number) height of the box 6582 o } 6583 \*/ 6584 Snap.path.bezierBBox = bezierBBox; 6585 /*\ 6586 * Snap.path.isPointInsideBBox 6587 [ method ] 6588 ** 6589 * Utility method 6590 ** 6591 * Returns `true` if given point is inside bounding box 6592 - bbox (string) bounding box 6593 - x (string) x coordinate of the point 6594 - y (string) y coordinate of the point 6595 = (boolean) `true` if point is inside 6596 \*/ 6597 Snap.path.isPointInsideBBox = isPointInsideBBox; 6598 Snap.closest = function (x, y, X, Y) { 6599 var r = 100, 6600 b = box(x - r / 2, y - r / 2, r, r), 6601 inside = [], 6602 getter = X[0].hasOwnProperty("x") ? function (i) { 6603 return { 6604 x: X[i].x, 6605 y: X[i].y 6606 }; 6607 } : function (i) { 6608 return { 6609 x: X[i], 6610 y: Y[i] 6611 }; 6612 }, 6613 found = 0; 6614 while (r <= 1e6 && !found) { 6615 for (var i = 0, ii = X.length; i < ii; i++) { 6616 var xy = getter(i); 6617 if (isPointInsideBBox(b, xy.x, xy.y)) { 6618 found++; 6619 inside.push(xy); 6620 break; 6621 } 6622 } 6623 if (!found) { 6624 r *= 2; 6625 b = box(x - r / 2, y - r / 2, r, r) 6626 } 6627 } 6628 if (r == 1e6) { 6629 return; 6630 } 6631 var len = Infinity, 6632 res; 6633 for (i = 0, ii = inside.length; i < ii; i++) { 6634 var l = Snap.len(x, y, inside[i].x, inside[i].y); 6635 if (len > l) { 6636 len = l; 6637 inside[i].len = l; 6638 res = inside[i]; 6639 } 6640 } 6641 return res; 6642 }; 6643 /*\ 6644 * Snap.path.isBBoxIntersect 6645 [ method ] 6646 ** 6647 * Utility method 6648 ** 6649 * Returns `true` if two bounding boxes intersect 6650 - bbox1 (string) first bounding box 6651 - bbox2 (string) second bounding box 6652 = (boolean) `true` if bounding boxes intersect 6653 \*/ 6654 Snap.path.isBBoxIntersect = isBBoxIntersect; 6655 /*\ 6656 * Snap.path.intersection 6657 [ method ] 6658 ** 6659 * Utility method 6660 ** 6661 * Finds intersections of two paths 6662 - path1 (string) path string 6663 - path2 (string) path string 6664 = (array) dots of intersection 6665 o [ 6666 o { 6667 o x: (number) x coordinate of the point, 6668 o y: (number) y coordinate of the point, 6669 o t1: (number) t value for segment of path1, 6670 o t2: (number) t value for segment of path2, 6671 o segment1: (number) order number for segment of path1, 6672 o segment2: (number) order number for segment of path2, 6673 o bez1: (array) eight coordinates representing beziér curve for the segment of path1, 6674 o bez2: (array) eight coordinates representing beziér curve for the segment of path2 6675 o } 6676 o ] 6677 \*/ 6678 Snap.path.intersection = pathIntersection; 6679 Snap.path.intersectionNumber = pathIntersectionNumber; 6680 /*\ 6681 * Snap.path.isPointInside 6682 [ method ] 6683 ** 6684 * Utility method 6685 ** 6686 * Returns `true` if given point is inside a given closed path. 6687 * 6688 * Note: fill mode doesn’t affect the result of this method. 6689 - path (string) path string 6690 - x (number) x of the point 6691 - y (number) y of the point 6692 = (boolean) `true` if point is inside the path 6693 \*/ 6694 Snap.path.isPointInside = isPointInsidePath; 6695 /*\ 6696 * Snap.path.getBBox 6697 [ method ] 6698 ** 6699 * Utility method 6700 ** 6701 * Returns the bounding box of a given path 6702 - path (string) path string 6703 = (object) bounding box 6704 o { 6705 o x: (number) x coordinate of the left top point of the box, 6706 o y: (number) y coordinate of the left top point of the box, 6707 o x2: (number) x coordinate of the right bottom point of the box, 6708 o y2: (number) y coordinate of the right bottom point of the box, 6709 o width: (number) width of the box, 6710 o height: (number) height of the box 6711 o } 6712 \*/ 6713 Snap.path.getBBox = pathBBox; 6714 Snap.path.get = getPath; 6715 /*\ 6716 * Snap.path.toRelative 6717 [ method ] 6718 ** 6719 * Utility method 6720 ** 6721 * Converts path coordinates into relative values 6722 - path (string) path string 6723 = (array) path string 6724 \*/ 6725 Snap.path.toRelative = pathToRelative; 6726 /*\ 6727 * Snap.path.toAbsolute 6728 [ method ] 6729 ** 6730 * Utility method 6731 ** 6732 * Converts path coordinates into absolute values 6733 - path (string) path string 6734 = (array) path string 6735 \*/ 6736 Snap.path.toAbsolute = pathToAbsolute; 6737 /*\ 6738 * Snap.path.toCubic 6739 [ method ] 6740 ** 6741 * Utility method 6742 ** 6743 * Converts path to a new path where all segments are cubic beziér curves 6744 - pathString (string|array) path string or array of segments 6745 = (array) array of segments 6746 \*/ 6747 Snap.path.toCubic = path2curve; 6748 /*\ 6749 * Snap.path.map 6750 [ method ] 6751 ** 6752 * Transform the path string with the given matrix 6753 - path (string) path string 6754 - matrix (object) see @Matrix 6755 = (string) transformed path string 6756 \*/ 6757 Snap.path.map = mapPath; 6758 Snap.path.toString = toString; 6759 Snap.path.clone = pathClone; 6760}); 6761 6762// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 6763// 6764// Licensed under the Apache License, Version 2.0 (the "License"); 6765// you may not use this file except in compliance with the License. 6766// You may obtain a copy of the License at 6767// 6768// http://www.apache.org/licenses/LICENSE-2.0 6769// 6770// Unless required by applicable law or agreed to in writing, software 6771// distributed under the License is distributed on an "AS IS" BASIS, 6772// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6773// See the License for the specific language governing permissions and 6774// limitations under the License. 6775Snap.plugin(function (Snap, Element, Paper, glob) { 6776 var mmax = Math.max, 6777 mmin = Math.min; 6778 6779 // Set 6780 var Set = function (items) { 6781 this.items = []; 6782 this.bindings = {}; 6783 this.length = 0; 6784 this.type = "set"; 6785 if (items) { 6786 for (var i = 0, ii = items.length; i < ii; i++) { 6787 if (items[i]) { 6788 this[this.items.length] = this.items[this.items.length] = items[i]; 6789 this.length++; 6790 } 6791 } 6792 } 6793 }, 6794 setproto = Set.prototype; 6795 /*\ 6796 * Set.push 6797 [ method ] 6798 ** 6799 * Adds each argument to the current set 6800 = (object) original element 6801 \*/ 6802 setproto.push = function () { 6803 var item, 6804 len; 6805 for (var i = 0, ii = arguments.length; i < ii; i++) { 6806 item = arguments[i]; 6807 if (item) { 6808 len = this.items.length; 6809 this[len] = this.items[len] = item; 6810 this.length++; 6811 } 6812 } 6813 return this; 6814 }; 6815 /*\ 6816 * Set.pop 6817 [ method ] 6818 ** 6819 * Removes last element and returns it 6820 = (object) element 6821 \*/ 6822 setproto.pop = function () { 6823 this.length && delete this[this.length--]; 6824 return this.items.pop(); 6825 }; 6826 /*\ 6827 * Set.forEach 6828 [ method ] 6829 ** 6830 * Executes given function for each element in the set 6831 * 6832 * If the function returns `false`, the loop stops running. 6833 ** 6834 - callback (function) function to run 6835 - thisArg (object) context object for the callback 6836 = (object) Set object 6837 \*/ 6838 setproto.forEach = function (callback, thisArg) { 6839 for (var i = 0, ii = this.items.length; i < ii; i++) { 6840 if (callback.call(thisArg, this.items[i], i) === false) { 6841 return this; 6842 } 6843 } 6844 return this; 6845 }; 6846 /*\ 6847 * Set.animate 6848 [ method ] 6849 ** 6850 * Animates each element in set in sync. 6851 * 6852 ** 6853 - attrs (object) key-value pairs of destination attributes 6854 - duration (number) duration of the animation in milliseconds 6855 - easing (function) #optional easing function from @mina or custom 6856 - callback (function) #optional callback function that executes when the animation ends 6857 * or 6858 - animation (array) array of animation parameter for each element in set in format `[attrs, duration, easing, callback]` 6859 > Usage 6860 | // animate all elements in set to radius 10 6861 | set.animate({r: 10}, 500, mina.easein); 6862 | // or 6863 | // animate first element to radius 10, but second to radius 20 and in different time 6864 | set.animate([{r: 10}, 500, mina.easein], [{r: 20}, 1500, mina.easein]); 6865 = (Element) the current element 6866 \*/ 6867 setproto.animate = function (attrs, ms, easing, callback) { 6868 if (typeof easing == "function" && !easing.length) { 6869 callback = easing; 6870 easing = mina.linear; 6871 } 6872 if (attrs instanceof Snap._.Animation) { 6873 callback = attrs.callback; 6874 easing = attrs.easing; 6875 ms = easing.dur; 6876 attrs = attrs.attr; 6877 } 6878 var args = arguments; 6879 if (Snap.is(attrs, "array") && Snap.is(args[args.length - 1], "array")) { 6880 var each = true; 6881 } 6882 var begin, 6883 handler = function () { 6884 if (begin) { 6885 this.b = begin; 6886 } else { 6887 begin = this.b; 6888 } 6889 }, 6890 cb = 0, 6891 set = this, 6892 callbacker = callback && function () { 6893 if (++cb == set.length) { 6894 callback.call(this); 6895 } 6896 }; 6897 return this.forEach(function (el, i) { 6898 eve.once("snap.animcreated." + el.id, handler); 6899 if (each) { 6900 args[i] && el.animate.apply(el, args[i]); 6901 } else { 6902 el.animate(attrs, ms, easing, callbacker); 6903 } 6904 }); 6905 }; 6906 setproto.remove = function () { 6907 while (this.length) { 6908 this.pop().remove(); 6909 } 6910 return this; 6911 }; 6912 /*\ 6913 * Set.bind 6914 [ method ] 6915 ** 6916 * Specifies how to handle a specific attribute when applied 6917 * to a set. 6918 * 6919 ** 6920 - attr (string) attribute name 6921 - callback (function) function to run 6922 * or 6923 - attr (string) attribute name 6924 - element (Element) specific element in the set to apply the attribute to 6925 * or 6926 - attr (string) attribute name 6927 - element (Element) specific element in the set to apply the attribute to 6928 - eattr (string) attribute on the element to bind the attribute to 6929 = (object) Set object 6930 \*/ 6931 setproto.bind = function (attr, a, b) { 6932 var data = {}; 6933 if (typeof a == "function") { 6934 this.bindings[attr] = a; 6935 } else { 6936 var aname = b || attr; 6937 this.bindings[attr] = function (v) { 6938 data[aname] = v; 6939 a.attr(data); 6940 }; 6941 } 6942 return this; 6943 }; 6944 setproto.attr = function (value) { 6945 var unbound = {}; 6946 for (var k in value) { 6947 if (this.bindings[k]) { 6948 this.bindings[k](value[k]); 6949 } else { 6950 unbound[k] = value[k]; 6951 } 6952 } 6953 for (var i = 0, ii = this.items.length; i < ii; i++) { 6954 this.items[i].attr(unbound); 6955 } 6956 return this; 6957 }; 6958 /*\ 6959 * Set.clear 6960 [ method ] 6961 ** 6962 * Removes all elements from the set 6963 \*/ 6964 setproto.clear = function () { 6965 while (this.length) { 6966 this.pop(); 6967 } 6968 }; 6969 /*\ 6970 * Set.splice 6971 [ method ] 6972 ** 6973 * Removes range of elements from the set 6974 ** 6975 - index (number) position of the deletion 6976 - count (number) number of element to remove 6977 - insertion… (object) #optional elements to insert 6978 = (object) set elements that were deleted 6979 \*/ 6980 setproto.splice = function (index, count, insertion) { 6981 index = index < 0 ? mmax(this.length + index, 0) : index; 6982 count = mmax(0, mmin(this.length - index, count)); 6983 var tail = [], 6984 todel = [], 6985 args = [], 6986 i; 6987 for (i = 2; i < arguments.length; i++) { 6988 args.push(arguments[i]); 6989 } 6990 for (i = 0; i < count; i++) { 6991 todel.push(this[index + i]); 6992 } 6993 for (; i < this.length - index; i++) { 6994 tail.push(this[index + i]); 6995 } 6996 var arglen = args.length; 6997 for (i = 0; i < arglen + tail.length; i++) { 6998 this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen]; 6999 } 7000 i = this.items.length = this.length -= count - arglen; 7001 while (this[i]) { 7002 delete this[i++]; 7003 } 7004 return new Set(todel); 7005 }; 7006 /*\ 7007 * Set.exclude 7008 [ method ] 7009 ** 7010 * Removes given element from the set 7011 ** 7012 - element (object) element to remove 7013 = (boolean) `true` if object was found and removed from the set 7014 \*/ 7015 setproto.exclude = function (el) { 7016 for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) { 7017 this.splice(i, 1); 7018 return true; 7019 } 7020 return false; 7021 }; 7022 setproto.insertAfter = function (el) { 7023 var i = this.items.length; 7024 while (i--) { 7025 this.items[i].insertAfter(el); 7026 } 7027 return this; 7028 }; 7029 setproto.getBBox = function () { 7030 var x = [], 7031 y = [], 7032 x2 = [], 7033 y2 = []; 7034 for (var i = this.items.length; i--;) if (!this.items[i].removed) { 7035 var box = this.items[i].getBBox(); 7036 x.push(box.x); 7037 y.push(box.y); 7038 x2.push(box.x + box.width); 7039 y2.push(box.y + box.height); 7040 } 7041 x = mmin.apply(0, x); 7042 y = mmin.apply(0, y); 7043 x2 = mmax.apply(0, x2); 7044 y2 = mmax.apply(0, y2); 7045 return { 7046 x: x, 7047 y: y, 7048 x2: x2, 7049 y2: y2, 7050 width: x2 - x, 7051 height: y2 - y, 7052 cx: x + (x2 - x) / 2, 7053 cy: y + (y2 - y) / 2 7054 }; 7055 }; 7056 setproto.clone = function (s) { 7057 s = new Set; 7058 for (var i = 0, ii = this.items.length; i < ii; i++) { 7059 s.push(this.items[i].clone()); 7060 } 7061 return s; 7062 }; 7063 setproto.toString = function () { 7064 return "Snap\u2018s set"; 7065 }; 7066 setproto.type = "set"; 7067 // export 7068 Snap.Set = Set; 7069 Snap.set = function () { 7070 var set = new Set; 7071 if (arguments.length) { 7072 set.push.apply(set, Array.prototype.slice.call(arguments, 0)); 7073 } 7074 return set; 7075 }; 7076}); 7077 7078// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 7079// 7080// Licensed under the Apache License, Version 2.0 (the "License"); 7081// you may not use this file except in compliance with the License. 7082// You may obtain a copy of the License at 7083// 7084// http://www.apache.org/licenses/LICENSE-2.0 7085// 7086// Unless required by applicable law or agreed to in writing, software 7087// distributed under the License is distributed on an "AS IS" BASIS, 7088// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7089// See the License for the specific language governing permissions and 7090// limitations under the License. 7091Snap.plugin(function (Snap, Element, Paper, glob) { 7092 var names = {}, 7093 reUnit = /[a-z]+$/i, 7094 Str = String; 7095 names.stroke = names.fill = "colour"; 7096 function getEmpty(item) { 7097 var l = item[0]; 7098 switch (l.toLowerCase()) { 7099 case "t": return [l, 0, 0]; 7100 case "m": return [l, 1, 0, 0, 1, 0, 0]; 7101 case "r": if (item.length == 4) { 7102 return [l, 0, item[2], item[3]]; 7103 } else { 7104 return [l, 0]; 7105 } 7106 case "s": if (item.length == 5) { 7107 return [l, 1, 1, item[3], item[4]]; 7108 } else if (item.length == 3) { 7109 return [l, 1, 1]; 7110 } else { 7111 return [l, 1]; 7112 } 7113 } 7114 } 7115 function equaliseTransform(t1, t2, getBBox) { 7116 t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); 7117 t1 = Snap.parseTransformString(t1) || []; 7118 t2 = Snap.parseTransformString(t2) || []; 7119 var maxlength = Math.max(t1.length, t2.length), 7120 from = [], 7121 to = [], 7122 i = 0, j, jj, 7123 tt1, tt2; 7124 for (; i < maxlength; i++) { 7125 tt1 = t1[i] || getEmpty(t2[i]); 7126 tt2 = t2[i] || getEmpty(tt1); 7127 if ((tt1[0] != tt2[0]) || 7128 (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || 7129 (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) 7130 ) { 7131 t1 = Snap._.transform2matrix(t1, getBBox()); 7132 t2 = Snap._.transform2matrix(t2, getBBox()); 7133 from = [["m", t1.a, t1.b, t1.c, t1.d, t1.e, t1.f]]; 7134 to = [["m", t2.a, t2.b, t2.c, t2.d, t2.e, t2.f]]; 7135 break; 7136 } 7137 from[i] = []; 7138 to[i] = []; 7139 for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) { 7140 j in tt1 && (from[i][j] = tt1[j]); 7141 j in tt2 && (to[i][j] = tt2[j]); 7142 } 7143 } 7144 return { 7145 from: path2array(from), 7146 to: path2array(to), 7147 f: getPath(from) 7148 }; 7149 } 7150 function getNumber(val) { 7151 return val; 7152 } 7153 function getUnit(unit) { 7154 return function (val) { 7155 return +val.toFixed(3) + unit; 7156 }; 7157 } 7158 function getViewBox(val) { 7159 return val.join(" "); 7160 } 7161 function getColour(clr) { 7162 return Snap.rgb(clr[0], clr[1], clr[2]); 7163 } 7164 function getPath(path) { 7165 var k = 0, i, ii, j, jj, out, a, b = []; 7166 for (i = 0, ii = path.length; i < ii; i++) { 7167 out = "["; 7168 a = ['"' + path[i][0] + '"']; 7169 for (j = 1, jj = path[i].length; j < jj; j++) { 7170 a[j] = "val[" + (k++) + "]"; 7171 } 7172 out += a + "]"; 7173 b[i] = out; 7174 } 7175 return Function("val", "return Snap.path.toString.call([" + b + "])"); 7176 } 7177 function path2array(path) { 7178 var out = []; 7179 for (var i = 0, ii = path.length; i < ii; i++) { 7180 for (var j = 1, jj = path[i].length; j < jj; j++) { 7181 out.push(path[i][j]); 7182 } 7183 } 7184 return out; 7185 } 7186 function isNumeric(obj) { 7187 return isFinite(parseFloat(obj)); 7188 } 7189 function arrayEqual(arr1, arr2) { 7190 if (!Snap.is(arr1, "array") || !Snap.is(arr2, "array")) { 7191 return false; 7192 } 7193 return arr1.toString() == arr2.toString(); 7194 } 7195 Element.prototype.equal = function (name, b) { 7196 return eve("snap.util.equal", this, name, b).firstDefined(); 7197 }; 7198 eve.on("snap.util.equal", function (name, b) { 7199 var A, B, a = Str(this.attr(name) || ""), 7200 el = this; 7201 if (isNumeric(a) && isNumeric(b)) { 7202 return { 7203 from: parseFloat(a), 7204 to: parseFloat(b), 7205 f: getNumber 7206 }; 7207 } 7208 if (names[name] == "colour") { 7209 A = Snap.color(a); 7210 B = Snap.color(b); 7211 return { 7212 from: [A.r, A.g, A.b, A.opacity], 7213 to: [B.r, B.g, B.b, B.opacity], 7214 f: getColour 7215 }; 7216 } 7217 if (name == "viewBox") { 7218 A = this.attr(name).vb.split(" ").map(Number); 7219 B = b.split(" ").map(Number); 7220 return { 7221 from: A, 7222 to: B, 7223 f: getViewBox 7224 }; 7225 } 7226 if (name == "transform" || name == "gradientTransform" || name == "patternTransform") { 7227 if (b instanceof Snap.Matrix) { 7228 b = b.toTransformString(); 7229 } 7230 if (!Snap._.rgTransform.test(b)) { 7231 b = Snap._.svgTransform2string(b); 7232 } 7233 return equaliseTransform(a, b, function () { 7234 return el.getBBox(1); 7235 }); 7236 } 7237 if (name == "d" || name == "path") { 7238 A = Snap.path.toCubic(a, b); 7239 return { 7240 from: path2array(A[0]), 7241 to: path2array(A[1]), 7242 f: getPath(A[0]) 7243 }; 7244 } 7245 if (name == "points") { 7246 A = Str(a).split(Snap._.separator); 7247 B = Str(b).split(Snap._.separator); 7248 return { 7249 from: A, 7250 to: B, 7251 f: function (val) { return val; } 7252 }; 7253 } 7254 var aUnit = a.match(reUnit), 7255 bUnit = Str(b).match(reUnit); 7256 if (aUnit && arrayEqual(aUnit, bUnit)) { 7257 return { 7258 from: parseFloat(a), 7259 to: parseFloat(b), 7260 f: getUnit(aUnit) 7261 }; 7262 } else { 7263 return { 7264 from: this.asPX(name), 7265 to: this.asPX(name, b), 7266 f: getNumber 7267 }; 7268 } 7269 }); 7270}); 7271 7272// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 7273// 7274// Licensed under the Apache License, Version 2.0 (the "License"); 7275// you may not use this file except in compliance with the License. 7276// You may obtain a copy of the License at 7277// 7278// http://www.apache.org/licenses/LICENSE-2.0 7279// 7280// Unless required by applicable law or agreed to in writing, software 7281// distributed under the License is distributed on an "AS IS" BASIS, 7282// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7283// See the License for the specific language governing permissions and 7284// limitations under the License. 7285Snap.plugin(function (Snap, Element, Paper, glob) { 7286 var elproto = Element.prototype, 7287 has = "hasOwnProperty", 7288 supportsTouch = "createTouch" in glob.doc, 7289 events = [ 7290 "click", "dblclick", "mousedown", "mousemove", "mouseout", 7291 "mouseover", "mouseup", "touchstart", "touchmove", "touchend", 7292 "touchcancel" 7293 ], 7294 touchMap = { 7295 mousedown: "touchstart", 7296 mousemove: "touchmove", 7297 mouseup: "touchend" 7298 }, 7299 getScroll = function (xy, el) { 7300 var name = xy == "y" ? "scrollTop" : "scrollLeft", 7301 doc = el && el.node ? el.node.ownerDocument : glob.doc; 7302 return doc[name in doc.documentElement ? "documentElement" : "body"][name]; 7303 }, 7304 preventDefault = function () { 7305 this.returnValue = false; 7306 }, 7307 preventTouch = function () { 7308 return this.originalEvent.preventDefault(); 7309 }, 7310 stopPropagation = function () { 7311 this.cancelBubble = true; 7312 }, 7313 stopTouch = function () { 7314 return this.originalEvent.stopPropagation(); 7315 }, 7316 addEvent = function (obj, type, fn, element) { 7317 var realName = supportsTouch && touchMap[type] ? touchMap[type] : type, 7318 f = function (e) { 7319 var scrollY = getScroll("y", element), 7320 scrollX = getScroll("x", element); 7321 if (supportsTouch && touchMap[has](type)) { 7322 for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) { 7323 if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) { 7324 var olde = e; 7325 e = e.targetTouches[i]; 7326 e.originalEvent = olde; 7327 e.preventDefault = preventTouch; 7328 e.stopPropagation = stopTouch; 7329 break; 7330 } 7331 } 7332 } 7333 var x = e.clientX + scrollX, 7334 y = e.clientY + scrollY; 7335 return fn.call(element, e, x, y); 7336 }; 7337 7338 if (type !== realName) { 7339 obj.addEventListener(type, f, false); 7340 } 7341 7342 obj.addEventListener(realName, f, false); 7343 7344 return function () { 7345 if (type !== realName) { 7346 obj.removeEventListener(type, f, false); 7347 } 7348 7349 obj.removeEventListener(realName, f, false); 7350 return true; 7351 }; 7352 }, 7353 drag = [], 7354 dragMove = function (e) { 7355 var x = e.clientX, 7356 y = e.clientY, 7357 scrollY = getScroll("y"), 7358 scrollX = getScroll("x"), 7359 dragi, 7360 j = drag.length; 7361 while (j--) { 7362 dragi = drag[j]; 7363 if (supportsTouch) { 7364 var i = e.touches && e.touches.length, 7365 touch; 7366 while (i--) { 7367 touch = e.touches[i]; 7368 if (touch.identifier == dragi.el._drag.id || dragi.el.node.contains(touch.target)) { 7369 x = touch.clientX; 7370 y = touch.clientY; 7371 (e.originalEvent ? e.originalEvent : e).preventDefault(); 7372 break; 7373 } 7374 } 7375 } else { 7376 e.preventDefault(); 7377 } 7378 var node = dragi.el.node, 7379 o, 7380 next = node.nextSibling, 7381 parent = node.parentNode, 7382 display = node.style.display; 7383 // glob.win.opera && parent.removeChild(node); 7384 // node.style.display = "none"; 7385 // o = dragi.el.paper.getElementByPoint(x, y); 7386 // node.style.display = display; 7387 // glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node)); 7388 // o && eve("snap.drag.over." + dragi.el.id, dragi.el, o); 7389 x += scrollX; 7390 y += scrollY; 7391 eve("snap.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e); 7392 } 7393 }, 7394 dragUp = function (e) { 7395 Snap.unmousemove(dragMove).unmouseup(dragUp); 7396 var i = drag.length, 7397 dragi; 7398 while (i--) { 7399 dragi = drag[i]; 7400 dragi.el._drag = {}; 7401 eve("snap.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e); 7402 eve.off("snap.drag.*." + dragi.el.id); 7403 } 7404 drag = []; 7405 }; 7406 /*\ 7407 * Element.click 7408 [ method ] 7409 ** 7410 * Adds a click event handler to the element 7411 - handler (function) handler for the event 7412 = (object) @Element 7413 \*/ 7414 /*\ 7415 * Element.unclick 7416 [ method ] 7417 ** 7418 * Removes a click event handler from the element 7419 - handler (function) handler for the event 7420 = (object) @Element 7421 \*/ 7422 7423 /*\ 7424 * Element.dblclick 7425 [ method ] 7426 ** 7427 * Adds a double click event handler to the element 7428 - handler (function) handler for the event 7429 = (object) @Element 7430 \*/ 7431 /*\ 7432 * Element.undblclick 7433 [ method ] 7434 ** 7435 * Removes a double click event handler from the element 7436 - handler (function) handler for the event 7437 = (object) @Element 7438 \*/ 7439 7440 /*\ 7441 * Element.mousedown 7442 [ method ] 7443 ** 7444 * Adds a mousedown event handler to the element 7445 - handler (function) handler for the event 7446 = (object) @Element 7447 \*/ 7448 /*\ 7449 * Element.unmousedown 7450 [ method ] 7451 ** 7452 * Removes a mousedown event handler from the element 7453 - handler (function) handler for the event 7454 = (object) @Element 7455 \*/ 7456 7457 /*\ 7458 * Element.mousemove 7459 [ method ] 7460 ** 7461 * Adds a mousemove event handler to the element 7462 - handler (function) handler for the event 7463 = (object) @Element 7464 \*/ 7465 /*\ 7466 * Element.unmousemove 7467 [ method ] 7468 ** 7469 * Removes a mousemove event handler from the element 7470 - handler (function) handler for the event 7471 = (object) @Element 7472 \*/ 7473 7474 /*\ 7475 * Element.mouseout 7476 [ method ] 7477 ** 7478 * Adds a mouseout event handler to the element 7479 - handler (function) handler for the event 7480 = (object) @Element 7481 \*/ 7482 /*\ 7483 * Element.unmouseout 7484 [ method ] 7485 ** 7486 * Removes a mouseout event handler from the element 7487 - handler (function) handler for the event 7488 = (object) @Element 7489 \*/ 7490 7491 /*\ 7492 * Element.mouseover 7493 [ method ] 7494 ** 7495 * Adds a mouseover event handler to the element 7496 - handler (function) handler for the event 7497 = (object) @Element 7498 \*/ 7499 /*\ 7500 * Element.unmouseover 7501 [ method ] 7502 ** 7503 * Removes a mouseover event handler from the element 7504 - handler (function) handler for the event 7505 = (object) @Element 7506 \*/ 7507 7508 /*\ 7509 * Element.mouseup 7510 [ method ] 7511 ** 7512 * Adds a mouseup event handler to the element 7513 - handler (function) handler for the event 7514 = (object) @Element 7515 \*/ 7516 /*\ 7517 * Element.unmouseup 7518 [ method ] 7519 ** 7520 * Removes a mouseup event handler from the element 7521 - handler (function) handler for the event 7522 = (object) @Element 7523 \*/ 7524 7525 /*\ 7526 * Element.touchstart 7527 [ method ] 7528 ** 7529 * Adds a touchstart event handler to the element 7530 - handler (function) handler for the event 7531 = (object) @Element 7532 \*/ 7533 /*\ 7534 * Element.untouchstart 7535 [ method ] 7536 ** 7537 * Removes a touchstart event handler from the element 7538 - handler (function) handler for the event 7539 = (object) @Element 7540 \*/ 7541 7542 /*\ 7543 * Element.touchmove 7544 [ method ] 7545 ** 7546 * Adds a touchmove event handler to the element 7547 - handler (function) handler for the event 7548 = (object) @Element 7549 \*/ 7550 /*\ 7551 * Element.untouchmove 7552 [ method ] 7553 ** 7554 * Removes a touchmove event handler from the element 7555 - handler (function) handler for the event 7556 = (object) @Element 7557 \*/ 7558 7559 /*\ 7560 * Element.touchend 7561 [ method ] 7562 ** 7563 * Adds a touchend event handler to the element 7564 - handler (function) handler for the event 7565 = (object) @Element 7566 \*/ 7567 /*\ 7568 * Element.untouchend 7569 [ method ] 7570 ** 7571 * Removes a touchend event handler from the element 7572 - handler (function) handler for the event 7573 = (object) @Element 7574 \*/ 7575 7576 /*\ 7577 * Element.touchcancel 7578 [ method ] 7579 ** 7580 * Adds a touchcancel event handler to the element 7581 - handler (function) handler for the event 7582 = (object) @Element 7583 \*/ 7584 /*\ 7585 * Element.untouchcancel 7586 [ method ] 7587 ** 7588 * Removes a touchcancel event handler from the element 7589 - handler (function) handler for the event 7590 = (object) @Element 7591 \*/ 7592 for (var i = events.length; i--;) { 7593 (function (eventName) { 7594 Snap[eventName] = elproto[eventName] = function (fn, scope) { 7595 if (Snap.is(fn, "function")) { 7596 this.events = this.events || []; 7597 this.events.push({ 7598 name: eventName, 7599 f: fn, 7600 unbind: addEvent(this.node || document, eventName, fn, scope || this) 7601 }); 7602 } else { 7603 for (var i = 0, ii = this.events.length; i < ii; i++) if (this.events[i].name == eventName) { 7604 try { 7605 this.events[i].f.call(this); 7606 } catch (e) {} 7607 } 7608 } 7609 return this; 7610 }; 7611 Snap["un" + eventName] = 7612 elproto["un" + eventName] = function (fn) { 7613 var events = this.events || [], 7614 l = events.length; 7615 while (l--) if (events[l].name == eventName && 7616 (events[l].f == fn || !fn)) { 7617 events[l].unbind(); 7618 events.splice(l, 1); 7619 !events.length && delete this.events; 7620 return this; 7621 } 7622 return this; 7623 }; 7624 })(events[i]); 7625 } 7626 /*\ 7627 * Element.hover 7628 [ method ] 7629 ** 7630 * Adds hover event handlers to the element 7631 - f_in (function) handler for hover in 7632 - f_out (function) handler for hover out 7633 - icontext (object) #optional context for hover in handler 7634 - ocontext (object) #optional context for hover out handler 7635 = (object) @Element 7636 \*/ 7637 elproto.hover = function (f_in, f_out, scope_in, scope_out) { 7638 return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in); 7639 }; 7640 /*\ 7641 * Element.unhover 7642 [ method ] 7643 ** 7644 * Removes hover event handlers from the element 7645 - f_in (function) handler for hover in 7646 - f_out (function) handler for hover out 7647 = (object) @Element 7648 \*/ 7649 elproto.unhover = function (f_in, f_out) { 7650 return this.unmouseover(f_in).unmouseout(f_out); 7651 }; 7652 var draggable = []; 7653 // SIERRA unclear what _context_ refers to for starting, ending, moving the drag gesture. 7654 // SIERRA Element.drag(): _x position of the mouse_: Where are the x/y values offset from? 7655 // SIERRA Element.drag(): much of this member's doc appears to be duplicated for some reason. 7656 // SIERRA Unclear about this sentence: _Additionally following drag events will be triggered: drag.start.<id> on start, drag.end.<id> on end and drag.move.<id> on every move._ Is there a global _drag_ object to which you can assign handlers keyed by an element's ID? 7657 /*\ 7658 * Element.drag 7659 [ method ] 7660 ** 7661 * Adds event handlers for an element's drag gesture 7662 ** 7663 - onmove (function) handler for moving 7664 - onstart (function) handler for drag start 7665 - onend (function) handler for drag end 7666 - mcontext (object) #optional context for moving handler 7667 - scontext (object) #optional context for drag start handler 7668 - econtext (object) #optional context for drag end handler 7669 * Additionaly following `drag` events are triggered: `drag.start.<id>` on start, 7670 * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element is dragged over another element 7671 * `drag.over.<id>` fires as well. 7672 * 7673 * Start event and start handler are called in specified context or in context of the element with following parameters: 7674 o x (number) x position of the mouse 7675 o y (number) y position of the mouse 7676 o event (object) DOM event object 7677 * Move event and move handler are called in specified context or in context of the element with following parameters: 7678 o dx (number) shift by x from the start point 7679 o dy (number) shift by y from the start point 7680 o x (number) x position of the mouse 7681 o y (number) y position of the mouse 7682 o event (object) DOM event object 7683 * End event and end handler are called in specified context or in context of the element with following parameters: 7684 o event (object) DOM event object 7685 = (object) @Element 7686 \*/ 7687 elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) { 7688 var el = this; 7689 if (!arguments.length) { 7690 var origTransform; 7691 return el.drag(function (dx, dy) { 7692 this.attr({ 7693 transform: origTransform + (origTransform ? "T" : "t") + [dx, dy] 7694 }); 7695 }, function () { 7696 origTransform = this.transform().local; 7697 }); 7698 } 7699 function start(e, x, y) { 7700 (e.originalEvent || e).preventDefault(); 7701 el._drag.x = x; 7702 el._drag.y = y; 7703 el._drag.id = e.identifier; 7704 !drag.length && Snap.mousemove(dragMove).mouseup(dragUp); 7705 drag.push({el: el, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope}); 7706 onstart && eve.on("snap.drag.start." + el.id, onstart); 7707 onmove && eve.on("snap.drag.move." + el.id, onmove); 7708 onend && eve.on("snap.drag.end." + el.id, onend); 7709 eve("snap.drag.start." + el.id, start_scope || move_scope || el, x, y, e); 7710 } 7711 function init(e, x, y) { 7712 eve("snap.draginit." + el.id, el, e, x, y); 7713 } 7714 eve.on("snap.draginit." + el.id, start); 7715 el._drag = {}; 7716 draggable.push({el: el, start: start, init: init}); 7717 el.mousedown(init); 7718 return el; 7719 }; 7720 /* 7721 * Element.onDragOver 7722 [ method ] 7723 ** 7724 * Shortcut to assign event handler for `drag.over.<id>` event, where `id` is the element's `id` (see @Element.id) 7725 - f (function) handler for event, first argument would be the element you are dragging over 7726 \*/ 7727 // elproto.onDragOver = function (f) { 7728 // f ? eve.on("snap.drag.over." + this.id, f) : eve.unbind("snap.drag.over." + this.id); 7729 // }; 7730 /*\ 7731 * Element.undrag 7732 [ method ] 7733 ** 7734 * Removes all drag event handlers from the given element 7735 \*/ 7736 elproto.undrag = function () { 7737 var i = draggable.length; 7738 while (i--) if (draggable[i].el == this) { 7739 this.unmousedown(draggable[i].init); 7740 draggable.splice(i, 1); 7741 eve.unbind("snap.drag.*." + this.id); 7742 eve.unbind("snap.draginit." + this.id); 7743 } 7744 !draggable.length && Snap.unmousemove(dragMove).unmouseup(dragUp); 7745 return this; 7746 }; 7747}); 7748 7749// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. 7750// 7751// Licensed under the Apache License, Version 2.0 (the "License"); 7752// you may not use this file except in compliance with the License. 7753// You may obtain a copy of the License at 7754// 7755// http://www.apache.org/licenses/LICENSE-2.0 7756// 7757// Unless required by applicable law or agreed to in writing, software 7758// distributed under the License is distributed on an "AS IS" BASIS, 7759// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7760// See the License for the specific language governing permissions and 7761// limitations under the License. 7762Snap.plugin(function (Snap, Element, Paper, glob) { 7763 var elproto = Element.prototype, 7764 pproto = Paper.prototype, 7765 rgurl = /^\s*url\((.+)\)/, 7766 Str = String, 7767 $ = Snap._.$; 7768 Snap.filter = {}; 7769 /*\ 7770 * Paper.filter 7771 [ method ] 7772 ** 7773 * Creates a `<filter>` element 7774 ** 7775 - filstr (string) SVG fragment of filter provided as a string 7776 = (object) @Element 7777 * Note: It is recommended to use filters embedded into the page inside an empty SVG element. 7778 > Usage 7779 | var f = paper.filter('<feGaussianBlur stdDeviation="2"/>'), 7780 | c = paper.circle(10, 10, 10).attr({ 7781 | filter: f 7782 | }); 7783 \*/ 7784 pproto.filter = function (filstr) { 7785 var paper = this; 7786 if (paper.type != "svg") { 7787 paper = paper.paper; 7788 } 7789 var f = Snap.parse(Str(filstr)), 7790 id = Snap._.id(), 7791 width = paper.node.offsetWidth, 7792 height = paper.node.offsetHeight, 7793 filter = $("filter"); 7794 $(filter, { 7795 id: id, 7796 filterUnits: "userSpaceOnUse" 7797 }); 7798 filter.appendChild(f.node); 7799 paper.defs.appendChild(filter); 7800 return new Element(filter); 7801 }; 7802 7803 eve.on("snap.util.getattr.filter", function () { 7804 eve.stop(); 7805 var p = $(this.node, "filter"); 7806 if (p) { 7807 var match = Str(p).match(rgurl); 7808 return match && Snap.select(match[1]); 7809 } 7810 }); 7811 eve.on("snap.util.attr.filter", function (value) { 7812 if (value instanceof Element && value.type == "filter") { 7813 eve.stop(); 7814 var id = value.node.id; 7815 if (!id) { 7816 $(value.node, {id: value.id}); 7817 id = value.id; 7818 } 7819 $(this.node, { 7820 filter: Snap.url(id) 7821 }); 7822 } 7823 if (!value || value == "none") { 7824 eve.stop(); 7825 this.node.removeAttribute("filter"); 7826 } 7827 }); 7828 /*\ 7829 * Snap.filter.blur 7830 [ method ] 7831 ** 7832 * Returns an SVG markup string for the blur filter 7833 ** 7834 - x (number) amount of horizontal blur, in pixels 7835 - y (number) #optional amount of vertical blur, in pixels 7836 = (string) filter representation 7837 > Usage 7838 | var f = paper.filter(Snap.filter.blur(5, 10)), 7839 | c = paper.circle(10, 10, 10).attr({ 7840 | filter: f 7841 | }); 7842 \*/ 7843 Snap.filter.blur = function (x, y) { 7844 if (x == null) { 7845 x = 2; 7846 } 7847 var def = y == null ? x : [x, y]; 7848 return Snap.format('\<feGaussianBlur stdDeviation="{def}"/>', { 7849 def: def 7850 }); 7851 }; 7852 Snap.filter.blur.toString = function () { 7853 return this(); 7854 }; 7855 /*\ 7856 * Snap.filter.shadow 7857 [ method ] 7858 ** 7859 * Returns an SVG markup string for the shadow filter 7860 ** 7861 - dx (number) #optional horizontal shift of the shadow, in pixels 7862 - dy (number) #optional vertical shift of the shadow, in pixels 7863 - blur (number) #optional amount of blur 7864 - color (string) #optional color of the shadow 7865 - opacity (number) #optional `0..1` opacity of the shadow 7866 * or 7867 - dx (number) #optional horizontal shift of the shadow, in pixels 7868 - dy (number) #optional vertical shift of the shadow, in pixels 7869 - color (string) #optional color of the shadow 7870 - opacity (number) #optional `0..1` opacity of the shadow 7871 * which makes blur default to `4`. Or 7872 - dx (number) #optional horizontal shift of the shadow, in pixels 7873 - dy (number) #optional vertical shift of the shadow, in pixels 7874 - opacity (number) #optional `0..1` opacity of the shadow 7875 = (string) filter representation 7876 > Usage 7877 | var f = paper.filter(Snap.filter.shadow(0, 2, 3)), 7878 | c = paper.circle(10, 10, 10).attr({ 7879 | filter: f 7880 | }); 7881 \*/ 7882 Snap.filter.shadow = function (dx, dy, blur, color, opacity) { 7883 if (typeof blur == "string") { 7884 color = blur; 7885 opacity = color; 7886 blur = 4; 7887 } 7888 if (typeof color != "string") { 7889 opacity = color; 7890 color = "#000"; 7891 } 7892 color = color || "#000"; 7893 if (blur == null) { 7894 blur = 4; 7895 } 7896 if (opacity == null) { 7897 opacity = 1; 7898 } 7899 if (dx == null) { 7900 dx = 0; 7901 dy = 2; 7902 } 7903 if (dy == null) { 7904 dy = dx; 7905 } 7906 color = Snap.color(color); 7907 return Snap.format('<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feComponentTransfer><feFuncA type="linear" slope="{opacity}"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>', { 7908 color: color, 7909 dx: dx, 7910 dy: dy, 7911 blur: blur, 7912 opacity: opacity 7913 }); 7914 }; 7915 Snap.filter.shadow.toString = function () { 7916 return this(); 7917 }; 7918 /*\ 7919 * Snap.filter.grayscale 7920 [ method ] 7921 ** 7922 * Returns an SVG markup string for the grayscale filter 7923 ** 7924 - amount (number) amount of filter (`0..1`) 7925 = (string) filter representation 7926 \*/ 7927 Snap.filter.grayscale = function (amount) { 7928 if (amount == null) { 7929 amount = 1; 7930 } 7931 return Snap.format('<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {b} {h} 0 0 0 0 0 1 0"/>', { 7932 a: 0.2126 + 0.7874 * (1 - amount), 7933 b: 0.7152 - 0.7152 * (1 - amount), 7934 c: 0.0722 - 0.0722 * (1 - amount), 7935 d: 0.2126 - 0.2126 * (1 - amount), 7936 e: 0.7152 + 0.2848 * (1 - amount), 7937 f: 0.0722 - 0.0722 * (1 - amount), 7938 g: 0.2126 - 0.2126 * (1 - amount), 7939 h: 0.0722 + 0.9278 * (1 - amount) 7940 }); 7941 }; 7942 Snap.filter.grayscale.toString = function () { 7943 return this(); 7944 }; 7945 /*\ 7946 * Snap.filter.sepia 7947 [ method ] 7948 ** 7949 * Returns an SVG markup string for the sepia filter 7950 ** 7951 - amount (number) amount of filter (`0..1`) 7952 = (string) filter representation 7953 \*/ 7954 Snap.filter.sepia = function (amount) { 7955 if (amount == null) { 7956 amount = 1; 7957 } 7958 return Snap.format('<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {h} {i} 0 0 0 0 0 1 0"/>', { 7959 a: 0.393 + 0.607 * (1 - amount), 7960 b: 0.769 - 0.769 * (1 - amount), 7961 c: 0.189 - 0.189 * (1 - amount), 7962 d: 0.349 - 0.349 * (1 - amount), 7963 e: 0.686 + 0.314 * (1 - amount), 7964 f: 0.168 - 0.168 * (1 - amount), 7965 g: 0.272 - 0.272 * (1 - amount), 7966 h: 0.534 - 0.534 * (1 - amount), 7967 i: 0.131 + 0.869 * (1 - amount) 7968 }); 7969 }; 7970 Snap.filter.sepia.toString = function () { 7971 return this(); 7972 }; 7973 /*\ 7974 * Snap.filter.saturate 7975 [ method ] 7976 ** 7977 * Returns an SVG markup string for the saturate filter 7978 ** 7979 - amount (number) amount of filter (`0..1`) 7980 = (string) filter representation 7981 \*/ 7982 Snap.filter.saturate = function (amount) { 7983 if (amount == null) { 7984 amount = 1; 7985 } 7986 return Snap.format('<feColorMatrix type="saturate" values="{amount}"/>', { 7987 amount: 1 - amount 7988 }); 7989 }; 7990 Snap.filter.saturate.toString = function () { 7991 return this(); 7992 }; 7993 /*\ 7994 * Snap.filter.hueRotate 7995 [ method ] 7996 ** 7997 * Returns an SVG markup string for the hue-rotate filter 7998 ** 7999 - angle (number) angle of rotation 8000 = (string) filter representation 8001 \*/ 8002 Snap.filter.hueRotate = function (angle) { 8003 angle = angle || 0; 8004 return Snap.format('<feColorMatrix type="hueRotate" values="{angle}"/>', { 8005 angle: angle 8006 }); 8007 }; 8008 Snap.filter.hueRotate.toString = function () { 8009 return this(); 8010 }; 8011 /*\ 8012 * Snap.filter.invert 8013 [ method ] 8014 ** 8015 * Returns an SVG markup string for the invert filter 8016 ** 8017 - amount (number) amount of filter (`0..1`) 8018 = (string) filter representation 8019 \*/ 8020 Snap.filter.invert = function (amount) { 8021 if (amount == null) { 8022 amount = 1; 8023 } 8024// <feColorMatrix type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" color-interpolation-filters="sRGB"/> 8025 return Snap.format('<feComponentTransfer><feFuncR type="table" tableValues="{amount} {amount2}"/><feFuncG type="table" tableValues="{amount} {amount2}"/><feFuncB type="table" tableValues="{amount} {amount2}"/></feComponentTransfer>', { 8026 amount: amount, 8027 amount2: 1 - amount 8028 }); 8029 }; 8030 Snap.filter.invert.toString = function () { 8031 return this(); 8032 }; 8033 /*\ 8034 * Snap.filter.brightness 8035 [ method ] 8036 ** 8037 * Returns an SVG markup string for the brightness filter 8038 ** 8039 - amount (number) amount of filter (`0..1`) 8040 = (string) filter representation 8041 \*/ 8042 Snap.filter.brightness = function (amount) { 8043 if (amount == null) { 8044 amount = 1; 8045 } 8046 return Snap.format('<feComponentTransfer><feFuncR type="linear" slope="{amount}"/><feFuncG type="linear" slope="{amount}"/><feFuncB type="linear" slope="{amount}"/></feComponentTransfer>', { 8047 amount: amount 8048 }); 8049 }; 8050 Snap.filter.brightness.toString = function () { 8051 return this(); 8052 }; 8053 /*\ 8054 * Snap.filter.contrast 8055 [ method ] 8056 ** 8057 * Returns an SVG markup string for the contrast filter 8058 ** 8059 - amount (number) amount of filter (`0..1`) 8060 = (string) filter representation 8061 \*/ 8062 Snap.filter.contrast = function (amount) { 8063 if (amount == null) { 8064 amount = 1; 8065 } 8066 return Snap.format('<feComponentTransfer><feFuncR type="linear" slope="{amount}" intercept="{amount2}"/><feFuncG type="linear" slope="{amount}" intercept="{amount2}"/><feFuncB type="linear" slope="{amount}" intercept="{amount2}"/></feComponentTransfer>', { 8067 amount: amount, 8068 amount2: .5 - amount / 2 8069 }); 8070 }; 8071 Snap.filter.contrast.toString = function () { 8072 return this(); 8073 }; 8074}); 8075 8076// Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved. 8077// 8078// Licensed under the Apache License, Version 2.0 (the "License"); 8079// you may not use this file except in compliance with the License. 8080// You may obtain a copy of the License at 8081// 8082// http://www.apache.org/licenses/LICENSE-2.0 8083// 8084// Unless required by applicable law or agreed to in writing, software 8085// distributed under the License is distributed on an "AS IS" BASIS, 8086// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8087// See the License for the specific language governing permissions and 8088// limitations under the License. 8089Snap.plugin(function (Snap, Element, Paper, glob, Fragment) { 8090 var box = Snap._.box, 8091 is = Snap.is, 8092 firstLetter = /^[^a-z]*([tbmlrc])/i, 8093 toString = function () { 8094 return "T" + this.dx + "," + this.dy; 8095 }; 8096 /*\ 8097 * Element.getAlign 8098 [ method ] 8099 ** 8100 * Returns shift needed to align the element relatively to given element. 8101 * If no elements specified, parent `<svg>` container will be used. 8102 - el (object) @optional alignment element 8103 - way (string) one of six values: `"top"`, `"middle"`, `"bottom"`, `"left"`, `"center"`, `"right"` 8104 = (object|string) Object in format `{dx: , dy: }` also has a string representation as a transformation string 8105 > Usage 8106 | el.transform(el.getAlign(el2, "top")); 8107 * or 8108 | var dy = el.getAlign(el2, "top").dy; 8109 \*/ 8110 Element.prototype.getAlign = function (el, way) { 8111 if (way == null && is(el, "string")) { 8112 way = el; 8113 el = null; 8114 } 8115 el = el || this.paper; 8116 var bx = el.getBBox ? el.getBBox() : box(el), 8117 bb = this.getBBox(), 8118 out = {}; 8119 way = way && way.match(firstLetter); 8120 way = way ? way[1].toLowerCase() : "c"; 8121 switch (way) { 8122 case "t": 8123 out.dx = 0; 8124 out.dy = bx.y - bb.y; 8125 break; 8126 case "b": 8127 out.dx = 0; 8128 out.dy = bx.y2 - bb.y2; 8129 break; 8130 case "m": 8131 out.dx = 0; 8132 out.dy = bx.cy - bb.cy; 8133 break; 8134 case "l": 8135 out.dx = bx.x - bb.x; 8136 out.dy = 0; 8137 break; 8138 case "r": 8139 out.dx = bx.x2 - bb.x2; 8140 out.dy = 0; 8141 break; 8142 default: 8143 out.dx = bx.cx - bb.cx; 8144 out.dy = 0; 8145 break; 8146 } 8147 out.toString = toString; 8148 return out; 8149 }; 8150 /*\ 8151 * Element.align 8152 [ method ] 8153 ** 8154 * Aligns the element relatively to given one via transformation. 8155 * If no elements specified, parent `<svg>` container will be used. 8156 - el (object) @optional alignment element 8157 - way (string) one of six values: `"top"`, `"middle"`, `"bottom"`, `"left"`, `"center"`, `"right"` 8158 = (object) this element 8159 > Usage 8160 | el.align(el2, "top"); 8161 * or 8162 | el.align("middle"); 8163 \*/ 8164 Element.prototype.align = function (el, way) { 8165 return this.transform("..." + this.getAlign(el, way)); 8166 }; 8167}); 8168 8169return Snap; 8170})); 8171