1/*! 2 * Paper.js v0.9.24 - The Swiss Army Knife of Vector Graphics Scripting. 3 * http://paperjs.org/ 4 * 5 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey 6 * http://scratchdisk.com/ & http://jonathanpuckey.com/ 7 * 8 * Distributed under the MIT license. See LICENSE file for details. 9 * 10 * All rights reserved. 11 * 12 * Date: Fri Aug 21 16:39:41 2015 +0200 13 * 14 *** 15 * 16 * Straps.js - Class inheritance library with support for bean-style accessors 17 * 18 * Copyright (c) 2006 - 2013 Juerg Lehni 19 * http://scratchdisk.com/ 20 * 21 * Distributed under the MIT license. 22 * 23 *** 24 * 25 * Acorn.js 26 * http://marijnhaverbeke.nl/acorn/ 27 * 28 * Acorn is a tiny, fast JavaScript parser written in JavaScript, 29 * created by Marijn Haverbeke and released under an MIT license. 30 * 31 */ 32 33var paper = new function(undefined) { 34 35var Base = new function() { 36 var hidden = /^(statics|enumerable|beans|preserve)$/, 37 38 forEach = [].forEach || function(iter, bind) { 39 for (var i = 0, l = this.length; i < l; i++) 40 iter.call(bind, this[i], i, this); 41 }, 42 43 forIn = function(iter, bind) { 44 for (var i in this) 45 if (this.hasOwnProperty(i)) 46 iter.call(bind, this[i], i, this); 47 }, 48 49 create = Object.create || function(proto) { 50 return { __proto__: proto }; 51 }, 52 53 describe = Object.getOwnPropertyDescriptor || function(obj, name) { 54 var get = obj.__lookupGetter__ && obj.__lookupGetter__(name); 55 return get 56 ? { get: get, set: obj.__lookupSetter__(name), 57 enumerable: true, configurable: true } 58 : obj.hasOwnProperty(name) 59 ? { value: obj[name], enumerable: true, 60 configurable: true, writable: true } 61 : null; 62 }, 63 64 _define = Object.defineProperty || function(obj, name, desc) { 65 if ((desc.get || desc.set) && obj.__defineGetter__) { 66 if (desc.get) 67 obj.__defineGetter__(name, desc.get); 68 if (desc.set) 69 obj.__defineSetter__(name, desc.set); 70 } else { 71 obj[name] = desc.value; 72 } 73 return obj; 74 }, 75 76 define = function(obj, name, desc) { 77 delete obj[name]; 78 return _define(obj, name, desc); 79 }; 80 81 function inject(dest, src, enumerable, beans, preserve) { 82 var beansNames = {}; 83 84 function field(name, val) { 85 val = val || (val = describe(src, name)) 86 && (val.get ? val : val.value); 87 if (typeof val === 'string' && val[0] === '#') 88 val = dest[val.substring(1)] || val; 89 var isFunc = typeof val === 'function', 90 res = val, 91 prev = preserve || isFunc && !val.base 92 ? (val && val.get ? name in dest : dest[name]) 93 : null, 94 bean; 95 if (!preserve || !prev) { 96 if (isFunc && prev) 97 val.base = prev; 98 if (isFunc && beans !== false 99 && (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/))) 100 beansNames[bean[3].toLowerCase() + bean[4]] = bean[2]; 101 if (!res || isFunc || !res.get || typeof res.get !== 'function' 102 || !Base.isPlainObject(res)) 103 res = { value: res, writable: true }; 104 if ((describe(dest, name) 105 || { configurable: true }).configurable) { 106 res.configurable = true; 107 res.enumerable = enumerable; 108 } 109 define(dest, name, res); 110 } 111 } 112 if (src) { 113 for (var name in src) { 114 if (src.hasOwnProperty(name) && !hidden.test(name)) 115 field(name); 116 } 117 for (var name in beansNames) { 118 var part = beansNames[name], 119 set = dest['set' + part], 120 get = dest['get' + part] || set && dest['is' + part]; 121 if (get && (beans === true || get.length === 0)) 122 field(name, { get: get, set: set }); 123 } 124 } 125 return dest; 126 } 127 128 function each(obj, iter, bind) { 129 if (obj) 130 ('length' in obj && !obj.getLength 131 && typeof obj.length === 'number' 132 ? forEach 133 : forIn).call(obj, iter, bind = bind || obj); 134 return bind; 135 } 136 137 function set(obj, props, exclude) { 138 for (var key in props) 139 if (props.hasOwnProperty(key) && !(exclude && exclude[key])) 140 obj[key] = props[key]; 141 return obj; 142 } 143 144 return inject(function Base() { 145 for (var i = 0, l = arguments.length; i < l; i++) 146 set(this, arguments[i]); 147 }, { 148 inject: function(src) { 149 if (src) { 150 var statics = src.statics === true ? src : src.statics, 151 beans = src.beans, 152 preserve = src.preserve; 153 if (statics !== src) 154 inject(this.prototype, src, src.enumerable, beans, preserve); 155 inject(this, statics, true, beans, preserve); 156 } 157 for (var i = 1, l = arguments.length; i < l; i++) 158 this.inject(arguments[i]); 159 return this; 160 }, 161 162 extend: function() { 163 var base = this, 164 ctor, 165 proto; 166 for (var i = 0, l = arguments.length; i < l; i++) 167 if (ctor = arguments[i].initialize) 168 break; 169 ctor = ctor || function() { 170 base.apply(this, arguments); 171 }; 172 proto = ctor.prototype = create(this.prototype); 173 define(proto, 'constructor', 174 { value: ctor, writable: true, configurable: true }); 175 inject(ctor, this, true); 176 if (arguments.length) 177 this.inject.apply(ctor, arguments); 178 ctor.base = base; 179 return ctor; 180 } 181 }, true).inject({ 182 inject: function() { 183 for (var i = 0, l = arguments.length; i < l; i++) { 184 var src = arguments[i]; 185 if (src) 186 inject(this, src, src.enumerable, src.beans, src.preserve); 187 } 188 return this; 189 }, 190 191 extend: function() { 192 var res = create(this); 193 return res.inject.apply(res, arguments); 194 }, 195 196 each: function(iter, bind) { 197 return each(this, iter, bind); 198 }, 199 200 set: function(props) { 201 return set(this, props); 202 }, 203 204 clone: function() { 205 return new this.constructor(this); 206 }, 207 208 statics: { 209 each: each, 210 create: create, 211 define: define, 212 describe: describe, 213 set: set, 214 215 clone: function(obj) { 216 return set(new obj.constructor(), obj); 217 }, 218 219 isPlainObject: function(obj) { 220 var ctor = obj != null && obj.constructor; 221 return ctor && (ctor === Object || ctor === Base 222 || ctor.name === 'Object'); 223 }, 224 225 pick: function(a, b) { 226 return a !== undefined ? a : b; 227 } 228 } 229 }); 230}; 231 232if (typeof module !== 'undefined') 233 module.exports = Base; 234 235Base.inject({ 236 toString: function() { 237 return this._id != null 238 ? (this._class || 'Object') + (this._name 239 ? " '" + this._name + "'" 240 : ' @' + this._id) 241 : '{ ' + Base.each(this, function(value, key) { 242 if (!/^_/.test(key)) { 243 var type = typeof value; 244 this.push(key + ': ' + (type === 'number' 245 ? Formatter.instance.number(value) 246 : type === 'string' ? "'" + value + "'" : value)); 247 } 248 }, []).join(', ') + ' }'; 249 }, 250 251 getClassName: function() { 252 return this._class || ''; 253 }, 254 255 exportJSON: function(options) { 256 return Base.exportJSON(this, options); 257 }, 258 259 toJSON: function() { 260 return Base.serialize(this); 261 }, 262 263 _set: function(props, exclude, dontCheck) { 264 if (props && (dontCheck || Base.isPlainObject(props))) { 265 var keys = Object.keys(props._filtering || props);
266 for (var i = 0, l = keys.length; i < l; i++) { 267 var key = keys[i]; 268 if (!(exclude && exclude[key])) { 269 var value = props[key]; 270 if (value !== undefined) 271 this[key] = value; 272 } 273 } 274 return true; 275 } 276 }, 277 278 statics: { 279 280 exports: { 281 enumerable: true 282 }, 283 284 extend: function extend() { 285 var res = extend.base.apply(this, arguments), 286 name = res.prototype._class; 287 if (name && !Base.exports[name]) 288 Base.exports[name] = res; 289 return res; 290 }, 291 292 equals: function(obj1, obj2) { 293 if (obj1 === obj2) 294 return true; 295 if (obj1 && obj1.equals) 296 return obj1.equals(obj2); 297 if (obj2 && obj2.equals) 298 return obj2.equals(obj1); 299 if (obj1 && obj2 300 && typeof obj1 === 'object' && typeof obj2 === 'object') { 301 if (Array.isArray(obj1) && Array.isArray(obj2)) { 302 var length = obj1.length; 303 if (length !== obj2.length) 304 return false; 305 while (length--) { 306 if (!Base.equals(obj1[length], obj2[length])) 307 return false; 308 } 309 } else { 310 var keys = Object.keys(obj1), 311 length = keys.length; 312 if (length !== Object.keys(obj2).length) 313 return false; 314 while (length--) { 315 var key = keys[length]; 316 if (!(obj2.hasOwnProperty(key) 317 && Base.equals(obj1[key], obj2[key]))) 318 return false; 319 } 320 } 321 return true; 322 } 323 return false; 324 }, 325 326 read: function(list, start, options, length) { 327 if (this === Base) { 328 var value = this.peek(list, start); 329 list.__index++; 330 return value; 331 } 332 var proto = this.prototype, 333 readIndex = proto._readIndex, 334 index = start || readIndex && list.__index || 0; 335 if (!length) 336 length = list.length - index; 337 var obj = list[index]; 338 if (obj instanceof this 339 || options && options.readNull && obj == null && length <= 1) { 340 if (readIndex) 341 list.__index = index + 1; 342 return obj && options && options.clone ? obj.clone() : obj; 343 } 344 obj = Base.create(this.prototype); 345 if (readIndex) 346 obj.__read = true; 347 obj = obj.initialize.apply(obj, index > 0 || length < list.length 348 ? Array.prototype.slice.call(list, index, index + length) 349 : list) || obj; 350 if (readIndex) { 351 list.__index = index + obj.__read; 352 obj.__read = undefined; 353 } 354 return obj; 355 }, 356 357 peek: function(list, start) { 358 return list[list.__index = start || list.__index || 0]; 359 }, 360 361 remain: function(list) { 362 return list.length - (list.__index || 0); 363 }, 364 365 readAll: function(list, start, options) { 366 var res = [], 367 entry; 368 for (var i = start || 0, l = list.length; i < l; i++) { 369 res.push(Array.isArray(entry = list[i]) 370 ? this.read(entry, 0, options) 371 : this.read(list, i, options, 1)); 372 } 373 return res; 374 }, 375 376 readNamed: function(list, name, start, options, length) { 377 var value = this.getNamed(list, name), 378 hasObject = value !== undefined; 379 if (hasObject) { 380 var filtered = list._filtered; 381 if (!filtered) { 382 filtered = list._filtered = Base.create(list[0]); 383 filtered._filtering = list[0]; 384 } 385 filtered[name] = undefined; 386 } 387 return this.read(hasObject ? [value] : list, start, options, length); 388 }, 389 390 getNamed: function(list, name) { 391 var arg = list[0]; 392 if (list._hasObject === undefined) 393 list._hasObject = list.length === 1 && Base.isPlainObject(arg); 394 if (list._hasObject) 395 return name ? arg[name] : list._filtered || arg; 396 }, 397 398 hasNamed: function(list, name) { 399 return !!this.getNamed(list, name); 400 }, 401 402 isPlainValue: function(obj, asString) { 403 return this.isPlainObject(obj) || Array.isArray(obj) 404 || asString && typeof obj === 'string'; 405 }, 406 407 serialize: function(obj, options, compact, dictionary) { 408 options = options || {}; 409 410 var root = !dictionary, 411 res; 412 if (root) { 413 options.formatter = new Formatter(options.precision); 414 dictionary = { 415 length: 0, 416 definitions: {}, 417 references: {}, 418 add: function(item, create) { 419 var id = '#' + item._id, 420 ref = this.references[id]; 421 if (!ref) { 422 this.length++; 423 var res = create.call(item), 424 name = item._class; 425 if (name && res[0] !== name) 426 res.unshift(name); 427 this.definitions[id] = res; 428 ref = this.references[id] = [id]; 429 } 430 return ref; 431 } 432 }; 433 } 434 if (obj && obj._serialize) { 435 res = obj._serialize(options, dictionary); 436 var name = obj._class; 437 if (name && !compact && !res._compact && res[0] !== name) 438 res.unshift(name); 439 } else if (Array.isArray(obj)) { 440 res = []; 441 for (var i = 0, l = obj.length; i < l; i++) 442 res[i] = Base.serialize(obj[i], options, compact, 443 dictionary); 444 if (compact) 445 res._compact = true; 446 } else if (Base.isPlainObject(obj)) { 447 res = {}; 448 var keys = Object.keys(obj);
449 for (var i = 0, l = keys.length; i < l; i++) { 450 var key = keys[i]; 451 res[key] = Base.serialize(obj[key], options, compact, 452 dictionary); 453 } 454 } else if (typeof obj === 'number') { 455 res = options.formatter.number(obj, options.precision); 456 } else { 457 res = obj; 458 } 459 return root && dictionary.length > 0 460 ? [['dictionary', dictionary.definitions], res] 461 : res; 462 }, 463 464 deserialize: function(json, create, _data, _isDictionary) { 465 var res = json, 466 isRoot = !_data; 467 _data = _data || {}; 468 if (Array.isArray(json)) { 469 var type = json[0], 470 isDictionary = type === 'dictionary'; 471 if (json.length == 1 && /^#/.test(type)) 472 return _data.dictionary[type]; 473 type = Base.exports[type]; 474 res = []; 475 if (_isDictionary) 476 _data.dictionary = res; 477 for (var i = type ? 1 : 0, l = json.length; i < l; i++) 478 res.push(Base.deserialize(json[i], create, _data, 479 isDictionary)); 480 if (type) { 481 var args = res; 482 if (create) { 483 res = create(type, args); 484 } else { 485 res = Base.create(type.prototype); 486 type.apply(res, args); 487 } 488 } 489 } else if (Base.isPlainObject(json)) { 490 res = {}; 491 if (_isDictionary) 492 _data.dictionary = res; 493 for (var key in json) 494 res[key] = Base.deserialize(json[key], create, _data); 495 } 496 return isRoot && json && json.length && json[0][0] === 'dictionary' 497 ? res[1] 498 : res; 499 }, 500 501 exportJSON: function(obj, options) { 502 var json = Base.serialize(obj, options); 503 return options && options.asString === false 504 ? json 505 : JSON.stringify(json); 506 }, 507 508 importJSON: function(json, target) { 509 return Base.deserialize( 510 typeof json === 'string' ? JSON.parse(json) : json, 511 function(type, args) { 512 var obj = target && target.constructor === type 513 ? target 514 : Base.create(type.prototype), 515 isTarget = obj === target; 516 if (args.length === 1 && obj instanceof Item 517 && (isTarget || !(obj instanceof Layer))) { 518 var arg = args[0]; 519 if (Base.isPlainObject(arg)) 520 arg.insert = false; 521 } 522 type.apply(obj, args); 523 if (isTarget) 524 target = null; 525 return obj; 526 }); 527 }, 528 529 splice: function(list, items, index, remove) { 530 var amount = items && items.length, 531 append = index === undefined; 532 index = append ? list.length : index; 533 if (index > list.length) 534 index = list.length; 535 for (var i = 0; i < amount; i++) 536 items[i]._index = index + i; 537 if (append) { 538 list.push.apply(list, items); 539 return []; 540 } else { 541 var args = [index, remove]; 542 if (items) 543 args.push.apply(args, items);
544 var removed = list.splice.apply(list, args); 545 for (var i = 0, l = removed.length; i < l; i++) 546 removed[i]._index = undefined; 547 for (var i = index + amount, l = list.length; i < l; i++) 548 list[i]._index = i; 549 return removed; 550 } 551 }, 552 553 capitalize: function(str) { 554 return str.replace(/\b[a-z]/g, function(match) { 555 return match.toUpperCase(); 556 }); 557 }, 558 559 camelize: function(str) { 560 return str.replace(/-(.)/g, function(all, chr) { 561 return chr.toUpperCase(); 562 }); 563 }, 564 565 hyphenate: function(str) { 566 return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); 567 } 568 } 569}); 570 571var Emitter = { 572 on: function(type, func) { 573 if (typeof type !== 'string') { 574 Base.each(type, function(value, key) { 575 this.on(key, value); 576 }, this); 577 } else { 578 var types = this._eventTypes, 579 entry = types && types[type], 580 handlers = this._callbacks = this._callbacks || {}; 581 handlers = handlers[type] = handlers[type] || []; 582 if (handlers.indexOf(func) === -1) { 583 handlers.push(func); 584 if (entry && entry.install && handlers.length == 1) 585 entry.install.call(this, type); 586 } 587 } 588 return this; 589 }, 590 591 off: function(type, func) { 592 if (typeof type !== 'string') { 593 Base.each(type, function(value, key) { 594 this.off(key, value); 595 }, this); 596 return; 597 } 598 var types = this._eventTypes, 599 entry = types && types[type], 600 handlers = this._callbacks && this._callbacks[type], 601 index; 602 if (handlers) { 603 if (!func || (index = handlers.indexOf(func)) !== -1 604 && handlers.length === 1) { 605 if (entry && entry.uninstall) 606 entry.uninstall.call(this, type); 607 delete this._callbacks[type]; 608 } else if (index !== -1) { 609 handlers.splice(index, 1); 610 } 611 } 612 return this; 613 }, 614 615 once: function(type, func) { 616 return this.on(type, function() { 617 func.apply(this, arguments); 618 this.off(type, func); 619 }); 620 }, 621 622 emit: function(type, event) { 623 var handlers = this._callbacks && this._callbacks[type]; 624 if (!handlers) 625 return false; 626 var args = [].slice.call(arguments, 1); 627 handlers = handlers.slice(); 628 for (var i = 0, l = handlers.length; i < l; i++) { 629 if (handlers[i].apply(this, args) === false) { 630 if (event && event.stop) 631 event.stop(); 632 break; 633 } 634 } 635 return true; 636 }, 637 638 responds: function(type) { 639 return !!(this._callbacks && this._callbacks[type]); 640 }, 641 642 attach: '#on', 643 detach: '#off', 644 fire: '#emit', 645 646 _installEvents: function(install) { 647 var handlers = this._callbacks, 648 key = install ? 'install' : 'uninstall'; 649 for (var type in handlers) { 650 if (handlers[type].length > 0) { 651 var types = this._eventTypes, 652 entry = types && types[type], 653 func = entry && entry[key]; 654 if (func) 655 func.call(this, type); 656 } 657 } 658 }, 659 660 statics: { 661 inject: function inject(src) { 662 var events = src._events; 663 if (events) { 664 var types = {}; 665 Base.each(events, function(entry, key) { 666 var isString = typeof entry === 'string', 667 name = isString ? entry : key, 668 part = Base.capitalize(name), 669 type = name.substring(2).toLowerCase(); 670 types[type] = isString ? {} : entry; 671 name = '_' + name; 672 src['get' + part] = function() { 673 return this[name]; 674 }; 675 src['set' + part] = function(func) { 676 var prev = this[name]; 677 if (prev) 678 this.off(type, prev); 679 if (func) 680 this.on(type, func); 681 this[name] = func; 682 }; 683 }); 684 src._eventTypes = types; 685 } 686 return inject.base.apply(this, arguments); 687 } 688 } 689}; 690 691var PaperScope = Base.extend({ 692 _class: 'PaperScope', 693 694 initialize: function PaperScope() { 695 paper = this; 696 this.settings = new Base({ 697 applyMatrix: true, 698 handleSize: 4, 699 hitTolerance: 0 700 }); 701 this.project = null; 702 this.projects = []; 703 this.tools = []; 704 this.palettes = []; 705 this._id = PaperScope._id++; 706 PaperScope._scopes[this._id] = this; 707 var proto = PaperScope.prototype; 708 if (!this.support) { 709 var ctx = CanvasProvider.getContext(1, 1); 710 proto.support = { 711 nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx, 712 nativeBlendModes: BlendMode.nativeModes 713 }; 714 CanvasProvider.release(ctx); 715 } 716 717 if (!this.browser) { 718 var agent = navigator.userAgent.toLowerCase(), 719 platform = (/(win)/.exec(agent) 720 || /(mac)/.exec(agent) 721 || /(linux)/.exec(agent) 722 || [])[0], 723 browser = proto.browser = { platform: platform }; 724 if (platform) 725 browser[platform] = true; 726 agent.replace( 727 /(opera|chrome|safari|webkit|firefox|msie|trident|atom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g, 728 function(all, n, v1, v2, rv) { 729 if (!browser.chrome) { 730 var v = n === 'opera' ? v2 : v1; 731 if (n === 'trident') { 732 v = rv; 733 n = 'msie'; 734 } 735 browser.version = v; 736 browser.versionNumber = parseFloat(v); 737 browser.name = n; 738 browser[n] = true; 739 } 740 } 741 ); 742 if (browser.chrome) 743 delete browser.webkit; 744 if (browser.atom) 745 delete browser.chrome; 746 } 747 }, 748 749 version: '0.9.24', 750 751 getView: function() { 752 return this.project && this.project.getView(); 753 }, 754 755 getPaper: function() { 756 return this; 757 }, 758 759 execute: function(code, url, options) { 760 paper.PaperScript.execute(code, this, url, options); 761 View.updateFocus(); 762 }, 763 764 install: function(scope) { 765 var that = this; 766 Base.each(['project', 'view', 'tool'], function(key) { 767 Base.define(scope, key, { 768 configurable: true, 769 get: function() {
vendor: 4,894 bytes, lines 770-916
770 return that[key]; 771 } 772 }); 773 }); 774 for (var key in this) 775 if (!/^_/.test(key) && this[key]) 776 scope[key] = this[key]; 777 }, 778 779 setup: function(element) { 780 paper = this; 781 this.project = new Project(element); 782 return this; 783 }, 784 785 activate: function() { 786 paper = this; 787 }, 788 789 clear: function() { 790 for (var i = this.projects.length - 1; i >= 0; i--) 791 this.projects[i].remove(); 792 for (var i = this.tools.length - 1; i >= 0; i--) 793 this.tools[i].remove(); 794 for (var i = this.palettes.length - 1; i >= 0; i--) 795 this.palettes[i].remove(); 796 }, 797 798 remove: function() { 799 this.clear(); 800 delete PaperScope._scopes[this._id]; 801 }, 802 803 statics: new function() { 804 function handleAttribute(name) { 805 name += 'Attribute'; 806 return function(el, attr) { 807 return el[name](attr) || el[name]('data-paper-' + attr); 808 }; 809 } 810 811 return { 812 _scopes: {}, 813 _id: 0, 814 815 get: function(id) { 816 return this._scopes[id] || null; 817 }, 818 819 getAttribute: handleAttribute('get'), 820 hasAttribute: handleAttribute('has') 821 }; 822 } 823}); 824 825var PaperScopeItem = Base.extend(Emitter, { 826 827 initialize: function(activate) { 828 this._scope = paper; 829 this._index = this._scope[this._list].push(this) - 1; 830 if (activate || !this._scope[this._reference]) 831 this.activate(); 832 }, 833 834 activate: function() { 835 if (!this._scope) 836 return false; 837 var prev = this._scope[this._reference]; 838 if (prev && prev !== this) 839 prev.emit('deactivate'); 840 this._scope[this._reference] = this; 841 this.emit('activate', prev); 842 return true; 843 }, 844 845 isActive: function() { 846 return this._scope[this._reference] === this; 847 }, 848 849 remove: function() { 850 if (this._index == null) 851 return false; 852 Base.splice(this._scope[this._list], null, this._index, 1); 853 if (this._scope[this._reference] == this) 854 this._scope[this._reference] = null; 855 this._scope = null; 856 return true; 857 } 858}); 859 860var Formatter = Base.extend({ 861 initialize: function(precision) { 862 this.precision = precision || 5; 863 this.multiplier = Math.pow(10, this.precision); 864 }, 865 866 number: function(val) { 867 return Math.round(val * this.multiplier) / this.multiplier; 868 }, 869 870 pair: function(val1, val2, separator) { 871 return this.number(val1) + (separator || ',') + this.number(val2); 872 }, 873 874 point: function(val, separator) { 875 return this.number(val.x) + (separator || ',') + this.number(val.y); 876 }, 877 878 size: function(val, separator) { 879 return this.number(val.width) + (separator || ',') 880 + this.number(val.height); 881 }, 882 883 rectangle: function(val, separator) { 884 return this.point(val, separator) + (separator || ',') 885 + this.size(val, separator); 886 } 887}); 888 889Formatter.instance = new Formatter(); 890 891var Numerical = new function() { 892 893 var abscissas = [ 894 [ 0.5773502691896257645091488], 895 [0,0.7745966692414833770358531], 896 [ 0.3399810435848562648026658,0.8611363115940525752239465], 897 [0,0.5384693101056830910363144,0.9061798459386639927976269], 898 [ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016], 899 [0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897], 900 [ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609], 901 [0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762], 902 [ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640], 903 [0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380], 904 [ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491], 905 [0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294], 906 [ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973], 907 [0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657], 908 [ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542] 909 ]; 910 911 var weights = [ 912 [1], 913 [0.8888888888888888888888889,0.5555555555555555555555556], 914 [0.6521451548625461426269361,0.3478548451374538573730639], 915 [0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640], 916 [0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],
917 [0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114], 918 [0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314], 919 [0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922], 920 [0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688], 921 [0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537], 922 [0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160], 923 [0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216], 924 [0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329], 925 [0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284], 926 [0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806] 927 ]; 928 929 var abs = Math.abs, 930 sqrt = Math.sqrt, 931 pow = Math.pow, 932 TOLERANCE = 1e-6, 933 EPSILON = 1e-12, 934 MACHINE_EPSILON = 1.12e-16; 935 936 return { 937 TOLERANCE: TOLERANCE, 938 EPSILON: EPSILON, 939 MACHINE_EPSILON: MACHINE_EPSILON, 940 KAPPA: 4 * (sqrt(2) - 1) / 3, 941 942 isZero: function(val) { 943 return abs(val) <= EPSILON; 944 }, 945 946 integrate: function(f, a, b, n) { 947 var x = abscissas[n - 2], 948 w = weights[n - 2], 949 A = (b - a) * 0.5, 950 B = A + a, 951 i = 0, 952 m = (n + 1) >> 1, 953 sum = n & 1 ? w[i++] * f(B) : 0; 954 while (i < m) { 955 var Ax = A * x[i]; 956 sum += w[i++] * (f(B + Ax) + f(B - Ax)); 957 } 958 return A * sum; 959 }, 960 961 findRoot: function(f, df, x, a, b, n, tolerance) { 962 for (var i = 0; i < n; i++) { 963 var fx = f(x), 964 dx = fx / df(x), 965 nx = x - dx; 966 if (abs(dx) < tolerance) 967 return nx; 968 if (fx > 0) { 969 b = x; 970 x = nx <= a ? (a + b) * 0.5 : nx; 971 } else { 972 a = x; 973 x = nx >= b ? (a + b) * 0.5 : nx; 974 } 975 } 976 return x; 977 }, 978 979 solveQuadratic: function(a, b, c, roots, min, max) { 980 var count = 0, 981 x1, x2 = Infinity, 982 B = b, 983 D; 984 b /= 2; 985 D = b * b - a * c; 986 if (D !== 0 && abs(D) < MACHINE_EPSILON) { 987 var gmC = pow(abs(a * b * c), 1 / 3); 988 if (gmC < 1e-8) { 989 var mult = pow(10, abs( 990 Math.floor(Math.log(gmC) * Math.LOG10E))); 991 if (!isFinite(mult)) 992 mult = 0; 993 a *= mult; 994 b *= mult; 995 c *= mult; 996 D = b * b - a * c; 997 } 998 }
999 if (abs(a) < EPSILON) { 1000 if (abs(B) < EPSILON) 1001 return abs(c) < EPSILON ? -1 : 0; 1002 x1 = -c / B; 1003 } else { 1004 if (D >= -MACHINE_EPSILON) { 1005 D = D < 0 ? 0 : D; 1006 var R = sqrt(D); 1007 if (b >= MACHINE_EPSILON && b <= MACHINE_EPSILON) { 1008 x1 = abs(a) >= abs(c) ? R / a : -c / R; 1009 x2 = -x1; 1010 } else { 1011 var q = -(b + (b < 0 ? -1 : 1) * R); 1012 x1 = q / a; 1013 x2 = c / q; 1014 } 1015 } 1016 } 1017 if (isFinite(x1) && (min == null || x1 >= min && x1 <= max)) 1018 roots[count++] = x1; 1019 if (x2 !== x1 1020 && isFinite(x2) && (min == null || x2 >= min && x2 <= max)) 1021 roots[count++] = x2; 1022 return count; 1023 }, 1024 1025 solveCubic: function(a, b, c, d, roots, min, max) { 1026 var count = 0, 1027 x, b1, c2; 1028 if (abs(a) < EPSILON) { 1029 a = b; 1030 b1 = c; 1031 c2 = d; 1032 x = Infinity; 1033 } else if (abs(d) < EPSILON) { 1034 b1 = b; 1035 c2 = c; 1036 x = 0; 1037 } else { 1038 var ec = 1 + MACHINE_EPSILON, 1039 x0, q, qd, t, r, s, tmp; 1040 x = -(b / a) / 3; 1041 tmp = a * x, 1042 b1 = tmp + b, 1043 c2 = b1 * x + c, 1044 qd = (tmp + b1) * x + c2, 1045 q = c2 * x + d; 1046 t = q /a; 1047 r = pow(abs(t), 1/3); 1048 s = t < 0 ? -1 : 1; 1049 t = -qd / a; 1050 r = t > 0 ? 1.3247179572 * Math.max(r, sqrt(t)) : r; 1051 x0 = x - s * r; 1052 if (x0 !== x) { 1053 do { 1054 x = x0; 1055 tmp = a * x, 1056 b1 = tmp + b, 1057 c2 = b1 * x + c, 1058 qd = (tmp + b1) * x + c2, 1059 q = c2 * x + d; 1060 x0 = qd === 0 ? x : x - q / qd / ec; 1061 if (x0 === x) { 1062 x = x0; 1063 break; 1064 } 1065 } while (s * x0 > s * x); 1066 if (abs(a) * x * x > abs(d / x)) { 1067 c2 = -d / x; 1068 b1 = (c2 - c) / x; 1069 } 1070 } 1071 } 1072 var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max); 1073 if (isFinite(x) && (count === 0 || x !== roots[count - 1]) 1074 && (min == null || x >= min && x <= max)) 1075 roots[count++] = x; 1076 return count; 1077 } 1078 }; 1079}; 1080 1081var UID = { 1082 _id: 1, 1083 _pools: {}, 1084 1085 get: function(ctor) { 1086 if (ctor) { 1087 var name = ctor._class, 1088 pool = this._pools[name]; 1089 if (!pool) 1090 pool = this._pools[name] = { _id: 1 }; 1091 return pool._id++; 1092 } else { 1093 return this._id++; 1094 } 1095 } 1096}; 1097 1098var Point = Base.extend({ 1099 _class: 'Point', 1100 _readIndex: true, 1101 1102 initialize: function Point(arg0, arg1) { 1103 var type = typeof arg0; 1104 if (type === 'number') { 1105 var hasY = typeof arg1 === 'number'; 1106 this.x = arg0; 1107 this.y = hasY ? arg1 : arg0; 1108 if (this.__read) 1109 this.__read = hasY ? 2 : 1; 1110 } else if (type === 'undefined' || arg0 === null) { 1111 this.x = this.y = 0; 1112 if (this.__read) 1113 this.__read = arg0 === null ? 1 : 0; 1114 } else { 1115 if (Array.isArray(arg0)) { 1116 this.x = arg0[0]; 1117 this.y = arg0.length > 1 ? arg0[1] : arg0[0]; 1118 } else if (arg0.x != null) { 1119 this.x = arg0.x; 1120 this.y = arg0.y; 1121 } else if (arg0.width != null) { 1122 this.x = arg0.width; 1123 this.y = arg0.height; 1124 } else if (arg0.angle != null) { 1125 this.x = arg0.length; 1126 this.y = 0; 1127 this.setAngle(arg0.angle); 1128 } else { 1129 this.x = this.y = 0; 1130 if (this.__read) 1131 this.__read = 0; 1132 } 1133 if (this.__read) 1134 this.__read = 1; 1135 } 1136 }, 1137 1138 set: function(x, y) { 1139 this.x = x; 1140 this.y = y; 1141 return this; 1142 }, 1143 1144 equals: function(point) { 1145 return this === point || point 1146 && (this.x === point.x && this.y === point.y 1147 || Array.isArray(point) 1148 && this.x === point[0] && this.y === point[1]) 1149 || false; 1150 }, 1151 1152 clone: function() { 1153 return new Point(this.x, this.y); 1154 }, 1155 1156 toString: function() { 1157 var f = Formatter.instance; 1158 return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }'; 1159 }, 1160 1161 _serialize: function(options) { 1162 var f = options.formatter; 1163 return [f.number(this.x), f.number(this.y)]; 1164 }, 1165 1166 getLength: function() { 1167 return Math.sqrt(this.x * this.x + this.y * this.y); 1168 }, 1169 1170 setLength: function(length) { 1171 if (this.isZero()) { 1172 var angle = this._angle || 0; 1173 this.set( 1174 Math.cos(angle) * length, 1175 Math.sin(angle) * length 1176 ); 1177 } else { 1178 var scale = length / this.getLength(); 1179 if (Numerical.isZero(scale)) 1180 this.getAngle(); 1181 this.set( 1182 this.x * scale, 1183 this.y * scale 1184 ); 1185 } 1186 }, 1187 getAngle: function() { 1188 return this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI; 1189 }, 1190 1191 setAngle: function(angle) { 1192 this.setAngleInRadians.call(this, angle * Math.PI / 180); 1193 }, 1194 1195 getAngleInDegrees: '#getAngle', 1196 setAngleInDegrees: '#setAngle', 1197 1198 getAngleInRadians: function() { 1199 if (!arguments.length) { 1200 return this.isZero() 1201 ? this._angle || 0 1202 : this._angle = Math.atan2(this.y, this.x); 1203 } else { 1204 var point = Point.read(arguments), 1205 div = this.getLength() * point.getLength(); 1206 if (Numerical.isZero(div)) { 1207 return NaN; 1208 } else { 1209 var a = this.dot(point) / div; 1210 return Math.acos(a < -1 ? -1 : a > 1 ? 1 : a); 1211 } 1212 } 1213 }, 1214 1215 setAngleInRadians: function(angle) { 1216 this._angle = angle; 1217 if (!this.isZero()) { 1218 var length = this.getLength(); 1219 this.set( 1220 Math.cos(angle) * length, 1221 Math.sin(angle) * length 1222 ); 1223 } 1224 }, 1225 1226 getQuadrant: function() { 1227 return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; 1228 } 1229}, {
1230 beans: false, 1231 1232 getDirectedAngle: function() { 1233 var point = Point.read(arguments); 1234 return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI; 1235 }, 1236 1237 getDistance: function() { 1238 var point = Point.read(arguments), 1239 x = point.x - this.x, 1240 y = point.y - this.y, 1241 d = x * x + y * y, 1242 squared = Base.read(arguments); 1243 return squared ? d : Math.sqrt(d); 1244 }, 1245 1246 normalize: function(length) { 1247 if (length === undefined) 1248 length = 1; 1249 var current = this.getLength(), 1250 scale = current !== 0 ? length / current : 0, 1251 point = new Point(this.x * scale, this.y * scale); 1252 if (scale >= 0) 1253 point._angle = this._angle; 1254 return point; 1255 }, 1256 1257 rotate: function(angle, center) { 1258 if (angle === 0) 1259 return this.clone(); 1260 angle = angle * Math.PI / 180; 1261 var point = center ? this.subtract(center) : this, 1262 s = Math.sin(angle), 1263 c = Math.cos(angle); 1264 point = new Point( 1265 point.x * c - point.y * s, 1266 point.x * s + point.y * c 1267 ); 1268 return center ? point.add(center) : point; 1269 }, 1270 1271 transform: function(matrix) { 1272 return matrix ? matrix._transformPoint(this) : this; 1273 }, 1274 1275 add: function() { 1276 var point = Point.read(arguments); 1277 return new Point(this.x + point.x, this.y + point.y); 1278 }, 1279 1280 subtract: function() { 1281 var point = Point.read(arguments); 1282 return new Point(this.x - point.x, this.y - point.y); 1283 }, 1284 1285 multiply: function() { 1286 var point = Point.read(arguments); 1287 return new Point(this.x * point.x, this.y * point.y); 1288 }, 1289 1290 divide: function() { 1291 var point = Point.read(arguments); 1292 return new Point(this.x / point.x, this.y / point.y); 1293 }, 1294 1295 modulo: function() { 1296 var point = Point.read(arguments); 1297 return new Point(this.x % point.x, this.y % point.y); 1298 }, 1299 1300 negate: function() { 1301 return new Point(-this.x, -this.y); 1302 }, 1303 1304 isInside: function() { 1305 return Rectangle.read(arguments).contains(this); 1306 }, 1307 1308 isClose: function(point, tolerance) { 1309 return this.getDistance(point) < tolerance; 1310 }, 1311 1312 isCollinear: function(point) { 1313 return Math.abs(this.cross(point)) < 0.000001; 1314 }, 1315 1316 isColinear: '#isCollinear', 1317 1318 isOrthogonal: function(point) { 1319 return Math.abs(this.dot(point)) < 0.000001; 1320 }, 1321 1322 isZero: function() { 1323 return Numerical.isZero(this.x) && Numerical.isZero(this.y); 1324 }, 1325 1326 isNaN: function() { 1327 return isNaN(this.x) || isNaN(this.y); 1328 }, 1329 1330 dot: function() { 1331 var point = Point.read(arguments); 1332 return this.x * point.x + this.y * point.y; 1333 }, 1334 1335 cross: function() { 1336 var point = Point.read(arguments); 1337 return this.x * point.y - this.y * point.x; 1338 }, 1339 1340 project: function() { 1341 var point = Point.read(arguments); 1342 if (point.isZero()) { 1343 return new Point(0, 0); 1344 } else { 1345 var scale = this.dot(point) / point.dot(point); 1346 return new Point( 1347 point.x * scale, 1348 point.y * scale 1349 ); 1350 } 1351 }, 1352 1353 statics: { 1354 min: function() { 1355 var point1 = Point.read(arguments), 1356 point2 = Point.read(arguments); 1357 return new Point( 1358 Math.min(point1.x, point2.x), 1359 Math.min(point1.y, point2.y) 1360 ); 1361 }, 1362 1363 max: function() { 1364 var point1 = Point.read(arguments), 1365 point2 = Point.read(arguments); 1366 return new Point( 1367 Math.max(point1.x, point2.x), 1368 Math.max(point1.y, point2.y) 1369 ); 1370 }, 1371 1372 random: function() { 1373 return new Point(Math.random(), Math.random()); 1374 } 1375 } 1376}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { 1377 var op = Math[name]; 1378 this[name] = function() { 1379 return new Point(op(this.x), op(this.y)); 1380 }; 1381}, {})); 1382 1383var LinkedPoint = Point.extend({ 1384 initialize: function Point(x, y, owner, setter) { 1385 this._x = x; 1386 this._y = y; 1387 this._owner = owner; 1388 this._setter = setter; 1389 }, 1390 1391 set: function(x, y, _dontNotify) { 1392 this._x = x; 1393 this._y = y; 1394 if (!_dontNotify) 1395 this._owner[this._setter](this); 1396 return this; 1397 }, 1398 1399 getX: function() { 1400 return this._x; 1401 }, 1402 1403 setX: function(x) { 1404 this._x = x; 1405 this._owner[this._setter](this); 1406 }, 1407 1408 getY: function() { 1409 return this._y; 1410 }, 1411 1412 setY: function(y) { 1413 this._y = y; 1414 this._owner[this._setter](this); 1415 } 1416}); 1417 1418var Size = Base.extend({ 1419 _class: 'Size', 1420 _readIndex: true, 1421 1422 initialize: function Size(arg0, arg1) { 1423 var type = typeof arg0; 1424 if (type === 'number') { 1425 var hasHeight = typeof arg1 === 'number'; 1426 this.width = arg0; 1427 this.height = hasHeight ? arg1 : arg0; 1428 if (this.__read) 1429 this.__read = hasHeight ? 2 : 1; 1430 } else if (type === 'undefined' || arg0 === null) { 1431 this.width = this.height = 0; 1432 if (this.__read) 1433 this.__read = arg0 === null ? 1 : 0; 1434 } else { 1435 if (Array.isArray(arg0)) { 1436 this.width = arg0[0]; 1437 this.height = arg0.length > 1 ? arg0[1] : arg0[0]; 1438 } else if (arg0.width != null) { 1439 this.width = arg0.width; 1440 this.height = arg0.height; 1441 } else if (arg0.x != null) { 1442 this.width = arg0.x; 1443 this.height = arg0.y; 1444 } else { 1445 this.width = this.height = 0; 1446 if (this.__read) 1447 this.__read = 0; 1448 } 1449 if (this.__read) 1450 this.__read = 1; 1451 } 1452 }, 1453 1454 set: function(width, height) { 1455 this.width = width; 1456 this.height = height; 1457 return this; 1458 }, 1459 1460 equals: function(size) { 1461 return size === this || size && (this.width === size.width 1462 && this.height === size.height 1463 || Array.isArray(size) && this.width === size[0] 1464 && this.height === size[1]) || false; 1465 }, 1466 1467 clone: function() { 1468 return new Size(this.width, this.height); 1469 }, 1470 1471 toString: function() { 1472 var f = Formatter.instance; 1473 return '{ width: ' + f.number(this.width) 1474 + ', height: ' + f.number(this.height) + ' }'; 1475 }, 1476
1477 _serialize: function(options) { 1478 var f = options.formatter; 1479 return [f.number(this.width), 1480 f.number(this.height)]; 1481 }, 1482 1483 add: function() { 1484 var size = Size.read(arguments); 1485 return new Size(this.width + size.width, this.height + size.height); 1486 }, 1487 1488 subtract: function() { 1489 var size = Size.read(arguments); 1490 return new Size(this.width - size.width, this.height - size.height); 1491 }, 1492 1493 multiply: function() { 1494 var size = Size.read(arguments); 1495 return new Size(this.width * size.width, this.height * size.height); 1496 }, 1497 1498 divide: function() { 1499 var size = Size.read(arguments); 1500 return new Size(this.width / size.width, this.height / size.height); 1501 }, 1502 1503 modulo: function() { 1504 var size = Size.read(arguments); 1505 return new Size(this.width % size.width, this.height % size.height); 1506 }, 1507 1508 negate: function() { 1509 return new Size(-this.width, -this.height); 1510 }, 1511 1512 isZero: function() { 1513 return Numerical.isZero(this.width) && Numerical.isZero(this.height); 1514 }, 1515 1516 isNaN: function() { 1517 return isNaN(this.width) || isNaN(this.height); 1518 }, 1519 1520 statics: { 1521 min: function(size1, size2) { 1522 return new Size( 1523 Math.min(size1.width, size2.width), 1524 Math.min(size1.height, size2.height)); 1525 }, 1526 1527 max: function(size1, size2) { 1528 return new Size( 1529 Math.max(size1.width, size2.width), 1530 Math.max(size1.height, size2.height)); 1531 }, 1532 1533 random: function() { 1534 return new Size(Math.random(), Math.random()); 1535 } 1536 } 1537}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { 1538 var op = Math[name]; 1539 this[name] = function() { 1540 return new Size(op(this.width), op(this.height)); 1541 }; 1542}, {})); 1543 1544var LinkedSize = Size.extend({ 1545 initialize: function Size(width, height, owner, setter) { 1546 this._width = width; 1547 this._height = height; 1548 this._owner = owner; 1549 this._setter = setter; 1550 }, 1551 1552 set: function(width, height, _dontNotify) { 1553 this._width = width; 1554 this._height = height; 1555 if (!_dontNotify) 1556 this._owner[this._setter](this); 1557 return this; 1558 }, 1559 1560 getWidth: function() { 1561 return this._width; 1562 }, 1563 1564 setWidth: function(width) { 1565 this._width = width; 1566 this._owner[this._setter](this); 1567 }, 1568 1569 getHeight: function() { 1570 return this._height; 1571 }, 1572 1573 setHeight: function(height) { 1574 this._height = height; 1575 this._owner[this._setter](this); 1576 } 1577}); 1578 1579var Rectangle = Base.extend({ 1580 _class: 'Rectangle', 1581 _readIndex: true, 1582 beans: true, 1583 1584 initialize: function Rectangle(arg0, arg1, arg2, arg3) { 1585 var type = typeof arg0, 1586 read = 0; 1587 if (type === 'number') { 1588 this.x = arg0; 1589 this.y = arg1; 1590 this.width = arg2; 1591 this.height = arg3; 1592 read = 4; 1593 } else if (type === 'undefined' || arg0 === null) { 1594 this.x = this.y = this.width = this.height = 0; 1595 read = arg0 === null ? 1 : 0; 1596 } else if (arguments.length === 1) { 1597 if (Array.isArray(arg0)) { 1598 this.x = arg0[0]; 1599 this.y = arg0[1]; 1600 this.width = arg0[2]; 1601 this.height = arg0[3]; 1602 read = 1; 1603 } else if (arg0.x !== undefined || arg0.width !== undefined) { 1604 this.x = arg0.x || 0; 1605 this.y = arg0.y || 0; 1606 this.width = arg0.width || 0; 1607 this.height = arg0.height || 0; 1608 read = 1; 1609 } else if (arg0.from === undefined && arg0.to === undefined) { 1610 this.x = this.y = this.width = this.height = 0; 1611 this._set(arg0); 1612 read = 1; 1613 } 1614 } 1615 if (!read) { 1616 var point = Point.readNamed(arguments, 'from'), 1617 next = Base.peek(arguments); 1618 this.x = point.x; 1619 this.y = point.y; 1620 if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) { 1621 var to = Point.readNamed(arguments, 'to'); 1622 this.width = to.x - point.x; 1623 this.height = to.y - point.y; 1624 if (this.width < 0) { 1625 this.x = to.x; 1626 this.width = -this.width; 1627 } 1628 if (this.height < 0) { 1629 this.y = to.y; 1630 this.height = -this.height; 1631 } 1632 } else { 1633 var size = Size.read(arguments); 1634 this.width = size.width; 1635 this.height = size.height; 1636 } 1637 read = arguments.__index; 1638 } 1639 if (this.__read) 1640 this.__read = read; 1641 }, 1642 1643 set: function(x, y, width, height) { 1644 this.x = x; 1645 this.y = y; 1646 this.width = width; 1647 this.height = height; 1648 return this; 1649 }, 1650 1651 clone: function() { 1652 return new Rectangle(this.x, this.y, this.width, this.height); 1653 }, 1654 1655 equals: function(rect) { 1656 var rt = Base.isPlainValue(rect) 1657 ? Rectangle.read(arguments) 1658 : rect; 1659 return rt === this 1660 || rt && this.x === rt.x && this.y === rt.y 1661 && this.width === rt.width && this.height === rt.height 1662 || false; 1663 }, 1664 1665 toString: function() { 1666 var f = Formatter.instance; 1667 return '{ x: ' + f.number(this.x) 1668 + ', y: ' + f.number(this.y) 1669 + ', width: ' + f.number(this.width) 1670 + ', height: ' + f.number(this.height) 1671 + ' }'; 1672 }, 1673
1674 _serialize: function(options) { 1675 var f = options.formatter; 1676 return [f.number(this.x), 1677 f.number(this.y), 1678 f.number(this.width), 1679 f.number(this.height)]; 1680 }, 1681 1682 getPoint: function(_dontLink) { 1683 var ctor = _dontLink ? Point : LinkedPoint; 1684 return new ctor(this.x, this.y, this, 'setPoint'); 1685 }, 1686 1687 setPoint: function() { 1688 var point = Point.read(arguments); 1689 this.x = point.x; 1690 this.y = point.y; 1691 }, 1692 1693 getSize: function(_dontLink) { 1694 var ctor = _dontLink ? Size : LinkedSize; 1695 return new ctor(this.width, this.height, this, 'setSize'); 1696 }, 1697 1698 setSize: function() { 1699 var size = Size.read(arguments); 1700 if (this._fixX) 1701 this.x += (this.width - size.width) * this._fixX; 1702 if (this._fixY) 1703 this.y += (this.height - size.height) * this._fixY; 1704 this.width = size.width; 1705 this.height = size.height; 1706 this._fixW = 1; 1707 this._fixH = 1; 1708 }, 1709 1710 getLeft: function() { 1711 return this.x; 1712 }, 1713 1714 setLeft: function(left) { 1715 if (!this._fixW) 1716 this.width -= left - this.x; 1717 this.x = left; 1718 this._fixX = 0; 1719 }, 1720 1721 getTop: function() { 1722 return this.y; 1723 }, 1724 1725 setTop: function(top) { 1726 if (!this._fixH) 1727 this.height -= top - this.y; 1728 this.y = top; 1729 this._fixY = 0; 1730 }, 1731 1732 getRight: function() { 1733 return this.x + this.width; 1734 }, 1735 1736 setRight: function(right) { 1737 if (this._fixX !== undefined && this._fixX !== 1) 1738 this._fixW = 0; 1739 if (this._fixW) 1740 this.x = right - this.width; 1741 else 1742 this.width = right - this.x; 1743 this._fixX = 1; 1744 }, 1745 1746 getBottom: function() { 1747 return this.y + this.height; 1748 }, 1749 1750 setBottom: function(bottom) { 1751 if (this._fixY !== undefined && this._fixY !== 1) 1752 this._fixH = 0; 1753 if (this._fixH) 1754 this.y = bottom - this.height; 1755 else 1756 this.height = bottom - this.y; 1757 this._fixY = 1; 1758 }, 1759 1760 getCenterX: function() { 1761 return this.x + this.width * 0.5; 1762 }, 1763 1764 setCenterX: function(x) { 1765 this.x = x - this.width * 0.5; 1766 this._fixX = 0.5; 1767 }, 1768 1769 getCenterY: function() { 1770 return this.y + this.height * 0.5; 1771 }, 1772 1773 setCenterY: function(y) { 1774 this.y = y - this.height * 0.5; 1775 this._fixY = 0.5; 1776 }, 1777 1778 getCenter: function(_dontLink) { 1779 var ctor = _dontLink ? Point : LinkedPoint; 1780 return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter'); 1781 }, 1782 1783 setCenter: function() { 1784 var point = Point.read(arguments); 1785 this.setCenterX(point.x); 1786 this.setCenterY(point.y); 1787 return this; 1788 }, 1789 1790 getArea: function() { 1791 return this.width * this.height; 1792 }, 1793 1794 isEmpty: function() { 1795 return this.width === 0 || this.height === 0; 1796 }, 1797 1798 contains: function(arg) { 1799 return arg && arg.width !== undefined 1800 || (Array.isArray(arg) ? arg : arguments).length == 4 1801 ? this._containsRectangle(Rectangle.read(arguments)) 1802 : this._containsPoint(Point.read(arguments)); 1803 }, 1804 1805 _containsPoint: function(point) { 1806 var x = point.x, 1807 y = point.y; 1808 return x >= this.x && y >= this.y 1809 && x <= this.x + this.width 1810 && y <= this.y + this.height; 1811 }, 1812 1813 _containsRectangle: function(rect) { 1814 var x = rect.x, 1815 y = rect.y; 1816 return x >= this.x && y >= this.y 1817 && x + rect.width <= this.x + this.width 1818 && y + rect.height <= this.y + this.height; 1819 }, 1820 1821 intersects: function() { 1822 var rect = Rectangle.read(arguments); 1823 return rect.x + rect.width > this.x 1824 && rect.y + rect.height > this.y 1825 && rect.x < this.x + this.width 1826 && rect.y < this.y + this.height; 1827 }, 1828 1829 touches: function() { 1830 var rect = Rectangle.read(arguments); 1831 return rect.x + rect.width >= this.x 1832 && rect.y + rect.height >= this.y 1833 && rect.x <= this.x + this.width 1834 && rect.y <= this.y + this.height; 1835 }, 1836 1837 intersect: function() { 1838 var rect = Rectangle.read(arguments), 1839 x1 = Math.max(this.x, rect.x), 1840 y1 = Math.max(this.y, rect.y), 1841 x2 = Math.min(this.x + this.width, rect.x + rect.width), 1842 y2 = Math.min(this.y + this.height, rect.y + rect.height); 1843 return new Rectangle(x1, y1, x2 - x1, y2 - y1); 1844 }, 1845 1846 unite: function() { 1847 var rect = Rectangle.read(arguments), 1848 x1 = Math.min(this.x, rect.x), 1849 y1 = Math.min(this.y, rect.y), 1850 x2 = Math.max(this.x + this.width, rect.x + rect.width), 1851 y2 = Math.max(this.y + this.height, rect.y + rect.height); 1852 return new Rectangle(x1, y1, x2 - x1, y2 - y1); 1853 }, 1854 1855 include: function() { 1856 var point = Point.read(arguments); 1857 var x1 = Math.min(this.x, point.x), 1858 y1 = Math.min(this.y, point.y), 1859 x2 = Math.max(this.x + this.width, point.x), 1860 y2 = Math.max(this.y + this.height, point.y); 1861 return new Rectangle(x1, y1, x2 - x1, y2 - y1); 1862 }, 1863 1864 expand: function() { 1865 var amount = Size.read(arguments), 1866 hor = amount.width, 1867 ver = amount.height; 1868 return new Rectangle(this.x - hor / 2, this.y - ver / 2, 1869 this.width + hor, this.height + ver); 1870 }, 1871 1872 scale: function(hor, ver) { 1873 return this.expand(this.width * hor - this.width, 1874 this.height * (ver === undefined ? hor : ver) - this.height); 1875 } 1876}, Base.each([ 1877 ['Top', 'Left'], ['Top', 'Right'], 1878 ['Bottom', 'Left'], ['Bottom', 'Right'], 1879 ['Left', 'Center'], ['Top', 'Center'], 1880 ['Right', 'Center'], ['Bottom', 'Center'] 1881 ], 1882 function(parts, index) { 1883 var part = parts.join(''); 1884 var xFirst = /^[RL]/.test(part); 1885 if (index >= 4) 1886 parts[1] += xFirst ? 'Y' : 'X'; 1887 var x = parts[xFirst ? 0 : 1], 1888 y = parts[xFirst ? 1 : 0], 1889 getX = 'get' + x, 1890 getY = 'get' + y, 1891 setX = 'set' + x, 1892 setY = 'set' + y, 1893 get = 'get' + part, 1894 set = 'set' + part;
vendor: 4,541 bytes, lines 1895-2089
1895 this[get] = function(_dontLink) { 1896 var ctor = _dontLink ? Point : LinkedPoint; 1897 return new ctor(this[getX](), this[getY](), this, set); 1898 }; 1899 this[set] = function() { 1900 var point = Point.read(arguments); 1901 this[setX](point.x); 1902 this[setY](point.y); 1903 }; 1904 }, { 1905 beans: true 1906 } 1907)); 1908 1909var LinkedRectangle = Rectangle.extend({ 1910 initialize: function Rectangle(x, y, width, height, owner, setter) { 1911 this.set(x, y, width, height, true); 1912 this._owner = owner; 1913 this._setter = setter; 1914 }, 1915 1916 set: function(x, y, width, height, _dontNotify) { 1917 this._x = x; 1918 this._y = y; 1919 this._width = width; 1920 this._height = height; 1921 if (!_dontNotify) 1922 this._owner[this._setter](this); 1923 return this; 1924 } 1925}, new function() { 1926 var proto = Rectangle.prototype; 1927 1928 return Base.each(['x', 'y', 'width', 'height'], function(key) { 1929 var part = Base.capitalize(key); 1930 var internal = '_' + key; 1931 this['get' + part] = function() { 1932 return this[internal]; 1933 }; 1934 1935 this['set' + part] = function(value) { 1936 this[internal] = value; 1937 if (!this._dontNotify) 1938 this._owner[this._setter](this); 1939 }; 1940 }, Base.each(['Point', 'Size', 'Center', 1941 'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY', 1942 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', 1943 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], 1944 function(key) { 1945 var name = 'set' + key; 1946 this[name] = function() { 1947 this._dontNotify = true; 1948 proto[name].apply(this, arguments); 1949 this._dontNotify = false; 1950 this._owner[this._setter](this); 1951 }; 1952 }, { 1953 isSelected: function() { 1954 return this._owner._boundsSelected; 1955 }, 1956 1957 setSelected: function(selected) { 1958 var owner = this._owner; 1959 if (owner.setSelected) { 1960 owner._boundsSelected = selected; 1961 owner.setSelected(selected || owner._selectedSegmentState > 0); 1962 } 1963 } 1964 }) 1965 ); 1966}); 1967 1968var Matrix = Base.extend({ 1969 _class: 'Matrix', 1970 1971 initialize: function Matrix(arg) { 1972 var count = arguments.length, 1973 ok = true; 1974 if (count === 6) { 1975 this.set.apply(this, arguments); 1976 } else if (count === 1) { 1977 if (arg instanceof Matrix) { 1978 this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty); 1979 } else if (Array.isArray(arg)) { 1980 this.set.apply(this, arg); 1981 } else { 1982 ok = false; 1983 } 1984 } else if (count === 0) { 1985 this.reset(); 1986 } else { 1987 ok = false; 1988 } 1989 if (!ok) 1990 throw new Error('Unsupported matrix parameters'); 1991 }, 1992 1993 set: function(a, c, b, d, tx, ty, _dontNotify) { 1994 this._a = a; 1995 this._c = c; 1996 this._b = b; 1997 this._d = d; 1998 this._tx = tx; 1999 this._ty = ty; 2000 if (!_dontNotify) 2001 this._changed(); 2002 return this; 2003 }, 2004 2005 _serialize: function(options) { 2006 return Base.serialize(this.getValues(), options); 2007 }, 2008 2009 _changed: function() { 2010 var owner = this._owner; 2011 if (owner) { 2012 if (owner._applyMatrix) { 2013 owner.transform(null, true); 2014 } else { 2015 owner._changed(9); 2016 } 2017 } 2018 }, 2019 2020 clone: function() { 2021 return new Matrix(this._a, this._c, this._b, this._d, 2022 this._tx, this._ty); 2023 }, 2024 2025 equals: function(mx) { 2026 return mx === this || mx && this._a === mx._a && this._b === mx._b 2027 && this._c === mx._c && this._d === mx._d 2028 && this._tx === mx._tx && this._ty === mx._ty 2029 || false; 2030 }, 2031 2032 toString: function() { 2033 var f = Formatter.instance; 2034 return '[[' + [f.number(this._a), f.number(this._b), 2035 f.number(this._tx)].join(', ') + '], [' 2036 + [f.number(this._c), f.number(this._d), 2037 f.number(this._ty)].join(', ') + ']]'; 2038 }, 2039 2040 reset: function(_dontNotify) { 2041 this._a = this._d = 1; 2042 this._c = this._b = this._tx = this._ty = 0; 2043 if (!_dontNotify) 2044 this._changed(); 2045 return this; 2046 }, 2047 2048 apply: function(recursively, _setApplyMatrix) { 2049 var owner = this._owner; 2050 if (owner) { 2051 owner.transform(null, true, Base.pick(recursively, true), 2052 _setApplyMatrix); 2053 return this.isIdentity(); 2054 } 2055 return false; 2056 }, 2057 2058 translate: function() { 2059 var point = Point.read(arguments), 2060 x = point.x, 2061 y = point.y; 2062 this._tx += x * this._a + y * this._b; 2063 this._ty += x * this._c + y * this._d; 2064 this._changed(); 2065 return this; 2066 }, 2067 2068 scale: function() { 2069 var scale = Point.read(arguments), 2070 center = Point.read(arguments, 0, { readNull: true }); 2071 if (center) 2072 this.translate(center); 2073 this._a *= scale.x; 2074 this._c *= scale.x; 2075 this._b *= scale.y; 2076 this._d *= scale.y; 2077 if (center) 2078 this.translate(center.negate()); 2079 this._changed(); 2080 return this; 2081 }, 2082 2083 rotate: function(angle ) { 2084 angle *= Math.PI / 180; 2085 var center = Point.read(arguments, 1), 2086 x = center.x, 2087 y = center.y, 2088 cos = Math.cos(angle), 2089 sin = Math.sin(angle),
2090 tx = x - x * cos + y * sin, 2091 ty = y - x * sin - y * cos, 2092 a = this._a, 2093 b = this._b, 2094 c = this._c, 2095 d = this._d; 2096 this._a = cos * a + sin * b; 2097 this._b = -sin * a + cos * b; 2098 this._c = cos * c + sin * d; 2099 this._d = -sin * c + cos * d; 2100 this._tx += tx * a + ty * b; 2101 this._ty += tx * c + ty * d; 2102 this._changed(); 2103 return this; 2104 }, 2105 2106 shear: function() { 2107 var shear = Point.read(arguments), 2108 center = Point.read(arguments, 0, { readNull: true }); 2109 if (center) 2110 this.translate(center); 2111 var a = this._a, 2112 c = this._c; 2113 this._a += shear.y * this._b; 2114 this._c += shear.y * this._d; 2115 this._b += shear.x * a; 2116 this._d += shear.x * c; 2117 if (center) 2118 this.translate(center.negate()); 2119 this._changed(); 2120 return this; 2121 }, 2122 2123 skew: function() { 2124 var skew = Point.read(arguments), 2125 center = Point.read(arguments, 0, { readNull: true }), 2126 toRadians = Math.PI / 180, 2127 shear = new Point(Math.tan(skew.x * toRadians), 2128 Math.tan(skew.y * toRadians)); 2129 return this.shear(shear, center); 2130 }, 2131 2132 concatenate: function(mx) { 2133 var a1 = this._a, 2134 b1 = this._b, 2135 c1 = this._c, 2136 d1 = this._d, 2137 a2 = mx._a, 2138 b2 = mx._b, 2139 c2 = mx._c, 2140 d2 = mx._d, 2141 tx2 = mx._tx, 2142 ty2 = mx._ty; 2143 this._a = a2 * a1 + c2 * b1; 2144 this._b = b2 * a1 + d2 * b1; 2145 this._c = a2 * c1 + c2 * d1; 2146 this._d = b2 * c1 + d2 * d1; 2147 this._tx += tx2 * a1 + ty2 * b1; 2148 this._ty += tx2 * c1 + ty2 * d1; 2149 this._changed(); 2150 return this; 2151 }, 2152 2153 preConcatenate: function(mx) { 2154 var a1 = this._a, 2155 b1 = this._b, 2156 c1 = this._c, 2157 d1 = this._d, 2158 tx1 = this._tx, 2159 ty1 = this._ty, 2160 a2 = mx._a, 2161 b2 = mx._b, 2162 c2 = mx._c, 2163 d2 = mx._d, 2164 tx2 = mx._tx, 2165 ty2 = mx._ty; 2166 this._a = a2 * a1 + b2 * c1; 2167 this._b = a2 * b1 + b2 * d1; 2168 this._c = c2 * a1 + d2 * c1; 2169 this._d = c2 * b1 + d2 * d1; 2170 this._tx = a2 * tx1 + b2 * ty1 + tx2; 2171 this._ty = c2 * tx1 + d2 * ty1 + ty2; 2172 this._changed(); 2173 return this; 2174 }, 2175 2176 chain: function(mx) { 2177 var a1 = this._a, 2178 b1 = this._b, 2179 c1 = this._c, 2180 d1 = this._d, 2181 tx1 = this._tx, 2182 ty1 = this._ty, 2183 a2 = mx._a, 2184 b2 = mx._b, 2185 c2 = mx._c, 2186 d2 = mx._d, 2187 tx2 = mx._tx, 2188 ty2 = mx._ty; 2189 return new Matrix( 2190 a2 * a1 + c2 * b1, 2191 a2 * c1 + c2 * d1, 2192 b2 * a1 + d2 * b1, 2193 b2 * c1 + d2 * d1, 2194 tx1 + tx2 * a1 + ty2 * b1, 2195 ty1 + tx2 * c1 + ty2 * d1); 2196 }, 2197 2198 isIdentity: function() { 2199 return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1 2200 && this._tx === 0 && this._ty === 0; 2201 }, 2202 2203 orNullIfIdentity: function() { 2204 return this.isIdentity() ? null : this; 2205 }, 2206 2207 isInvertible: function() { 2208 return !!this._getDeterminant(); 2209 }, 2210 2211 isSingular: function() { 2212 return !this._getDeterminant(); 2213 }, 2214 2215 transform: function( src, dst, count) { 2216 return arguments.length < 3 2217 ? this._transformPoint(Point.read(arguments)) 2218 : this._transformCoordinates(src, dst, count); 2219 }, 2220 2221 _transformPoint: function(point, dest, _dontNotify) { 2222 var x = point.x, 2223 y = point.y; 2224 if (!dest) 2225 dest = new Point(); 2226 return dest.set( 2227 x * this._a + y * this._b + this._tx, 2228 x * this._c + y * this._d + this._ty, 2229 _dontNotify 2230 ); 2231 }, 2232 2233 _transformCoordinates: function(src, dst, count) { 2234 var i = 0, 2235 j = 0, 2236 max = 2 * count; 2237 while (i < max) { 2238 var x = src[i++], 2239 y = src[i++]; 2240 dst[j++] = x * this._a + y * this._b + this._tx; 2241 dst[j++] = x * this._c + y * this._d + this._ty; 2242 } 2243 return dst; 2244 }, 2245 2246 _transformCorners: function(rect) { 2247 var x1 = rect.x, 2248 y1 = rect.y, 2249 x2 = x1 + rect.width, 2250 y2 = y1 + rect.height, 2251 coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ]; 2252 return this._transformCoordinates(coords, coords, 4); 2253 }, 2254 2255 _transformBounds: function(bounds, dest, _dontNotify) { 2256 var coords = this._transformCorners(bounds), 2257 min = coords.slice(0, 2), 2258 max = coords.slice(); 2259 for (var i = 2; i < 8; i++) { 2260 var val = coords[i], 2261 j = i & 1; 2262 if (val < min[j]) 2263 min[j] = val; 2264 else if (val > max[j]) 2265 max[j] = val; 2266 } 2267 if (!dest) 2268 dest = new Rectangle(); 2269 return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1], 2270 _dontNotify); 2271 }, 2272 2273 inverseTransform: function() { 2274 return this._inverseTransform(Point.read(arguments)); 2275 }, 2276 2277 _getDeterminant: function() { 2278 var det = this._a * this._d - this._b * this._c; 2279 return isFinite(det) && !Numerical.isZero(det) 2280 && isFinite(this._tx) && isFinite(this._ty) 2281 ? det : null; 2282 }, 2283 2284 _inverseTransform: function(point, dest, _dontNotify) { 2285 var det = this._getDeterminant(); 2286 if (!det) 2287 return null; 2288 var x = point.x - this._tx, 2289 y = point.y - this._ty; 2290 if (!dest) 2291 dest = new Point(); 2292 return dest.set( 2293 (x * this._d - y * this._b) / det, 2294 (y * this._a - x * this._c) / det, 2295 _dontNotify 2296 ); 2297 }, 2298 2299 decompose: function() { 2300 var a = this._a, b = this._b, c = this._c, d = this._d;
2301 if (Numerical.isZero(a * d - b * c)) 2302 return null; 2303 2304 var scaleX = Math.sqrt(a * a + b * b); 2305 a /= scaleX; 2306 b /= scaleX; 2307 2308 var shear = a * c + b * d; 2309 c -= a * shear; 2310 d -= b * shear; 2311 2312 var scaleY = Math.sqrt(c * c + d * d); 2313 c /= scaleY; 2314 d /= scaleY; 2315 shear /= scaleY; 2316 2317 if (a * d < b * c) { 2318 a = -a; 2319 b = -b; 2320 shear = -shear; 2321 scaleX = -scaleX; 2322 } 2323 2324 return { 2325 scaling: new Point(scaleX, scaleY), 2326 rotation: -Math.atan2(b, a) * 180 / Math.PI, 2327 shearing: shear 2328 }; 2329 }, 2330 2331 getValues: function() { 2332 return [ this._a, this._c, this._b, this._d, this._tx, this._ty ]; 2333 }, 2334 2335 getTranslation: function() { 2336 return new Point(this._tx, this._ty); 2337 }, 2338 2339 getScaling: function() { 2340 return (this.decompose() || {}).scaling; 2341 }, 2342 2343 getRotation: function() { 2344 return (this.decompose() || {}).rotation; 2345 }, 2346 2347 inverted: function() { 2348 var det = this._getDeterminant(); 2349 return det && new Matrix( 2350 this._d / det, 2351 -this._c / det, 2352 -this._b / det, 2353 this._a / det, 2354 (this._b * this._ty - this._d * this._tx) / det, 2355 (this._c * this._tx - this._a * this._ty) / det); 2356 }, 2357 2358 shiftless: function() { 2359 return new Matrix(this._a, this._c, this._b, this._d, 0, 0); 2360 }, 2361 2362 applyToContext: function(ctx) { 2363 ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty); 2364 } 2365}, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) { 2366 var part = Base.capitalize(name), 2367 prop = '_' + name; 2368 this['get' + part] = function() { 2369 return this[prop]; 2370 }; 2371 this['set' + part] = function(value) { 2372 this[prop] = value; 2373 this._changed(); 2374 }; 2375}, {})); 2376 2377var Line = Base.extend({ 2378 _class: 'Line', 2379 2380 initialize: function Line(arg0, arg1, arg2, arg3, arg4) { 2381 var asVector = false; 2382 if (arguments.length >= 4) { 2383 this._px = arg0; 2384 this._py = arg1; 2385 this._vx = arg2; 2386 this._vy = arg3; 2387 asVector = arg4; 2388 } else { 2389 this._px = arg0.x; 2390 this._py = arg0.y; 2391 this._vx = arg1.x; 2392 this._vy = arg1.y; 2393 asVector = arg2; 2394 } 2395 if (!asVector) { 2396 this._vx -= this._px; 2397 this._vy -= this._py; 2398 } 2399 }, 2400 2401 getPoint: function() { 2402 return new Point(this._px, this._py); 2403 }, 2404 2405 getVector: function() { 2406 return new Point(this._vx, this._vy); 2407 }, 2408 2409 getLength: function() { 2410 return this.getVector().getLength(); 2411 }, 2412 2413 intersect: function(line, isInfinite) { 2414 return Line.intersect( 2415 this._px, this._py, this._vx, this._vy, 2416 line._px, line._py, line._vx, line._vy, 2417 true, isInfinite); 2418 }, 2419 2420 getSide: function(point) { 2421 return Line.getSide( 2422 this._px, this._py, this._vx, this._vy, 2423 point.x, point.y, true); 2424 }, 2425 2426 getDistance: function(point) { 2427 return Math.abs(Line.getSignedDistance( 2428 this._px, this._py, this._vx, this._vy, 2429 point.x, point.y, true)); 2430 }, 2431 2432 statics: { 2433 intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector, 2434 isInfinite) { 2435 if (!asVector) { 2436 avx -= apx; 2437 avy -= apy; 2438 bvx -= bpx; 2439 bvy -= bpy; 2440 } 2441 var cross = avx * bvy - avy * bvx; 2442 if (!Numerical.isZero(cross)) { 2443 var dx = apx - bpx, 2444 dy = apy - bpy, 2445 ta = (bvx * dy - bvy * dx) / cross, 2446 tb = (avx * dy - avy * dx) / cross; 2447 if (isInfinite || 0 <= ta && ta <= 1 && 0 <= tb && tb <= 1) 2448 return new Point( 2449 apx + ta * avx, 2450 apy + ta * avy); 2451 } 2452 }, 2453 2454 getSide: function(px, py, vx, vy, x, y, asVector) { 2455 if (!asVector) { 2456 vx -= px; 2457 vy -= py; 2458 } 2459 var v2x = x - px, 2460 v2y = y - py, 2461 ccw = v2x * vy - v2y * vx; 2462 if (ccw === 0) { 2463 ccw = v2x * vx + v2y * vy; 2464 if (ccw > 0) { 2465 v2x -= vx; 2466 v2y -= vy; 2467 ccw = v2x * vx + v2y * vy; 2468 if (ccw < 0) 2469 ccw = 0; 2470 } 2471 } 2472 return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; 2473 }, 2474 2475 getSignedDistance: function(px, py, vx, vy, x, y, asVector) { 2476 if (!asVector) { 2477 vx -= px; 2478 vy -= py; 2479 } 2480 return Numerical.isZero(vx) 2481 ? vy >= 0 ? px - x : x - px 2482 : Numerical.isZero(vy) 2483 ? vx >= 0 ? y - py : py - y 2484 : (vx * (y - py) - vy * (x - px)) / Math.sqrt(vx * vx + vy * vy); 2485 } 2486 } 2487}); 2488 2489var Project = PaperScopeItem.extend({ 2490 _class: 'Project', 2491 _list: 'projects', 2492 _reference: 'project', 2493 2494 initialize: function Project(element) { 2495 PaperScopeItem.call(this, true); 2496 this.layers = []; 2497 this._activeLayer = null; 2498 this.symbols = []; 2499 this._currentStyle = new Style(null, null, this); 2500 this._view = View.create(this, 2501 element || CanvasProvider.getCanvas(1, 1)); 2502 this._selectedItems = {}; 2503 this._selectedItemCount = 0; 2504 this._updateVersion = 0; 2505 }, 2506
2507 _serialize: function(options, dictionary) { 2508 return Base.serialize(this.layers, options, true, dictionary); 2509 }, 2510 2511 clear: function() { 2512 for (var i = this.layers.length - 1; i >= 0; i--) 2513 this.layers[i].remove(); 2514 this.symbols = []; 2515 }, 2516 2517 isEmpty: function() { 2518 return this.layers.length === 0; 2519 }, 2520 2521 remove: function remove() { 2522 if (!remove.base.call(this)) 2523 return false; 2524 if (this._view) 2525 this._view.remove(); 2526 return true; 2527 }, 2528 2529 getView: function() { 2530 return this._view; 2531 }, 2532 2533 getCurrentStyle: function() { 2534 return this._currentStyle; 2535 }, 2536 2537 setCurrentStyle: function(style) { 2538 this._currentStyle.initialize(style); 2539 }, 2540 2541 getIndex: function() { 2542 return this._index; 2543 }, 2544 2545 getOptions: function() { 2546 return this._scope.settings; 2547 }, 2548 2549 getActiveLayer: function() { 2550 return this._activeLayer || new Layer({ project: this }); 2551 }, 2552 2553 getSelectedItems: function() { 2554 var items = []; 2555 for (var id in this._selectedItems) { 2556 var item = this._selectedItems[id]; 2557 if (item.isInserted()) 2558 items.push(item); 2559 } 2560 return items; 2561 }, 2562 2563 insertChild: function(index, item, _preserve) { 2564 if (item instanceof Layer) { 2565 item._remove(false, true); 2566 Base.splice(this.layers, [item], index, 0); 2567 item._setProject(this, true); 2568 if (this._changes) 2569 item._changed(5); 2570 if (!this._activeLayer) 2571 this._activeLayer = item; 2572 } else if (item instanceof Item) { 2573 (this._activeLayer 2574 || this.insertChild(index, new Layer(Item.NO_INSERT))) 2575 .insertChild(index, item, _preserve); 2576 } else { 2577 item = null; 2578 } 2579 return item; 2580 }, 2581 2582 addChild: function(item, _preserve) { 2583 return this.insertChild(undefined, item, _preserve); 2584 }, 2585 2586 _updateSelection: function(item) { 2587 var id = item._id, 2588 selectedItems = this._selectedItems; 2589 if (item._selected) { 2590 if (selectedItems[id] !== item) { 2591 this._selectedItemCount++; 2592 selectedItems[id] = item; 2593 } 2594 } else if (selectedItems[id] === item) { 2595 this._selectedItemCount--; 2596 delete selectedItems[id]; 2597 } 2598 }, 2599 2600 selectAll: function() { 2601 var layers = this.layers; 2602 for (var i = 0, l = layers.length; i < l; i++) 2603 layers[i].setFullySelected(true); 2604 }, 2605 2606 deselectAll: function() { 2607 var selectedItems = this._selectedItems; 2608 for (var i in selectedItems) 2609 selectedItems[i].setFullySelected(false); 2610 }, 2611 2612 hitTest: function() { 2613 var point = Point.read(arguments), 2614 options = HitResult.getOptions(Base.read(arguments)); 2615 for (var i = this.layers.length - 1; i >= 0; i--) { 2616 var res = this.layers[i]._hitTest(point, options); 2617 if (res) return res; 2618 } 2619 return null; 2620 }, 2621 2622 getItems: function(match) { 2623 return Item._getItems(this.layers, match); 2624 }, 2625 2626 getItem: function(match) { 2627 return Item._getItems(this.layers, match, null, null, true)[0] || null; 2628 }, 2629 2630 importJSON: function(json) { 2631 this.activate(); 2632 var layer = this._activeLayer; 2633 return Base.importJSON(json, layer && layer.isEmpty() && layer); 2634 }, 2635 2636 draw: function(ctx, matrix, pixelRatio) { 2637 this._updateVersion++; 2638 ctx.save(); 2639 matrix.applyToContext(ctx); 2640 var param = new Base({ 2641 offset: new Point(0, 0), 2642 pixelRatio: pixelRatio, 2643 viewMatrix: matrix.isIdentity() ? null : matrix, 2644 matrices: [new Matrix()], 2645 updateMatrix: true 2646 }); 2647 for (var i = 0, layers = this.layers, l = layers.length; i < l; i++) 2648 layers[i].draw(ctx, param); 2649 ctx.restore(); 2650 2651 if (this._selectedItemCount > 0) { 2652 ctx.save(); 2653 ctx.strokeWidth = 1; 2654 var items = this._selectedItems, 2655 size = this._scope.settings.handleSize, 2656 version = this._updateVersion; 2657 for (var id in items) 2658 items[id]._drawSelection(ctx, matrix, size, items, version); 2659 ctx.restore(); 2660 } 2661 } 2662}); 2663 2664var Symbol = Base.extend({ 2665 _class: 'Symbol', 2666 2667 initialize: function Symbol(item, dontCenter) { 2668 this._id = UID.get(); 2669 this.project = paper.project; 2670 this.project.symbols.push(this); 2671 if (item) 2672 this.setDefinition(item, dontCenter); 2673 }, 2674 2675 _serialize: function(options, dictionary) { 2676 return dictionary.add(this, function() { 2677 return Base.serialize([this._class, this._definition], 2678 options, false, dictionary); 2679 }); 2680 }, 2681 2682 _changed: function(flags) { 2683 if (flags & 8) { 2684 Item._clearBoundsCache(this); 2685 } 2686 if (flags & 1) { 2687 this.project._needsUpdate = true; 2688 } 2689 }, 2690 2691 getDefinition: function() { 2692 return this._definition; 2693 }, 2694 2695 setDefinition: function(item, _dontCenter) { 2696 if (item._parentSymbol) 2697 item = item.clone(); 2698 if (this._definition) 2699 this._definition._parentSymbol = null; 2700 this._definition = item; 2701 item.remove(); 2702 item.setSelected(false); 2703 if (!_dontCenter) 2704 item.setPosition(new Point()); 2705 item._parentSymbol = this; 2706 this._changed(9); 2707 }, 2708 2709 place: function(position) { 2710 return new PlacedSymbol(this, position); 2711 }, 2712 2713 clone: function() { 2714 return new Symbol(this._definition.clone(false)); 2715 }, 2716 2717 equals: function(symbol) { 2718 return symbol === this 2719 || symbol && this.definition.equals(symbol.definition) 2720 || false; 2721 } 2722}); 2723 2724var Item = Base.extend(Emitter, { 2725 statics: { 2726 extend: function extend(src) { 2727 if (src._serializeFields) 2728 src._serializeFields = new Base( 2729 this.prototype._serializeFields, src._serializeFields); 2730 return extend.base.apply(this, arguments); 2731 }, 2732 2733 NO_INSERT: { insert: false } 2734 }, 2735 2736 _class: 'Item', 2737 _applyMatrix: true, 2738 _canApplyMatrix: true, 2739 _boundsSelected: false, 2740 _selectChildren: false, 2741 _serializeFields: { 2742 name: null, 2743 applyMatrix: null, 2744 matrix: new Matrix(), 2745 pivot: null,
2746 locked: false, 2747 visible: true, 2748 blendMode: 'normal', 2749 opacity: 1, 2750 guide: false, 2751 selected: false, 2752 clipMask: false, 2753 data: {} 2754 }, 2755 2756 initialize: function Item() { 2757 }, 2758 2759 _initialize: function(props, point) { 2760 var hasProps = props && Base.isPlainObject(props), 2761 internal = hasProps && props.internal === true, 2762 matrix = this._matrix = new Matrix(), 2763 project = hasProps && props.project || paper.project; 2764 if (!internal) 2765 this._id = UID.get(); 2766 this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix; 2767 if (point) 2768 matrix.translate(point); 2769 matrix._owner = this; 2770 this._style = new Style(project._currentStyle, this, project); 2771 if (!this._project) { 2772 if (internal || hasProps && props.insert === false) { 2773 this._setProject(project); 2774 } else if (hasProps && props.parent) { 2775 this.setParent(props.parent); 2776 } else { 2777 (project._activeLayer || new Layer()).addChild(this); 2778 } 2779 } 2780 if (hasProps && props !== Item.NO_INSERT) 2781 this._set(props, { insert: true, project: true, parent: true }, 2782 true); 2783 return hasProps; 2784 }, 2785 2786 _events: new function() { 2787 2788 var mouseFlags = { 2789 mousedown: { 2790 mousedown: 1, 2791 mousedrag: 1, 2792 click: 1, 2793 doubleclick: 1 2794 }, 2795 mouseup: { 2796 mouseup: 1, 2797 mousedrag: 1, 2798 click: 1, 2799 doubleclick: 1 2800 }, 2801 mousemove: { 2802 mousedrag: 1, 2803 mousemove: 1, 2804 mouseenter: 1, 2805 mouseleave: 1 2806 } 2807 }; 2808 2809 var mouseEvent = { 2810 install: function(type) { 2811 var counters = this.getView()._eventCounters; 2812 if (counters) { 2813 for (var key in mouseFlags) { 2814 counters[key] = (counters[key] || 0) 2815 + (mouseFlags[key][type] || 0); 2816 } 2817 } 2818 }, 2819 uninstall: function(type) { 2820 var counters = this.getView()._eventCounters; 2821 if (counters) { 2822 for (var key in mouseFlags) 2823 counters[key] -= mouseFlags[key][type] || 0; 2824 } 2825 } 2826 }; 2827 2828 return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick', 2829 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'], 2830 function(name) { 2831 this[name] = mouseEvent; 2832 }, { 2833 onFrame: { 2834 install: function() { 2835 this._animateItem(true); 2836 }, 2837 uninstall: function() { 2838 this._animateItem(false); 2839 } 2840 }, 2841 2842 onLoad: {} 2843 } 2844 ); 2845 }, 2846 2847 _animateItem: function(animate) { 2848 this.getView()._animateItem(this, animate); 2849 }, 2850 2851 _serialize: function(options, dictionary) { 2852 var props = {}, 2853 that = this; 2854 2855 function serialize(fields) { 2856 for (var key in fields) { 2857 var value = that[key]; 2858 if (!Base.equals(value, key === 'leading' 2859 ? fields.fontSize * 1.2 : fields[key])) { 2860 props[key] = Base.serialize(value, options, 2861 key !== 'data', dictionary); 2862 } 2863 } 2864 } 2865 2866 serialize(this._serializeFields); 2867 if (!(this instanceof Group)) 2868 serialize(this._style._defaults); 2869 return [ this._class, props ]; 2870 }, 2871 2872 _changed: function(flags) { 2873 var symbol = this._parentSymbol, 2874 cacheParent = this._parent || symbol, 2875 project = this._project; 2876 if (flags & 8) { 2877 this._bounds = this._position = this._decomposed = 2878 this._globalMatrix = this._currentPath = undefined; 2879 } 2880 if (cacheParent 2881 && (flags & 40)) { 2882 Item._clearBoundsCache(cacheParent); 2883 } 2884 if (flags & 2) { 2885 Item._clearBoundsCache(this); 2886 } 2887 if (project) { 2888 if (flags & 1) { 2889 project._needsUpdate = true; 2890 } 2891 if (project._changes) { 2892 var entry = project._changesById[this._id]; 2893 if (entry) { 2894 entry.flags |= flags; 2895 } else { 2896 entry = { item: this, flags: flags }; 2897 project._changesById[this._id] = entry; 2898 project._changes.push(entry); 2899 } 2900 } 2901 } 2902 if (symbol) 2903 symbol._changed(flags); 2904 }, 2905 2906 set: function(props) { 2907 if (props) 2908 this._set(props); 2909 return this; 2910 }, 2911 2912 getId: function() { 2913 return this._id; 2914 }, 2915 2916 getName: function() { 2917 return this._name; 2918 }, 2919 2920 setName: function(name, unique) { 2921 2922 if (this._name) 2923 this._removeNamed(); 2924 if (name === (+name) + '') 2925 throw new Error( 2926 'Names consisting only of numbers are not supported.'); 2927 var parent = this._parent; 2928 if (name && parent) { 2929 var children = parent._children, 2930 namedChildren = parent._namedChildren, 2931 orig = name, 2932 i = 1; 2933 while (unique && children[name]) 2934 name = orig + ' ' + (i++); 2935 (namedChildren[name] = namedChildren[name] || []).push(this); 2936 children[name] = this; 2937 } 2938 this._name = name || undefined; 2939 this._changed(128); 2940 }, 2941 2942 getStyle: function() { 2943 return this._style; 2944 }, 2945 2946 setStyle: function(style) {
2947 this.getStyle().set(style); 2948 } 2949}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'], 2950 function(name) { 2951 var part = Base.capitalize(name), 2952 name = '_' + name; 2953 this['get' + part] = function() { 2954 return this[name]; 2955 }; 2956 this['set' + part] = function(value) { 2957 if (value != this[name]) { 2958 this[name] = value; 2959 this._changed(name === '_locked' 2960 ? 128 : 129); 2961 } 2962 }; 2963 }, 2964{}), { 2965 beans: true, 2966 2967 _locked: false, 2968 2969 _visible: true, 2970 2971 _blendMode: 'normal', 2972 2973 _opacity: 1, 2974 2975 _guide: false, 2976 2977 isSelected: function() { 2978 if (this._selectChildren) { 2979 var children = this._children; 2980 for (var i = 0, l = children.length; i < l; i++) 2981 if (children[i].isSelected()) 2982 return true; 2983 } 2984 return this._selected; 2985 }, 2986 2987 setSelected: function(selected, noChildren) { 2988 if (!noChildren && this._selectChildren) { 2989 var children = this._children; 2990 for (var i = 0, l = children.length; i < l; i++) 2991 children[i].setSelected(selected); 2992 } 2993 if ((selected = !!selected) ^ this._selected) { 2994 this._selected = selected; 2995 this._project._updateSelection(this); 2996 this._changed(129); 2997 } 2998 }, 2999 3000 _selected: false, 3001 3002 isFullySelected: function() { 3003 var children = this._children; 3004 if (children && this._selected) { 3005 for (var i = 0, l = children.length; i < l; i++) 3006 if (!children[i].isFullySelected()) 3007 return false; 3008 return true; 3009 } 3010 return this._selected; 3011 }, 3012 3013 setFullySelected: function(selected) { 3014 var children = this._children; 3015 if (children) { 3016 for (var i = 0, l = children.length; i < l; i++) 3017 children[i].setFullySelected(selected); 3018 } 3019 this.setSelected(selected, true); 3020 }, 3021 3022 isClipMask: function() { 3023 return this._clipMask; 3024 }, 3025 3026 setClipMask: function(clipMask) { 3027 if (this._clipMask != (clipMask = !!clipMask)) { 3028 this._clipMask = clipMask; 3029 if (clipMask) { 3030 this.setFillColor(null); 3031 this.setStrokeColor(null); 3032 } 3033 this._changed(129); 3034 if (this._parent) 3035 this._parent._changed(1024); 3036 } 3037 }, 3038 3039 _clipMask: false, 3040 3041 getData: function() { 3042 if (!this._data) 3043 this._data = {}; 3044 return this._data; 3045 }, 3046 3047 setData: function(data) { 3048 this._data = data; 3049 }, 3050 3051 getPosition: function(_dontLink) { 3052 var position = this._position, 3053 ctor = _dontLink ? Point : LinkedPoint; 3054 if (!position) { 3055 var pivot = this._pivot; 3056 position = this._position = pivot 3057 ? this._matrix._transformPoint(pivot) 3058 : this.getBounds().getCenter(true); 3059 } 3060 return new ctor(position.x, position.y, this, 'setPosition'); 3061 }, 3062 3063 setPosition: function() { 3064 this.translate(Point.read(arguments).subtract(this.getPosition(true))); 3065 }, 3066 3067 getPivot: function(_dontLink) { 3068 var pivot = this._pivot; 3069 if (pivot) { 3070 var ctor = _dontLink ? Point : LinkedPoint; 3071 pivot = new ctor(pivot.x, pivot.y, this, 'setPivot'); 3072 } 3073 return pivot; 3074 }, 3075 3076 setPivot: function() { 3077 this._pivot = Point.read(arguments, 0, { clone: true, readNull: true }); 3078 this._position = undefined; 3079 }, 3080 3081 _pivot: null, 3082}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds', 3083 'internalBounds', 'internalRoughBounds'], 3084 function(key) { 3085 var getter = 'get' + Base.capitalize(key), 3086 match = key.match(/^internal(.*)$/), 3087 internalGetter = match ? 'get' + match[1] : null; 3088 this[getter] = function(_matrix) { 3089 var boundsGetter = this._boundsGetter, 3090 name = !internalGetter && (typeof boundsGetter === 'string' 3091 ? boundsGetter : boundsGetter && boundsGetter[getter]) 3092 || getter, 3093 bounds = this._getCachedBounds(name, _matrix, this, 3094 internalGetter); 3095 return key === 'bounds' 3096 ? new LinkedRectangle(bounds.x, bounds.y, bounds.width, 3097 bounds.height, this, 'setBounds') 3098 : bounds; 3099 }; 3100 }, 3101{ 3102 beans: true, 3103 3104 _getBounds: function(getter, matrix, cacheItem) { 3105 var children = this._children; 3106 if (!children || children.length == 0) 3107 return new Rectangle(); 3108 Item._updateBoundsCache(this, cacheItem); 3109 var x1 = Infinity, 3110 x2 = -x1, 3111 y1 = x1, 3112 y2 = x2; 3113 for (var i = 0, l = children.length; i < l; i++) { 3114 var child = children[i]; 3115 if (child._visible && !child.isEmpty()) { 3116 var rect = child._getCachedBounds(getter, 3117 matrix && matrix.chain(child._matrix), cacheItem); 3118 x1 = Math.min(rect.x, x1); 3119 y1 = Math.min(rect.y, y1); 3120 x2 = Math.max(rect.x + rect.width, x2); 3121 y2 = Math.max(rect.y + rect.height, y2); 3122 } 3123 } 3124 return isFinite(x1) 3125 ? new Rectangle(x1, y1, x2 - x1, y2 - y1) 3126 : new Rectangle(); 3127 }, 3128 3129 setBounds: function() { 3130 var rect = Rectangle.read(arguments), 3131 bounds = this.getBounds(), 3132 matrix = new Matrix(), 3133 center = rect.getCenter(); 3134 matrix.translate(center); 3135 if (rect.width != bounds.width || rect.height != bounds.height) { 3136 matrix.scale( 3137 bounds.width != 0 ? rect.width / bounds.width : 1, 3138 bounds.height != 0 ? rect.height / bounds.height : 1); 3139 } 3140 center = bounds.getCenter(); 3141 matrix.translate(-center.x, -center.y); 3142 this.transform(matrix); 3143 }, 3144 3145 _getCachedBounds: function(getter, matrix, cacheItem, internalGetter) { 3146 matrix = matrix && matrix.orNullIfIdentity(); 3147 var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(), 3148 cache = (!matrix || matrix.equals(_matrix)) && getter; 3149 Item._updateBoundsCache(this._parent || this._parentSymbol, cacheItem);
3150 if (cache && this._bounds && this._bounds[cache]) 3151 return this._bounds[cache].clone(); 3152 var bounds = this._getBounds(internalGetter || getter, 3153 matrix || _matrix, cacheItem); 3154 if (cache) { 3155 if (!this._bounds) 3156 this._bounds = {}; 3157 var cached = this._bounds[cache] = bounds.clone(); 3158 cached._internal = !!internalGetter; 3159 } 3160 return bounds; 3161 }, 3162 3163 statics: { 3164 _updateBoundsCache: function(parent, item) { 3165 if (parent) { 3166 var id = item._id, 3167 ref = parent._boundsCache = parent._boundsCache || { 3168 ids: {}, 3169 list: [] 3170 }; 3171 if (!ref.ids[id]) { 3172 ref.list.push(item); 3173 ref.ids[id] = item; 3174 } 3175 } 3176 }, 3177 3178 _clearBoundsCache: function(item) { 3179 var cache = item._boundsCache; 3180 if (cache) { 3181 item._bounds = item._position = item._boundsCache = undefined; 3182 for (var i = 0, list = cache.list, l = list.length; i < l; i++){ 3183 var other = list[i]; 3184 if (other !== item) { 3185 other._bounds = other._position = undefined; 3186 if (other._boundsCache) 3187 Item._clearBoundsCache(other); 3188 } 3189 } 3190 } 3191 } 3192 } 3193 3194}), { 3195 beans: true, 3196 3197 _decompose: function() { 3198 return this._decomposed = this._matrix.decompose(); 3199 }, 3200 3201 getRotation: function() { 3202 var decomposed = this._decomposed || this._decompose(); 3203 return decomposed && decomposed.rotation; 3204 }, 3205 3206 setRotation: function(rotation) { 3207 var current = this.getRotation(); 3208 if (current != null && rotation != null) { 3209 var decomposed = this._decomposed; 3210 this.rotate(rotation - current); 3211 decomposed.rotation = rotation; 3212 this._decomposed = decomposed; 3213 } 3214 }, 3215 3216 getScaling: function(_dontLink) { 3217 var decomposed = this._decomposed || this._decompose(), 3218 scaling = decomposed && decomposed.scaling, 3219 ctor = _dontLink ? Point : LinkedPoint; 3220 return scaling && new ctor(scaling.x, scaling.y, this, 'setScaling'); 3221 }, 3222 3223 setScaling: function() { 3224 var current = this.getScaling(); 3225 if (current) { 3226 var scaling = Point.read(arguments, 0, { clone: true }), 3227 decomposed = this._decomposed; 3228 this.scale(scaling.x / current.x, scaling.y / current.y); 3229 decomposed.scaling = scaling; 3230 this._decomposed = decomposed; 3231 } 3232 }, 3233 3234 getMatrix: function() { 3235 return this._matrix; 3236 }, 3237 3238 setMatrix: function() { 3239 var matrix = this._matrix; 3240 matrix.initialize.apply(matrix, arguments); 3241 if (this._applyMatrix) { 3242 this.transform(null, true); 3243 } else { 3244 this._changed(9); 3245 } 3246 }, 3247 3248 getGlobalMatrix: function(_dontClone) { 3249 var matrix = this._globalMatrix, 3250 updateVersion = this._project._updateVersion; 3251 if (matrix && matrix._updateVersion !== updateVersion) 3252 matrix = null; 3253 if (!matrix) { 3254 matrix = this._globalMatrix = this._matrix.clone(); 3255 var parent = this._parent; 3256 if (parent) 3257 matrix.preConcatenate(parent.getGlobalMatrix(true)); 3258 matrix._updateVersion = updateVersion; 3259 } 3260 return _dontClone ? matrix : matrix.clone(); 3261 }, 3262 3263 getApplyMatrix: function() { 3264 return this._applyMatrix; 3265 }, 3266 3267 setApplyMatrix: function(apply) { 3268 if (this._applyMatrix = this._canApplyMatrix && !!apply) 3269 this.transform(null, true); 3270 }, 3271 3272 getTransformContent: '#getApplyMatrix', 3273 setTransformContent: '#setApplyMatrix', 3274}, { 3275 getProject: function() { 3276 return this._project; 3277 }, 3278 3279 _setProject: function(project, installEvents) { 3280 if (this._project !== project) { 3281 if (this._project) 3282 this._installEvents(false); 3283 this._project = project; 3284 var children = this._children; 3285 for (var i = 0, l = children && children.length; i < l; i++) 3286 children[i]._setProject(project); 3287 installEvents = true; 3288 } 3289 if (installEvents) 3290 this._installEvents(true); 3291 }, 3292 3293 getView: function() { 3294 return this._project.getView(); 3295 }, 3296 3297 _installEvents: function _installEvents(install) { 3298 _installEvents.base.call(this, install); 3299 var children = this._children; 3300 for (var i = 0, l = children && children.length; i < l; i++) 3301 children[i]._installEvents(install); 3302 }, 3303 3304 getLayer: function() { 3305 var parent = this; 3306 while (parent = parent._parent) { 3307 if (parent instanceof Layer) 3308 return parent; 3309 } 3310 return null; 3311 }, 3312 3313 getParent: function() { 3314 return this._parent; 3315 }, 3316 3317 setParent: function(item) { 3318 return item.addChild(this); 3319 }, 3320 3321 getChildren: function() { 3322 return this._children; 3323 }, 3324 3325 setChildren: function(items) { 3326 this.removeChildren(); 3327 this.addChildren(items); 3328 }, 3329 3330 getFirstChild: function() { 3331 return this._children && this._children[0] || null; 3332 }, 3333 3334 getLastChild: function() { 3335 return this._children && this._children[this._children.length - 1] 3336 || null; 3337 }, 3338 3339 getNextSibling: function() { 3340 return this._parent && this._parent._children[this._index + 1] || null; 3341 }, 3342 3343 getPreviousSibling: function() { 3344 return this._parent && this._parent._children[this._index - 1] || null; 3345 }, 3346 3347 getIndex: function() { 3348 return this._index; 3349 }, 3350 3351 equals: function(item) { 3352 return item === this || item && this._class === item._class 3353 && this._style.equals(item._style)
3354 && this._matrix.equals(item._matrix) 3355 && this._locked === item._locked 3356 && this._visible === item._visible 3357 && this._blendMode === item._blendMode 3358 && this._opacity === item._opacity 3359 && this._clipMask === item._clipMask 3360 && this._guide === item._guide 3361 && this._equals(item) 3362 || false; 3363 }, 3364 3365 _equals: function(item) { 3366 return Base.equals(this._children, item._children); 3367 }, 3368 3369 clone: function(insert) { 3370 return this._clone(new this.constructor(Item.NO_INSERT), insert); 3371 }, 3372 3373 _clone: function(copy, insert, includeMatrix) { 3374 var keys = ['_locked', '_visible', '_blendMode', '_opacity', 3375 '_clipMask', '_guide'], 3376 children = this._children; 3377 copy.setStyle(this._style); 3378 for (var i = 0, l = children && children.length; i < l; i++) { 3379 copy.addChild(children[i].clone(false), true); 3380 } 3381 for (var i = 0, l = keys.length; i < l; i++) { 3382 var key = keys[i]; 3383 if (this.hasOwnProperty(key)) 3384 copy[key] = this[key]; 3385 } 3386 if (includeMatrix !== false) 3387 copy._matrix.initialize(this._matrix); 3388 copy.setApplyMatrix(this._applyMatrix); 3389 copy.setPivot(this._pivot); 3390 copy.setSelected(this._selected); 3391 copy._data = this._data ? Base.clone(this._data) : null; 3392 if (insert || insert === undefined) 3393 copy.insertAbove(this); 3394 if (this._name) 3395 copy.setName(this._name, true); 3396 return copy; 3397 }, 3398 3399 copyTo: function(itemOrProject) { 3400 return itemOrProject.addChild(this.clone(false)); 3401 }, 3402 3403 rasterize: function(resolution) { 3404 var bounds = this.getStrokeBounds(), 3405 scale = (resolution || this.getView().getResolution()) / 72, 3406 topLeft = bounds.getTopLeft().floor(), 3407 bottomRight = bounds.getBottomRight().ceil(), 3408 size = new Size(bottomRight.subtract(topLeft)), 3409 canvas = CanvasProvider.getCanvas(size.multiply(scale)), 3410 ctx = canvas.getContext('2d'), 3411 matrix = new Matrix().scale(scale).translate(topLeft.negate()); 3412 ctx.save(); 3413 matrix.applyToContext(ctx); 3414 this.draw(ctx, new Base({ matrices: [matrix] })); 3415 ctx.restore(); 3416 var raster = new Raster(Item.NO_INSERT); 3417 raster.setCanvas(canvas); 3418 raster.transform(new Matrix().translate(topLeft.add(size.divide(2))) 3419 .scale(1 / scale)); 3420 raster.insertAbove(this); 3421 return raster; 3422 }, 3423 3424 contains: function() { 3425 return !!this._contains( 3426 this._matrix._inverseTransform(Point.read(arguments))); 3427 }, 3428 3429 _contains: function(point) { 3430 if (this._children) { 3431 for (var i = this._children.length - 1; i >= 0; i--) { 3432 if (this._children[i].contains(point)) 3433 return true; 3434 } 3435 return false; 3436 } 3437 return point.isInside(this.getInternalBounds()); 3438 }, 3439 3440 isInside: function() { 3441 return Rectangle.read(arguments).contains(this.getBounds()); 3442 }, 3443 3444 _asPathItem: function() { 3445 return new Path.Rectangle({ 3446 rectangle: this.getInternalBounds(), 3447 matrix: this._matrix, 3448 insert: false, 3449 }); 3450 }, 3451 3452 intersects: function(item, _matrix) { 3453 if (!(item instanceof Item)) 3454 return false; 3455 return this._asPathItem().getIntersections(item._asPathItem(),
3456 _matrix || item._matrix).length > 0; 3457 }, 3458 3459 hitTest: function() { 3460 return this._hitTest( 3461 Point.read(arguments), 3462 HitResult.getOptions(Base.read(arguments))); 3463 }, 3464 3465 _hitTest: function(point, options) { 3466 if (this._locked || !this._visible || this._guide && !options.guides 3467 || this.isEmpty()) 3468 return null; 3469 3470 var matrix = this._matrix, 3471 parentTotalMatrix = options._totalMatrix, 3472 view = this.getView(), 3473 totalMatrix = options._totalMatrix = parentTotalMatrix 3474 ? parentTotalMatrix.chain(matrix) 3475 : this.getGlobalMatrix().preConcatenate(view._matrix), 3476 tolerancePadding = options._tolerancePadding = new Size( 3477 Path._getPenPadding(1, totalMatrix.inverted()) 3478 ).multiply( 3479 Math.max(options.tolerance, 0.000001) 3480 ); 3481 point = matrix._inverseTransform(point); 3482 3483 if (!this._children && !this.getInternalRoughBounds() 3484 .expand(tolerancePadding.multiply(2))._containsPoint(point)) 3485 return null; 3486 var checkSelf = !(options.guides && !this._guide 3487 || options.selected && !this._selected 3488 || options.type && options.type !== Base.hyphenate(this._class) 3489 || options.class && !(this instanceof options.class)), 3490 that = this, 3491 res; 3492 3493 function checkBounds(type, part) { 3494 var pt = bounds['get' + part](); 3495 if (point.subtract(pt).divide(tolerancePadding).length <= 1) 3496 return new HitResult(type, that, 3497 { name: Base.hyphenate(part), point: pt }); 3498 } 3499 3500 if (checkSelf && (options.center || options.bounds) && this._parent) { 3501 var bounds = this.getInternalBounds(); 3502 if (options.center) 3503 res = checkBounds('center', 'Center'); 3504 if (!res && options.bounds) { 3505 var points = [ 3506 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', 3507 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter' 3508 ]; 3509 for (var i = 0; i < 8 && !res; i++) 3510 res = checkBounds('bounds', points[i]); 3511 } 3512 } 3513 3514 var children = !res && this._children; 3515 if (children) { 3516 var opts = this._getChildHitTestOptions(options); 3517 for (var i = children.length - 1; i >= 0 && !res; i--) 3518 res = children[i]._hitTest(point, opts); 3519 } 3520 if (!res && checkSelf) 3521 res = this._hitTestSelf(point, options); 3522 if (res && res.point) 3523 res.point = matrix.transform(res.point); 3524 options._totalMatrix = parentTotalMatrix; 3525 return res; 3526 }, 3527 3528 _getChildHitTestOptions: function(options) { 3529 return options; 3530 }, 3531 3532 _hitTestSelf: function(point, options) { 3533 if (options.fill && this.hasFill() && this._contains(point)) 3534 return new HitResult('fill', this); 3535 }, 3536 3537 matches: function(name, compare) { 3538 function matchObject(obj1, obj2) { 3539 for (var i in obj1) { 3540 if (obj1.hasOwnProperty(i)) { 3541 var val1 = obj1[i], 3542 val2 = obj2[i]; 3543 if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) { 3544 if (!matchObject(val1, val2)) 3545 return false; 3546 } else if (!Base.equals(val1, val2)) { 3547 return false; 3548 } 3549 } 3550 } 3551 return true; 3552 } 3553 var type = typeof name; 3554 if (type === 'object') { 3555 for (var key in name) { 3556 if (name.hasOwnProperty(key) && !this.matches(key, name[key])) 3557 return false; 3558 } 3559 } else if (type === 'function') { 3560 return name(this); 3561 } else { 3562 var value = /^(empty|editable)$/.test(name) 3563 ? this['is' + Base.capitalize(name)]() 3564 : name === 'type' 3565 ? Base.hyphenate(this._class) 3566 : this[name]; 3567 if (/^(constructor|class)$/.test(name)) { 3568 if (!(this instanceof compare)) 3569 return false; 3570 } else if (compare instanceof RegExp) { 3571 if (!compare.test(value)) 3572 return false; 3573 } else if (typeof compare === 'function') { 3574 if (!compare(value)) 3575 return false; 3576 } else if (Base.isPlainObject(compare)) { 3577 if (!matchObject(compare, value)) 3578 return false; 3579 } else if (!Base.equals(value, compare)) { 3580 return false; 3581 } 3582 } 3583 return true; 3584 }, 3585 3586 getItems: function(match) { 3587 return Item._getItems(this._children, match, this._matrix); 3588 }, 3589 3590 getItem: function(match) { 3591 return Item._getItems(this._children, match, this._matrix, null, true) 3592 [0] || null; 3593 }, 3594 3595 statics: { 3596 _getItems: function _getItems(children, match, matrix, param, 3597 firstOnly) { 3598 if (!param && typeof match === 'object') { 3599 var overlapping = match.overlapping, 3600 inside = match.inside, 3601 bounds = overlapping || inside, 3602 rect = bounds && Rectangle.read([bounds]); 3603 param = { 3604 items: [], 3605 inside: !!inside, 3606 overlapping: !!overlapping, 3607 rect: rect, 3608 path: overlapping && new Path.Rectangle({ 3609 rectangle: rect, 3610 insert: false 3611 }) 3612 }; 3613 if (bounds) 3614 match = Base.set({}, match, 3615 { inside: true, overlapping: true }); 3616 } 3617 var items = param && param.items, 3618 rect = param && param.rect; 3619 matrix = rect && (matrix || new Matrix()); 3620 for (var i = 0, l = children && children.length; i < l; i++) { 3621 var child = children[i], 3622 childMatrix = matrix && matrix.chain(child._matrix), 3623 add = true; 3624 if (rect) { 3625 var bounds = child.getBounds(childMatrix); 3626 if (!rect.intersects(bounds)) 3627 continue; 3628 if (!(param.inside && rect.contains(bounds)) 3629 && !(param.overlapping && (bounds.contains(rect) 3630 || param.path.intersects(child, childMatrix)))) 3631 add = false;
3632 } 3633 if (add && child.matches(match)) { 3634 items.push(child); 3635 if (firstOnly) 3636 break; 3637 } 3638 _getItems(child._children, match, 3639 childMatrix, param, 3640 firstOnly); 3641 if (firstOnly && items.length > 0) 3642 break; 3643 } 3644 return items; 3645 } 3646 } 3647}, { 3648 3649 importJSON: function(json) { 3650 var res = Base.importJSON(json, this); 3651 return res !== this 3652 ? this.addChild(res) 3653 : res; 3654 }, 3655 3656 addChild: function(item, _preserve) { 3657 return this.insertChild(undefined, item, _preserve); 3658 }, 3659 3660 insertChild: function(index, item, _preserve) { 3661 var res = item ? this.insertChildren(index, [item], _preserve) : null; 3662 return res && res[0]; 3663 }, 3664 3665 addChildren: function(items, _preserve) { 3666 return this.insertChildren(this._children.length, items, _preserve); 3667 }, 3668 3669 insertChildren: function(index, items, _preserve, _proto) { 3670 var children = this._children; 3671 if (children && items && items.length > 0) { 3672 items = Array.prototype.slice.apply(items); 3673 for (var i = items.length - 1; i >= 0; i--) { 3674 var item = items[i]; 3675 if (_proto && !(item instanceof _proto)) { 3676 items.splice(i, 1); 3677 } else { 3678 var shift = item._parent === this && item._index < index; 3679 if (item._remove(false, true) && shift) 3680 index--; 3681 } 3682 } 3683 Base.splice(children, items, index, 0); 3684 var project = this._project, 3685 notifySelf = project && project._changes; 3686 for (var i = 0, l = items.length; i < l; i++) { 3687 var item = items[i]; 3688 item._parent = this; 3689 item._setProject(this._project, true); 3690 if (item._name) 3691 item.setName(item._name); 3692 if (notifySelf) 3693 this._changed(5); 3694 } 3695 this._changed(11); 3696 } else { 3697 items = null; 3698 } 3699 return items; 3700 }, 3701 3702 _insertSibling: function(index, item, _preserve) { 3703 return this._parent 3704 ? this._parent.insertChild(index, item, _preserve) 3705 : null; 3706 }, 3707 3708 insertAbove: function(item, _preserve) { 3709 return item._insertSibling(item._index + 1, this, _preserve); 3710 }, 3711 3712 insertBelow: function(item, _preserve) { 3713 return item._insertSibling(item._index, this, _preserve); 3714 }, 3715 3716 sendToBack: function() { 3717 return (this._parent || this instanceof Layer && this._project) 3718 .insertChild(0, this); 3719 }, 3720 3721 bringToFront: function() { 3722 return (this._parent || this instanceof Layer && this._project) 3723 .addChild(this); 3724 }, 3725 3726 appendTop: '#addChild', 3727 3728 appendBottom: function(item) { 3729 return this.insertChild(0, item); 3730 }, 3731 3732 moveAbove: '#insertAbove', 3733 3734 moveBelow: '#insertBelow', 3735 3736 reduce: function() { 3737 if (this._children && this._children.length === 1) { 3738 var child = this._children[0].reduce(); 3739 child.insertAbove(this); 3740 child.setStyle(this._style); 3741 this.remove(); 3742 return child; 3743 } 3744 return this; 3745 }, 3746 3747 _removeNamed: function() { 3748 var parent = this._parent; 3749 if (parent) { 3750 var children = parent._children, 3751 namedChildren = parent._namedChildren, 3752 name = this._name, 3753 namedArray = namedChildren[name], 3754 index = namedArray ? namedArray.indexOf(this) : -1; 3755 if (index !== -1) { 3756 if (children[name] == this) 3757 delete children[name]; 3758 namedArray.splice(index, 1); 3759 if (namedArray.length) { 3760 children[name] = namedArray[namedArray.length - 1]; 3761 } else { 3762 delete namedChildren[name]; 3763 } 3764 } 3765 } 3766 }, 3767 3768 _remove: function(notifySelf, notifyParent) { 3769 var parent = this._parent; 3770 if (parent) { 3771 if (this._name) 3772 this._removeNamed(); 3773 if (this._index != null) 3774 Base.splice(parent._children, null, this._index, 1); 3775 this._installEvents(false); 3776 if (notifySelf) { 3777 var project = this._project; 3778 if (project && project._changes) 3779 this._changed(5); 3780 } 3781 if (notifyParent) 3782 parent._changed(11); 3783 this._parent = null; 3784 return true; 3785 } 3786 return false; 3787 }, 3788 3789 remove: function() { 3790 return this._remove(true, true); 3791 }, 3792 3793 replaceWith: function(item) { 3794 var ok = item && item.insertBelow(this); 3795 if (ok) 3796 this.remove(); 3797 return ok; 3798 }, 3799 3800 removeChildren: function(from, to) { 3801 if (!this._children) 3802 return null; 3803 from = from || 0; 3804 to = Base.pick(to, this._children.length); 3805 var removed = Base.splice(this._children, null, from, to - from); 3806 for (var i = removed.length - 1; i >= 0; i--) { 3807 removed[i]._remove(true, false); 3808 } 3809 if (removed.length > 0) 3810 this._changed(11); 3811 return removed; 3812 }, 3813 3814 clear: '#removeChildren', 3815 3816 reverseChildren: function() { 3817 if (this._children) { 3818 this._children.reverse(); 3819 for (var i = 0, l = this._children.length; i < l; i++) 3820 this._children[i]._index = i; 3821 this._changed(11); 3822 } 3823 }, 3824 3825 isEmpty: function() { 3826 return !this._children || this._children.length === 0; 3827 }, 3828 3829 isEditable: function() { 3830 var item = this; 3831 while (item) { 3832 if (!item._visible || item._locked) 3833 return false;
3834 item = item._parent; 3835 } 3836 return true; 3837 }, 3838 3839 hasFill: function() { 3840 return this.getStyle().hasFill(); 3841 }, 3842 3843 hasStroke: function() { 3844 return this.getStyle().hasStroke(); 3845 }, 3846 3847 hasShadow: function() { 3848 return this.getStyle().hasShadow(); 3849 }, 3850 3851 _getOrder: function(item) { 3852 function getList(item) { 3853 var list = []; 3854 do { 3855 list.unshift(item); 3856 } while (item = item._parent); 3857 return list; 3858 } 3859 var list1 = getList(this), 3860 list2 = getList(item); 3861 for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) { 3862 if (list1[i] != list2[i]) { 3863 return list1[i]._index < list2[i]._index ? 1 : -1; 3864 } 3865 } 3866 return 0; 3867 }, 3868 3869 hasChildren: function() { 3870 return this._children && this._children.length > 0; 3871 }, 3872 3873 isInserted: function() { 3874 return this._parent ? this._parent.isInserted() : false; 3875 }, 3876 3877 isAbove: function(item) { 3878 return this._getOrder(item) === -1; 3879 }, 3880 3881 isBelow: function(item) { 3882 return this._getOrder(item) === 1; 3883 }, 3884 3885 isParent: function(item) { 3886 return this._parent === item; 3887 }, 3888 3889 isChild: function(item) { 3890 return item && item._parent === this; 3891 }, 3892 3893 isDescendant: function(item) { 3894 var parent = this; 3895 while (parent = parent._parent) { 3896 if (parent == item) 3897 return true; 3898 } 3899 return false; 3900 }, 3901 3902 isAncestor: function(item) { 3903 return item ? item.isDescendant(this) : false; 3904 }, 3905 3906 isGroupedWith: function(item) { 3907 var parent = this._parent; 3908 while (parent) { 3909 if (parent._parent 3910 && /^(Group|Layer|CompoundPath)$/.test(parent._class) 3911 && item.isDescendant(parent)) 3912 return true; 3913 parent = parent._parent; 3914 } 3915 return false; 3916 }, 3917 3918 translate: function() { 3919 var mx = new Matrix(); 3920 return this.transform(mx.translate.apply(mx, arguments)); 3921 }, 3922 3923 rotate: function(angle ) { 3924 return this.transform(new Matrix().rotate(angle, 3925 Point.read(arguments, 1, { readNull: true }) 3926 || this.getPosition(true))); 3927 } 3928}, Base.each(['scale', 'shear', 'skew'], function(name) { 3929 this[name] = function() { 3930 var point = Point.read(arguments), 3931 center = Point.read(arguments, 0, { readNull: true }); 3932 return this.transform(new Matrix()[name](point, 3933 center || this.getPosition(true))); 3934 }; 3935}, { 3936 3937}), { 3938 transform: function(matrix, _applyMatrix, _applyRecursively, 3939 _setApplyMatrix) { 3940 if (matrix && matrix.isIdentity()) 3941 matrix = null; 3942 var _matrix = this._matrix, 3943 applyMatrix = (_applyMatrix || this._applyMatrix) 3944 && ((!_matrix.isIdentity() || matrix) 3945 || _applyMatrix && _applyRecursively && this._children); 3946 if (!matrix && !applyMatrix) 3947 return this; 3948 if (matrix) 3949 _matrix.preConcatenate(matrix);
3950 if (applyMatrix = applyMatrix && this._transformContent(_matrix, 3951 _applyRecursively, _setApplyMatrix)) { 3952 var pivot = this._pivot, 3953 style = this._style, 3954 fillColor = style.getFillColor(true), 3955 strokeColor = style.getStrokeColor(true); 3956 if (pivot) 3957 _matrix._transformPoint(pivot, pivot, true); 3958 if (fillColor) 3959 fillColor.transform(_matrix); 3960 if (strokeColor) 3961 strokeColor.transform(_matrix); 3962 _matrix.reset(true); 3963 if (_setApplyMatrix && this._canApplyMatrix) 3964 this._applyMatrix = true; 3965 } 3966 var bounds = this._bounds, 3967 position = this._position; 3968 this._changed(9); 3969 var decomp = bounds && matrix && matrix.decompose(); 3970 if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) { 3971 for (var key in bounds) { 3972 var rect = bounds[key]; 3973 if (applyMatrix || !rect._internal) 3974 matrix._transformBounds(rect, rect); 3975 } 3976 var getter = this._boundsGetter, 3977 rect = bounds[getter && getter.getBounds || getter || 'getBounds']; 3978 if (rect) 3979 this._position = rect.getCenter(true); 3980 this._bounds = bounds; 3981 } else if (matrix && position) { 3982 this._position = matrix._transformPoint(position, position); 3983 } 3984 return this; 3985 }, 3986 3987 _transformContent: function(matrix, applyRecursively, setApplyMatrix) { 3988 var children = this._children; 3989 if (children) { 3990 for (var i = 0, l = children.length; i < l; i++) 3991 children[i].transform(matrix, true, applyRecursively, 3992 setApplyMatrix); 3993 return true; 3994 } 3995 }, 3996 3997 globalToLocal: function() { 3998 return this.getGlobalMatrix(true)._inverseTransform( 3999 Point.read(arguments)); 4000 }, 4001 4002 localToGlobal: function() { 4003 return this.getGlobalMatrix(true)._transformPoint( 4004 Point.read(arguments)); 4005 }, 4006 4007 parentToLocal: function() { 4008 return this._matrix._inverseTransform(Point.read(arguments)); 4009 }, 4010 4011 localToParent: function() { 4012 return this._matrix._transformPoint(Point.read(arguments)); 4013 }, 4014 4015 fitBounds: function(rectangle, fill) { 4016 rectangle = Rectangle.read(arguments); 4017 var bounds = this.getBounds(), 4018 itemRatio = bounds.height / bounds.width, 4019 rectRatio = rectangle.height / rectangle.width, 4020 scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio) 4021 ? rectangle.width / bounds.width 4022 : rectangle.height / bounds.height, 4023 newBounds = new Rectangle(new Point(), 4024 new Size(bounds.width * scale, bounds.height * scale)); 4025 newBounds.setCenter(rectangle.getCenter()); 4026 this.setBounds(newBounds); 4027 }, 4028 4029 _setStyles: function(ctx) { 4030 var style = this._style, 4031 fillColor = style.getFillColor(), 4032 strokeColor = style.getStrokeColor(), 4033 shadowColor = style.getShadowColor(); 4034 if (fillColor) 4035 ctx.fillStyle = fillColor.toCanvasStyle(ctx); 4036 if (strokeColor) { 4037 var strokeWidth = style.getStrokeWidth(); 4038 if (strokeWidth > 0) { 4039 ctx.strokeStyle = strokeColor.toCanvasStyle(ctx); 4040 ctx.lineWidth = strokeWidth; 4041 var strokeJoin = style.getStrokeJoin(), 4042 strokeCap = style.getStrokeCap(), 4043 miterLimit = style.getMiterLimit(); 4044 if (strokeJoin) 4045 ctx.lineJoin = strokeJoin; 4046 if (strokeCap) 4047 ctx.lineCap = strokeCap; 4048 if (miterLimit) 4049 ctx.miterLimit = miterLimit; 4050 if (paper.support.nativeDash) { 4051 var dashArray = style.getDashArray(), 4052 dashOffset = style.getDashOffset(); 4053 if (dashArray && dashArray.length) { 4054 if ('setLineDash' in ctx) { 4055 ctx.setLineDash(dashArray); 4056 ctx.lineDashOffset = dashOffset; 4057 } else { 4058 ctx.mozDash = dashArray; 4059 ctx.mozDashOffset = dashOffset; 4060 } 4061 } 4062 } 4063 } 4064 } 4065 if (shadowColor) { 4066 var shadowBlur = style.getShadowBlur(); 4067 if (shadowBlur > 0) { 4068 ctx.shadowColor = shadowColor.toCanvasStyle(ctx); 4069 ctx.shadowBlur = shadowBlur; 4070 var offset = this.getShadowOffset(); 4071 ctx.shadowOffsetX = offset.x; 4072 ctx.shadowOffsetY = offset.y; 4073 } 4074 } 4075 }, 4076 4077 draw: function(ctx, param, parentStrokeMatrix) { 4078 var updateVersion = this._updateVersion = this._project._updateVersion; 4079 if (!this._visible || this._opacity === 0) 4080 return; 4081 var matrices = param.matrices, 4082 viewMatrix = param.viewMatrix, 4083 matrix = this._matrix, 4084 globalMatrix = matrices[matrices.length - 1].chain(matrix); 4085 if (!globalMatrix.isInvertible()) 4086 return; 4087 4088 function getViewMatrix(matrix) { 4089 return viewMatrix ? viewMatrix.chain(matrix) : matrix; 4090 } 4091 4092 matrices.push(globalMatrix); 4093 if (param.updateMatrix) { 4094 globalMatrix._updateVersion = updateVersion; 4095 this._globalMatrix = globalMatrix; 4096 } 4097 4098 var blendMode = this._blendMode, 4099 opacity = this._opacity, 4100 normalBlend = blendMode === 'normal',
4101 nativeBlend = BlendMode.nativeModes[blendMode], 4102 direct = normalBlend && opacity === 1 4103 || param.dontStart 4104 || param.clip 4105 || (nativeBlend || normalBlend && opacity < 1) 4106 && this._canComposite(), 4107 pixelRatio = param.pixelRatio || 1, 4108 mainCtx, itemOffset, prevOffset; 4109 if (!direct) { 4110 var bounds = this.getStrokeBounds(getViewMatrix(globalMatrix)); 4111 if (!bounds.width || !bounds.height) 4112 return; 4113 prevOffset = param.offset; 4114 itemOffset = param.offset = bounds.getTopLeft().floor(); 4115 mainCtx = ctx; 4116 ctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1) 4117 .multiply(pixelRatio)); 4118 if (pixelRatio !== 1) 4119 ctx.scale(pixelRatio, pixelRatio); 4120 } 4121 ctx.save(); 4122 var strokeMatrix = parentStrokeMatrix 4123 ? parentStrokeMatrix.chain(matrix) 4124 : !this.getStrokeScaling(true) && getViewMatrix(globalMatrix), 4125 clip = !direct && param.clipItem, 4126 transform = !strokeMatrix || clip; 4127 if (direct) { 4128 ctx.globalAlpha = opacity; 4129 if (nativeBlend) 4130 ctx.globalCompositeOperation = blendMode; 4131 } else if (transform) { 4132 ctx.translate(-itemOffset.x, -itemOffset.y); 4133 } 4134 if (transform) 4135 (direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx); 4136 if (clip) 4137 param.clipItem.draw(ctx, param.extend({ clip: true })); 4138 if (strokeMatrix) { 4139 ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); 4140 var offset = param.offset; 4141 if (offset) 4142 ctx.translate(-offset.x, -offset.y); 4143 } 4144 this._draw(ctx, param, strokeMatrix); 4145 ctx.restore(); 4146 matrices.pop(); 4147 if (param.clip && !param.dontFinish) 4148 ctx.clip(); 4149 if (!direct) { 4150 BlendMode.process(blendMode, ctx, mainCtx, opacity, 4151 itemOffset.subtract(prevOffset).multiply(pixelRatio)); 4152 CanvasProvider.release(ctx); 4153 param.offset = prevOffset; 4154 } 4155 }, 4156 4157 _isUpdated: function(updateVersion) { 4158 var parent = this._parent; 4159 if (parent instanceof CompoundPath) 4160 return parent._isUpdated(updateVersion); 4161 var updated = this._updateVersion === updateVersion; 4162 if (!updated && parent && parent._visible 4163 && parent._isUpdated(updateVersion)) { 4164 this._updateVersion = updateVersion; 4165 updated = true; 4166 } 4167 return updated; 4168 }, 4169 4170 _drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) { 4171 if ((this._drawSelected || this._boundsSelected) 4172 && this._isUpdated(updateVersion)) { 4173 var color = this.getSelectedColor(true) 4174 || this.getLayer().getSelectedColor(true), 4175 mx = matrix.chain(this.getGlobalMatrix(true)); 4176 ctx.strokeStyle = ctx.fillStyle = color 4177 ? color.toCanvasStyle(ctx) : '#009dec'; 4178 if (this._drawSelected) 4179 this._drawSelected(ctx, mx, selectedItems); 4180 if (this._boundsSelected) { 4181 var half = size / 2; 4182 coords = mx._transformCorners(this.getInternalBounds()); 4183 ctx.beginPath(); 4184 for (var i = 0; i < 8; i++) 4185 ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]); 4186 ctx.closePath(); 4187 ctx.stroke(); 4188 for (var i = 0; i < 8; i++) 4189 ctx.fillRect(coords[i] - half, coords[++i] - half, 4190 size, size); 4191 } 4192 } 4193 }, 4194 4195 _canComposite: function() { 4196 return false; 4197 } 4198}, Base.each(['down', 'drag', 'up', 'move'], function(name) { 4199 this['removeOn' + Base.capitalize(name)] = function() { 4200 var hash = {}; 4201 hash[name] = true; 4202 return this.removeOn(hash); 4203 }; 4204}, { 4205 4206 removeOn: function(obj) { 4207 for (var name in obj) { 4208 if (obj[name]) { 4209 var key = 'mouse' + name, 4210 project = this._project, 4211 sets = project._removeSets = project._removeSets || {}; 4212 sets[key] = sets[key] || {}; 4213 sets[key][this._id] = this; 4214 } 4215 } 4216 return this; 4217 } 4218})); 4219 4220var Group = Item.extend({ 4221 _class: 'Group', 4222 _selectChildren: true, 4223 _serializeFields: { 4224 children: [] 4225 }, 4226 4227 initialize: function Group(arg) { 4228 this._children = []; 4229 this._namedChildren = {}; 4230 if (!this._initialize(arg)) 4231 this.addChildren(Array.isArray(arg) ? arg : arguments); 4232 }, 4233 4234 _changed: function _changed(flags) { 4235 _changed.base.call(this, flags); 4236 if (flags & 1026) { 4237 this._clipItem = undefined; 4238 } 4239 }, 4240 4241 _getClipItem: function() { 4242 var clipItem = this._clipItem; 4243 if (clipItem === undefined) { 4244 clipItem = null; 4245 for (var i = 0, l = this._children.length; i < l; i++) { 4246 var child = this._children[i]; 4247 if (child._clipMask) { 4248 clipItem = child; 4249 break; 4250 } 4251 } 4252 this._clipItem = clipItem; 4253 } 4254 return clipItem; 4255 }, 4256 4257 isClipped: function() { 4258 return !!this._getClipItem(); 4259 }, 4260 4261 setClipped: function(clipped) { 4262 var child = this.getFirstChild(); 4263 if (child) 4264 child.setClipMask(clipped); 4265 }, 4266 4267 _draw: function(ctx, param) { 4268 var clip = param.clip, 4269 clipItem = !clip && this._getClipItem(), 4270 draw = true; 4271 param = param.extend({ clipItem: clipItem, clip: false }); 4272 if (clip) { 4273 if (this._currentPath) { 4274 ctx.currentPath = this._currentPath; 4275 draw = false;
4276 } else { 4277 ctx.beginPath(); 4278 param.dontStart = param.dontFinish = true; 4279 } 4280 } else if (clipItem) { 4281 clipItem.draw(ctx, param.extend({ clip: true })); 4282 } 4283 if (draw) { 4284 for (var i = 0, l = this._children.length; i < l; i++) { 4285 var item = this._children[i]; 4286 if (item !== clipItem) 4287 item.draw(ctx, param); 4288 } 4289 } 4290 if (clip) { 4291 this._currentPath = ctx.currentPath; 4292 } 4293 } 4294}); 4295 4296var Layer = Group.extend({ 4297 _class: 'Layer', 4298 4299 initialize: function Layer(arg) { 4300 var props = Base.isPlainObject(arg) 4301 ? new Base(arg) 4302 : { children: Array.isArray(arg) ? arg : arguments }, 4303 insert = props.insert; 4304 props.insert = false; 4305 Group.call(this, props); 4306 if (insert || insert === undefined) { 4307 this._project.addChild(this); 4308 this.activate(); 4309 } 4310 }, 4311 4312 _remove: function _remove(notifySelf, notifyParent) { 4313 if (this._parent) 4314 return _remove.base.call(this, notifySelf, notifyParent); 4315 if (this._index != null) { 4316 var project = this._project; 4317 if (project._activeLayer === this) 4318 project._activeLayer = this.getNextSibling() 4319 || this.getPreviousSibling(); 4320 Base.splice(project.layers, null, this._index, 1); 4321 this._installEvents(false); 4322 if (notifySelf && project._changes) 4323 this._changed(5); 4324 if (notifyParent) { 4325 project._needsUpdate = true; 4326 } 4327 return true; 4328 } 4329 return false; 4330 }, 4331 4332 getNextSibling: function getNextSibling() { 4333 return this._parent ? getNextSibling.base.call(this) 4334 : this._project.layers[this._index + 1] || null; 4335 }, 4336 4337 getPreviousSibling: function getPreviousSibling() { 4338 return this._parent ? getPreviousSibling.base.call(this) 4339 : this._project.layers[this._index - 1] || null; 4340 }, 4341 4342 isInserted: function isInserted() { 4343 return this._parent ? isInserted.base.call(this) : this._index != null; 4344 }, 4345 4346 activate: function() { 4347 this._project._activeLayer = this; 4348 }, 4349 4350 _insertSibling: function _insertSibling(index, item, _preserve) { 4351 return !this._parent 4352 ? this._project.insertChild(index, item, _preserve) 4353 : _insertSibling.base.call(this, index, item, _preserve); 4354 } 4355}); 4356 4357var Shape = Item.extend({ 4358 _class: 'Shape', 4359 _applyMatrix: false, 4360 _canApplyMatrix: false, 4361 _boundsSelected: true, 4362 _serializeFields: { 4363 type: null, 4364 size: null, 4365 radius: null 4366 }, 4367 4368 initialize: function Shape(props) { 4369 this._initialize(props); 4370 }, 4371 4372 _equals: function(item) { 4373 return this._type === item._type 4374 && this._size.equals(item._size) 4375 && Base.equals(this._radius, item._radius); 4376 }, 4377 4378 clone: function(insert) { 4379 var copy = new Shape(Item.NO_INSERT); 4380 copy.setType(this._type); 4381 copy.setSize(this._size); 4382 copy.setRadius(this._radius); 4383 return this._clone(copy, insert); 4384 }, 4385 4386 getType: function() { 4387 return this._type; 4388 }, 4389 4390 setType: function(type) { 4391 this._type = type; 4392 }, 4393 4394 getShape: '#getType', 4395 setShape: '#setType', 4396 4397 getSize: function() { 4398 var size = this._size; 4399 return new LinkedSize(size.width, size.height, this, 'setSize'); 4400 }, 4401 4402 setSize: function() { 4403 var size = Size.read(arguments); 4404 if (!this._size) { 4405 this._size = size.clone(); 4406 } else if (!this._size.equals(size)) { 4407 var type = this._type, 4408 width = size.width, 4409 height = size.height; 4410 if (type === 'rectangle') { 4411 var radius = Size.min(this._radius, size.divide(2)); 4412 this._radius.set(radius.width, radius.height); 4413 } else if (type === 'circle') { 4414 width = height = (width + height) / 2; 4415 this._radius = width / 2; 4416 } else if (type === 'ellipse') { 4417 this._radius.set(width / 2, height / 2); 4418 } 4419 this._size.set(width, height); 4420 this._changed(9); 4421 } 4422 }, 4423 4424 getRadius: function() { 4425 var rad = this._radius; 4426 return this._type === 'circle' 4427 ? rad 4428 : new LinkedSize(rad.width, rad.height, this, 'setRadius'); 4429 }, 4430 4431 setRadius: function(radius) { 4432 var type = this._type; 4433 if (type === 'circle') { 4434 if (radius === this._radius) 4435 return; 4436 var size = radius * 2; 4437 this._radius = radius; 4438 this._size.set(size, size); 4439 } else { 4440 radius = Size.read(arguments); 4441 if (!this._radius) { 4442 this._radius = radius.clone(); 4443 } else { 4444 if (this._radius.equals(radius)) 4445 return; 4446 this._radius.set(radius.width, radius.height); 4447 if (type === 'rectangle') { 4448 var size = Size.max(this._size, radius.multiply(2)); 4449 this._size.set(size.width, size.height); 4450 } else if (type === 'ellipse') { 4451 this._size.set(radius.width * 2, radius.height * 2); 4452 } 4453 } 4454 } 4455 this._changed(9); 4456 }, 4457 4458 isEmpty: function() { 4459 return false;
4460 }, 4461 4462 toPath: function(insert) { 4463 var path = this._clone(new Path[Base.capitalize(this._type)]({ 4464 center: new Point(), 4465 size: this._size, 4466 radius: this._radius, 4467 insert: false 4468 }), insert); 4469 if (paper.settings.applyMatrix) 4470 path.setApplyMatrix(true); 4471 return path; 4472 }, 4473 4474 _draw: function(ctx, param, strokeMatrix) { 4475 var style = this._style, 4476 hasFill = style.hasFill(), 4477 hasStroke = style.hasStroke(), 4478 dontPaint = param.dontFinish || param.clip, 4479 untransformed = !strokeMatrix; 4480 if (hasFill || hasStroke || dontPaint) { 4481 var type = this._type, 4482 radius = this._radius, 4483 isCircle = type === 'circle'; 4484 if (!param.dontStart) 4485 ctx.beginPath(); 4486 if (untransformed && isCircle) { 4487 ctx.arc(0, 0, radius, 0, Math.PI * 2, true); 4488 } else { 4489 var rx = isCircle ? radius : radius.width, 4490 ry = isCircle ? radius : radius.height, 4491 size = this._size, 4492 width = size.width, 4493 height = size.height; 4494 if (untransformed && type === 'rectangle' && rx === 0 && ry === 0) { 4495 ctx.rect(-width / 2, -height / 2, width, height); 4496 } else { 4497 var x = width / 2, 4498 y = height / 2, 4499 kappa = 1 - 0.5522847498307936, 4500 cx = rx * kappa, 4501 cy = ry * kappa, 4502 c = [ 4503 -x, -y + ry, 4504 -x, -y + cy, 4505 -x + cx, -y, 4506 -x + rx, -y, 4507 x - rx, -y, 4508 x - cx, -y, 4509 x, -y + cy, 4510 x, -y + ry, 4511 x, y - ry, 4512 x, y - cy, 4513 x - cx, y, 4514 x - rx, y, 4515 -x + rx, y, 4516 -x + cx, y, 4517 -x, y - cy, 4518 -x, y - ry 4519 ]; 4520 if (strokeMatrix) 4521 strokeMatrix.transform(c, c, 32); 4522 ctx.moveTo(c[0], c[1]); 4523 ctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]); 4524 if (x !== rx) 4525 ctx.lineTo(c[8], c[9]); 4526 ctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]); 4527 if (y !== ry) 4528 ctx.lineTo(c[16], c[17]); 4529 ctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]); 4530 if (x !== rx) 4531 ctx.lineTo(c[24], c[25]); 4532 ctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]); 4533 } 4534 } 4535 ctx.closePath(); 4536 } 4537 if (!dontPaint && (hasFill || hasStroke)) { 4538 this._setStyles(ctx); 4539 if (hasFill) { 4540 ctx.fill(style.getWindingRule()); 4541 ctx.shadowColor = 'rgba(0,0,0,0)'; 4542 } 4543 if (hasStroke) 4544 ctx.stroke(); 4545 } 4546 }, 4547 4548 _canComposite: function() { 4549 return !(this.hasFill() && this.hasStroke()); 4550 }, 4551 4552 _getBounds: function(getter, matrix) { 4553 var rect = new Rectangle(this._size).setCenter(0, 0); 4554 if (getter !== 'getBounds' && this.hasStroke()) 4555 rect = rect.expand(this.getStrokeWidth()); 4556 return matrix ? matrix._transformBounds(rect) : rect; 4557 } 4558}, 4559new function() { 4560 4561 function getCornerCenter(that, point, expand) { 4562 var radius = that._radius; 4563 if (!radius.isZero()) { 4564 var halfSize = that._size.divide(2); 4565 for (var i = 0; i < 4; i++) { 4566 var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1), 4567 corner = dir.multiply(halfSize), 4568 center = corner.subtract(dir.multiply(radius)), 4569 rect = new Rectangle(corner, center); 4570 if ((expand ? rect.expand(expand) : rect).contains(point)) 4571 return center; 4572 } 4573 } 4574 } 4575 4576 function getEllipseRadius(point, radius) { 4577 var angle = point.getAngleInRadians(), 4578 width = radius.width * 2, 4579 height = radius.height * 2, 4580 x = width * Math.sin(angle), 4581 y = height * Math.cos(angle); 4582 return width * height / (2 * Math.sqrt(x * x + y * y)); 4583 } 4584 4585 return { 4586 _contains: function _contains(point) { 4587 if (this._type === 'rectangle') { 4588 var center = getCornerCenter(this, point); 4589 return center 4590 ? point.subtract(center).divide(this._radius) 4591 .getLength() <= 1 4592 : _contains.base.call(this, point); 4593 } else { 4594 return point.divide(this.size).getLength() <= 0.5; 4595 } 4596 }, 4597 4598 _hitTestSelf: function _hitTestSelf(point, options) { 4599 var hit = false; 4600 if (this.hasStroke()) { 4601 var type = this._type, 4602 radius = this._radius, 4603 strokeWidth = this.getStrokeWidth() + 2 * options.tolerance; 4604 if (type === 'rectangle') { 4605 var center = getCornerCenter(this, point, strokeWidth); 4606 if (center) { 4607 var pt = point.subtract(center); 4608 hit = 2 * Math.abs(pt.getLength() 4609 - getEllipseRadius(pt, radius)) <= strokeWidth; 4610 } else { 4611 var rect = new Rectangle(this._size).setCenter(0, 0), 4612 outer = rect.expand(strokeWidth), 4613 inner = rect.expand(-strokeWidth); 4614 hit = outer._containsPoint(point) 4615 && !inner._containsPoint(point); 4616 } 4617 } else { 4618 if (type === 'ellipse') 4619 radius = getEllipseRadius(point, radius);
4620 hit = 2 * Math.abs(point.getLength() - radius) 4621 <= strokeWidth; 4622 } 4623 } 4624 return hit 4625 ? new HitResult('stroke', this) 4626 : _hitTestSelf.base.apply(this, arguments); 4627 } 4628 }; 4629}, { 4630 4631statics: new function() { 4632 function createShape(type, point, size, radius, args) { 4633 var item = new Shape(Base.getNamed(args)); 4634 item._type = type; 4635 item._size = size; 4636 item._radius = radius; 4637 return item.translate(point); 4638 } 4639 4640 return { 4641 Circle: function() { 4642 var center = Point.readNamed(arguments, 'center'), 4643 radius = Base.readNamed(arguments, 'radius'); 4644 return createShape('circle', center, new Size(radius * 2), radius, 4645 arguments); 4646 }, 4647 4648 Rectangle: function() { 4649 var rect = Rectangle.readNamed(arguments, 'rectangle'), 4650 radius = Size.min(Size.readNamed(arguments, 'radius'), 4651 rect.getSize(true).divide(2)); 4652 return createShape('rectangle', rect.getCenter(true), 4653 rect.getSize(true), radius, arguments); 4654 }, 4655 4656 Ellipse: function() { 4657 var ellipse = Shape._readEllipse(arguments), 4658 radius = ellipse.radius; 4659 return createShape('ellipse', ellipse.center, radius.multiply(2), 4660 radius, arguments); 4661 }, 4662 4663 _readEllipse: function(args) { 4664 var center, 4665 radius; 4666 if (Base.hasNamed(args, 'radius')) { 4667 center = Point.readNamed(args, 'center'); 4668 radius = Size.readNamed(args, 'radius'); 4669 } else { 4670 var rect = Rectangle.readNamed(args, 'rectangle'); 4671 center = rect.getCenter(true); 4672 radius = rect.getSize(true).divide(2); 4673 } 4674 return { center: center, radius: radius }; 4675 } 4676 }; 4677}}); 4678 4679var Raster = Item.extend({ 4680 _class: 'Raster', 4681 _applyMatrix: false, 4682 _canApplyMatrix: false, 4683 _boundsGetter: 'getBounds', 4684 _boundsSelected: true, 4685 _serializeFields: { 4686 crossOrigin: null, 4687 source: null 4688 }, 4689 4690 initialize: function Raster(object, position) { 4691 if (!this._initialize(object, 4692 position !== undefined && Point.read(arguments, 1))) { 4693 if (typeof object === 'string') { 4694 this.setSource(object); 4695 } else { 4696 this.setImage(object); 4697 } 4698 } 4699 if (!this._size) { 4700 this._size = new Size(); 4701 this._loaded = false; 4702 } 4703 }, 4704 4705 _equals: function(item) { 4706 return this.getSource() === item.getSource(); 4707 }, 4708 4709 clone: function(insert) { 4710 var copy = new Raster(Item.NO_INSERT), 4711 image = this._image, 4712 canvas = this._canvas; 4713 if (image) { 4714 copy.setImage(image); 4715 } else if (canvas) { 4716 var copyCanvas = CanvasProvider.getCanvas(this._size); 4717 copyCanvas.getContext('2d').drawImage(canvas, 0, 0); 4718 copy.setImage(copyCanvas); 4719 } 4720 copy._crossOrigin = this._crossOrigin; 4721 return this._clone(copy, insert); 4722 }, 4723 4724 getSize: function() { 4725 var size = this._size; 4726 return new LinkedSize(size ? size.width : 0, size ? size.height : 0, 4727 this, 'setSize'); 4728 }, 4729 4730 setSize: function() { 4731 var size = Size.read(arguments); 4732 if (!size.equals(this._size)) { 4733 if (size.width > 0 && size.height > 0) { 4734 var element = this.getElement(); 4735 this.setImage(CanvasProvider.getCanvas(size)); 4736 if (element) 4737 this.getContext(true).drawImage(element, 0, 0, 4738 size.width, size.height); 4739 } else { 4740 if (this._canvas) 4741 CanvasProvider.release(this._canvas); 4742 this._size = size.clone(); 4743 } 4744 } 4745 }, 4746
vendor: 4,874 bytes, lines 4747-4946
4747 getWidth: function() { 4748 return this._size ? this._size.width : 0; 4749 }, 4750 4751 setWidth: function(width) { 4752 this.setSize(width, this.getHeight()); 4753 }, 4754 4755 getHeight: function() { 4756 return this._size ? this._size.height : 0; 4757 }, 4758 4759 setHeight: function(height) { 4760 this.setSize(this.getWidth(), height); 4761 }, 4762 4763 isEmpty: function() { 4764 var size = this._size; 4765 return !size || size.width === 0 && size.height === 0; 4766 }, 4767 4768 getResolution: function() { 4769 var matrix = this._matrix, 4770 orig = new Point(0, 0).transform(matrix), 4771 u = new Point(1, 0).transform(matrix).subtract(orig), 4772 v = new Point(0, 1).transform(matrix).subtract(orig); 4773 return new Size( 4774 72 / u.getLength(), 4775 72 / v.getLength() 4776 ); 4777 }, 4778 4779 getPpi: '#getResolution', 4780 4781 getImage: function() { 4782 return this._image; 4783 }, 4784 4785 setImage: function(image) { 4786 if (this._canvas) 4787 CanvasProvider.release(this._canvas); 4788 if (image && image.getContext) { 4789 this._image = null; 4790 this._canvas = image; 4791 this._loaded = true; 4792 } else { 4793 this._image = image; 4794 this._canvas = null; 4795 this._loaded = image && image.complete; 4796 } 4797 this._size = new Size( 4798 image ? image.naturalWidth || image.width : 0, 4799 image ? image.naturalHeight || image.height : 0); 4800 this._context = null; 4801 this._changed(521); 4802 }, 4803 4804 getCanvas: function() { 4805 if (!this._canvas) { 4806 var ctx = CanvasProvider.getContext(this._size); 4807 try { 4808 if (this._image) 4809 ctx.drawImage(this._image, 0, 0); 4810 this._canvas = ctx.canvas; 4811 } catch (e) { 4812 CanvasProvider.release(ctx); 4813 } 4814 } 4815 return this._canvas; 4816 }, 4817 4818 setCanvas: '#setImage', 4819 4820 getContext: function(modify) { 4821 if (!this._context) 4822 this._context = this.getCanvas().getContext('2d'); 4823 if (modify) { 4824 this._image = null; 4825 this._changed(513); 4826 } 4827 return this._context; 4828 }, 4829 4830 setContext: function(context) { 4831 this._context = context; 4832 }, 4833 4834 getSource: function() { 4835 return this._image && this._image.src || this.toDataURL(); 4836 }, 4837 4838 setSource: function(src) { 4839 var that = this, 4840 crossOrigin = this._crossOrigin, 4841 image; 4842 4843 function loaded() { 4844 var view = that.getView(); 4845 if (view) { 4846 paper = view._scope; 4847 that.setImage(image); 4848 that.emit('load'); 4849 view.update(); 4850 } 4851 } 4852 4853 image = document.getElementById(src) || new Image(); 4854 if (crossOrigin) 4855 image.crossOrigin = crossOrigin; 4856 if (image.naturalWidth && image.naturalHeight) { 4857 setTimeout(loaded, 0); 4858 } else { 4859 DomEvent.add(image, { load: loaded }); 4860 if (!image.src) 4861 image.src = src; 4862 } 4863 this.setImage(image); 4864 }, 4865 4866 getCrossOrigin: function() { 4867 return this._image && this._image.crossOrigin || this._crossOrigin || ''; 4868 }, 4869 4870 setCrossOrigin: function(crossOrigin) { 4871 this._crossOrigin = crossOrigin; 4872 if (this._image) 4873 this._image.crossOrigin = crossOrigin; 4874 }, 4875 4876 getElement: function() { 4877 return this._canvas || this._loaded && this._image; 4878 } 4879}, { 4880 beans: false, 4881 4882 getSubCanvas: function() { 4883 var rect = Rectangle.read(arguments), 4884 ctx = CanvasProvider.getContext(rect.getSize()); 4885 ctx.drawImage(this.getCanvas(), rect.x, rect.y, 4886 rect.width, rect.height, 0, 0, rect.width, rect.height); 4887 return ctx.canvas; 4888 }, 4889 4890 getSubRaster: function() { 4891 var rect = Rectangle.read(arguments), 4892 raster = new Raster(Item.NO_INSERT); 4893 raster.setImage(this.getSubCanvas(rect)); 4894 raster.translate(rect.getCenter().subtract(this.getSize().divide(2))); 4895 raster._matrix.preConcatenate(this._matrix); 4896 raster.insertAbove(this); 4897 return raster; 4898 }, 4899 4900 toDataURL: function() { 4901 var src = this._image && this._image.src; 4902 if (/^data:/.test(src)) 4903 return src; 4904 var canvas = this.getCanvas(); 4905 return canvas ? canvas.toDataURL() : null; 4906 }, 4907 4908 drawImage: function(image ) { 4909 var point = Point.read(arguments, 1); 4910 this.getContext(true).drawImage(image, point.x, point.y); 4911 }, 4912 4913 getAverageColor: function(object) { 4914 var bounds, path; 4915 if (!object) { 4916 bounds = this.getBounds(); 4917 } else if (object instanceof PathItem) { 4918 path = object; 4919 bounds = object.getBounds(); 4920 } else if (object.width) { 4921 bounds = new Rectangle(object); 4922 } else if (object.x) { 4923 bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1); 4924 } 4925 var sampleSize = 32, 4926 width = Math.min(bounds.width, sampleSize), 4927 height = Math.min(bounds.height, sampleSize); 4928 var ctx = Raster._sampleContext; 4929 if (!ctx) { 4930 ctx = Raster._sampleContext = CanvasProvider.getContext( 4931 new Size(sampleSize)); 4932 } else { 4933 ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1); 4934 } 4935 ctx.save(); 4936 var matrix = new Matrix() 4937 .scale(width / bounds.width, height / bounds.height) 4938 .translate(-bounds.x, -bounds.y); 4939 matrix.applyToContext(ctx); 4940 if (path) 4941 path.draw(ctx, new Base({ clip: true, matrices: [matrix] })); 4942 this._matrix.applyToContext(ctx); 4943 var element = this.getElement(), 4944 size = this._size; 4945 if (element) 4946 ctx.drawImage(element, -size.width / 2, -size.height / 2);
4947 ctx.restore(); 4948 var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width), 4949 Math.ceil(height)).data, 4950 channels = [0, 0, 0], 4951 total = 0; 4952 for (var i = 0, l = pixels.length; i < l; i += 4) { 4953 var alpha = pixels[i + 3]; 4954 total += alpha; 4955 alpha /= 255; 4956 channels[0] += pixels[i] * alpha; 4957 channels[1] += pixels[i + 1] * alpha; 4958 channels[2] += pixels[i + 2] * alpha; 4959 } 4960 for (var i = 0; i < 3; i++) 4961 channels[i] /= total; 4962 return total ? Color.read(channels) : null; 4963 }, 4964 4965 getPixel: function() { 4966 var point = Point.read(arguments); 4967 var data = this.getContext().getImageData(point.x, point.y, 1, 1).data; 4968 return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255], 4969 data[3] / 255); 4970 }, 4971 4972 setPixel: function() { 4973 var point = Point.read(arguments), 4974 color = Color.read(arguments), 4975 components = color._convert('rgb'), 4976 alpha = color._alpha, 4977 ctx = this.getContext(true), 4978 imageData = ctx.createImageData(1, 1), 4979 data = imageData.data; 4980 data[0] = components[0] * 255; 4981 data[1] = components[1] * 255; 4982 data[2] = components[2] * 255; 4983 data[3] = alpha != null ? alpha * 255 : 255; 4984 ctx.putImageData(imageData, point.x, point.y); 4985 }, 4986 4987 createImageData: function() { 4988 var size = Size.read(arguments); 4989 return this.getContext().createImageData(size.width, size.height); 4990 }, 4991 4992 getImageData: function() { 4993 var rect = Rectangle.read(arguments); 4994 if (rect.isEmpty()) 4995 rect = new Rectangle(this._size); 4996 return this.getContext().getImageData(rect.x, rect.y, 4997 rect.width, rect.height); 4998 }, 4999 5000 setImageData: function(data ) { 5001 var point = Point.read(arguments, 1); 5002 this.getContext(true).putImageData(data, point.x, point.y); 5003 }, 5004 5005 _getBounds: function(getter, matrix) { 5006 var rect = new Rectangle(this._size).setCenter(0, 0); 5007 return matrix ? matrix._transformBounds(rect) : rect; 5008 }, 5009 5010 _hitTestSelf: function(point) { 5011 if (this._contains(point)) { 5012 var that = this; 5013 return new HitResult('pixel', that, { 5014 offset: point.add(that._size.divide(2)).round(), 5015 color: { 5016 get: function() { 5017 return that.getPixel(this.offset); 5018 } 5019 } 5020 }); 5021 } 5022 }, 5023 5024 _draw: function(ctx) { 5025 var element = this.getElement(); 5026 if (element) { 5027 ctx.globalAlpha = this._opacity; 5028 ctx.drawImage(element, 5029 -this._size.width / 2, -this._size.height / 2); 5030 } 5031 }, 5032 5033 _canComposite: function() { 5034 return true; 5035 } 5036}); 5037 5038var PlacedSymbol = Item.extend({ 5039 _class: 'PlacedSymbol', 5040 _applyMatrix: false, 5041 _canApplyMatrix: false, 5042 _boundsGetter: { getBounds: 'getStrokeBounds' }, 5043 _boundsSelected: true, 5044 _serializeFields: { 5045 symbol: null 5046 }, 5047 5048 initialize: function PlacedSymbol(arg0, arg1) { 5049 if (!this._initialize(arg0, 5050 arg1 !== undefined && Point.read(arguments, 1))) 5051 this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0)); 5052 }, 5053 5054 _equals: function(item) { 5055 return this._symbol === item._symbol; 5056 }, 5057 5058 getSymbol: function() { 5059 return this._symbol; 5060 }, 5061 5062 setSymbol: function(symbol) { 5063 this._symbol = symbol; 5064 this._changed(9); 5065 }, 5066 5067 clone: function(insert) { 5068 var copy = new PlacedSymbol(Item.NO_INSERT); 5069 copy.setSymbol(this._symbol); 5070 return this._clone(copy, insert); 5071 }, 5072 5073 isEmpty: function() { 5074 return this._symbol._definition.isEmpty(); 5075 }, 5076 5077 _getBounds: function(getter, matrix, cacheItem) { 5078 var definition = this.symbol._definition; 5079 return definition._getCachedBounds(getter, 5080 matrix && matrix.chain(definition._matrix), cacheItem); 5081 }, 5082 5083 _hitTestSelf: function(point, options) { 5084 var res = this._symbol._definition._hitTest(point, options); 5085 if (res) 5086 res.item = this; 5087 return res; 5088 }, 5089 5090 _draw: function(ctx, param) { 5091 this.symbol._definition.draw(ctx, param); 5092 } 5093 5094}); 5095 5096var HitResult = Base.extend({ 5097 _class: 'HitResult', 5098 5099 initialize: function HitResult(type, item, values) { 5100 this.type = type; 5101 this.item = item; 5102 if (values) { 5103 values.enumerable = true; 5104 this.inject(values); 5105 } 5106 }, 5107 5108 statics: { 5109 getOptions: function(options) { 5110 return new Base({ 5111 type: null, 5112 tolerance: paper.settings.hitTolerance, 5113 fill: !options, 5114 stroke: !options, 5115 segments: !options, 5116 handles: false, 5117 ends: false, 5118 center: false, 5119 bounds: false, 5120 guides: false, 5121 selected: false 5122 }, options); 5123 } 5124 } 5125}); 5126 5127var Segment = Base.extend({ 5128 _class: 'Segment', 5129 beans: true, 5130 5131 initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) { 5132 var count = arguments.length, 5133 point, handleIn, handleOut; 5134 if (count === 0) { 5135 } else if (count === 1) { 5136 if (arg0.point) { 5137 point = arg0.point; 5138 handleIn = arg0.handleIn; 5139 handleOut = arg0.handleOut; 5140 } else { 5141 point = arg0; 5142 } 5143 } else if (count === 2 && typeof arg0 === 'number') { 5144 point = arguments; 5145 } else if (count <= 3) { 5146 point = arg0; 5147 handleIn = arg1; 5148 handleOut = arg2; 5149 } else { 5150 point = arg0 !== undefined ? [ arg0, arg1 ] : null; 5151 handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null; 5152 handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null; 5153 } 5154 new SegmentPoint(point, this, '_point'); 5155 new SegmentPoint(handleIn, this, '_handleIn'); 5156 new SegmentPoint(handleOut, this, '_handleOut'); 5157 }, 5158
5159 _serialize: function(options) { 5160 return Base.serialize(this.isStraight() ? this._point 5161 : [this._point, this._handleIn, this._handleOut], 5162 options, true); 5163 }, 5164 5165 _changed: function(point) { 5166 var path = this._path; 5167 if (!path) 5168 return; 5169 var curves = path._curves, 5170 index = this._index, 5171 curve; 5172 if (curves) { 5173 if ((!point || point === this._point || point === this._handleIn) 5174 && (curve = index > 0 ? curves[index - 1] : path._closed 5175 ? curves[curves.length - 1] : null)) 5176 curve._changed(); 5177 if ((!point || point === this._point || point === this._handleOut) 5178 && (curve = curves[index])) 5179 curve._changed(); 5180 } 5181 path._changed(25); 5182 }, 5183 5184 getPoint: function() { 5185 return this._point; 5186 }, 5187 5188 setPoint: function() { 5189 var point = Point.read(arguments); 5190 this._point.set(point.x, point.y); 5191 }, 5192 5193 getHandleIn: function() { 5194 return this._handleIn; 5195 }, 5196 5197 setHandleIn: function() { 5198 var point = Point.read(arguments); 5199 this._handleIn.set(point.x, point.y); 5200 }, 5201 5202 getHandleOut: function() { 5203 return this._handleOut; 5204 }, 5205 5206 setHandleOut: function() { 5207 var point = Point.read(arguments); 5208 this._handleOut.set(point.x, point.y); 5209 }, 5210 5211 hasHandles: function() { 5212 return !this.isStraight(); 5213 }, 5214 5215 isStraight: function() { 5216 return this._handleIn.isZero() && this._handleOut.isZero(); 5217 }, 5218 5219 isLinear: function() { 5220 return Segment.isLinear(this, this.getNext()); 5221 }, 5222 5223 isCollinear: function(segment) { 5224 return Segment.isCollinear(this, this.getNext(), 5225 segment, segment.getNext()); 5226 }, 5227 5228 isColinear: '#isCollinear', 5229 5230 isOrthogonal: function() { 5231 return Segment.isOrthogonal(this.getPrevious(), this, this.getNext()); 5232 }, 5233 5234 isOrthogonalArc: function() { 5235 return Segment.isOrthogonalArc(this, this.getNext()); 5236 }, 5237 5238 isArc: '#isOrthogonalArc', 5239 5240 _selectionState: 0, 5241 5242 isSelected: function(_point) { 5243 var state = this._selectionState; 5244 return !_point ? !!(state & 7) 5245 : _point === this._point ? !!(state & 4) 5246 : _point === this._handleIn ? !!(state & 1) 5247 : _point === this._handleOut ? !!(state & 2) 5248 : false; 5249 }, 5250 5251 setSelected: function(selected, _point) { 5252 var path = this._path, 5253 selected = !!selected, 5254 state = this._selectionState, 5255 oldState = state, 5256 flag = !_point ? 7 5257 : _point === this._point ? 4 5258 : _point === this._handleIn ? 1 5259 : _point === this._handleOut ? 2 5260 : 0; 5261 if (selected) { 5262 state |= flag; 5263 } else { 5264 state &= ~flag; 5265 } 5266 this._selectionState = state; 5267 if (path && state !== oldState) { 5268 path._updateSelection(this, oldState, state); 5269 path._changed(129); 5270 } 5271 }, 5272 5273 getIndex: function() { 5274 return this._index !== undefined ? this._index : null; 5275 }, 5276 5277 getPath: function() { 5278 return this._path || null; 5279 }, 5280 5281 getCurve: function() { 5282 var path = this._path, 5283 index = this._index; 5284 if (path) { 5285 if (index > 0 && !path._closed 5286 && index === path._segments.length - 1) 5287 index--; 5288 return path.getCurves()[index] || null; 5289 } 5290 return null; 5291 }, 5292 5293 getLocation: function() { 5294 var curve = this.getCurve(); 5295 return curve 5296 ? new CurveLocation(curve, this === curve._segment1 ? 0 : 1) 5297 : null; 5298 }, 5299 5300 getNext: function() { 5301 var segments = this._path && this._path._segments; 5302 return segments && (segments[this._index + 1] 5303 || this._path._closed && segments[0]) || null; 5304 }, 5305 5306 getPrevious: function() { 5307 var segments = this._path && this._path._segments; 5308 return segments && (segments[this._index - 1] 5309 || this._path._closed && segments[segments.length - 1]) || null; 5310 }, 5311 5312 reverse: function() { 5313 return new Segment(this._point, this._handleOut, this._handleIn); 5314 }, 5315 5316 remove: function() { 5317 return this._path ? !!this._path.removeSegment(this._index) : false; 5318 }, 5319 5320 clone: function() { 5321 return new Segment(this._point, this._handleIn, this._handleOut); 5322 }, 5323 5324 equals: function(segment) { 5325 return segment === this || segment && this._class === segment._class 5326 && this._point.equals(segment._point) 5327 && this._handleIn.equals(segment._handleIn) 5328 && this._handleOut.equals(segment._handleOut) 5329 || false; 5330 }, 5331 5332 toString: function() { 5333 var parts = [ 'point: ' + this._point ]; 5334 if (!this._handleIn.isZero()) 5335 parts.push('handleIn: ' + this._handleIn); 5336 if (!this._handleOut.isZero()) 5337 parts.push('handleOut: ' + this._handleOut); 5338 return '{ ' + parts.join(', ') + ' }'; 5339 }, 5340 5341 transform: function(matrix) { 5342 this._transformCoordinates(matrix, new Array(6), true); 5343 this._changed(); 5344 }, 5345 5346 _transformCoordinates: function(matrix, coords, change) { 5347 var point = this._point, 5348 handleIn = !change || !this._handleIn.isZero() 5349 ? this._handleIn : null, 5350 handleOut = !change || !this._handleOut.isZero() 5351 ? this._handleOut : null, 5352 x = point._x, 5353 y = point._y, 5354 i = 2; 5355 coords[0] = x; 5356 coords[1] = y; 5357 if (handleIn) { 5358 coords[i++] = handleIn._x + x; 5359 coords[i++] = handleIn._y + y; 5360 } 5361 if (handleOut) { 5362 coords[i++] = handleOut._x + x; 5363 coords[i++] = handleOut._y + y; 5364 } 5365 if (matrix) { 5366 matrix._transformCoordinates(coords, coords, i / 2); 5367 x = coords[0]; 5368 y = coords[1]; 5369 if (change) { 5370 point._x = x; 5371 point._y = y; 5372 i = 2; 5373 if (handleIn) { 5374 handleIn._x = coords[i++] - x; 5375 handleIn._y = coords[i++] - y; 5376 } 5377 if (handleOut) { 5378 handleOut._x = coords[i++] - x;
5379 handleOut._y = coords[i++] - y; 5380 } 5381 } else { 5382 if (!handleIn) { 5383 coords[i++] = x; 5384 coords[i++] = y; 5385 } 5386 if (!handleOut) { 5387 coords[i++] = x; 5388 coords[i++] = y; 5389 } 5390 } 5391 } 5392 return coords; 5393 }, 5394 5395 statics: { 5396 5397 isLinear: function(seg1, seg2) { 5398 var l = seg2._point.subtract(seg1._point); 5399 return l.isCollinear(seg1._handleOut) 5400 && l.isCollinear(seg2._handleIn); 5401 }, 5402 5403 isCollinear: function(seg1, seg2, seg3, seg4) { 5404 return seg1._handleOut.isZero() && seg2._handleIn.isZero() 5405 && seg3._handleOut.isZero() && seg4._handleIn.isZero() 5406 && seg2._point.subtract(seg1._point).isCollinear( 5407 seg4._point.subtract(seg3._point)); 5408 }, 5409 5410 isOrthogonal: function(seg1, seg2, seg3) { 5411 return seg1._handleOut.isZero() && seg2._handleIn.isZero() 5412 && seg2._handleOut.isZero() && seg3._handleIn.isZero() 5413 && seg2._point.subtract(seg1._point).isOrthogonal( 5414 seg3._point.subtract(seg2._point)); 5415 }, 5416 5417 isOrthogonalArc: function(seg1, seg2) { 5418 var handle1 = seg1._handleOut, 5419 handle2 = seg2._handleIn, 5420 kappa = 0.5522847498307936; 5421 if (handle1.isOrthogonal(handle2)) { 5422 var pt1 = seg1._point, 5423 pt2 = seg2._point, 5424 corner = new Line(pt1, handle1, true).intersect( 5425 new Line(pt2, handle2, true), true); 5426 return corner && Numerical.isZero(handle1.getLength() / 5427 corner.subtract(pt1).getLength() - kappa) 5428 && Numerical.isZero(handle2.getLength() / 5429 corner.subtract(pt2).getLength() - kappa); 5430 } 5431 return false; 5432 }, 5433 } 5434}); 5435 5436var SegmentPoint = Point.extend({ 5437 initialize: function SegmentPoint(point, owner, key) { 5438 var x, y, selected; 5439 if (!point) { 5440 x = y = 0; 5441 } else if ((x = point[0]) !== undefined) { 5442 y = point[1]; 5443 } else { 5444 var pt = point; 5445 if ((x = pt.x) === undefined) { 5446 pt = Point.read(arguments); 5447 x = pt.x; 5448 } 5449 y = pt.y; 5450 selected = pt.selected; 5451 } 5452 this._x = x; 5453 this._y = y; 5454 this._owner = owner; 5455 owner[key] = this; 5456 if (selected) 5457 this.setSelected(true); 5458 }, 5459 5460 set: function(x, y) { 5461 this._x = x; 5462 this._y = y; 5463 this._owner._changed(this); 5464 return this; 5465 }, 5466 5467 _serialize: function(options) { 5468 var f = options.formatter, 5469 x = f.number(this._x), 5470 y = f.number(this._y); 5471 return this.isSelected() 5472 ? { x: x, y: y, selected: true } 5473 : [x, y]; 5474 }, 5475 5476 getX: function() { 5477 return this._x; 5478 }, 5479 5480 setX: function(x) { 5481 this._x = x; 5482 this._owner._changed(this); 5483 }, 5484 5485 getY: function() { 5486 return this._y; 5487 }, 5488 5489 setY: function(y) { 5490 this._y = y; 5491 this._owner._changed(this); 5492 }, 5493 5494 isZero: function() { 5495 return Numerical.isZero(this._x) && Numerical.isZero(this._y); 5496 }, 5497 5498 setSelected: function(selected) { 5499 this._owner.setSelected(selected, this); 5500 }, 5501 5502 isSelected: function() { 5503 return this._owner.isSelected(this); 5504 } 5505}); 5506 5507var Curve = Base.extend({ 5508 _class: 'Curve', 5509 5510 initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { 5511 var count = arguments.length; 5512 if (count === 3) { 5513 this._path = arg0; 5514 this._segment1 = arg1; 5515 this._segment2 = arg2; 5516 } else if (count === 0) { 5517 this._segment1 = new Segment(); 5518 this._segment2 = new Segment(); 5519 } else if (count === 1) { 5520 this._segment1 = new Segment(arg0.segment1); 5521 this._segment2 = new Segment(arg0.segment2); 5522 } else if (count === 2) { 5523 this._segment1 = new Segment(arg0); 5524 this._segment2 = new Segment(arg1); 5525 } else { 5526 var point1, handle1, handle2, point2; 5527 if (count === 4) { 5528 point1 = arg0; 5529 handle1 = arg1; 5530 handle2 = arg2; 5531 point2 = arg3; 5532 } else if (count === 8) { 5533 point1 = [arg0, arg1]; 5534 point2 = [arg6, arg7]; 5535 handle1 = [arg2 - arg0, arg3 - arg1]; 5536 handle2 = [arg4 - arg6, arg5 - arg7]; 5537 } 5538 this._segment1 = new Segment(point1, null, handle1); 5539 this._segment2 = new Segment(point2, handle2, null); 5540 } 5541 }, 5542 5543 _changed: function() { 5544 this._length = this._bounds = undefined; 5545 }, 5546 5547 getPoint1: function() { 5548 return this._segment1._point; 5549 }, 5550 5551 setPoint1: function() { 5552 var point = Point.read(arguments); 5553 this._segment1._point.set(point.x, point.y); 5554 }, 5555 5556 getPoint2: function() { 5557 return this._segment2._point; 5558 }, 5559 5560 setPoint2: function() { 5561 var point = Point.read(arguments); 5562 this._segment2._point.set(point.x, point.y); 5563 }, 5564 5565 getHandle1: function() { 5566 return this._segment1._handleOut; 5567 }, 5568 5569 setHandle1: function() { 5570 var point = Point.read(arguments); 5571 this._segment1._handleOut.set(point.x, point.y); 5572 }, 5573 5574 getHandle2: function() { 5575 return this._segment2._handleIn; 5576 }, 5577 5578 setHandle2: function() { 5579 var point = Point.read(arguments); 5580 this._segment2._handleIn.set(point.x, point.y); 5581 }, 5582 5583 getSegment1: function() { 5584 return this._segment1; 5585 }, 5586 5587 getSegment2: function() { 5588 return this._segment2; 5589 }, 5590 5591 getPath: function() { 5592 return this._path; 5593 }, 5594 5595 getIndex: function() { 5596 return this._segment1._index; 5597 }, 5598 5599 getNext: function() { 5600 var curves = this._path && this._path._curves; 5601 return curves && (curves[this._segment1._index + 1] 5602 || this._path._closed && curves[0]) || null; 5603 }, 5604 5605 getPrevious: function() { 5606 var curves = this._path && this._path._curves; 5607 return curves && (curves[this._segment1._index - 1] 5608 || this._path._closed && curves[curves.length - 1]) || null; 5609 }, 5610 5611 isSelected: function() { 5612 return this.getPoint1().isSelected() 5613 && this.getHandle2().isSelected() 5614 && this.getHandle2().isSelected() 5615 && this.getPoint2().isSelected(); 5616 }, 5617 5618 setSelected: function(selected) { 5619 this.getPoint1().setSelected(selected); 5620 this.getHandle1().setSelected(selected); 5621 this.getHandle2().setSelected(selected); 5622 this.getPoint2().setSelected(selected); 5623 }, 5624 5625 getValues: function(matrix) {
5626 return Curve.getValues(this._segment1, this._segment2, matrix); 5627 }, 5628 5629 getPoints: function() { 5630 var coords = this.getValues(), 5631 points = []; 5632 for (var i = 0; i < 8; i += 2) 5633 points.push(new Point(coords[i], coords[i + 1])); 5634 return points; 5635 }, 5636 5637 getLength: function() { 5638 if (this._length == null) { 5639 this._length = this.isLinear() 5640 ? this._segment2._point.getDistance(this._segment1._point) 5641 : Curve.getLength(this.getValues(), 0, 1); 5642 } 5643 return this._length; 5644 }, 5645 5646 getArea: function() { 5647 return Curve.getArea(this.getValues()); 5648 }, 5649 5650 getPart: function(from, to) { 5651 return new Curve(Curve.getPart(this.getValues(), from, to)); 5652 }, 5653 5654 getPartLength: function(from, to) { 5655 return Curve.getLength(this.getValues(), from, to); 5656 }, 5657 5658 hasHandles: function() { 5659 return !this._segment1._handleOut.isZero() 5660 || !this._segment2._handleIn.isZero(); 5661 }, 5662 5663 isLinear: function() { 5664 return Segment.isLinear(this._segment1, this._segment2); 5665 }, 5666 5667 isCollinear: function(curve) { 5668 return Ssegment.isCollinear(this._segment1, this._segment2, 5669 curve._segment1, curve._segment2); 5670 }, 5671 5672 isOrthogonalArc: function() { 5673 return Segment.isOrthogonalArc(this._segment1, this._segment2); 5674 }, 5675 5676 getIntersections: function(curve) { 5677 return Curve.filterIntersections(Curve.getIntersections( 5678 this.getValues(), curve.getValues(), this, curve, [])); 5679 }, 5680 5681 _getParameter: function(offset, isParameter) { 5682 return isParameter 5683 ? offset 5684 : offset && offset.curve === this 5685 ? offset.parameter 5686 : offset === undefined && isParameter === undefined 5687 ? 0.5 5688 : this.getParameterAt(offset, 0); 5689 }, 5690 5691 divide: function(offset, isParameter, ignoreLinear) { 5692 var parameter = this._getParameter(offset, isParameter), 5693 tolerance = 0.000001, 5694 res = null; 5695 if (parameter > tolerance && parameter < 1 - tolerance) { 5696 var parts = Curve.subdivide(this.getValues(), parameter), 5697 isLinear = ignoreLinear ? false : this.isLinear(), 5698 left = parts[0], 5699 right = parts[1]; 5700 5701 if (!isLinear) { 5702 this._segment1._handleOut.set(left[2] - left[0], 5703 left[3] - left[1]); 5704 this._segment2._handleIn.set(right[4] - right[6], 5705 right[5] - right[7]); 5706 } 5707 5708 var x = left[6], y = left[7], 5709 segment = new Segment(new Point(x, y), 5710 !isLinear && new Point(left[4] - x, left[5] - y), 5711 !isLinear && new Point(right[2] - x, right[3] - y)); 5712 5713 if (this._path) { 5714 if (this._segment1._index > 0 && this._segment2._index === 0) { 5715 this._path.add(segment); 5716 } else { 5717 this._path.insert(this._segment2._index, segment); 5718 } 5719 res = this; 5720 } else { 5721 var end = this._segment2; 5722 this._segment2 = segment; 5723 res = new Curve(segment, end); 5724 } 5725 } 5726 return res; 5727 }, 5728 5729 split: function(offset, isParameter) { 5730 return this._path 5731 ? this._path.split(this._segment1._index, 5732 this._getParameter(offset, isParameter)) 5733 : null; 5734 }, 5735 5736 reverse: function() { 5737 return new Curve(this._segment2.reverse(), this._segment1.reverse()); 5738 }, 5739 5740 remove: function() { 5741 var removed = false; 5742 if (this._path) { 5743 var segment2 = this._segment2, 5744 handleOut = segment2._handleOut; 5745 removed = segment2.remove(); 5746 if (removed) 5747 this._segment1._handleOut.set(handleOut.x, handleOut.y); 5748 } 5749 return removed; 5750 }, 5751 5752 clone: function() { 5753 return new Curve(this._segment1, this._segment2); 5754 }, 5755 5756 toString: function() { 5757 var parts = [ 'point1: ' + this._segment1._point ]; 5758 if (!this._segment1._handleOut.isZero()) 5759 parts.push('handle1: ' + this._segment1._handleOut); 5760 if (!this._segment2._handleIn.isZero()) 5761 parts.push('handle2: ' + this._segment2._handleIn); 5762 parts.push('point2: ' + this._segment2._point); 5763 return '{ ' + parts.join(', ') + ' }'; 5764 }, 5765 5766statics: { 5767 getValues: function(segment1, segment2, matrix) { 5768 var p1 = segment1._point, 5769 h1 = segment1._handleOut, 5770 h2 = segment2._handleIn, 5771 p2 = segment2._point, 5772 values = [ 5773 p1._x, p1._y, 5774 p1._x + h1._x, p1._y + h1._y, 5775 p2._x + h2._x, p2._y + h2._y, 5776 p2._x, p2._y 5777 ]; 5778 if (matrix) 5779 matrix._transformCoordinates(values, values, 4); 5780 return values; 5781 }, 5782 5783 subdivide: function(v, t) { 5784 var p1x = v[0], p1y = v[1], 5785 c1x = v[2], c1y = v[3], 5786 c2x = v[4], c2y = v[5], 5787 p2x = v[6], p2y = v[7]; 5788 if (t === undefined) 5789 t = 0.5; 5790 var u = 1 - t, 5791 p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y, 5792 p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y, 5793 p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y, 5794 p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y, 5795 p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y, 5796 p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y; 5797 return [ 5798 [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y], 5799 [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y] 5800 ]; 5801 }, 5802 5803 solveCubic: function (v, coord, val, roots, min, max) { 5804 var p1 = v[coord], 5805 c1 = v[coord + 2], 5806 c2 = v[coord + 4], 5807 p2 = v[coord + 6], 5808 c = 3 * (c1 - p1), 5809 b = 3 * (c2 - c1) - c, 5810 a = p2 - p1 - c - b; 5811 return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); 5812 }, 5813 5814 getParameterOf: function(v, x, y) { 5815 var tolerance = 0.000001; 5816 if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance) 5817 return 0; 5818 if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance) 5819 return 1; 5820 var txs = [], 5821 tys = [], 5822 sx = Curve.solveCubic(v, 0, x, txs, 0, 1), 5823 sy = Curve.solveCubic(v, 1, y, tys, 0, 1), 5824 tx, ty; 5825 for (var cx = 0; sx === -1 || cx < sx;) { 5826 if (sx === -1 || (tx = txs[cx++]) > 0 && tx < 1) { 5827 for (var cy = 0; sy === -1 || cy < sy;) {
5828 if (sy === -1 || (ty = tys[cy++]) > 0 && ty < 1) { 5829 if (sx === -1) { 5830 tx = ty; 5831 } else if (sy === -1) { 5832 ty = tx; 5833 } 5834 if (Math.abs(tx - ty) < tolerance) 5835 return (tx + ty) * 0.5; 5836 } 5837 } 5838 if (sx === -1) 5839 break; 5840 } 5841 } 5842 return null; 5843 }, 5844 5845 getPart: function(v, from, to) { 5846 if (from > 0) 5847 v = Curve.subdivide(v, from)[1]; 5848 if (to < 1) 5849 v = Curve.subdivide(v, (to - from) / (1 - from))[0]; 5850 return v; 5851 }, 5852 5853 hasHandles: function(v) { 5854 var isZero = Numerical.isZero; 5855 return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3]) 5856 && isZero(v[4] - v[6]) && isZero(v[5] - v[7])); 5857 }, 5858 5859 isLinear: function(v) { 5860 var p1x = v[0], p1y = v[1], 5861 p2x = v[6], p2y = v[7], 5862 l = new Point(p2x - p1x, p2y - p1y); 5863 return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y)) 5864 && l.isCollinear(new Point(v[4] - p2x, v[5] - p2y)); 5865 }, 5866 5867 isFlatEnough: function(v, tolerance) { 5868 var p1x = v[0], p1y = v[1], 5869 c1x = v[2], c1y = v[3], 5870 c2x = v[4], c2y = v[5], 5871 p2x = v[6], p2y = v[7], 5872 ux = 3 * c1x - 2 * p1x - p2x, 5873 uy = 3 * c1y - 2 * p1y - p2y, 5874 vx = 3 * c2x - 2 * p2x - p1x, 5875 vy = 3 * c2y - 2 * p2y - p1y; 5876 return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy) 5877 < 10 * tolerance * tolerance; 5878 }, 5879 5880 getArea: function(v) { 5881 var p1x = v[0], p1y = v[1], 5882 c1x = v[2], c1y = v[3], 5883 c2x = v[4], c2y = v[5], 5884 p2x = v[6], p2y = v[7]; 5885 return ( 3.0 * c1y * p1x - 1.5 * c1y * c2x 5886 - 1.5 * c1y * p2x - 3.0 * p1y * c1x 5887 - 1.5 * p1y * c2x - 0.5 * p1y * p2x 5888 + 1.5 * c2y * p1x + 1.5 * c2y * c1x 5889 - 3.0 * c2y * p2x + 0.5 * p2y * p1x 5890 + 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10; 5891 }, 5892 5893 getEdgeSum: function(v) { 5894 return (v[0] - v[2]) * (v[3] + v[1]) 5895 + (v[2] - v[4]) * (v[5] + v[3]) 5896 + (v[4] - v[6]) * (v[7] + v[5]); 5897 }, 5898 5899 getBounds: function(v) { 5900 var min = v.slice(0, 2), 5901 max = min.slice(), 5902 roots = [0, 0]; 5903 for (var i = 0; i < 2; i++) 5904 Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6], 5905 i, 0, min, max, roots); 5906 return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]); 5907 }, 5908 5909 _addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) { 5910 function add(value, padding) { 5911 var left = value - padding, 5912 right = value + padding; 5913 if (left < min[coord]) 5914 min[coord] = left; 5915 if (right > max[coord]) 5916 max[coord] = right; 5917 } 5918 var a = 3 * (v1 - v2) - v0 + v3, 5919 b = 2 * (v0 + v2) - 4 * v1, 5920 c = v1 - v0, 5921 count = Numerical.solveQuadratic(a, b, c, roots), 5922 tMin = 0.000001, 5923 tMax = 1 - tMin; 5924 add(v3, 0); 5925 for (var i = 0; i < count; i++) { 5926 var t = roots[i], 5927 u = 1 - t; 5928 if (tMin < t && t < tMax) 5929 add(u * u * u * v0 5930 + 3 * u * u * t * v1 5931 + 3 * u * t * t * v2 5932 + t * t * t * v3, 5933 padding); 5934 } 5935 } 5936}}, Base.each( 5937 ['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'], 5938 function(name) { 5939 this[name] = function() { 5940 if (!this._bounds) 5941 this._bounds = {}; 5942 var bounds = this._bounds[name]; 5943 if (!bounds) { 5944 bounds = this._bounds[name] = Path[name]([this._segment1, 5945 this._segment2], false, this._path.getStyle()); 5946 } 5947 return bounds.clone(); 5948 }; 5949 }, 5950{ 5951 5952}), { 5953 beans: false, 5954 5955 getParameterAt: function(offset, start) { 5956 return Curve.getParameterAt(this.getValues(), offset, start); 5957 }, 5958 5959 getParameterOf: function() { 5960 var point = Point.read(arguments); 5961 return Curve.getParameterOf(this.getValues(), point.x, point.y); 5962 }, 5963 5964 getLocationAt: function(offset, isParameter) { 5965 var t = isParameter ? offset : this.getParameterAt(offset); 5966 return t != null && t >= 0 && t <= 1 5967 ? new CurveLocation(this, t) 5968 : null; 5969 }, 5970 5971 getLocationOf: function() { 5972 return this.getLocationAt(this.getParameterOf(Point.read(arguments)), 5973 true); 5974 }, 5975 5976 getOffsetOf: function() { 5977 var loc = this.getLocationOf.apply(this, arguments); 5978 return loc ? loc.getOffset() : null; 5979 }, 5980 5981 getNearestLocation: function() { 5982 var point = Point.read(arguments), 5983 values = this.getValues(), 5984 count = 100, 5985 minDist = Infinity, 5986 minT = 0; 5987 5988 function refine(t) { 5989 if (t >= 0 && t <= 1) { 5990 var dist = point.getDistance(Curve.getPoint(values, t), true); 5991 if (dist < minDist) { 5992 minDist = dist; 5993 minT = t; 5994 return true; 5995 } 5996 } 5997 } 5998 5999 for (var i = 0; i <= count; i++) 6000 refine(i / count); 6001 6002 var step = 1 / (count * 2); 6003 while (step > 0.000001) { 6004 if (!refine(minT - step) && !refine(minT + step)) 6005 step /= 2; 6006 } 6007 var pt = Curve.getPoint(values, minT); 6008 return new CurveLocation(this, minT, pt, null, null, null, 6009 point.getDistance(pt)); 6010 }, 6011 6012 getNearestPoint: function() { 6013 return this.getNearestLocation.apply(this, arguments).getPoint(); 6014 } 6015 6016}, 6017new function() { 6018 var methods = ['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent', 6019 'getWeightedNormal', 'getCurvature']; 6020 return Base.each(methods, 6021 function(name) { 6022 this[name + 'At'] = function(offset, isParameter) { 6023 var values = this.getValues(); 6024 return Curve[name](values, isParameter ? offset 6025 : Curve.getParameterAt(values, offset, 0)); 6026 }; 6027 }, { 6028 statics: { 6029 evaluateMethods: methods 6030 } 6031 }) 6032}, 6033new function() { 6034 6035 function getLengthIntegrand(v) { 6036 var p1x = v[0], p1y = v[1], 6037 c1x = v[2], c1y = v[3], 6038 c2x = v[4], c2y = v[5], 6039 p2x = v[6], p2y = v[7], 6040 6041 ax = 9 * (c1x - c2x) + 3 * (p2x - p1x), 6042 bx = 6 * (p1x + c2x) - 12 * c1x, 6043 cx = 3 * (c1x - p1x), 6044 6045 ay = 9 * (c1y - c2y) + 3 * (p2y - p1y), 6046 by = 6 * (p1y + c2y) - 12 * c1y, 6047 cy = 3 * (c1y - p1y); 6048 6049 return function(t) { 6050 var dx = (ax * t + bx) * t + cx, 6051 dy = (ay * t + by) * t + cy; 6052 return Math.sqrt(dx * dx + dy * dy); 6053 }; 6054 } 6055 6056 function getIterations(a, b) { 6057 return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32))); 6058 } 6059 6060 function evaluate(v, t, type, normalized) { 6061 if (t == null || t < 0 || t > 1) 6062 return null; 6063 var p1x = v[0], p1y = v[1], 6064 c1x = v[2], c1y = v[3], 6065 c2x = v[4], c2y = v[5], 6066 p2x = v[6], p2y = v[7], 6067 tolerance = 0.000001, 6068 x, y; 6069 6070 if (type === 0 && (t < tolerance || t > 1 - tolerance)) { 6071 var isZero = t < tolerance; 6072 x = isZero ? p1x : p2x; 6073 y = isZero ? p1y : p2y; 6074 } else { 6075 var cx = 3 * (c1x - p1x), 6076 bx = 3 * (c2x - c1x) - cx, 6077 ax = p2x - p1x - cx - bx, 6078 6079 cy = 3 * (c1y - p1y), 6080 by = 3 * (c2y - c1y) - cy, 6081 ay = p2y - p1y - cy - by; 6082 if (type === 0) { 6083 x = ((ax * t + bx) * t + cx) * t + p1x; 6084 y = ((ay * t + by) * t + cy) * t + p1y; 6085 } else { 6086 if (t < tolerance) { 6087 x = cx; 6088 y = cy; 6089 } else if (t > 1 - tolerance) { 6090 x = 3 * (p2x - c2x); 6091 y = 3 * (p2y - c2y); 6092 } else { 6093 x = (3 * ax * t + 2 * bx) * t + cx; 6094 y = (3 * ay * t + 2 * by) * t + cy; 6095 } 6096 if (normalized) { 6097 if (x === 0 && y === 0 6098 && (t < tolerance || t > 1 - tolerance)) { 6099 x = c2x - c1x; 6100 y = c2y - c1y; 6101 } 6102 var len = Math.sqrt(x * x + y * y); 6103 x /= len; 6104 y /= len; 6105 } 6106 if (type === 3) { 6107 var x2 = 6 * ax * t + 2 * bx, 6108 y2 = 6 * ay * t + 2 * by, 6109 d = Math.pow(x * x + y * y, 3 / 2); 6110 x = d !== 0 ? (x * y2 - y * x2) / d : 0; 6111 y = 0; 6112 } 6113 } 6114 } 6115 return type === 2 ? new Point(y, -x) : new Point(x, y); 6116 } 6117 6118 return { 6119 statics: true, 6120 6121 getLength: function(v, a, b) { 6122 if (a === undefined) 6123 a = 0; 6124 if (b === undefined) 6125 b = 1; 6126 var isZero = Numerical.isZero; 6127 if (a === 0 && b === 1 6128 && isZero(v[0] - v[2]) && isZero(v[1] - v[3]) 6129 && isZero(v[6] - v[4]) && isZero(v[7] - v[5])) { 6130 var dx = v[6] - v[0], 6131 dy = v[7] - v[1]; 6132 return Math.sqrt(dx * dx + dy * dy); 6133 } 6134 var ds = getLengthIntegrand(v); 6135 return Numerical.integrate(ds, a, b, getIterations(a, b)); 6136 }, 6137 6138 getParameterAt: function(v, offset, start) { 6139 if (start === undefined) 6140 start = offset < 0 ? 1 : 0 6141 if (offset === 0) 6142 return start; 6143 var tolerance = 0.000001, 6144 abs = Math.abs, 6145 forward = offset > 0, 6146 a = forward ? start : 0, 6147 b = forward ? 1 : start, 6148 ds = getLengthIntegrand(v), 6149 rangeLength = Numerical.integrate(ds, a, b, 6150 getIterations(a, b)); 6151 if (abs(offset - rangeLength) < tolerance) { 6152 return forward ? b : a; 6153 } else if (abs(offset) > rangeLength) { 6154 return null; 6155 } 6156 var guess = offset / rangeLength, 6157 length = 0; 6158 function f(t) { 6159 length += Numerical.integrate(ds, start, t, 6160 getIterations(start, t)); 6161 start = t; 6162 return length - offset; 6163 } 6164 return Numerical.findRoot(f, ds, start + guess, a, b, 16, 6165 tolerance); 6166 }, 6167 6168 getPoint: function(v, t) { 6169 return evaluate(v, t, 0, false); 6170 }, 6171 6172 getTangent: function(v, t) { 6173 return evaluate(v, t, 1, true); 6174 }, 6175 6176 getWeightedTangent: function(v, t) { 6177 return evaluate(v, t, 1, false); 6178 }, 6179 6180 getNormal: function(v, t) { 6181 return evaluate(v, t, 2, true); 6182 }, 6183 6184 getWeightedNormal: function(v, t) { 6185 return evaluate(v, t, 2, false); 6186 }, 6187 6188 getCurvature: function(v, t) { 6189 return evaluate(v, t, 3, false).x; 6190 } 6191 }; 6192}, new function() { 6193 function addLocation(locations, include, curve1, t1, point1, curve2, t2, 6194 point2) { 6195 var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2); 6196 if (!include || include(loc)) 6197 locations.push(loc); 6198 } 6199 6200 function addCurveIntersections(v1, v2, curve1, curve2, locations, include, 6201 tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) { 6202 if (recursion > 32) 6203 return; 6204 var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7], 6205 tolerance = 0.000001, 6206 getSignedDistance = Line.getSignedDistance, 6207 d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0, 6208 d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0, 6209 factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9, 6210 dMin = factor * Math.min(0, d1, d2), 6211 dMax = factor * Math.max(0, d1, d2), 6212 dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]), 6213 dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]), 6214 dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]), 6215 dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]), 6216 tMinNew, tMaxNew, tDiff; 6217 if (q0x === q3x && uMax - uMin < tolerance && recursion > 3) { 6218 tMaxNew = tMinNew = (tMax + tMin) / 2; 6219 tDiff = 0; 6220 } else { 6221 var hull = getConvexHull(dp0, dp1, dp2, dp3), 6222 top = hull[0], 6223 bottom = hull[1], 6224 tMinClip, tMaxClip; 6225 tMinClip = clipConvexHull(top, bottom, dMin, dMax); 6226 top.reverse(); 6227 bottom.reverse(); 6228 tMaxClip = clipConvexHull(top, bottom, dMin, dMax); 6229 if (tMinClip == null || tMaxClip == null) 6230 return; 6231 v1 = Curve.getPart(v1, tMinClip, tMaxClip); 6232 tDiff = tMaxClip - tMinClip; 6233 tMinNew = tMax * tMinClip + tMin * (1 - tMinClip); 6234 tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip); 6235 } 6236 if (oldTDiff > 0.5 && tDiff > 0.5) { 6237 if (tMaxNew - tMinNew > uMax - uMin) { 6238 var parts = Curve.subdivide(v1, 0.5), 6239 t = tMinNew + (tMaxNew - tMinNew) / 2; 6240 addCurveIntersections( 6241 v2, parts[0], curve2, curve1, locations, include, 6242 uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion); 6243 addCurveIntersections( 6244 v2, parts[1], curve2, curve1, locations, include, 6245 uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion); 6246 } else { 6247 var parts = Curve.subdivide(v2, 0.5), 6248 t = uMin + (uMax - uMin) / 2; 6249 addCurveIntersections( 6250 parts[0], v1, curve2, curve1, locations, include, 6251 uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion); 6252 addCurveIntersections( 6253 parts[1], v1, curve2, curve1, locations, include, 6254 t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion); 6255 } 6256 } else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) { 6257 var t1 = tMinNew + (tMaxNew - tMinNew) / 2, 6258 t2 = uMin + (uMax - uMin) / 2; 6259 if (reverse) { 6260 addLocation(locations, include, 6261 curve2, t2, Curve.getPoint(v2, t2), 6262 curve1, t1, Curve.getPoint(v1, t1)); 6263 } else { 6264 addLocation(locations, include, 6265 curve1, t1, Curve.getPoint(v1, t1), 6266 curve2, t2, Curve.getPoint(v2, t2)); 6267 } 6268 } else if (tDiff > 0) { 6269 addCurveIntersections(v2, v1, curve2, curve1, locations, include, 6270 uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion); 6271 } 6272 } 6273 6274 function getConvexHull(dq0, dq1, dq2, dq3) { 6275 var p0 = [ 0, dq0 ], 6276 p1 = [ 1 / 3, dq1 ], 6277 p2 = [ 2 / 3, dq2 ], 6278 p3 = [ 1, dq3 ], 6279 getSignedDistance = Line.getSignedDistance, 6280 dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1), 6281 dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2), 6282 flip = false, 6283 hull; 6284 if (dist1 * dist2 < 0) { 6285 hull = [[p0, p1, p3], [p0, p2, p3]]; 6286 flip = dist1 < 0; 6287 } else { 6288 var pmax, cross = 0, 6289 distZero = dist1 === 0 || dist2 === 0; 6290 if (Math.abs(dist1) > Math.abs(dist2)) { 6291 pmax = p1; 6292 cross = (dq3 - dq2 - (dq3 - dq0) / 3) 6293 * (2 * (dq3 - dq2) - dq3 + dq1) / 3; 6294 } else { 6295 pmax = p2; 6296 cross = (dq1 - dq0 + (dq0 - dq3) / 3) 6297 * (-2 * (dq0 - dq1) + dq0 - dq2) / 3; 6298 } 6299 hull = cross < 0 || distZero 6300 ? [[p0, pmax, p3], [p0, p3]] 6301 : [[p0, p1, p2, p3], [p0, p3]];
6302 flip = dist1 ? dist1 < 0 : dist2 < 0; 6303 } 6304 return flip ? hull.reverse() : hull; 6305 } 6306 6307 function clipConvexHull(hullTop, hullBottom, dMin, dMax) { 6308 if (hullTop[0][1] < dMin) { 6309 return clipConvexHullPart(hullTop, true, dMin); 6310 } else if (hullBottom[0][1] > dMax) { 6311 return clipConvexHullPart(hullBottom, false, dMax); 6312 } else { 6313 return hullTop[0][0]; 6314 } 6315 } 6316 6317 function clipConvexHullPart(part, top, threshold) { 6318 var px = part[0][0], 6319 py = part[0][1]; 6320 for (var i = 1, l = part.length; i < l; i++) { 6321 var qx = part[i][0], 6322 qy = part[i][1]; 6323 if (top ? qy >= threshold : qy <= threshold) 6324 return px + (threshold - py) * (qx - px) / (qy - py); 6325 px = qx; 6326 py = qy; 6327 } 6328 return null; 6329 } 6330 6331 function addCurveLineIntersections(v1, v2, curve1, curve2, locations, 6332 include) { 6333 var flip = Curve.isLinear(v1), 6334 vc = flip ? v2 : v1, 6335 vl = flip ? v1 : v2, 6336 lx1 = vl[0], ly1 = vl[1], 6337 lx2 = vl[6], ly2 = vl[7], 6338 ldx = lx2 - lx1, 6339 ldy = ly2 - ly1, 6340 angle = Math.atan2(-ldy, ldx), 6341 sin = Math.sin(angle), 6342 cos = Math.cos(angle), 6343 rlx2 = ldx * cos - ldy * sin, 6344 rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0], 6345 rvc = []; 6346 for(var i = 0; i < 8; i += 2) { 6347 var x = vc[i] - lx1, 6348 y = vc[i + 1] - ly1; 6349 rvc.push( 6350 x * cos - y * sin, 6351 y * cos + x * sin); 6352 } 6353 var roots = [], 6354 count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1); 6355 for (var i = 0; i < count; i++) { 6356 var tc = roots[i], 6357 x = Curve.getPoint(rvc, tc).x; 6358 if (x >= 0 && x <= rlx2) { 6359 var tl = Curve.getParameterOf(rvl, x, 0), 6360 t1 = flip ? tl : tc, 6361 t2 = flip ? tc : tl; 6362 addLocation(locations, include, 6363 curve1, t1, Curve.getPoint(v1, t1), 6364 curve2, t2, Curve.getPoint(v2, t2)); 6365 } 6366 } 6367 } 6368 6369 function addLineIntersection(v1, v2, curve1, curve2, locations, include) { 6370 var point = Line.intersect( 6371 v1[0], v1[1], v1[6], v1[7], 6372 v2[0], v2[1], v2[6], v2[7]); 6373 if (point) { 6374 var x = point.x, 6375 y = point.y; 6376 addLocation(locations, include, 6377 curve1, Curve.getParameterOf(v1, x, y), point, 6378 curve2, Curve.getParameterOf(v2, x, y), point); 6379 } 6380 } 6381 6382 return { statics: { 6383 getIntersections: function(v1, v2, c1, c2, locations, include) { 6384 var linear1 = Curve.isLinear(v1), 6385 linear2 = Curve.isLinear(v2), 6386 c1p1 = c1.getPoint1(), 6387 c1p2 = c1.getPoint2(), 6388 c2p1 = c2.getPoint1(), 6389 c2p2 = c2.getPoint2(), 6390 tolerance = 0.000001; 6391 if (c1p1.isClose(c2p1, tolerance)) 6392 addLocation(locations, include, c1, 0, c1p1, c2, 0, c1p1); 6393 if (c1p1.isClose(c2p2, tolerance)) 6394 addLocation(locations, include, c1, 0, c1p1, c2, 1, c1p1); 6395 (linear1 && linear2 6396 ? addLineIntersection 6397 : linear1 || linear2 6398 ? addCurveLineIntersections 6399 : addCurveIntersections)( 6400 v1, v2, c1, c2, locations, include, 6401 0, 1, 0, 1, 0, false, 0); 6402 if (c1p2.isClose(c2p1, tolerance)) 6403 addLocation(locations, include, c1, 1, c1p2, c2, 0, c1p2); 6404 if (c1p2.isClose(c2p2, tolerance)) 6405 addLocation(locations, include, c1, 1, c1p2, c2, 1, c1p2); 6406 return locations; 6407 }, 6408 6409 filterIntersections: function(locations, _expand) { 6410 var last = locations.length - 1, 6411 tMax = 1 - 0.000001; 6412 for (var i = last; i >= 0; i--) { 6413 var loc = locations[i], 6414 next = loc._curve.getNext(), 6415 next2 = loc._curve2.getNext(); 6416 if (next && loc._parameter >= tMax) { 6417 loc._parameter = 0; 6418 loc._curve = next; 6419 } 6420 if (next2 && loc._parameter2 >= tMax) { 6421 loc._parameter2 = 0; 6422 loc._curve2 = next2; 6423 } 6424 } 6425 6426 function compare(loc1, loc2) { 6427 var path1 = loc1.getPath(), 6428 path2 = loc2.getPath(); 6429 return path1 === path2 6430 ? (loc1.getIndex() + loc1.getParameter()) 6431 - (loc2.getIndex() + loc2.getParameter()) 6432 : path1._id - path2._id; 6433 } 6434 6435 if (last > 0) { 6436 locations.sort(compare); 6437 for (var i = last; i > 0; i--) { 6438 if (locations[i].equals(locations[i - 1])) { 6439 locations.splice(i, 1); 6440 last--; 6441 } 6442 } 6443 } 6444 if (_expand) { 6445 for (var i = last; i >= 0; i--) 6446 locations.push(locations[i].getIntersection()); 6447 locations.sort(compare); 6448 } 6449 return locations; 6450 } 6451 }}; 6452}); 6453 6454var CurveLocation = Base.extend({ 6455 _class: 'CurveLocation', 6456 beans: true, 6457 6458 initialize: function CurveLocation(curve, parameter, point, _curve2, 6459 _parameter2, _point2, _distance) { 6460 this._id = UID.get(CurveLocation); 6461 var path = curve._path; 6462 this._version = path ? path._version : 0; 6463 this._curve = curve; 6464 this._parameter = parameter; 6465 this._point = point || curve.getPointAt(parameter, true); 6466 this._curve2 = _curve2; 6467 this._parameter2 = _parameter2; 6468 this._point2 = _point2; 6469 this._distance = _distance; 6470 this._segment1 = curve._segment1; 6471 this._segment2 = curve._segment2; 6472 }, 6473 6474 getSegment: function(_preferFirst) { 6475 if (!this._segment) {
6476 var curve = this.getCurve(), 6477 parameter = this.getParameter(); 6478 if (parameter === 1) { 6479 this._segment = curve._segment2; 6480 } else if (parameter === 0 || _preferFirst) { 6481 this._segment = curve._segment1; 6482 } else if (parameter == null) { 6483 return null; 6484 } else { 6485 this._segment = curve.getPartLength(0, parameter) 6486 < curve.getPartLength(parameter, 1) 6487 ? curve._segment1 6488 : curve._segment2; 6489 } 6490 } 6491 return this._segment; 6492 }, 6493 6494 getCurve: function() { 6495 var curve = this._curve, 6496 path = curve && curve._path; 6497 if (path && path._version !== this._version) { 6498 curve = null; 6499 this._parameter = null; 6500 } 6501 if (!curve) { 6502 curve = this._segment1.getCurve(); 6503 if (curve.getParameterOf(this._point) == null) 6504 curve = this._segment2.getPrevious().getCurve(); 6505 this._curve = curve; 6506 path = curve._path; 6507 this._version = path ? path._version : 0; 6508 } 6509 return curve; 6510 }, 6511 6512 getPath: function() { 6513 var curve = this.getCurve(); 6514 return curve && curve._path; 6515 }, 6516 6517 getIndex: function() { 6518 var curve = this.getCurve(); 6519 return curve && curve.getIndex(); 6520 }, 6521 6522 getParameter: function() { 6523 var curve = this.getCurve(), 6524 parameter = this._parameter; 6525 return curve && parameter == null 6526 ? this._parameter = curve.getParameterOf(this._point) 6527 : parameter; 6528 }, 6529 6530 getPoint: function() { 6531 return this._point; 6532 }, 6533 6534 getOffset: function() { 6535 var path = this.getPath(); 6536 return path ? path._getOffset(this) : this.getCurveOffset(); 6537 }, 6538 6539 getCurveOffset: function() { 6540 var curve = this.getCurve(), 6541 parameter = this.getParameter(); 6542 return parameter != null && curve && curve.getPartLength(0, parameter); 6543 }, 6544 6545 getIntersection: function() { 6546 var intersection = this._intersection; 6547 if (!intersection && this._curve2) { 6548 this._intersection = intersection = new CurveLocation(this._curve2, 6549 this._parameter2, this._point2 || this._point, this); 6550 intersection._intersection = this; 6551 } 6552 return intersection; 6553 }, 6554 6555 getDistance: function() { 6556 return this._distance; 6557 }, 6558 6559 divide: function() { 6560 var curve = this.getCurve(); 6561 return curve && curve.divide(this.getParameter(), true); 6562 }, 6563 6564 split: function() { 6565 var curve = this.getCurve(); 6566 return curve && curve.split(this.getParameter(), true); 6567 }, 6568 6569 equals: function(loc) { 6570 var abs = Math.abs, 6571 tolerance = 0.000001; 6572 return this === loc 6573 || loc instanceof CurveLocation 6574 && this.getCurve() === loc.getCurve() 6575 && abs(this.getParameter() - loc.getParameter()) < tolerance 6576 && this._curve2 === loc._curve2 6577 && abs(this._parameter2 - loc._parameter2) < tolerance 6578 || false; 6579 }, 6580 6581 toString: function() { 6582 var parts = [], 6583 point = this.getPoint(), 6584 f = Formatter.instance; 6585 if (point) 6586 parts.push('point: ' + point); 6587 var index = this.getIndex(); 6588 if (index != null) 6589 parts.push('index: ' + index); 6590 var parameter = this.getParameter(); 6591 if (parameter != null) 6592 parts.push('parameter: ' + f.number(parameter)); 6593 if (this._distance != null) 6594 parts.push('distance: ' + f.number(this._distance)); 6595 return '{ ' + parts.join(', ') + ' }'; 6596 } 6597}, Base.each(Curve.evaluateMethods, function(name) { 6598 if (name !== 'getPoint') { 6599 var get = name + 'At'; 6600 this[name] = function() { 6601 var parameter = this.getParameter(), 6602 curve = this.getCurve(); 6603 return parameter != null && curve && curve[get](parameter, true); 6604 }; 6605 } 6606}, {})); 6607 6608var PathItem = Item.extend({ 6609 _class: 'PathItem', 6610 6611 initialize: function PathItem() { 6612 }, 6613 6614 getIntersections: function(path, _matrix, _expand) { 6615 if (this === path) 6616 path = null; 6617 var locations = [], 6618 curves1 = this.getCurves(), 6619 curves2 = path ? path.getCurves() : curves1, 6620 matrix1 = this._matrix.orNullIfIdentity(), 6621 matrix2 = path ? (_matrix || path._matrix).orNullIfIdentity() 6622 : matrix1, 6623 length1 = curves1.length, 6624 length2 = path ? curves2.length : length1, 6625 values2 = [], 6626 tMin = 0.000001, 6627 tMax = 1 - tMin; 6628 if (path && !this.getBounds(matrix1).touches(path.getBounds(matrix2))) 6629 return []; 6630 for (var i = 0; i < length2; i++) 6631 values2[i] = curves2[i].getValues(matrix2); 6632 for (var i = 0; i < length1; i++) { 6633 var curve1 = curves1[i], 6634 values1 = path ? curve1.getValues(matrix1) : values2[i]; 6635 if (!path) { 6636 var seg1 = curve1.getSegment1(), 6637 seg2 = curve1.getSegment2(), 6638 h1 = seg1._handleOut, 6639 h2 = seg2._handleIn; 6640 if (new Line(seg1._point.subtract(h1), h1.multiply(2), true) 6641 .intersect(new Line(seg2._point.subtract(h2), 6642 h2.multiply(2), true), false)) { 6643 var parts = Curve.subdivide(values1); 6644 Curve.getIntersections( 6645 parts[0], parts[1], curve1, curve1, locations, 6646 function(loc) { 6647 if (loc._parameter <= tMax) { 6648 loc._parameter /= 2; 6649 loc._parameter2 = 0.5 + loc._parameter2 / 2; 6650 return true; 6651 } 6652 } 6653 ); 6654 } 6655 } 6656 for (var j = path ? 0 : i + 1; j < length2; j++) { 6657 Curve.getIntersections( 6658 values1, values2[j], curve1, curves2[j], locations, 6659 !path && (j === i + 1 || j === length2 - 1 && i === 0) 6660 && function(loc) { 6661 var t = loc._parameter; 6662 return t >= tMin && t <= tMax; 6663 } 6664 ); 6665 } 6666 } 6667 return Curve.filterIntersections(locations, _expand); 6668 }, 6669 6670 _asPathItem: function() { 6671 return this; 6672 }, 6673 6674 setPathData: function(data) { 6675 6676 var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig), 6677 coords, 6678 relative = false, 6679 previous, 6680 control, 6681 current = new Point(), 6682 start = new Point(); 6683 6684 function getCoord(index, coord) { 6685 var val = +coords[index]; 6686 if (relative) 6687 val += current[coord]; 6688 return val; 6689 } 6690 6691 function getPoint(index) { 6692 return new Point( 6693 getCoord(index, 'x'), 6694 getCoord(index + 1, 'y') 6695 ); 6696 } 6697 6698 this.clear(); 6699 6700 for (var i = 0, l = parts && parts.length; i < l; i++) { 6701 var part = parts[i], 6702 command = part[0], 6703 lower = command.toLowerCase(); 6704 coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g); 6705 var length = coords && coords.length; 6706 relative = command === lower; 6707 if (previous === 'z' && !/[mz]/.test(lower)) 6708 this.moveTo(current = start); 6709 switch (lower) { 6710 case 'm': 6711 case 'l': 6712 var move = lower === 'm'; 6713 for (var j = 0; j < length; j += 2) 6714 this[j === 0 && move ? 'moveTo' : 'lineTo']( 6715 current = getPoint(j)); 6716 control = current; 6717 if (move) 6718 start = current; 6719 break; 6720 case 'h': 6721 case 'v': 6722 var coord = lower === 'h' ? 'x' : 'y'; 6723 for (var j = 0; j < length; j++) { 6724 current[coord] = getCoord(j, coord); 6725 this.lineTo(current); 6726 } 6727 control = current; 6728 break; 6729 case 'c': 6730 for (var j = 0; j < length; j += 6) { 6731 this.cubicCurveTo( 6732 getPoint(j), 6733 control = getPoint(j + 2), 6734 current = getPoint(j + 4)); 6735 } 6736 break; 6737 case 's': 6738 for (var j = 0; j < length; j += 4) { 6739 this.cubicCurveTo( 6740 /[cs]/.test(previous) 6741 ? current.multiply(2).subtract(control) 6742 : current, 6743 control = getPoint(j), 6744 current = getPoint(j + 2)); 6745 previous = lower; 6746 } 6747 break; 6748 case 'q': 6749 for (var j = 0; j < length; j += 4) { 6750 this.quadraticCurveTo( 6751 control = getPoint(j), 6752 current = getPoint(j + 2)); 6753 } 6754 break; 6755 case 't': 6756 for (var j = 0; j < length; j += 2) { 6757 this.quadraticCurveTo( 6758 control = (/[qt]/.test(previous) 6759 ? current.multiply(2).subtract(control) 6760 : current), 6761 current = getPoint(j)); 6762 previous = lower; 6763 } 6764 break; 6765 case 'a': 6766 for (var j = 0; j < length; j += 7) { 6767 this.arcTo(current = getPoint(j + 5), 6768 new Size(+coords[j], +coords[j + 1]), 6769 +coords[j + 2], +coords[j + 4], +coords[j + 3]); 6770 } 6771 break; 6772 case 'z': 6773 this.closePath(true); 6774 break; 6775 } 6776 previous = lower; 6777 } 6778 }, 6779 6780 _canComposite: function() { 6781 return !(this.hasFill() && this.hasStroke()); 6782 }, 6783 6784 _contains: function(point) { 6785 var winding = this._getWinding(point, false, true); 6786 return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding); 6787 } 6788 6789}); 6790
6791var Path = PathItem.extend({ 6792 _class: 'Path', 6793 _serializeFields: { 6794 segments: [], 6795 closed: false 6796 }, 6797 6798 initialize: function Path(arg) { 6799 this._closed = false; 6800 this._segments = []; 6801 this._version = 0; 6802 var segments = Array.isArray(arg) 6803 ? typeof arg[0] === 'object' 6804 ? arg 6805 : arguments 6806 : arg && (arg.size === undefined && (arg.x !== undefined 6807 || arg.point !== undefined)) 6808 ? arguments 6809 : null; 6810 if (segments && segments.length > 0) { 6811 this.setSegments(segments); 6812 } else { 6813 this._curves = undefined; 6814 this._selectedSegmentState = 0; 6815 if (!segments && typeof arg === 'string') { 6816 this.setPathData(arg); 6817 arg = null; 6818 } 6819 } 6820 this._initialize(!segments && arg); 6821 }, 6822 6823 _equals: function(item) { 6824 return this._closed === item._closed 6825 && Base.equals(this._segments, item._segments); 6826 }, 6827 6828 clone: function(insert) { 6829 var copy = new Path(Item.NO_INSERT); 6830 copy.setSegments(this._segments); 6831 copy._closed = this._closed; 6832 if (this._clockwise !== undefined) 6833 copy._clockwise = this._clockwise; 6834 return this._clone(copy, insert); 6835 }, 6836 6837 _changed: function _changed(flags) { 6838 _changed.base.call(this, flags); 6839 if (flags & 8) { 6840 var parent = this._parent; 6841 if (parent) 6842 parent._currentPath = undefined; 6843 this._length = this._clockwise = undefined; 6844 if (flags & 16) { 6845 this._version++; 6846 } else if (this._curves) { 6847 for (var i = 0, l = this._curves.length; i < l; i++) 6848 this._curves[i]._changed(); 6849 } 6850 this._monoCurves = undefined; 6851 } else if (flags & 32) { 6852 this._bounds = undefined; 6853 } 6854 }, 6855 6856 getStyle: function() { 6857 var parent = this._parent; 6858 return (parent instanceof CompoundPath ? parent : this)._style; 6859 }, 6860 6861 getSegments: function() { 6862 return this._segments; 6863 }, 6864 6865 setSegments: function(segments) { 6866 var fullySelected = this.isFullySelected(); 6867 this._segments.length = 0; 6868 this._selectedSegmentState = 0; 6869 this._curves = undefined; 6870 if (segments && segments.length > 0) 6871 this._add(Segment.readAll(segments)); 6872 if (fullySelected) 6873 this.setFullySelected(true); 6874 }, 6875 6876 getFirstSegment: function() { 6877 return this._segments[0]; 6878 }, 6879 6880 getLastSegment: function() { 6881 return this._segments[this._segments.length - 1]; 6882 }, 6883 6884 getCurves: function() { 6885 var curves = this._curves, 6886 segments = this._segments; 6887 if (!curves) { 6888 var length = this._countCurves(); 6889 curves = this._curves = new Array(length); 6890 for (var i = 0; i < length; i++) 6891 curves[i] = new Curve(this, segments[i], 6892 segments[i + 1] || segments[0]); 6893 } 6894 return curves; 6895 }, 6896 6897 getFirstCurve: function() { 6898 return this.getCurves()[0]; 6899 }, 6900 6901 getLastCurve: function() { 6902 var curves = this.getCurves(); 6903 return curves[curves.length - 1]; 6904 }, 6905 6906 isClosed: function() { 6907 return this._closed; 6908 }, 6909 6910 setClosed: function(closed) { 6911 if (this._closed != (closed = !!closed)) { 6912 this._closed = closed; 6913 if (this._curves) { 6914 var length = this._curves.length = this._countCurves(); 6915 if (closed) 6916 this._curves[length - 1] = new Curve(this, 6917 this._segments[length - 1], this._segments[0]); 6918 } 6919 this._changed(25); 6920 } 6921 } 6922}, { 6923 beans: true, 6924 6925 getPathData: function(_matrix, _precision) { 6926 var segments = this._segments, 6927 length = segments.length, 6928 f = new Formatter(_precision), 6929 coords = new Array(6), 6930 first = true, 6931 curX, curY, 6932 prevX, prevY, 6933 inX, inY, 6934 outX, outY, 6935 parts = []; 6936 6937 function addSegment(segment, skipLine) { 6938 segment._transformCoordinates(_matrix, coords, false); 6939 curX = coords[0]; 6940 curY = coords[1]; 6941 if (first) { 6942 parts.push('M' + f.pair(curX, curY)); 6943 first = false; 6944 } else { 6945 inX = coords[2]; 6946 inY = coords[3]; 6947 if (inX === curX && inY === curY 6948 && outX === prevX && outY === prevY) { 6949 if (!skipLine) 6950 parts.push('l' + f.pair(curX - prevX, curY - prevY)); 6951 } else { 6952 parts.push('c' + f.pair(outX - prevX, outY - prevY) 6953 + ' ' + f.pair(inX - prevX, inY - prevY) 6954 + ' ' + f.pair(curX - prevX, curY - prevY)); 6955 } 6956 } 6957 prevX = curX; 6958 prevY = curY; 6959 outX = coords[4]; 6960 outY = coords[5]; 6961 } 6962 6963 if (length === 0) 6964 return ''; 6965 6966 for (var i = 0; i < length; i++) 6967 addSegment(segments[i]); 6968 if (this._closed && length > 0) { 6969 addSegment(segments[0], true); 6970 parts.push('z'); 6971 } 6972 return parts.join(''); 6973 } 6974}, { 6975 6976 isEmpty: function() { 6977 return this._segments.length === 0; 6978 }, 6979 6980 isLinear: function() { 6981 var segments = this._segments; 6982 for (var i = 0, l = segments.length; i < l; i++) { 6983 if (!segments[i].isLinear()) 6984 return false;
6985 } 6986 return true; 6987 }, 6988 6989 hasHandles: function() { 6990 var segments = this._segments; 6991 for (var i = 0, l = segments.length; i < l; i++) { 6992 if (segments[i].hasHandles()) 6993 return true; 6994 } 6995 return false; 6996 }, 6997 6998 _transformContent: function(matrix) { 6999 var coords = new Array(6); 7000 for (var i = 0, l = this._segments.length; i < l; i++) 7001 this._segments[i]._transformCoordinates(matrix, coords, true); 7002 return true; 7003 }, 7004 7005 _add: function(segs, index) { 7006 var segments = this._segments, 7007 curves = this._curves, 7008 amount = segs.length, 7009 append = index == null, 7010 index = append ? segments.length : index; 7011 for (var i = 0; i < amount; i++) { 7012 var segment = segs[i]; 7013 if (segment._path) 7014 segment = segs[i] = segment.clone(); 7015 segment._path = this; 7016 segment._index = index + i; 7017 if (segment._selectionState) 7018 this._updateSelection(segment, 0, segment._selectionState); 7019 } 7020 if (append) { 7021 segments.push.apply(segments, segs); 7022 } else { 7023 segments.splice.apply(segments, [index, 0].concat(segs)); 7024 for (var i = index + amount, l = segments.length; i < l; i++) 7025 segments[i]._index = i; 7026 } 7027 if (curves || segs._curves) { 7028 if (!curves) 7029 curves = this._curves = []; 7030 var from = index > 0 ? index - 1 : index, 7031 start = from, 7032 to = Math.min(from + amount, this._countCurves()); 7033 if (segs._curves) { 7034 curves.splice.apply(curves, [from, 0].concat(segs._curves)); 7035 start += segs._curves.length; 7036 } 7037 for (var i = start; i < to; i++) 7038 curves.splice(i, 0, new Curve(this, null, null)); 7039 this._adjustCurves(from, to); 7040 } 7041 this._changed(25); 7042 return segs; 7043 }, 7044 7045 _adjustCurves: function(from, to) { 7046 var segments = this._segments, 7047 curves = this._curves, 7048 curve; 7049 for (var i = from; i < to; i++) { 7050 curve = curves[i]; 7051 curve._path = this; 7052 curve._segment1 = segments[i]; 7053 curve._segment2 = segments[i + 1] || segments[0]; 7054 curve._changed(); 7055 } 7056 if (curve = curves[this._closed && from === 0 ? segments.length - 1 7057 : from - 1]) { 7058 curve._segment2 = segments[from] || segments[0]; 7059 curve._changed(); 7060 } 7061 if (curve = curves[to]) { 7062 curve._segment1 = segments[to]; 7063 curve._changed(); 7064 } 7065 }, 7066 7067 _countCurves: function() { 7068 var length = this._segments.length; 7069 return !this._closed && length > 0 ? length - 1 : length; 7070 }, 7071 7072 add: function(segment1 ) { 7073 return arguments.length > 1 && typeof segment1 !== 'number' 7074 ? this._add(Segment.readAll(arguments)) 7075 : this._add([ Segment.read(arguments) ])[0]; 7076 }, 7077 7078 insert: function(index, segment1 ) { 7079 return arguments.length > 2 && typeof segment1 !== 'number' 7080 ? this._add(Segment.readAll(arguments, 1), index) 7081 : this._add([ Segment.read(arguments, 1) ], index)[0]; 7082 }, 7083 7084 addSegment: function() { 7085 return this._add([ Segment.read(arguments) ])[0]; 7086 }, 7087 7088 insertSegment: function(index ) { 7089 return this._add([ Segment.read(arguments, 1) ], index)[0]; 7090 }, 7091 7092 addSegments: function(segments) { 7093 return this._add(Segment.readAll(segments)); 7094 }, 7095 7096 insertSegments: function(index, segments) { 7097 return this._add(Segment.readAll(segments), index); 7098 }, 7099 7100 removeSegment: function(index) { 7101 return this.removeSegments(index, index + 1)[0] || null; 7102 }, 7103 7104 removeSegments: function(from, to, _includeCurves) { 7105 from = from || 0; 7106 to = Base.pick(to, this._segments.length); 7107 var segments = this._segments, 7108 curves = this._curves, 7109 count = segments.length, 7110 removed = segments.splice(from, to - from), 7111 amount = removed.length; 7112 if (!amount) 7113 return removed; 7114 for (var i = 0; i < amount; i++) { 7115 var segment = removed[i]; 7116 if (segment._selectionState) 7117 this._updateSelection(segment, segment._selectionState, 0); 7118 segment._index = segment._path = null; 7119 } 7120 for (var i = from, l = segments.length; i < l; i++) 7121 segments[i]._index = i; 7122 if (curves) { 7123 var index = from > 0 && to === count + (this._closed ? 1 : 0) 7124 ? from - 1 7125 : from, 7126 curves = curves.splice(index, amount); 7127 if (_includeCurves) 7128 removed._curves = curves.slice(1); 7129 this._adjustCurves(index, index); 7130 } 7131 this._changed(25); 7132 return removed; 7133 }, 7134 7135 clear: '#removeSegments', 7136 7137 getLength: function() { 7138 if (this._length == null) { 7139 var curves = this.getCurves(); 7140 this._length = 0; 7141 for (var i = 0, l = curves.length; i < l; i++) 7142 this._length += curves[i].getLength(); 7143 } 7144 return this._length; 7145 }, 7146 7147 getArea: function() { 7148 var curves = this.getCurves(); 7149 var area = 0; 7150 for (var i = 0, l = curves.length; i < l; i++) 7151 area += curves[i].getArea(); 7152 return area; 7153 }, 7154 7155 isFullySelected: function() { 7156 var length = this._segments.length; 7157 return this._selected && length > 0 && this._selectedSegmentState 7158 === length * 7; 7159 }, 7160 7161 setFullySelected: function(selected) { 7162 if (selected) 7163 this._selectSegments(true); 7164 this.setSelected(selected); 7165 }, 7166 7167 setSelected: function setSelected(selected) { 7168 if (!selected) 7169 this._selectSegments(false); 7170 setSelected.base.call(this, selected); 7171 }, 7172 7173 _selectSegments: function(selected) { 7174 var length = this._segments.length; 7175 this._selectedSegmentState = selected 7176 ? length * 7 : 0; 7177 for (var i = 0; i < length; i++) 7178 this._segments[i]._selectionState = selected 7179 ? 7 : 0; 7180 }, 7181 7182 _updateSelection: function(segment, oldState, newState) { 7183 segment._selectionState = newState; 7184 var total = this._selectedSegmentState += newState - oldState; 7185 if (total > 0) 7186 this.setSelected(true); 7187 }, 7188 7189 flatten: function(maxDistance) { 7190 var iterator = new PathIterator(this, 64, 0.1), 7191 pos = 0, 7192 step = iterator.length / Math.ceil(iterator.length / maxDistance), 7193 end = iterator.length + (this._closed ? -step : step) / 2; 7194 var segments = []; 7195 while (pos <= end) { 7196 segments.push(new Segment(iterator.getPointAt(pos))); 7197 pos += step; 7198 } 7199 this.setSegments(segments); 7200 }, 7201 7202 reduce: function() { 7203 var curves = this.getCurves(); 7204 for (var i = curves.length - 1; i >= 0; i--) { 7205 var curve = curves[i]; 7206 if (curve.isLinear() && curve.getLength() === 0) 7207 curve.remove(); 7208 } 7209 return this; 7210 }, 7211 7212 simplify: function(tolerance) { 7213 if (this._segments.length > 2) { 7214 var fitter = new PathFitter(this, tolerance || 2.5); 7215 this.setSegments(fitter.fit()); 7216 } 7217 }, 7218 7219 split: function(index, parameter) { 7220 if (parameter === null) 7221 return null; 7222 if (arguments.length === 1) { 7223 var arg = index; 7224 if (typeof arg === 'number') 7225 arg = this.getLocationAt(arg); 7226 if (!arg) 7227 return null 7228 index = arg.index; 7229 parameter = arg.parameter; 7230 } 7231 var tolerance = 0.000001; 7232 if (parameter >= 1 - tolerance) { 7233 index++; 7234 parameter--; 7235 } 7236 var curves = this.getCurves(); 7237 if (index >= 0 && index < curves.length) { 7238 if (parameter > tolerance) { 7239 curves[index++].divide(parameter, true); 7240 } 7241 var segs = this.removeSegments(index, this._segments.length, true), 7242 path; 7243 if (this._closed) { 7244 this.setClosed(false); 7245 path = this; 7246 } else { 7247 path = this._clone(new Path().insertAbove(this, true)); 7248 } 7249 path._add(segs, 0); 7250 this.addSegment(segs[0]); 7251 return path; 7252 } 7253 return null; 7254 }, 7255 7256 isClockwise: function() { 7257 if (this._clockwise !== undefined) 7258 return this._clockwise; 7259 return Path.isClockwise(this._segments); 7260 }, 7261 7262 setClockwise: function(clockwise) { 7263 if (this.isClockwise() != (clockwise = !!clockwise)) 7264 this.reverse(); 7265 this._clockwise = clockwise; 7266 }, 7267 7268 reverse: function() { 7269 this._segments.reverse(); 7270 for (var i = 0, l = this._segments.length; i < l; i++) { 7271 var segment = this._segments[i]; 7272 var handleIn = segment._handleIn; 7273 segment._handleIn = segment._handleOut; 7274 segment._handleOut = handleIn; 7275 segment._index = i; 7276 } 7277 this._curves = null; 7278 if (this._clockwise !== undefined) 7279 this._clockwise = !this._clockwise; 7280 this._changed(9); 7281 }, 7282 7283 join: function(path) { 7284 if (path) { 7285 var segments = path._segments, 7286 last1 = this.getLastSegment(), 7287 last2 = path.getLastSegment(); 7288 if (!last2) 7289 return this; 7290 if (last1 && last1._point.equals(last2._point)) 7291 path.reverse(); 7292 var first2 = path.getFirstSegment(); 7293 if (last1 && last1._point.equals(first2._point)) { 7294 last1.setHandleOut(first2._handleOut); 7295 this._add(segments.slice(1)); 7296 } else { 7297 var first1 = this.getFirstSegment(); 7298 if (first1 && first1._point.equals(first2._point)) 7299 path.reverse(); 7300 last2 = path.getLastSegment(); 7301 if (first1 && first1._point.equals(last2._point)) { 7302 first1.setHandleIn(last2._handleIn); 7303 this._add(segments.slice(0, segments.length - 1), 0); 7304 } else { 7305 this._add(segments.slice()); 7306 } 7307 } 7308 if (path.closed) 7309 this._add([segments[0]]); 7310 path.remove(); 7311 } 7312 var first = this.getFirstSegment(), 7313 last = this.getLastSegment(); 7314 if (first !== last && first._point.equals(last._point)) { 7315 first.setHandleIn(last._handleIn); 7316 last.remove(); 7317 this.setClosed(true); 7318 } 7319 return this; 7320 }, 7321 7322 toShape: function(insert) { 7323 if (!this._closed) 7324 return null; 7325 7326 var segments = this._segments, 7327 type, 7328 size, 7329 radius, 7330 topCenter; 7331 7332 function isCollinear(i, j) { 7333 return segments[i].isCollinear(segments[j]); 7334 } 7335 7336 function isOrthogonal(i) { 7337 return segments[i].isOrthogonal(); 7338 } 7339 7340 function isArc(i) { 7341 return segments[i].isOrthogonalArc(); 7342 } 7343 7344 function getDistance(i, j) { 7345 return segments[i]._point.getDistance(segments[j]._point); 7346 } 7347 7348 if (!this.hasHandles() && segments.length === 4 7349 && isCollinear(0, 2) && isCollinear(1, 3) && isOrthogonal(1)) { 7350 type = Shape.Rectangle; 7351 size = new Size(getDistance(0, 3), getDistance(0, 1)); 7352 topCenter = segments[1]._point.add(segments[2]._point).divide(2); 7353 } else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4) 7354 && isArc(6) && isCollinear(1, 5) && isCollinear(3, 7)) { 7355 type = Shape.Rectangle; 7356 size = new Size(getDistance(1, 6), getDistance(0, 3)); 7357 radius = size.subtract(new Size(getDistance(0, 7), 7358 getDistance(1, 2))).divide(2); 7359 topCenter = segments[3]._point.add(segments[4]._point).divide(2); 7360 } else if (segments.length === 4 7361 && isArc(0) && isArc(1) && isArc(2) && isArc(3)) { 7362 if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) { 7363 type = Shape.Circle; 7364 radius = getDistance(0, 2) / 2; 7365 } else { 7366 type = Shape.Ellipse; 7367 radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2); 7368 } 7369 topCenter = segments[1]._point; 7370 } 7371 7372 if (type) { 7373 var center = this.getPosition(true), 7374 shape = this._clone(new type({ 7375 center: center, 7376 size: size, 7377 radius: radius, 7378 insert: false 7379 }), insert, false); 7380 shape.rotate(topCenter.subtract(center).getAngle() + 90); 7381 return shape; 7382 } 7383 return null; 7384 }, 7385 7386 _hitTestSelf: function(point, options) { 7387 var that = this,
7388 style = this.getStyle(), 7389 segments = this._segments, 7390 numSegments = segments.length, 7391 closed = this._closed, 7392 tolerancePadding = options._tolerancePadding, 7393 strokePadding = tolerancePadding, 7394 join, cap, miterLimit, 7395 area, loc, res, 7396 hitStroke = options.stroke && style.hasStroke(), 7397 hitFill = options.fill && style.hasFill(), 7398 hitCurves = options.curves, 7399 radius = hitStroke 7400 ? style.getStrokeWidth() / 2 7401 : hitFill && options.tolerance > 0 || hitCurves 7402 ? 0 : null; 7403 if (radius !== null) { 7404 if (radius > 0) { 7405 join = style.getStrokeJoin(); 7406 cap = style.getStrokeCap(); 7407 miterLimit = radius * style.getMiterLimit(); 7408 strokePadding = tolerancePadding.add(new Point(radius, radius)); 7409 } else { 7410 join = cap = 'round'; 7411 } 7412 } 7413 7414 function isCloseEnough(pt, padding) { 7415 return point.subtract(pt).divide(padding).length <= 1; 7416 } 7417 7418 function checkSegmentPoint(seg, pt, name) { 7419 if (!options.selected || pt.isSelected()) { 7420 var anchor = seg._point; 7421 if (pt !== anchor) 7422 pt = pt.add(anchor); 7423 if (isCloseEnough(pt, strokePadding)) { 7424 return new HitResult(name, that, { 7425 segment: seg, 7426 point: pt 7427 }); 7428 } 7429 } 7430 } 7431 7432 function checkSegmentPoints(seg, ends) { 7433 return (ends || options.segments) 7434 && checkSegmentPoint(seg, seg._point, 'segment') 7435 || (!ends && options.handles) && ( 7436 checkSegmentPoint(seg, seg._handleIn, 'handle-in') || 7437 checkSegmentPoint(seg, seg._handleOut, 'handle-out')); 7438 } 7439 7440 function addToArea(point) { 7441 area.add(point); 7442 } 7443 7444 function checkSegmentStroke(segment) { 7445 if (join !== 'round' || cap !== 'round') { 7446 area = new Path({ internal: true, closed: true }); 7447 if (closed || segment._index > 0 7448 && segment._index < numSegments - 1) { 7449 if (join !== 'round' && (segment._handleIn.isZero() 7450 || segment._handleOut.isZero())) 7451 Path._addBevelJoin(segment, join, radius, miterLimit, 7452 addToArea, true); 7453 } else if (cap !== 'round') { 7454 Path._addSquareCap(segment, cap, radius, addToArea, true); 7455 } 7456 if (!area.isEmpty()) { 7457 var loc; 7458 return area.contains(point) 7459 || (loc = area.getNearestLocation(point)) 7460 && isCloseEnough(loc.getPoint(), tolerancePadding); 7461 } 7462 } 7463 return isCloseEnough(segment._point, strokePadding); 7464 } 7465 7466 if (options.ends && !options.segments && !closed) { 7467 if (res = checkSegmentPoints(segments[0], true) 7468 || checkSegmentPoints(segments[numSegments - 1], true)) 7469 return res; 7470 } else if (options.segments || options.handles) { 7471 for (var i = 0; i < numSegments; i++) 7472 if (res = checkSegmentPoints(segments[i])) 7473 return res; 7474 } 7475 if (radius !== null) { 7476 loc = this.getNearestLocation(point); 7477 if (loc) { 7478 var parameter = loc.getParameter(); 7479 if (parameter === 0 || parameter === 1 && numSegments > 1) { 7480 if (!checkSegmentStroke(loc.getSegment())) 7481 loc = null; 7482 } else if (!isCloseEnough(loc.getPoint(), strokePadding)) { 7483 loc = null; 7484 } 7485 } 7486 if (!loc && join === 'miter' && numSegments > 1) { 7487 for (var i = 0; i < numSegments; i++) { 7488 var segment = segments[i]; 7489 if (point.getDistance(segment._point) <= miterLimit 7490 && checkSegmentStroke(segment)) { 7491 loc = segment.getLocation(); 7492 break; 7493 } 7494 } 7495 } 7496 } 7497 return !loc && hitFill && this._contains(point) 7498 || loc && !hitStroke && !hitCurves 7499 ? new HitResult('fill', this) 7500 : loc 7501 ? new HitResult(hitStroke ? 'stroke' : 'curve', this, { 7502 location: loc, 7503 point: loc.getPoint() 7504 }) 7505 : null; 7506 } 7507 7508}, Base.each(Curve.evaluateMethods, 7509 function(name) { 7510 this[name + 'At'] = function(offset, isParameter) { 7511 var loc = this.getLocationAt(offset, isParameter); 7512 return loc && loc[name](); 7513 }; 7514 }, 7515{ 7516 beans: false, 7517 7518 _getOffset: function(location) { 7519 var index = location && location.getIndex(); 7520 if (index != null) { 7521 var curves = this.getCurves(), 7522 offset = 0; 7523 for (var i = 0; i < index; i++) 7524 offset += curves[i].getLength(); 7525 var curve = curves[index], 7526 parameter = location.getParameter(); 7527 if (parameter > 0) 7528 offset += curve.getPartLength(0, parameter); 7529 return offset; 7530 } 7531 return null; 7532 }, 7533 7534 getLocationOf: function() { 7535 var point = Point.read(arguments), 7536 curves = this.getCurves(); 7537 for (var i = 0, l = curves.length; i < l; i++) { 7538 var loc = curves[i].getLocationOf(point); 7539 if (loc) 7540 return loc; 7541 } 7542 return null; 7543 }, 7544 7545 getOffsetOf: function() { 7546 var loc = this.getLocationOf.apply(this, arguments); 7547 return loc ? loc.getOffset() : null; 7548 }, 7549 7550 getLocationAt: function(offset, isParameter) { 7551 var curves = this.getCurves(), 7552 length = 0; 7553 if (isParameter) { 7554 var index = ~~offset, 7555 curve = curves[index]; 7556 return curve ? curve.getLocationAt(offset - index, true) : null; 7557 } 7558 for (var i = 0, l = curves.length; i < l; i++) { 7559 var start = length, 7560 curve = curves[i]; 7561 length += curve.getLength(); 7562 if (length > offset) { 7563 return curve.getLocationAt(offset - start); 7564 } 7565 } 7566 if (offset <= this.getLength())
7567 return new CurveLocation(curves[curves.length - 1], 1); 7568 return null; 7569 }, 7570 7571 getNearestLocation: function() { 7572 var point = Point.read(arguments), 7573 curves = this.getCurves(), 7574 minDist = Infinity, 7575 minLoc = null; 7576 for (var i = 0, l = curves.length; i < l; i++) { 7577 var loc = curves[i].getNearestLocation(point); 7578 if (loc._distance < minDist) { 7579 minDist = loc._distance; 7580 minLoc = loc; 7581 } 7582 } 7583 return minLoc; 7584 }, 7585 7586 getNearestPoint: function() { 7587 return this.getNearestLocation.apply(this, arguments).getPoint(); 7588 } 7589}), new function() { 7590 7591 function drawHandles(ctx, segments, matrix, size) { 7592 var half = size / 2; 7593 7594 function drawHandle(index) { 7595 var hX = coords[index], 7596 hY = coords[index + 1]; 7597 if (pX != hX || pY != hY) { 7598 ctx.beginPath(); 7599 ctx.moveTo(pX, pY); 7600 ctx.lineTo(hX, hY); 7601 ctx.stroke(); 7602 ctx.beginPath(); 7603 ctx.arc(hX, hY, half, 0, Math.PI * 2, true); 7604 ctx.fill(); 7605 } 7606 } 7607 7608 var coords = new Array(6); 7609 for (var i = 0, l = segments.length; i < l; i++) { 7610 var segment = segments[i]; 7611 segment._transformCoordinates(matrix, coords, false); 7612 var state = segment._selectionState, 7613 pX = coords[0], 7614 pY = coords[1]; 7615 if (state & 1) 7616 drawHandle(2); 7617 if (state & 2) 7618 drawHandle(4); 7619 ctx.fillRect(pX - half, pY - half, size, size); 7620 if (!(state & 4)) { 7621 var fillStyle = ctx.fillStyle; 7622 ctx.fillStyle = '#ffffff'; 7623 ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2); 7624 ctx.fillStyle = fillStyle; 7625 } 7626 } 7627 } 7628 7629 function drawSegments(ctx, path, matrix) { 7630 var segments = path._segments, 7631 length = segments.length, 7632 coords = new Array(6), 7633 first = true, 7634 curX, curY, 7635 prevX, prevY, 7636 inX, inY, 7637 outX, outY; 7638 7639 function drawSegment(segment) { 7640 if (matrix) { 7641 segment._transformCoordinates(matrix, coords, false); 7642 curX = coords[0]; 7643 curY = coords[1]; 7644 } else { 7645 var point = segment._point; 7646 curX = point._x; 7647 curY = point._y; 7648 } 7649 if (first) { 7650 ctx.moveTo(curX, curY); 7651 first = false; 7652 } else { 7653 if (matrix) { 7654 inX = coords[2]; 7655 inY = coords[3]; 7656 } else { 7657 var handle = segment._handleIn; 7658 inX = curX + handle._x; 7659 inY = curY + handle._y; 7660 } 7661 if (inX === curX && inY === curY 7662 && outX === prevX && outY === prevY) { 7663 ctx.lineTo(curX, curY); 7664 } else { 7665 ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY); 7666 } 7667 } 7668 prevX = curX; 7669 prevY = curY; 7670 if (matrix) { 7671 outX = coords[4]; 7672 outY = coords[5]; 7673 } else { 7674 var handle = segment._handleOut; 7675 outX = prevX + handle._x; 7676 outY = prevY + handle._y; 7677 } 7678 } 7679 7680 for (var i = 0; i < length; i++) 7681 drawSegment(segments[i]); 7682 if (path._closed && length > 0) 7683 drawSegment(segments[0]); 7684 } 7685 7686 return { 7687 _draw: function(ctx, param, strokeMatrix) { 7688 var dontStart = param.dontStart, 7689 dontPaint = param.dontFinish || param.clip, 7690 style = this.getStyle(), 7691 hasFill = style.hasFill(), 7692 hasStroke = style.hasStroke(), 7693 dashArray = style.getDashArray(), 7694 dashLength = !paper.support.nativeDash && hasStroke 7695 && dashArray && dashArray.length; 7696 7697 if (!dontStart) 7698 ctx.beginPath(); 7699 7700 if (!dontStart && this._currentPath) { 7701 ctx.currentPath = this._currentPath; 7702 } else if (hasFill || hasStroke && !dashLength || dontPaint) { 7703 drawSegments(ctx, this, strokeMatrix); 7704 if (this._closed) 7705 ctx.closePath(); 7706 if (!dontStart) 7707 this._currentPath = ctx.currentPath; 7708 } 7709 7710 function getOffset(i) { 7711 return dashArray[((i % dashLength) + dashLength) % dashLength]; 7712 } 7713 7714 if (!dontPaint && (hasFill || hasStroke)) { 7715 this._setStyles(ctx); 7716 if (hasFill) { 7717 ctx.fill(style.getWindingRule()); 7718 ctx.shadowColor = 'rgba(0,0,0,0)'; 7719 } 7720 if (hasStroke) { 7721 if (dashLength) { 7722 if (!dontStart) 7723 ctx.beginPath(); 7724 var iterator = new PathIterator(this, 32, 0.25, 7725 strokeMatrix), 7726 length = iterator.length, 7727 from = -style.getDashOffset(), to, 7728 i = 0; 7729 from = from % length; 7730 while (from > 0) { 7731 from -= getOffset(i--) + getOffset(i--); 7732 } 7733 while (from < length) { 7734 to = from + getOffset(i++); 7735 if (from > 0 || to > 0) 7736 iterator.drawPart(ctx, 7737 Math.max(from, 0), Math.max(to, 0)); 7738 from = to + getOffset(i++); 7739 } 7740 } 7741 ctx.stroke(); 7742 } 7743 } 7744 }, 7745 7746 _drawSelected: function(ctx, matrix) { 7747 ctx.beginPath(); 7748 drawSegments(ctx, this, matrix); 7749 ctx.stroke(); 7750 drawHandles(ctx, this._segments, matrix, paper.settings.handleSize); 7751 } 7752 }; 7753}, new function() { 7754 7755 function getFirstControlPoints(rhs) { 7756 var n = rhs.length, 7757 x = [], 7758 tmp = [], 7759 b = 2; 7760 x[0] = rhs[0] / b; 7761 for (var i = 1; i < n; i++) { 7762 tmp[i] = 1 / b; 7763 b = (i < n - 1 ? 4 : 2) - tmp[i]; 7764 x[i] = (rhs[i] - x[i - 1]) / b; 7765 } 7766 for (var i = 1; i < n; i++) { 7767 x[n - i - 1] -= tmp[n - i] * x[n - i]; 7768 } 7769 return x; 7770 } 7771 7772 return { 7773 smooth: function() { 7774 var segments = this._segments, 7775 size = segments.length, 7776 closed = this._closed, 7777 n = size, 7778 overlap = 0; 7779 if (size <= 2) 7780 return; 7781 if (closed) { 7782 overlap = Math.min(size, 4); 7783 n += Math.min(size, overlap) * 2; 7784 } 7785 var knots = []; 7786 for (var i = 0; i < size; i++) 7787 knots[i + overlap] = segments[i]._point; 7788 if (closed) { 7789 for (var i = 0; i < overlap; i++) { 7790 knots[i] = segments[i + size - overlap]._point; 7791 knots[i + size + overlap] = segments[i]._point; 7792 } 7793 } else { 7794 n--; 7795 } 7796 var rhs = []; 7797 7798 for (var i = 1; i < n - 1; i++) 7799 rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x; 7800 rhs[0] = knots[0]._x + 2 * knots[1]._x; 7801 rhs[n - 1] = 3 * knots[n - 1]._x; 7802 var x = getFirstControlPoints(rhs); 7803 7804 for (var i = 1; i < n - 1; i++) 7805 rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y; 7806 rhs[0] = knots[0]._y + 2 * knots[1]._y; 7807 rhs[n - 1] = 3 * knots[n - 1]._y; 7808 var y = getFirstControlPoints(rhs); 7809 7810 if (closed) { 7811 for (var i = 0, j = size; i < overlap; i++, j++) { 7812 var f1 = i / overlap, 7813 f2 = 1 - f1, 7814 ie = i + overlap, 7815 je = j + overlap; 7816 x[j] = x[i] * f1 + x[j] * f2; 7817 y[j] = y[i] * f1 + y[j] * f2; 7818 x[je] = x[ie] * f2 + x[je] * f1; 7819 y[je] = y[ie] * f2 + y[je] * f1; 7820 } 7821 n--; 7822 } 7823 var handleIn = null; 7824 for (var i = overlap; i <= n - overlap; i++) { 7825 var segment = segments[i - overlap]; 7826 if (handleIn) 7827 segment.setHandleIn(handleIn.subtract(segment._point)); 7828 if (i < n) { 7829 segment.setHandleOut( 7830 new Point(x[i], y[i]).subtract(segment._point)); 7831 handleIn = i < n - 1 7832 ? new Point( 7833 2 * knots[i + 1]._x - x[i + 1], 7834 2 * knots[i + 1]._y - y[i + 1]) 7835 : new Point( 7836 (knots[n]._x + x[n - 1]) / 2, 7837 (knots[n]._y + y[n - 1]) / 2); 7838 } 7839 } 7840 if (closed && handleIn) { 7841 var segment = this._segments[0]; 7842 segment.setHandleIn(handleIn.subtract(segment._point)); 7843 } 7844 } 7845 }; 7846}, new function() { 7847 function getCurrentSegment(that) { 7848 var segments = that._segments; 7849 if (segments.length === 0) 7850 throw new Error('Use a moveTo() command first'); 7851 return segments[segments.length - 1]; 7852 } 7853 7854 return { 7855 moveTo: function() { 7856 var segments = this._segments; 7857 if (segments.length === 1) 7858 this.removeSegment(0); 7859 if (!segments.length) 7860 this._add([ new Segment(Point.read(arguments)) ]); 7861 }, 7862 7863 moveBy: function() { 7864 throw new Error('moveBy() is unsupported on Path items.'); 7865 }, 7866 7867 lineTo: function() { 7868 this._add([ new Segment(Point.read(arguments)) ]); 7869 }, 7870 7871 cubicCurveTo: function() { 7872 var handle1 = Point.read(arguments), 7873 handle2 = Point.read(arguments), 7874 to = Point.read(arguments), 7875 current = getCurrentSegment(this); 7876 current.setHandleOut(handle1.subtract(current._point)); 7877 this._add([ new Segment(to, handle2.subtract(to)) ]); 7878 }, 7879 7880 quadraticCurveTo: function() { 7881 var handle = Point.read(arguments), 7882 to = Point.read(arguments), 7883 current = getCurrentSegment(this)._point; 7884 this.cubicCurveTo( 7885 handle.add(current.subtract(handle).multiply(1 / 3)), 7886 handle.add(to.subtract(handle).multiply(1 / 3)), 7887 to 7888 ); 7889 }, 7890 7891 curveTo: function() { 7892 var through = Point.read(arguments), 7893 to = Point.read(arguments), 7894 t = Base.pick(Base.read(arguments), 0.5), 7895 t1 = 1 - t, 7896 current = getCurrentSegment(this)._point, 7897 handle = through.subtract(current.multiply(t1 * t1)) 7898 .subtract(to.multiply(t * t)).divide(2 * t * t1); 7899 if (handle.isNaN()) 7900 throw new Error( 7901 'Cannot put a curve through points with parameter = ' + t);
vendor: 4,933 bytes, lines 7902-8071
7902 this.quadraticCurveTo(handle, to); 7903 }, 7904 7905 arcTo: function() { 7906 var current = getCurrentSegment(this), 7907 from = current._point, 7908 to = Point.read(arguments), 7909 through, 7910 peek = Base.peek(arguments), 7911 clockwise = Base.pick(peek, true), 7912 center, extent, vector, matrix; 7913 if (typeof clockwise === 'boolean') { 7914 var middle = from.add(to).divide(2), 7915 through = middle.add(middle.subtract(from).rotate( 7916 clockwise ? -90 : 90)); 7917 } else if (Base.remain(arguments) <= 2) { 7918 through = to; 7919 to = Point.read(arguments); 7920 } else { 7921 var radius = Size.read(arguments); 7922 if (radius.isZero()) 7923 return this.lineTo(to); 7924 var rotation = Base.read(arguments), 7925 clockwise = !!Base.read(arguments), 7926 large = !!Base.read(arguments), 7927 middle = from.add(to).divide(2), 7928 pt = from.subtract(middle).rotate(-rotation), 7929 x = pt.x, 7930 y = pt.y, 7931 abs = Math.abs, 7932 epsilon = 1e-12, 7933 rx = abs(radius.width), 7934 ry = abs(radius.height), 7935 rxSq = rx * rx, 7936 rySq = ry * ry, 7937 xSq = x * x, 7938 ySq = y * y; 7939 var factor = Math.sqrt(xSq / rxSq + ySq / rySq); 7940 if (factor > 1) { 7941 rx *= factor; 7942 ry *= factor; 7943 rxSq = rx * rx; 7944 rySq = ry * ry; 7945 } 7946 factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) / 7947 (rxSq * ySq + rySq * xSq); 7948 if (abs(factor) < epsilon) 7949 factor = 0; 7950 if (factor < 0) 7951 throw new Error( 7952 'Cannot create an arc with the given arguments'); 7953 center = new Point(rx * y / ry, -ry * x / rx) 7954 .multiply((large === clockwise ? -1 : 1) 7955 * Math.sqrt(factor)) 7956 .rotate(rotation).add(middle); 7957 matrix = new Matrix().translate(center).rotate(rotation) 7958 .scale(rx, ry); 7959 vector = matrix._inverseTransform(from); 7960 extent = vector.getDirectedAngle(matrix._inverseTransform(to)); 7961 if (!clockwise && extent > 0) 7962 extent -= 360; 7963 else if (clockwise && extent < 0) 7964 extent += 360; 7965 } 7966 if (through) { 7967 var l1 = new Line(from.add(through).divide(2), 7968 through.subtract(from).rotate(90), true), 7969 l2 = new Line(through.add(to).divide(2), 7970 to.subtract(through).rotate(90), true), 7971 line = new Line(from, to), 7972 throughSide = line.getSide(through); 7973 center = l1.intersect(l2, true); 7974 if (!center) { 7975 if (!throughSide) 7976 return this.lineTo(to); 7977 throw new Error( 7978 'Cannot create an arc with the given arguments'); 7979 } 7980 vector = from.subtract(center); 7981 extent = vector.getDirectedAngle(to.subtract(center)); 7982 var centerSide = line.getSide(center); 7983 if (centerSide === 0) { 7984 extent = throughSide * Math.abs(extent); 7985 } else if (throughSide === centerSide) { 7986 extent += extent < 0 ? 360 : -360; 7987 } 7988 } 7989 var ext = Math.abs(extent), 7990 count = ext >= 360 ? 4 : Math.ceil(ext / 90), 7991 inc = extent / count, 7992 half = inc * Math.PI / 360, 7993 z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)), 7994 segments = []; 7995 for (var i = 0; i <= count; i++) { 7996 var pt = to, 7997 out = null; 7998 if (i < count) { 7999 out = vector.rotate(90).multiply(z); 8000 if (matrix) { 8001 pt = matrix._transformPoint(vector); 8002 out = matrix._transformPoint(vector.add(out)) 8003 .subtract(pt); 8004 } else { 8005 pt = center.add(vector); 8006 } 8007 } 8008 if (i === 0) { 8009 current.setHandleOut(out); 8010 } else { 8011 var _in = vector.rotate(-90).multiply(z); 8012 if (matrix) { 8013 _in = matrix._transformPoint(vector.add(_in)) 8014 .subtract(pt); 8015 } 8016 segments.push(new Segment(pt, _in, out)); 8017 } 8018 vector = vector.rotate(inc); 8019 } 8020 this._add(segments); 8021 }, 8022 8023 lineBy: function() { 8024 var to = Point.read(arguments), 8025 current = getCurrentSegment(this)._point; 8026 this.lineTo(current.add(to)); 8027 }, 8028 8029 curveBy: function() { 8030 var through = Point.read(arguments), 8031 to = Point.read(arguments), 8032 parameter = Base.read(arguments), 8033 current = getCurrentSegment(this)._point; 8034 this.curveTo(current.add(through), current.add(to), parameter); 8035 }, 8036 8037 cubicCurveBy: function() { 8038 var handle1 = Point.read(arguments), 8039 handle2 = Point.read(arguments), 8040 to = Point.read(arguments), 8041 current = getCurrentSegment(this)._point; 8042 this.cubicCurveTo(current.add(handle1), current.add(handle2), 8043 current.add(to)); 8044 }, 8045 8046 quadraticCurveBy: function() { 8047 var handle = Point.read(arguments), 8048 to = Point.read(arguments), 8049 current = getCurrentSegment(this)._point; 8050 this.quadraticCurveTo(current.add(handle), current.add(to)); 8051 }, 8052 8053 arcBy: function() { 8054 var current = getCurrentSegment(this)._point, 8055 point = current.add(Point.read(arguments)), 8056 clockwise = Base.pick(Base.peek(arguments), true); 8057 if (typeof clockwise === 'boolean') { 8058 this.arcTo(point, clockwise); 8059 } else { 8060 this.arcTo(point, current.add(Point.read(arguments))); 8061 } 8062 }, 8063 8064 closePath: function(join) { 8065 this.setClosed(true); 8066 if (join) 8067 this.join(); 8068 } 8069 }; 8070}, { 8071
8072 _getBounds: function(getter, matrix) { 8073 return Path[getter](this._segments, this._closed, this.getStyle(), 8074 matrix); 8075 }, 8076 8077statics: { 8078 isClockwise: function(segments) { 8079 var sum = 0; 8080 for (var i = 0, l = segments.length; i < l; i++) 8081 sum += Curve.getEdgeSum(Curve.getValues( 8082 segments[i], segments[i + 1 < l ? i + 1 : 0])); 8083 return sum > 0; 8084 }, 8085 8086 getBounds: function(segments, closed, style, matrix, strokePadding) { 8087 var first = segments[0]; 8088 if (!first) 8089 return new Rectangle(); 8090 var coords = new Array(6), 8091 prevCoords = first._transformCoordinates(matrix, new Array(6), false), 8092 min = prevCoords.slice(0, 2), 8093 max = min.slice(), 8094 roots = new Array(2); 8095 8096 function processSegment(segment) { 8097 segment._transformCoordinates(matrix, coords, false); 8098 for (var i = 0; i < 2; i++) { 8099 Curve._addBounds( 8100 prevCoords[i], 8101 prevCoords[i + 4], 8102 coords[i + 2], 8103 coords[i], 8104 i, strokePadding ? strokePadding[i] : 0, min, max, roots); 8105 } 8106 var tmp = prevCoords; 8107 prevCoords = coords; 8108 coords = tmp; 8109 } 8110 8111 for (var i = 1, l = segments.length; i < l; i++) 8112 processSegment(segments[i]); 8113 if (closed) 8114 processSegment(first); 8115 return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]); 8116 }, 8117 8118 getStrokeBounds: function(segments, closed, style, matrix) { 8119 if (!style.hasStroke()) 8120 return Path.getBounds(segments, closed, style, matrix); 8121 var length = segments.length - (closed ? 0 : 1), 8122 radius = style.getStrokeWidth() / 2, 8123 padding = Path._getPenPadding(radius, matrix), 8124 bounds = Path.getBounds(segments, closed, style, matrix, padding), 8125 join = style.getStrokeJoin(), 8126 cap = style.getStrokeCap(), 8127 miterLimit = radius * style.getMiterLimit(); 8128 var joinBounds = new Rectangle(new Size(padding).multiply(2)); 8129 8130 function add(point) { 8131 bounds = bounds.include(matrix 8132 ? matrix._transformPoint(point, point) : point); 8133 } 8134 8135 function addRound(segment) { 8136 bounds = bounds.unite(joinBounds.setCenter(matrix 8137 ? matrix._transformPoint(segment._point) : segment._point)); 8138 } 8139 8140 function addJoin(segment, join) { 8141 var handleIn = segment._handleIn, 8142 handleOut = segment._handleOut; 8143 if (join === 'round' || !handleIn.isZero() && !handleOut.isZero() 8144 && handleIn.isCollinear(handleOut)) { 8145 addRound(segment); 8146 } else { 8147 Path._addBevelJoin(segment, join, radius, miterLimit, add); 8148 } 8149 } 8150 8151 function addCap(segment, cap) { 8152 if (cap === 'round') { 8153 addRound(segment); 8154 } else { 8155 Path._addSquareCap(segment, cap, radius, add); 8156 } 8157 } 8158 8159 for (var i = 1; i < length; i++) 8160 addJoin(segments[i], join); 8161 if (closed) { 8162 addJoin(segments[0], join); 8163 } else if (length > 0) { 8164 addCap(segments[0], cap); 8165 addCap(segments[segments.length - 1], cap); 8166 } 8167 return bounds; 8168 }, 8169 8170 _getPenPadding: function(radius, matrix) { 8171 if (!matrix) 8172 return [radius, radius]; 8173 var mx = matrix.shiftless(), 8174 hor = mx.transform(new Point(radius, 0)), 8175 ver = mx.transform(new Point(0, radius)), 8176 phi = hor.getAngleInRadians(), 8177 a = hor.getLength(), 8178 b = ver.getLength(); 8179 var sin = Math.sin(phi), 8180 cos = Math.cos(phi), 8181 tan = Math.tan(phi), 8182 tx = -Math.atan(b * tan / a), 8183 ty = Math.atan(b / (tan * a)); 8184 return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin), 8185 Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)]; 8186 }, 8187 8188 _addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) { 8189 var curve2 = segment.getCurve(), 8190 curve1 = curve2.getPrevious(), 8191 point = curve2.getPointAt(0, true), 8192 normal1 = curve1.getNormalAt(1, true), 8193 normal2 = curve2.getNormalAt(0, true), 8194 step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius; 8195 normal1.setLength(step); 8196 normal2.setLength(step); 8197 if (area) { 8198 addPoint(point); 8199 addPoint(point.add(normal1)); 8200 } 8201 if (join === 'miter') { 8202 var corner = new Line( 8203 point.add(normal1), 8204 new Point(-normal1.y, normal1.x), true 8205 ).intersect(new Line( 8206 point.add(normal2), 8207 new Point(-normal2.y, normal2.x), true 8208 ), true); 8209 if (corner && point.getDistance(corner) <= miterLimit) { 8210 addPoint(corner); 8211 if (!area) 8212 return; 8213 } 8214 } 8215 if (!area) 8216 addPoint(point.add(normal1)); 8217 addPoint(point.add(normal2)); 8218 }, 8219 8220 _addSquareCap: function(segment, cap, radius, addPoint, area) { 8221 var point = segment._point, 8222 loc = segment.getLocation(), 8223 normal = loc.getNormal().multiply(radius); 8224 if (area) { 8225 addPoint(point.subtract(normal)); 8226 addPoint(point.add(normal)); 8227 } 8228 if (cap === 'square') 8229 point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90)); 8230 addPoint(point.add(normal)); 8231 addPoint(point.subtract(normal)); 8232 }, 8233 8234 getHandleBounds: function(segments, closed, style, matrix, strokePadding, 8235 joinPadding) { 8236 var coords = new Array(6), 8237 x1 = Infinity, 8238 x2 = -x1, 8239 y1 = x1, 8240 y2 = x2; 8241 for (var i = 0, l = segments.length; i < l; i++) { 8242 var segment = segments[i]; 8243 segment._transformCoordinates(matrix, coords, false); 8244 for (var j = 0; j < 6; j += 2) { 8245 var padding = j === 0 ? joinPadding : strokePadding, 8246 paddingX = padding ? padding[0] : 0, 8247 paddingY = padding ? padding[1] : 0, 8248 x = coords[j], 8249 y = coords[j + 1], 8250 xn = x - paddingX, 8251 xx = x + paddingX, 8252 yn = y - paddingY, 8253 yx = y + paddingY; 8254 if (xn < x1) x1 = xn; 8255 if (xx > x2) x2 = xx;
vendor: 4,292 bytes, lines 8256-8397
8256 if (yn < y1) y1 = yn; 8257 if (yx > y2) y2 = yx; 8258 } 8259 } 8260 return new Rectangle(x1, y1, x2 - x1, y2 - y1); 8261 }, 8262 8263 getRoughBounds: function(segments, closed, style, matrix) { 8264 var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0, 8265 joinRadius = strokeRadius; 8266 if (strokeRadius > 0) { 8267 if (style.getStrokeJoin() === 'miter') 8268 joinRadius = strokeRadius * style.getMiterLimit(); 8269 if (style.getStrokeCap() === 'square') 8270 joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2)); 8271 } 8272 return Path.getHandleBounds(segments, closed, style, matrix, 8273 Path._getPenPadding(strokeRadius, matrix), 8274 Path._getPenPadding(joinRadius, matrix)); 8275 } 8276}}); 8277 8278Path.inject({ statics: new function() { 8279 8280 var kappa = 0.5522847498307936, 8281 ellipseSegments = [ 8282 new Segment([-1, 0], [0, kappa ], [0, -kappa]), 8283 new Segment([0, -1], [-kappa, 0], [kappa, 0 ]), 8284 new Segment([1, 0], [0, -kappa], [0, kappa ]), 8285 new Segment([0, 1], [kappa, 0 ], [-kappa, 0]) 8286 ]; 8287 8288 function createPath(segments, closed, args) { 8289 var props = Base.getNamed(args), 8290 path = new Path(props && props.insert === false && Item.NO_INSERT); 8291 path._add(segments); 8292 path._closed = closed; 8293 return path.set(props); 8294 } 8295 8296 function createEllipse(center, radius, args) { 8297 var segments = new Array(4); 8298 for (var i = 0; i < 4; i++) { 8299 var segment = ellipseSegments[i]; 8300 segments[i] = new Segment( 8301 segment._point.multiply(radius).add(center), 8302 segment._handleIn.multiply(radius), 8303 segment._handleOut.multiply(radius) 8304 ); 8305 } 8306 return createPath(segments, true, args); 8307 } 8308 8309 return { 8310 Line: function() { 8311 return createPath([ 8312 new Segment(Point.readNamed(arguments, 'from')), 8313 new Segment(Point.readNamed(arguments, 'to')) 8314 ], false, arguments); 8315 }, 8316 8317 Circle: function() { 8318 var center = Point.readNamed(arguments, 'center'), 8319 radius = Base.readNamed(arguments, 'radius'); 8320 return createEllipse(center, new Size(radius), arguments); 8321 }, 8322 8323 Rectangle: function() { 8324 var rect = Rectangle.readNamed(arguments, 'rectangle'), 8325 radius = Size.readNamed(arguments, 'radius', 0, 8326 { readNull: true }), 8327 bl = rect.getBottomLeft(true), 8328 tl = rect.getTopLeft(true), 8329 tr = rect.getTopRight(true), 8330 br = rect.getBottomRight(true), 8331 segments; 8332 if (!radius || radius.isZero()) { 8333 segments = [ 8334 new Segment(bl), 8335 new Segment(tl), 8336 new Segment(tr), 8337 new Segment(br) 8338 ]; 8339 } else { 8340 radius = Size.min(radius, rect.getSize(true).divide(2)); 8341 var rx = radius.width, 8342 ry = radius.height, 8343 hx = rx * kappa, 8344 hy = ry * kappa; 8345 segments = [ 8346 new Segment(bl.add(rx, 0), null, [-hx, 0]), 8347 new Segment(bl.subtract(0, ry), [0, hy]), 8348 new Segment(tl.add(0, ry), null, [0, -hy]), 8349 new Segment(tl.add(rx, 0), [-hx, 0], null), 8350 new Segment(tr.subtract(rx, 0), null, [hx, 0]), 8351 new Segment(tr.add(0, ry), [0, -hy], null), 8352 new Segment(br.subtract(0, ry), null, [0, hy]), 8353 new Segment(br.subtract(rx, 0), [hx, 0]) 8354 ]; 8355 } 8356 return createPath(segments, true, arguments); 8357 }, 8358 8359 RoundRectangle: '#Rectangle', 8360 8361 Ellipse: function() { 8362 var ellipse = Shape._readEllipse(arguments); 8363 return createEllipse(ellipse.center, ellipse.radius, arguments); 8364 }, 8365 8366 Oval: '#Ellipse', 8367 8368 Arc: function() { 8369 var from = Point.readNamed(arguments, 'from'), 8370 through = Point.readNamed(arguments, 'through'), 8371 to = Point.readNamed(arguments, 'to'), 8372 props = Base.getNamed(arguments), 8373 path = new Path(props && props.insert === false 8374 && Item.NO_INSERT); 8375 path.moveTo(from); 8376 path.arcTo(through, to); 8377 return path.set(props); 8378 }, 8379 8380 RegularPolygon: function() { 8381 var center = Point.readNamed(arguments, 'center'), 8382 sides = Base.readNamed(arguments, 'sides'), 8383 radius = Base.readNamed(arguments, 'radius'), 8384 step = 360 / sides, 8385 three = !(sides % 3), 8386 vector = new Point(0, three ? -radius : radius), 8387 offset = three ? -1 : 0.5, 8388 segments = new Array(sides); 8389 for (var i = 0; i < sides; i++) 8390 segments[i] = new Segment(center.add( 8391 vector.rotate((i + offset) * step))); 8392 return createPath(segments, true, arguments); 8393 }, 8394 8395 Star: function() { 8396 var center = Point.readNamed(arguments, 'center'), 8397 points = Base.readNamed(arguments, 'points') * 2,
8398 radius1 = Base.readNamed(arguments, 'radius1'), 8399 radius2 = Base.readNamed(arguments, 'radius2'), 8400 step = 360 / points, 8401 vector = new Point(0, -1), 8402 segments = new Array(points); 8403 for (var i = 0; i < points; i++) 8404 segments[i] = new Segment(center.add(vector.rotate(step * i) 8405 .multiply(i % 2 ? radius2 : radius1))); 8406 return createPath(segments, true, arguments); 8407 } 8408 }; 8409}}); 8410 8411var CompoundPath = PathItem.extend({ 8412 _class: 'CompoundPath', 8413 _serializeFields: { 8414 children: [] 8415 }, 8416 8417 initialize: function CompoundPath(arg) { 8418 this._children = []; 8419 this._namedChildren = {}; 8420 if (!this._initialize(arg)) { 8421 if (typeof arg === 'string') { 8422 this.setPathData(arg); 8423 } else { 8424 this.addChildren(Array.isArray(arg) ? arg : arguments); 8425 } 8426 } 8427 }, 8428 8429 insertChildren: function insertChildren(index, items, _preserve) { 8430 items = insertChildren.base.call(this, index, items, _preserve, Path); 8431 for (var i = 0, l = !_preserve && items && items.length; i < l; i++) { 8432 var item = items[i]; 8433 if (item._clockwise === undefined) 8434 item.setClockwise(item._index === 0); 8435 } 8436 return items; 8437 }, 8438 8439 reverse: function() { 8440 var children = this._children; 8441 for (var i = 0, l = children.length; i < l; i++) 8442 children[i].reverse(); 8443 }, 8444 8445 smooth: function() { 8446 for (var i = 0, l = this._children.length; i < l; i++) 8447 this._children[i].smooth(); 8448 }, 8449 8450 reduce: function reduce() { 8451 if (this._children.length === 0) { 8452 var path = new Path(Item.NO_INSERT); 8453 path.insertAbove(this); 8454 path.setStyle(this._style); 8455 this.remove(); 8456 return path; 8457 } else { 8458 return reduce.base.call(this); 8459 } 8460 }, 8461 8462 isClockwise: function() { 8463 var child = this.getFirstChild(); 8464 return child && child.isClockwise(); 8465 }, 8466 8467 setClockwise: function(clockwise) { 8468 if (this.isClockwise() !== !!clockwise) 8469 this.reverse(); 8470 }, 8471 8472 getFirstSegment: function() { 8473 var first = this.getFirstChild(); 8474 return first && first.getFirstSegment(); 8475 }, 8476 8477 getLastSegment: function() { 8478 var last = this.getLastChild(); 8479 return last && last.getLastSegment(); 8480 }, 8481 8482 getCurves: function() { 8483 var children = this._children, 8484 curves = []; 8485 for (var i = 0, l = children.length; i < l; i++) 8486 curves.push.apply(curves, children[i].getCurves()); 8487 return curves; 8488 }, 8489 8490 getFirstCurve: function() { 8491 var first = this.getFirstChild(); 8492 return first && first.getFirstCurve(); 8493 }, 8494 8495 getLastCurve: function() { 8496 var last = this.getLastChild(); 8497 return last && last.getFirstCurve(); 8498 }, 8499 8500 getArea: function() { 8501 var children = this._children, 8502 area = 0; 8503 for (var i = 0, l = children.length; i < l; i++) 8504 area += children[i].getArea(); 8505 return area; 8506 } 8507}, { 8508 beans: true, 8509 8510 getPathData: function(_matrix, _precision) { 8511 var children = this._children, 8512 paths = []; 8513 for (var i = 0, l = children.length; i < l; i++) { 8514 var child = children[i], 8515 mx = child._matrix; 8516 paths.push(child.getPathData(_matrix && !mx.isIdentity() 8517 ? _matrix.chain(mx) : mx, _precision)); 8518 } 8519 return paths.join(' '); 8520 } 8521}, { 8522 _getChildHitTestOptions: function(options) { 8523 return options.class === Path || options.type === 'path' 8524 ? options 8525 : new Base(options, { fill: false }); 8526 }, 8527 8528 _draw: function(ctx, param, strokeMatrix) { 8529 var children = this._children; 8530 if (children.length === 0) 8531 return; 8532 8533 if (this._currentPath) { 8534 ctx.currentPath = this._currentPath; 8535 } else { 8536 param = param.extend({ dontStart: true, dontFinish: true }); 8537 ctx.beginPath(); 8538 for (var i = 0, l = children.length; i < l; i++) 8539 children[i].draw(ctx, param, strokeMatrix); 8540 this._currentPath = ctx.currentPath; 8541 } 8542 8543 if (!param.clip) { 8544 this._setStyles(ctx); 8545 var style = this._style; 8546 if (style.hasFill()) { 8547 ctx.fill(style.getWindingRule()); 8548 ctx.shadowColor = 'rgba(0,0,0,0)'; 8549 } 8550 if (style.hasStroke()) 8551 ctx.stroke(); 8552 } 8553 }, 8554 8555 _drawSelected: function(ctx, matrix, selectedItems) { 8556 var children = this._children; 8557 for (var i = 0, l = children.length; i < l; i++) { 8558 var child = children[i], 8559 mx = child._matrix; 8560 if (!selectedItems[child._id]) 8561 child._drawSelected(ctx, mx.isIdentity() ? matrix 8562 : matrix.chain(mx)); 8563 } 8564 } 8565}, new function() { 8566 function getCurrentPath(that, check) { 8567 var children = that._children; 8568 if (check && children.length === 0) 8569 throw new Error('Use a moveTo() command first'); 8570 return children[children.length - 1]; 8571 } 8572 8573 var fields = { 8574 moveTo: function() { 8575 var current = getCurrentPath(this), 8576 path = current && current.isEmpty() ? current : new Path(); 8577 if (path !== current) 8578 this.addChild(path); 8579 path.moveTo.apply(path, arguments); 8580 }, 8581 8582 moveBy: function() { 8583 var current = getCurrentPath(this, true), 8584 last = current && current.getLastSegment(), 8585 point = Point.read(arguments); 8586 this.moveTo(last ? point.add(last._point) : point); 8587 }, 8588 8589 closePath: function(join) { 8590 getCurrentPath(this, true).closePath(join); 8591 } 8592 }; 8593 8594 Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo', 8595 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'], 8596 function(key) { 8597 fields[key] = function() { 8598 var path = getCurrentPath(this, true);
8599 path[key].apply(path, arguments); 8600 }; 8601 } 8602 ); 8603 8604 return fields; 8605}); 8606 8607PathItem.inject(new function() { 8608 var operators = { 8609 unite: function(w) { 8610 return w === 1 || w === 0; 8611 }, 8612 8613 intersect: function(w) { 8614 return w === 2; 8615 }, 8616 8617 subtract: function(w) { 8618 return w === 1; 8619 }, 8620 8621 exclude: function(w) { 8622 return w === 1; 8623 } 8624 }; 8625 8626 function computeBoolean(path1, path2, operation) { 8627 var operator = operators[operation]; 8628 function preparePath(path) { 8629 return path.clone(false).reduce().reorient().transform(null, true, 8630 true); 8631 } 8632 8633 var _path1 = preparePath(path1), 8634 _path2 = path2 && path1 !== path2 && preparePath(path2); 8635 if (_path2 && /^(subtract|exclude)$/.test(operation) 8636 ^ (_path2.isClockwise() !== _path1.isClockwise())) 8637 _path2.reverse(); 8638 splitPath(_path1.getIntersections(_path2, null, true)); 8639 8640 var chain = [], 8641 segments = [], 8642 monoCurves = [], 8643 tolerance = 0.000001; 8644 8645 function collect(paths) { 8646 for (var i = 0, l = paths.length; i < l; i++) { 8647 var path = paths[i]; 8648 segments.push.apply(segments, path._segments); 8649 monoCurves.push.apply(monoCurves, path._getMonoCurves()); 8650 } 8651 } 8652 8653 collect(_path1._children || [_path1]); 8654 if (_path2) 8655 collect(_path2._children || [_path2]); 8656 segments.sort(function(a, b) { 8657 var _a = a._intersection, 8658 _b = b._intersection; 8659 return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1; 8660 }); 8661 for (var i = 0, l = segments.length; i < l; i++) { 8662 var segment = segments[i]; 8663 if (segment._winding != null) 8664 continue; 8665 chain.length = 0; 8666 var startSeg = segment, 8667 totalLength = 0, 8668 windingSum = 0; 8669 do { 8670 var length = segment.getCurve().getLength(); 8671 chain.push({ segment: segment, length: length }); 8672 totalLength += length; 8673 segment = segment.getNext(); 8674 } while (segment && !segment._intersection && segment !== startSeg); 8675 for (var j = 0; j < 3; j++) { 8676 var length = totalLength * (j + 1) / 4; 8677 for (var k = 0, m = chain.length; k < m; k++) { 8678 var node = chain[k], 8679 curveLength = node.length; 8680 if (length <= curveLength) { 8681 if (length < tolerance 8682 || curveLength - length < tolerance) 8683 length = curveLength / 2; 8684 var curve = node.segment.getCurve(), 8685 pt = curve.getPointAt(length), 8686 hor = curve.isLinear() && Math.abs(curve 8687 .getTangentAt(0.5, true).y) < tolerance, 8688 path = curve._path; 8689 if (path._parent instanceof CompoundPath) 8690 path = path._parent; 8691 windingSum += operation === 'subtract' && _path2 8692 && (path === _path1 && _path2._getWinding(pt, hor) 8693 || path === _path2 && !_path1._getWinding(pt, hor)) 8694 ? 0 8695 : getWinding(pt, monoCurves, hor); 8696 break; 8697 } 8698 length -= curveLength; 8699 } 8700 } 8701 var winding = Math.round(windingSum / 3); 8702 for (var j = chain.length - 1; j >= 0; j--) 8703 chain[j].segment._winding = winding; 8704 } 8705 var result = new CompoundPath(Item.NO_INSERT); 8706 result.insertAbove(path1); 8707 result.addChildren(tracePaths(segments, operator), true); 8708 result = result.reduce(); 8709 result.setStyle(path1._style); 8710 return result; 8711 } 8712 8713 function splitPath(intersections) { 8714 var tMin = 0.000001, 8715 tMax = 1 - tMin, 8716 linearHandles; 8717 8718 function resetLinear() { 8719 for (var i = 0, l = linearHandles.length; i < l; i++) 8720 linearHandles[i].set(0, 0); 8721 } 8722 8723 for (var i = intersections.length - 1, curve, prev; i >= 0; i--) { 8724 var loc = intersections[i], 8725 t = loc._parameter; 8726 if (prev && prev._curve === loc._curve && prev._parameter > 0) { 8727 t /= prev._parameter; 8728 } else { 8729 curve = loc._curve; 8730 if (linearHandles) 8731 resetLinear(); 8732 linearHandles = curve.isLinear() ? [ 8733 curve._segment1._handleOut, 8734 curve._segment2._handleIn 8735 ] : null; 8736 } 8737 var newCurve, 8738 segment; 8739 if (newCurve = curve.divide(t, true, true)) { 8740 segment = newCurve._segment1; 8741 curve = newCurve.getPrevious(); 8742 if (linearHandles) 8743 linearHandles.push(segment._handleOut, segment._handleIn); 8744 } else { 8745 segment = t < tMin 8746 ? curve._segment1 8747 : t > tMax 8748 ? curve._segment2 8749 : curve.getPartLength(0, t) < curve.getPartLength(t, 1) 8750 ? curve._segment1 8751 : curve._segment2; 8752 } 8753 segment._intersection = loc.getIntersection(); 8754 loc._segment = segment; 8755 prev = loc; 8756 } 8757 if (linearHandles) 8758 resetLinear(); 8759 } 8760 8761 function getWinding(point, curves, horizontal, testContains) { 8762 var tolerance = 0.000001, 8763 tMin = tolerance, 8764 tMax = 1 - tMin, 8765 px = point.x, 8766 py = point.y, 8767 windLeft = 0, 8768 windRight = 0, 8769 roots = [], 8770 abs = Math.abs; 8771 if (horizontal) { 8772 var yTop = -Infinity, 8773 yBottom = Infinity, 8774 yBefore = py - tolerance, 8775 yAfter = py + tolerance; 8776 for (var i = 0, l = curves.length; i < l; i++) { 8777 var values = curves[i].values; 8778 if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) { 8779 for (var j = roots.length - 1; j >= 0; j--) { 8780 var y = Curve.getPoint(values, roots[j]).y; 8781 if (y < yBefore && y > yTop) { 8782 yTop = y; 8783 } else if (y > yAfter && y < yBottom) { 8784 yBottom = y; 8785 } 8786 } 8787 } 8788 } 8789 yTop = (yTop + py) / 2; 8790 yBottom = (yBottom + py) / 2; 8791 if (yTop > -Infinity) 8792 windLeft = getWinding(new Point(px, yTop), curves); 8793 if (yBottom < Infinity) 8794 windRight = getWinding(new Point(px, yBottom), curves); 8795 } else { 8796 var xBefore = px - tolerance, 8797 xAfter = px + tolerance; 8798 var startCounted = false, 8799 prevCurve, 8800 prevT; 8801 for (var i = 0, l = curves.length; i < l; i++) { 8802 var curve = curves[i], 8803 values = curve.values, 8804 winding = curve.winding; 8805 if (winding && (winding === 1 8806 && py >= values[1] && py <= values[7] 8807 || py >= values[7] && py <= values[1]) 8808 && Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) { 8809 var t = roots[0]; 8810 if (!( 8811 t > tMax && startCounted && curve.next !== curves[i + 1] 8812 || t < tMin && prevT > tMax 8813 && curve.previous === prevCurve)) { 8814 var x = Curve.getPoint(values, t).x, 8815 slope = Curve.getTangent(values, t).y, 8816 counted = false;
8817 if (Numerical.isZero(slope) && !Curve.isLinear(values) 8818 || t < tMin && slope * Curve.getTangent( 8819 curve.previous.values, 1).y < 0 8820 || t > tMax && slope * Curve.getTangent( 8821 curve.next.values, 0).y < 0) { 8822 if (testContains && x >= xBefore && x <= xAfter) { 8823 ++windLeft; 8824 ++windRight; 8825 counted = true; 8826 } 8827 } else if (x <= xBefore) { 8828 windLeft += winding; 8829 counted = true; 8830 } else if (x >= xAfter) { 8831 windRight += winding; 8832 counted = true; 8833 } 8834 if (curve.previous !== curves[i - 1]) 8835 startCounted = t < tMin && counted; 8836 } 8837 prevCurve = curve; 8838 prevT = t; 8839 } 8840 } 8841 } 8842 return Math.max(abs(windLeft), abs(windRight)); 8843 } 8844 8845 function tracePaths(segments, operator, selfOp) { 8846 var paths = [], 8847 tMin = 0.000001, 8848 tMax = 1 - tMin; 8849 for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) { 8850 seg = startSeg = segments[i]; 8851 if (seg._visited || !operator(seg._winding)) 8852 continue; 8853 var path = new Path(Item.NO_INSERT), 8854 inter = seg._intersection, 8855 startInterSeg = inter && inter._segment, 8856 added = false, 8857 dir = 1; 8858 do { 8859 var handleIn = dir > 0 ? seg._handleIn : seg._handleOut, 8860 handleOut = dir > 0 ? seg._handleOut : seg._handleIn, 8861 interSeg; 8862 if (added && (!operator(seg._winding) || selfOp) 8863 && (inter = seg._intersection) 8864 && (interSeg = inter._segment) 8865 && interSeg !== startSeg) { 8866 if (selfOp) { 8867 seg._visited = interSeg._visited; 8868 seg = interSeg; 8869 dir = 1; 8870 } else { 8871 var c1 = seg.getCurve(); 8872 if (dir > 0) 8873 c1 = c1.getPrevious(); 8874 var t1 = c1.getTangentAt(dir < 1 ? tMin : tMax, true), 8875 c4 = interSeg.getCurve(), 8876 c3 = c4.getPrevious(), 8877 t3 = c3.getTangentAt(tMax, true), 8878 t4 = c4.getTangentAt(tMin, true), 8879 w3 = t1.cross(t3), 8880 w4 = t1.cross(t4); 8881 if (w3 * w4 !== 0) { 8882 var curve = w3 < w4 ? c3 : c4, 8883 nextCurve = operator(curve._segment1._winding) 8884 ? curve 8885 : w3 < w4 ? c4 : c3, 8886 nextSeg = nextCurve._segment1; 8887 dir = nextCurve === c3 ? -1 : 1; 8888 if (nextSeg._visited && seg._path !== nextSeg._path 8889 || !operator(nextSeg._winding)) { 8890 dir = 1; 8891 } else { 8892 seg._visited = interSeg._visited; 8893 seg = interSeg; 8894 if (nextSeg._visited) 8895 dir = 1; 8896 } 8897 } else { 8898 dir = 1; 8899 } 8900 } 8901 handleOut = dir > 0 ? seg._handleOut : seg._handleIn; 8902 } 8903 path.add(new Segment(seg._point, added && handleIn, handleOut)); 8904 added = true; 8905 seg._visited = true; 8906 seg = dir > 0 ? seg.getNext() : seg. getPrevious(); 8907 } while (seg && !seg._visited 8908 && seg !== startSeg && seg !== startInterSeg 8909 && (seg._intersection || operator(seg._winding))); 8910 if (seg && (seg === startSeg || seg === startInterSeg)) { 8911 path.firstSegment.setHandleIn((seg === startInterSeg 8912 ? startInterSeg : seg)._handleIn); 8913 path.setClosed(true); 8914 } else { 8915 path.lastSegment._handleOut.set(0, 0); 8916 } 8917 if (path._segments.length > 8918 (path._closed ? path.isLinear() ? 2 : 0 : 1)) 8919 paths.push(path); 8920 } 8921 return paths; 8922 } 8923 8924 return { 8925 _getWinding: function(point, horizontal, testContains) { 8926 return getWinding(point, this._getMonoCurves(), 8927 horizontal, testContains); 8928 }, 8929 8930 unite: function(path) { 8931 return computeBoolean(this, path, 'unite'); 8932 }, 8933 8934 intersect: function(path) { 8935 return computeBoolean(this, path, 'intersect'); 8936 }, 8937 8938 subtract: function(path) { 8939 return computeBoolean(this, path, 'subtract'); 8940 }, 8941 8942 exclude: function(path) { 8943 return computeBoolean(this, path, 'exclude'); 8944 }, 8945 8946 divide: function(path) { 8947 return new Group([this.subtract(path), this.intersect(path)]); 8948 } 8949 }; 8950}); 8951 8952Path.inject({ 8953 _getMonoCurves: function() { 8954 var monoCurves = this._monoCurves, 8955 prevCurve; 8956 8957 function insertCurve(v) { 8958 var y0 = v[1], 8959 y1 = v[7], 8960 curve = { 8961 values: v, 8962 winding: y0 === y1 8963 ? 0 8964 : y0 > y1 8965 ? -1 8966 : 1, 8967 previous: prevCurve, 8968 next: null 8969 }; 8970 if (prevCurve) 8971 prevCurve.next = curve; 8972 monoCurves.push(curve); 8973 prevCurve = curve; 8974 } 8975 8976 function handleCurve(v) { 8977 if (Curve.getLength(v) === 0) 8978 return; 8979 var y0 = v[1], 8980 y1 = v[3], 8981 y2 = v[5], 8982 y3 = v[7]; 8983 if (Curve.isLinear(v)) { 8984 insertCurve(v); 8985 } else { 8986 var a = 3 * (y1 - y2) - y0 + y3, 8987 b = 2 * (y0 + y2) - 4 * y1, 8988 c = y1 - y0, 8989 tolerance = 0.000001, 8990 roots = []; 8991 var count = Numerical.solveQuadratic(a, b, c, roots, tolerance, 8992 1 - tolerance); 8993 if (count === 0) { 8994 insertCurve(v); 8995 } else { 8996 roots.sort(); 8997 var t = roots[0], 8998 parts = Curve.subdivide(v, t); 8999 insertCurve(parts[0]); 9000 if (count > 1) { 9001 t = (roots[1] - t) / (1 - t); 9002 parts = Curve.subdivide(parts[1], t); 9003 insertCurve(parts[0]); 9004 } 9005 insertCurve(parts[1]); 9006 } 9007 } 9008 } 9009 9010 if (!monoCurves) { 9011 monoCurves = this._monoCurves = []; 9012 var curves = this.getCurves(), 9013 segments = this._segments; 9014 for (var i = 0, l = curves.length; i < l; i++) 9015 handleCurve(curves[i].getValues()); 9016 if (!this._closed && segments.length > 1) { 9017 var p1 = segments[segments.length - 1]._point, 9018 p2 = segments[0]._point, 9019 p1x = p1._x, p1y = p1._y, 9020 p2x = p2._x, p2y = p2._y; 9021 handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]); 9022 } 9023 if (monoCurves.length > 0) { 9024 var first = monoCurves[0], 9025 last = monoCurves[monoCurves.length - 1]; 9026 first.previous = last; 9027 last.next = first; 9028 } 9029 } 9030 return monoCurves; 9031 }, 9032 9033 getInteriorPoint: function() { 9034 var bounds = this.getBounds(), 9035 point = bounds.getCenter(true); 9036 if (!this.contains(point)) { 9037 var curves = this._getMonoCurves(), 9038 roots = [], 9039 y = point.y, 9040 xIntercepts = []; 9041 for (var i = 0, l = curves.length; i < l; i++) { 9042 var values = curves[i].values; 9043 if ((curves[i].winding === 1 9044 && y >= values[1] && y <= values[7] 9045 || y >= values[7] && y <= values[1]) 9046 && Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) { 9047 for (var j = roots.length - 1; j >= 0; j--) 9048 xIntercepts.push(Curve.getPoint(values, roots[j]).x); 9049 } 9050 if (xIntercepts.length > 1) 9051 break; 9052 } 9053 point.x = (xIntercepts[0] + xIntercepts[1]) / 2; 9054 } 9055 return point; 9056 }, 9057 9058 reorient: function() { 9059 this.setClockwise(true); 9060 return this; 9061 } 9062}); 9063 9064CompoundPath.inject({ 9065 _getMonoCurves: function() { 9066 var children = this._children, 9067 monoCurves = []; 9068 for (var i = 0, l = children.length; i < l; i++) 9069 monoCurves.push.apply(monoCurves, children[i]._getMonoCurves()); 9070 return monoCurves; 9071 }, 9072 9073 reorient: function() { 9074 var children = this.removeChildren().sort(function(a, b) { 9075 return b.getBounds().getArea() - a.getBounds().getArea(); 9076 }); 9077 if (children.length > 0) { 9078 this.addChildren(children); 9079 var clockwise = children[0].isClockwise(); 9080 for (var i = 1, l = children.length; i < l; i++) { 9081 var point = children[i].getInteriorPoint(), 9082 counters = 0;
9083 for (var j = i - 1; j >= 0; j--) { 9084 if (children[j].contains(point)) 9085 counters++; 9086 } 9087 children[i].setClockwise(counters % 2 === 0 && clockwise); 9088 } 9089 } 9090 return this; 9091 } 9092}); 9093 9094var PathIterator = Base.extend({ 9095 _class: 'PathIterator', 9096 9097 initialize: function(path, maxRecursion, tolerance, matrix) { 9098 var curves = [], 9099 parts = [], 9100 length = 0, 9101 minDifference = 1 / (maxRecursion || 32), 9102 segments = path._segments, 9103 segment1 = segments[0], 9104 segment2; 9105 9106 function addCurve(segment1, segment2) { 9107 var curve = Curve.getValues(segment1, segment2, matrix); 9108 curves.push(curve); 9109 computeParts(curve, segment1._index, 0, 1); 9110 } 9111 9112 function computeParts(curve, index, minT, maxT) { 9113 if ((maxT - minT) > minDifference 9114 && !Curve.isFlatEnough(curve, tolerance || 0.25)) { 9115 var split = Curve.subdivide(curve), 9116 halfT = (minT + maxT) / 2; 9117 computeParts(split[0], index, minT, halfT); 9118 computeParts(split[1], index, halfT, maxT); 9119 } else { 9120 var x = curve[6] - curve[0], 9121 y = curve[7] - curve[1], 9122 dist = Math.sqrt(x * x + y * y); 9123 if (dist > 0.000001) { 9124 length += dist; 9125 parts.push({ 9126 offset: length, 9127 value: maxT, 9128 index: index 9129 }); 9130 } 9131 } 9132 } 9133 9134 for (var i = 1, l = segments.length; i < l; i++) { 9135 segment2 = segments[i]; 9136 addCurve(segment1, segment2); 9137 segment1 = segment2; 9138 } 9139 if (path._closed) 9140 addCurve(segment2, segments[0]); 9141 9142 this.curves = curves; 9143 this.parts = parts; 9144 this.length = length; 9145 this.index = 0; 9146 }, 9147 9148 getParameterAt: function(offset) { 9149 var i, j = this.index; 9150 for (;;) { 9151 i = j; 9152 if (j == 0 || this.parts[--j].offset < offset) 9153 break; 9154 } 9155 for (var l = this.parts.length; i < l; i++) { 9156 var part = this.parts[i]; 9157 if (part.offset >= offset) { 9158 this.index = i; 9159 var prev = this.parts[i - 1]; 9160 var prevVal = prev && prev.index == part.index ? prev.value : 0, 9161 prevLen = prev ? prev.offset : 0; 9162 return { 9163 value: prevVal + (part.value - prevVal) 9164 * (offset - prevLen) / (part.offset - prevLen), 9165 index: part.index 9166 }; 9167 } 9168 } 9169 var part = this.parts[this.parts.length - 1]; 9170 return { 9171 value: 1, 9172 index: part.index 9173 }; 9174 }, 9175 9176 drawPart: function(ctx, from, to) { 9177 from = this.getParameterAt(from); 9178 to = this.getParameterAt(to); 9179 for (var i = from.index; i <= to.index; i++) { 9180 var curve = Curve.getPart(this.curves[i], 9181 i == from.index ? from.value : 0, 9182 i == to.index ? to.value : 1); 9183 if (i == from.index) 9184 ctx.moveTo(curve[0], curve[1]); 9185 ctx.bezierCurveTo.apply(ctx, curve.slice(2)); 9186 } 9187 } 9188}, Base.each(Curve.evaluateMethods, 9189 function(name) { 9190 this[name + 'At'] = function(offset, weighted) { 9191 var param = this.getParameterAt(offset); 9192 return Curve[name](this.curves[param.index], param.value, weighted); 9193 }; 9194 }, {}) 9195); 9196 9197var PathFitter = Base.extend({ 9198 initialize: function(path, error) { 9199 var points = this.points = [], 9200 segments = path._segments, 9201 prev; 9202 for (var i = 0, l = segments.length; i < l; i++) { 9203 var point = segments[i].point.clone(); 9204 if (!prev || !prev.equals(point)) { 9205 points.push(point); 9206 prev = point; 9207 } 9208 } 9209 9210 if (path._closed) { 9211 this.closed = true; 9212 points.unshift(points[points.length - 1]); 9213 points.push(points[1]); 9214 } 9215 9216 this.error = error; 9217 }, 9218 9219 fit: function() { 9220 var points = this.points, 9221 length = points.length, 9222 segments = this.segments = length > 0 9223 ? [new Segment(points[0])] : []; 9224 if (length > 1) 9225 this.fitCubic(0, length - 1, 9226 points[1].subtract(points[0]).normalize(), 9227 points[length - 2].subtract(points[length - 1]).normalize()); 9228 9229 if (this.closed) { 9230 segments.shift(); 9231 segments.pop(); 9232 } 9233 9234 return segments; 9235 }, 9236 9237 fitCubic: function(first, last, tan1, tan2) { 9238 if (last - first == 1) { 9239 var pt1 = this.points[first], 9240 pt2 = this.points[last], 9241 dist = pt1.getDistance(pt2) / 3; 9242 this.addCurve([pt1, pt1.add(tan1.normalize(dist)), 9243 pt2.add(tan2.normalize(dist)), pt2]); 9244 return; 9245 } 9246 var uPrime = this.chordLengthParameterize(first, last), 9247 maxError = Math.max(this.error, this.error * this.error), 9248 split, 9249 parametersInOrder = true; 9250 for (var i = 0; i <= 4; i++) { 9251 var curve = this.generateBezier(first, last, uPrime, tan1, tan2); 9252 var max = this.findMaxError(first, last, curve, uPrime); 9253 if (max.error < this.error && parametersInOrder) { 9254 this.addCurve(curve); 9255 return; 9256 } 9257 split = max.index; 9258 if (max.error >= maxError) 9259 break; 9260 parametersInOrder = this.reparameterize(first, last, uPrime, curve); 9261 maxError = max.error; 9262 } 9263 var V1 = this.points[split - 1].subtract(this.points[split]), 9264 V2 = this.points[split].subtract(this.points[split + 1]), 9265 tanCenter = V1.add(V2).divide(2).normalize(); 9266 this.fitCubic(first, split, tan1, tanCenter); 9267 this.fitCubic(split, last, tanCenter.negate(), tan2); 9268 }, 9269 9270 addCurve: function(curve) { 9271 var prev = this.segments[this.segments.length - 1]; 9272 prev.setHandleOut(curve[1].subtract(curve[0])); 9273 this.segments.push( 9274 new Segment(curve[3], curve[2].subtract(curve[3]))); 9275 }, 9276 9277 generateBezier: function(first, last, uPrime, tan1, tan2) { 9278 var epsilon = 1e-12, 9279 pt1 = this.points[first], 9280 pt2 = this.points[last], 9281 C = [[0, 0], [0, 0]], 9282 X = [0, 0]; 9283 9284 for (var i = 0, l = last - first + 1; i < l; i++) { 9285 var u = uPrime[i], 9286 t = 1 - u, 9287 b = 3 * u * t, 9288 b0 = t * t * t, 9289 b1 = b * t, 9290 b2 = b * u, 9291 b3 = u * u * u, 9292 a1 = tan1.normalize(b1), 9293 a2 = tan2.normalize(b2), 9294 tmp = this.points[first + i] 9295 .subtract(pt1.multiply(b0 + b1)) 9296 .subtract(pt2.multiply(b2 + b3)); 9297 C[0][0] += a1.dot(a1); 9298 C[0][1] += a1.dot(a2); 9299 C[1][0] = C[0][1]; 9300 C[1][1] += a2.dot(a2); 9301 X[0] += a1.dot(tmp); 9302 X[1] += a2.dot(tmp); 9303 } 9304 9305 var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1], 9306 alpha1, alpha2; 9307 if (Math.abs(detC0C1) > epsilon) { 9308 var detC0X = C[0][0] * X[1] - C[1][0] * X[0], 9309 detXC1 = X[0] * C[1][1] - X[1] * C[0][1]; 9310 alpha1 = detXC1 / detC0C1; 9311 alpha2 = detC0X / detC0C1; 9312 } else { 9313 var c0 = C[0][0] + C[0][1], 9314 c1 = C[1][0] + C[1][1]; 9315 if (Math.abs(c0) > epsilon) { 9316 alpha1 = alpha2 = X[0] / c0; 9317 } else if (Math.abs(c1) > epsilon) { 9318 alpha1 = alpha2 = X[1] / c1; 9319 } else { 9320 alpha1 = alpha2 = 0; 9321 } 9322 } 9323 9324 var segLength = pt2.getDistance(pt1), 9325 eps = epsilon * segLength, 9326 handle1, 9327 handle2; 9328 if (alpha1 < eps || alpha2 < eps) { 9329 alpha1 = alpha2 = segLength / 3; 9330 } else { 9331 var line = pt2.subtract(pt1); 9332 handle1 = tan1.normalize(alpha1); 9333 handle2 = tan2.normalize(alpha2); 9334 if (handle1.dot(line) - handle2.dot(line) > segLength * segLength) { 9335 alpha1 = alpha2 = segLength / 3; 9336 handle1 = handle2 = null; 9337 } 9338 } 9339 9340 return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)), 9341 pt2.add(handle2 || tan2.normalize(alpha2)), pt2]; 9342 }, 9343 9344 reparameterize: function(first, last, u, curve) { 9345 for (var i = first; i <= last; i++) { 9346 u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); 9347 } 9348 for (var i = 1, l = u.length; i < l; i++) { 9349 if (u[i] <= u[i - 1]) 9350 return false;
vendor: 18,151 bytes, lines 9351-10073
9351 } 9352 return true; 9353 }, 9354 9355 findRoot: function(curve, point, u) { 9356 var curve1 = [], 9357 curve2 = []; 9358 for (var i = 0; i <= 2; i++) { 9359 curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3); 9360 } 9361 for (var i = 0; i <= 1; i++) { 9362 curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2); 9363 } 9364 var pt = this.evaluate(3, curve, u), 9365 pt1 = this.evaluate(2, curve1, u), 9366 pt2 = this.evaluate(1, curve2, u), 9367 diff = pt.subtract(point), 9368 df = pt1.dot(pt1) + diff.dot(pt2); 9369 if (Math.abs(df) < 0.000001) 9370 return u; 9371 return u - diff.dot(pt1) / df; 9372 }, 9373 9374 evaluate: function(degree, curve, t) { 9375 var tmp = curve.slice(); 9376 for (var i = 1; i <= degree; i++) { 9377 for (var j = 0; j <= degree - i; j++) { 9378 tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t)); 9379 } 9380 } 9381 return tmp[0]; 9382 }, 9383 9384 chordLengthParameterize: function(first, last) { 9385 var u = [0]; 9386 for (var i = first + 1; i <= last; i++) { 9387 u[i - first] = u[i - first - 1] 9388 + this.points[i].getDistance(this.points[i - 1]); 9389 } 9390 for (var i = 1, m = last - first; i <= m; i++) { 9391 u[i] /= u[m]; 9392 } 9393 return u; 9394 }, 9395 9396 findMaxError: function(first, last, curve, u) { 9397 var index = Math.floor((last - first + 1) / 2), 9398 maxDist = 0; 9399 for (var i = first + 1; i < last; i++) { 9400 var P = this.evaluate(3, curve, u[i - first]); 9401 var v = P.subtract(this.points[i]); 9402 var dist = v.x * v.x + v.y * v.y; 9403 if (dist >= maxDist) { 9404 maxDist = dist; 9405 index = i; 9406 } 9407 } 9408 return { 9409 error: maxDist, 9410 index: index 9411 }; 9412 } 9413}); 9414 9415var TextItem = Item.extend({ 9416 _class: 'TextItem', 9417 _boundsSelected: true, 9418 _applyMatrix: false, 9419 _canApplyMatrix: false, 9420 _serializeFields: { 9421 content: null 9422 }, 9423 _boundsGetter: 'getBounds', 9424 9425 initialize: function TextItem(arg) { 9426 this._content = ''; 9427 this._lines = []; 9428 var hasProps = arg && Base.isPlainObject(arg) 9429 && arg.x === undefined && arg.y === undefined; 9430 this._initialize(hasProps && arg, !hasProps && Point.read(arguments)); 9431 }, 9432 9433 _equals: function(item) { 9434 return this._content === item._content; 9435 }, 9436 9437 _clone: function _clone(copy, insert, includeMatrix) { 9438 copy.setContent(this._content); 9439 return _clone.base.call(this, copy, insert, includeMatrix); 9440 }, 9441 9442 getContent: function() { 9443 return this._content; 9444 }, 9445 9446 setContent: function(content) { 9447 this._content = '' + content; 9448 this._lines = this._content.split(/\r\n|\n|\r/mg); 9449 this._changed(265); 9450 }, 9451 9452 isEmpty: function() { 9453 return !this._content; 9454 }, 9455 9456 getCharacterStyle: '#getStyle', 9457 setCharacterStyle: '#setStyle', 9458 9459 getParagraphStyle: '#getStyle', 9460 setParagraphStyle: '#setStyle' 9461}); 9462 9463var PointText = TextItem.extend({ 9464 _class: 'PointText', 9465 9466 initialize: function PointText() { 9467 TextItem.apply(this, arguments); 9468 }, 9469 9470 clone: function(insert) { 9471 return this._clone(new PointText(Item.NO_INSERT), insert); 9472 }, 9473 9474 getPoint: function() { 9475 var point = this._matrix.getTranslation(); 9476 return new LinkedPoint(point.x, point.y, this, 'setPoint'); 9477 }, 9478 9479 setPoint: function() { 9480 var point = Point.read(arguments); 9481 this.translate(point.subtract(this._matrix.getTranslation())); 9482 }, 9483 9484 _draw: function(ctx) { 9485 if (!this._content) 9486 return; 9487 this._setStyles(ctx); 9488 var style = this._style, 9489 lines = this._lines, 9490 leading = style.getLeading(), 9491 shadowColor = ctx.shadowColor; 9492 ctx.font = style.getFontStyle(); 9493 ctx.textAlign = style.getJustification(); 9494 for (var i = 0, l = lines.length; i < l; i++) { 9495 ctx.shadowColor = shadowColor; 9496 var line = lines[i]; 9497 if (style.hasFill()) { 9498 ctx.fillText(line, 0, 0); 9499 ctx.shadowColor = 'rgba(0,0,0,0)'; 9500 } 9501 if (style.hasStroke()) 9502 ctx.strokeText(line, 0, 0); 9503 ctx.translate(0, leading); 9504 } 9505 }, 9506 9507 _getBounds: function(getter, matrix) { 9508 var style = this._style, 9509 lines = this._lines, 9510 numLines = lines.length, 9511 justification = style.getJustification(), 9512 leading = style.getLeading(), 9513 width = this.getView().getTextWidth(style.getFontStyle(), lines), 9514 x = 0; 9515 if (justification !== 'left') 9516 x -= width / (justification === 'center' ? 2: 1); 9517 var bounds = new Rectangle(x, 9518 numLines ? - 0.75 * leading : 0, 9519 width, numLines * leading); 9520 return matrix ? matrix._transformBounds(bounds, bounds) : bounds; 9521 } 9522}); 9523 9524var Color = Base.extend(new function() { 9525 var types = { 9526 gray: ['gray'], 9527 rgb: ['red', 'green', 'blue'], 9528 hsb: ['hue', 'saturation', 'brightness'], 9529 hsl: ['hue', 'saturation', 'lightness'], 9530 gradient: ['gradient', 'origin', 'destination', 'highlight'] 9531 }; 9532 9533 var componentParsers = {}, 9534 colorCache = {}, 9535 colorCtx; 9536 9537 function fromCSS(string) { 9538 var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/), 9539 components; 9540 if (match) { 9541 components = [0, 0, 0]; 9542 for (var i = 0; i < 3; i++) { 9543 var value = match[i + 1]; 9544 components[i] = parseInt(value.length == 1 9545 ? value + value : value, 16) / 255; 9546 } 9547 } else if (match = string.match(/^rgba?\((.*)\)$/)) { 9548 components = match[1].split(','); 9549 for (var i = 0, l = components.length; i < l; i++) { 9550 var value = +components[i]; 9551 components[i] = i < 3 ? value / 255 : value; 9552 } 9553 } else { 9554 var cached = colorCache[string]; 9555 if (!cached) { 9556 if (!colorCtx) { 9557 colorCtx = CanvasProvider.getContext(1, 1); 9558 colorCtx.globalCompositeOperation = 'copy'; 9559 } 9560 colorCtx.fillStyle = 'rgba(0,0,0,0)'; 9561 colorCtx.fillStyle = string; 9562 colorCtx.fillRect(0, 0, 1, 1); 9563 var data = colorCtx.getImageData(0, 0, 1, 1).data; 9564 cached = colorCache[string] = [ 9565 data[0] / 255, 9566 data[1] / 255, 9567 data[2] / 255 9568 ]; 9569 } 9570 components = cached.slice(); 9571 } 9572 return components; 9573 } 9574 9575 var hsbIndices = [ 9576 [0, 3, 1], 9577 [2, 0, 1], 9578 [1, 0, 3], 9579 [1, 2, 0], 9580 [3, 1, 0], 9581 [0, 1, 2] 9582 ]; 9583 9584 var converters = { 9585 'rgb-hsb': function(r, g, b) { 9586 var max = Math.max(r, g, b), 9587 min = Math.min(r, g, b), 9588 delta = max - min, 9589 h = delta === 0 ? 0 9590 : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) 9591 : max == g ? (b - r) / delta + 2 9592 : (r - g) / delta + 4) * 60; 9593 return [h, max === 0 ? 0 : delta / max, max]; 9594 }, 9595 9596 'hsb-rgb': function(h, s, b) { 9597 h = (((h / 60) % 6) + 6) % 6; 9598 var i = Math.floor(h), 9599 f = h - i, 9600 i = hsbIndices[i], 9601 v = [ 9602 b, 9603 b * (1 - s), 9604 b * (1 - s * f), 9605 b * (1 - s * (1 - f)) 9606 ]; 9607 return [v[i[0]], v[i[1]], v[i[2]]]; 9608 }, 9609 9610 'rgb-hsl': function(r, g, b) { 9611 var max = Math.max(r, g, b), 9612 min = Math.min(r, g, b), 9613 delta = max - min, 9614 achromatic = delta === 0, 9615 h = achromatic ? 0 9616 : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) 9617 : max == g ? (b - r) / delta + 2 9618 : (r - g) / delta + 4) * 60, 9619 l = (max + min) / 2, 9620 s = achromatic ? 0 : l < 0.5 9621 ? delta / (max + min) 9622 : delta / (2 - max - min); 9623 return [h, s, l]; 9624 }, 9625 9626 'hsl-rgb': function(h, s, l) { 9627 h = (((h / 360) % 1) + 1) % 1; 9628 if (s === 0) 9629 return [l, l, l]; 9630 var t3s = [ h + 1 / 3, h, h - 1 / 3 ], 9631 t2 = l < 0.5 ? l * (1 + s) : l + s - l * s, 9632 t1 = 2 * l - t2, 9633 c = []; 9634 for (var i = 0; i < 3; i++) { 9635 var t3 = t3s[i]; 9636 if (t3 < 0) t3 += 1; 9637 if (t3 > 1) t3 -= 1; 9638 c[i] = 6 * t3 < 1 9639 ? t1 + (t2 - t1) * 6 * t3 9640 : 2 * t3 < 1 9641 ? t2 9642 : 3 * t3 < 2 9643 ? t1 + (t2 - t1) * ((2 / 3) - t3) * 6 9644 : t1; 9645 } 9646 return c; 9647 }, 9648 9649 'rgb-gray': function(r, g, b) { 9650 return [r * 0.2989 + g * 0.587 + b * 0.114]; 9651 }, 9652 9653 'gray-rgb': function(g) { 9654 return [g, g, g]; 9655 }, 9656 9657 'gray-hsb': function(g) { 9658 return [0, 0, g]; 9659 }, 9660 9661 'gray-hsl': function(g) { 9662 return [0, 0, g]; 9663 }, 9664 9665 'gradient-rgb': function() { 9666 return []; 9667 }, 9668 9669 'rgb-gradient': function() { 9670 return []; 9671 } 9672 9673 }; 9674 9675 return Base.each(types, function(properties, type) { 9676 componentParsers[type] = []; 9677 Base.each(properties, function(name, index) { 9678 var part = Base.capitalize(name), 9679 hasOverlap = /^(hue|saturation)$/.test(name), 9680 parser = componentParsers[type][index] = name === 'gradient' 9681 ? function(value) { 9682 var current = this._components[0]; 9683 value = Gradient.read(Array.isArray(value) ? value 9684 : arguments, 0, { readNull: true }); 9685 if (current !== value) { 9686 if (current) 9687 current._removeOwner(this); 9688 if (value) 9689 value._addOwner(this); 9690 } 9691 return value; 9692 } 9693 : type === 'gradient' 9694 ? function() { 9695 return Point.read(arguments, 0, { 9696 readNull: name === 'highlight', 9697 clone: true 9698 }); 9699 } 9700 : function(value) { 9701 return value == null || isNaN(value) ? 0 : value; 9702 }; 9703 9704 this['get' + part] = function() { 9705 return this._type === type 9706 || hasOverlap && /^hs[bl]$/.test(this._type) 9707 ? this._components[index] 9708 : this._convert(type)[index]; 9709 }; 9710 9711 this['set' + part] = function(value) { 9712 if (this._type !== type 9713 && !(hasOverlap && /^hs[bl]$/.test(this._type))) { 9714 this._components = this._convert(type); 9715 this._properties = types[type]; 9716 this._type = type; 9717 } 9718 this._components[index] = parser.call(this, value); 9719 this._changed(); 9720 }; 9721 }, this); 9722 }, { 9723 _class: 'Color', 9724 _readIndex: true, 9725 9726 initialize: function Color(arg) { 9727 var slice = Array.prototype.slice, 9728 args = arguments, 9729 read = 0, 9730 type, 9731 components, 9732 alpha, 9733 values; 9734 if (Array.isArray(arg)) { 9735 args = arg; 9736 arg = args[0]; 9737 } 9738 var argType = arg != null && typeof arg; 9739 if (argType === 'string' && arg in types) { 9740 type = arg; 9741 arg = args[1]; 9742 if (Array.isArray(arg)) { 9743 components = arg; 9744 alpha = args[2]; 9745 } else { 9746 if (this.__read) 9747 read = 1; 9748 args = slice.call(args, 1); 9749 argType = typeof arg; 9750 } 9751 } 9752 if (!components) { 9753 values = argType === 'number' 9754 ? args 9755 : argType === 'object' && arg.length != null 9756 ? arg 9757 : null; 9758 if (values) { 9759 if (!type) 9760 type = values.length >= 3 9761 ? 'rgb' 9762 : 'gray'; 9763 var length = types[type].length; 9764 alpha = values[length]; 9765 if (this.__read) 9766 read += values === arguments 9767 ? length + (alpha != null ? 1 : 0) 9768 : 1; 9769 if (values.length > length) 9770 values = slice.call(values, 0, length); 9771 } else if (argType === 'string') { 9772 type = 'rgb'; 9773 components = fromCSS(arg); 9774 if (components.length === 4) { 9775 alpha = components[3]; 9776 components.length--; 9777 } 9778 } else if (argType === 'object') { 9779 if (arg.constructor === Color) { 9780 type = arg._type; 9781 components = arg._components.slice(); 9782 alpha = arg._alpha; 9783 if (type === 'gradient') { 9784 for (var i = 1, l = components.length; i < l; i++) { 9785 var point = components[i]; 9786 if (point) 9787 components[i] = point.clone(); 9788 } 9789 } 9790 } else if (arg.constructor === Gradient) { 9791 type = 'gradient'; 9792 values = args; 9793 } else { 9794 type = 'hue' in arg 9795 ? 'lightness' in arg 9796 ? 'hsl' 9797 : 'hsb' 9798 : 'gradient' in arg || 'stops' in arg 9799 || 'radial' in arg 9800 ? 'gradient' 9801 : 'gray' in arg 9802 ? 'gray' 9803 : 'rgb'; 9804 var properties = types[type]; 9805 parsers = componentParsers[type]; 9806 this._components = components = []; 9807 for (var i = 0, l = properties.length; i < l; i++) { 9808 var value = arg[properties[i]]; 9809 if (value == null && i === 0 && type === 'gradient' 9810 && 'stops' in arg) { 9811 value = { 9812 stops: arg.stops, 9813 radial: arg.radial 9814 }; 9815 } 9816 value = parsers[i].call(this, value); 9817 if (value != null) 9818 components[i] = value; 9819 } 9820 alpha = arg.alpha; 9821 } 9822 } 9823 if (this.__read && type) 9824 read = 1; 9825 } 9826 this._type = type || 'rgb'; 9827 this._id = UID.get(Color); 9828 if (!components) { 9829 this._components = components = []; 9830 var parsers = componentParsers[this._type]; 9831 for (var i = 0, l = parsers.length; i < l; i++) { 9832 var value = parsers[i].call(this, values && values[i]); 9833 if (value != null) 9834 components[i] = value; 9835 } 9836 } 9837 this._components = components; 9838 this._properties = types[this._type]; 9839 this._alpha = alpha; 9840 if (this.__read) 9841 this.__read = read; 9842 }, 9843 9844 _serialize: function(options, dictionary) { 9845 var components = this.getComponents(); 9846 return Base.serialize( 9847 /^(gray|rgb)$/.test(this._type) 9848 ? components 9849 : [this._type].concat(components), 9850 options, true, dictionary); 9851 }, 9852 9853 _changed: function() { 9854 this._canvasStyle = null; 9855 if (this._owner) 9856 this._owner._changed(65); 9857 }, 9858 9859 _convert: function(type) { 9860 var converter; 9861 return this._type === type 9862 ? this._components.slice() 9863 : (converter = converters[this._type + '-' + type]) 9864 ? converter.apply(this, this._components) 9865 : converters['rgb-' + type].apply(this, 9866 converters[this._type + '-rgb'].apply(this, 9867 this._components)); 9868 }, 9869 9870 convert: function(type) { 9871 return new Color(type, this._convert(type), this._alpha); 9872 }, 9873 9874 getType: function() { 9875 return this._type; 9876 }, 9877 9878 setType: function(type) { 9879 this._components = this._convert(type); 9880 this._properties = types[type]; 9881 this._type = type; 9882 }, 9883 9884 getComponents: function() { 9885 var components = this._components.slice(); 9886 if (this._alpha != null) 9887 components.push(this._alpha); 9888 return components; 9889 }, 9890 9891 getAlpha: function() { 9892 return this._alpha != null ? this._alpha : 1; 9893 }, 9894 9895 setAlpha: function(alpha) { 9896 this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1); 9897 this._changed(); 9898 }, 9899 9900 hasAlpha: function() { 9901 return this._alpha != null; 9902 }, 9903 9904 equals: function(color) { 9905 var col = Base.isPlainValue(color, true) 9906 ? Color.read(arguments) 9907 : color; 9908 return col === this || col && this._class === col._class 9909 && this._type === col._type 9910 && this._alpha === col._alpha 9911 && Base.equals(this._components, col._components) 9912 || false; 9913 }, 9914 9915 toString: function() { 9916 var properties = this._properties, 9917 parts = [], 9918 isGradient = this._type === 'gradient', 9919 f = Formatter.instance; 9920 for (var i = 0, l = properties.length; i < l; i++) { 9921 var value = this._components[i]; 9922 if (value != null) 9923 parts.push(properties[i] + ': ' 9924 + (isGradient ? value : f.number(value))); 9925 } 9926 if (this._alpha != null) 9927 parts.push('alpha: ' + f.number(this._alpha)); 9928 return '{ ' + parts.join(', ') + ' }'; 9929 }, 9930 9931 toCSS: function(hex) { 9932 var components = this._convert('rgb'), 9933 alpha = hex || this._alpha == null ? 1 : this._alpha; 9934 function convert(val) { 9935 return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255); 9936 } 9937 components = [ 9938 convert(components[0]), 9939 convert(components[1]), 9940 convert(components[2]) 9941 ]; 9942 if (alpha < 1) 9943 components.push(alpha < 0 ? 0 : alpha); 9944 return hex 9945 ? '#' + ((1 << 24) + (components[0] << 16) 9946 + (components[1] << 8) 9947 + components[2]).toString(16).slice(1) 9948 : (components.length == 4 ? 'rgba(' : 'rgb(') 9949 + components.join(',') + ')'; 9950 }, 9951 9952 toCanvasStyle: function(ctx) { 9953 if (this._canvasStyle) 9954 return this._canvasStyle; 9955 if (this._type !== 'gradient') 9956 return this._canvasStyle = this.toCSS(); 9957 var components = this._components, 9958 gradient = components[0], 9959 stops = gradient._stops, 9960 origin = components[1], 9961 destination = components[2], 9962 canvasGradient; 9963 if (gradient._radial) { 9964 var radius = destination.getDistance(origin), 9965 highlight = components[3]; 9966 if (highlight) { 9967 var vector = highlight.subtract(origin); 9968 if (vector.getLength() > radius) 9969 highlight = origin.add(vector.normalize(radius - 0.1)); 9970 } 9971 var start = highlight || origin; 9972 canvasGradient = ctx.createRadialGradient(start.x, start.y, 9973 0, origin.x, origin.y, radius); 9974 } else { 9975 canvasGradient = ctx.createLinearGradient(origin.x, origin.y, 9976 destination.x, destination.y); 9977 } 9978 for (var i = 0, l = stops.length; i < l; i++) { 9979 var stop = stops[i]; 9980 canvasGradient.addColorStop(stop._rampPoint, 9981 stop._color.toCanvasStyle()); 9982 } 9983 return this._canvasStyle = canvasGradient; 9984 }, 9985 9986 transform: function(matrix) { 9987 if (this._type === 'gradient') { 9988 var components = this._components; 9989 for (var i = 1, l = components.length; i < l; i++) { 9990 var point = components[i]; 9991 matrix._transformPoint(point, point, true); 9992 } 9993 this._changed(); 9994 } 9995 }, 9996 9997 statics: { 9998 _types: types, 9999 10000 random: function() { 10001 var random = Math.random; 10002 return new Color(random(), random(), random()); 10003 } 10004 } 10005 }); 10006}, new function() { 10007 var operators = { 10008 add: function(a, b) { 10009 return a + b; 10010 }, 10011 10012 subtract: function(a, b) { 10013 return a - b; 10014 }, 10015 10016 multiply: function(a, b) { 10017 return a * b; 10018 }, 10019 10020 divide: function(a, b) { 10021 return a / b; 10022 } 10023 }; 10024 10025 return Base.each(operators, function(operator, name) { 10026 this[name] = function(color) { 10027 color = Color.read(arguments); 10028 var type = this._type, 10029 components1 = this._components, 10030 components2 = color._convert(type); 10031 for (var i = 0, l = components1.length; i < l; i++) 10032 components2[i] = operator(components1[i], components2[i]); 10033 return new Color(type, components2, 10034 this._alpha != null 10035 ? operator(this._alpha, color.getAlpha()) 10036 : null); 10037 }; 10038 }, { 10039 }); 10040}); 10041 10042Base.each(Color._types, function(properties, type) { 10043 var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) { 10044 var argType = arg != null && typeof arg, 10045 components = argType === 'object' && arg.length != null 10046 ? arg 10047 : argType === 'string' 10048 ? null 10049 : arguments; 10050 return components 10051 ? new Color(type, components) 10052 : new Color(arg); 10053 }; 10054 if (type.length == 3) { 10055 var acronym = type.toUpperCase(); 10056 Color[acronym] = this[acronym + 'Color'] = ctor; 10057 } 10058}, Base.exports); 10059 10060var Gradient = Base.extend({ 10061 _class: 'Gradient', 10062 10063 initialize: function Gradient(stops, radial) { 10064 this._id = UID.get(); 10065 if (stops && this._set(stops)) 10066 stops = radial = null; 10067 if (!this._stops) 10068 this.setStops(stops || ['white', 'black']); 10069 if (this._radial == null) 10070 this.setRadial(typeof radial === 'string' && radial === 'radial' 10071 || radial || false); 10072 }, 10073
10074 _serialize: function(options, dictionary) { 10075 return dictionary.add(this, function() { 10076 return Base.serialize([this._stops, this._radial], 10077 options, true, dictionary); 10078 }); 10079 }, 10080 10081 _changed: function() { 10082 for (var i = 0, l = this._owners && this._owners.length; i < l; i++) 10083 this._owners[i]._changed(); 10084 }, 10085 10086 _addOwner: function(color) { 10087 if (!this._owners) 10088 this._owners = []; 10089 this._owners.push(color); 10090 }, 10091 10092 _removeOwner: function(color) { 10093 var index = this._owners ? this._owners.indexOf(color) : -1; 10094 if (index != -1) { 10095 this._owners.splice(index, 1); 10096 if (this._owners.length === 0) 10097 this._owners = undefined; 10098 } 10099 }, 10100 10101 clone: function() { 10102 var stops = []; 10103 for (var i = 0, l = this._stops.length; i < l; i++) 10104 stops[i] = this._stops[i].clone(); 10105 return new Gradient(stops, this._radial); 10106 }, 10107 10108 getStops: function() { 10109 return this._stops; 10110 }, 10111 10112 setStops: function(stops) { 10113 if (this.stops) { 10114 for (var i = 0, l = this._stops.length; i < l; i++) 10115 this._stops[i]._owner = undefined; 10116 } 10117 if (stops.length < 2) 10118 throw new Error( 10119 'Gradient stop list needs to contain at least two stops.'); 10120 this._stops = GradientStop.readAll(stops, 0, { clone: true }); 10121 for (var i = 0, l = this._stops.length; i < l; i++) { 10122 var stop = this._stops[i]; 10123 stop._owner = this; 10124 if (stop._defaultRamp) 10125 stop.setRampPoint(i / (l - 1)); 10126 } 10127 this._changed(); 10128 }, 10129 10130 getRadial: function() { 10131 return this._radial; 10132 }, 10133 10134 setRadial: function(radial) { 10135 this._radial = radial; 10136 this._changed(); 10137 }, 10138 10139 equals: function(gradient) { 10140 if (gradient === this) 10141 return true; 10142 if (gradient && this._class === gradient._class 10143 && this._stops.length === gradient._stops.length) { 10144 for (var i = 0, l = this._stops.length; i < l; i++) { 10145 if (!this._stops[i].equals(gradient._stops[i])) 10146 return false; 10147 } 10148 return true; 10149 } 10150 return false; 10151 } 10152}); 10153 10154var GradientStop = Base.extend({ 10155 _class: 'GradientStop', 10156 10157 initialize: function GradientStop(arg0, arg1) { 10158 if (arg0) { 10159 var color, rampPoint; 10160 if (arg1 === undefined && Array.isArray(arg0)) { 10161 color = arg0[0]; 10162 rampPoint = arg0[1]; 10163 } else if (arg0.color) { 10164 color = arg0.color; 10165 rampPoint = arg0.rampPoint; 10166 } else { 10167 color = arg0; 10168 rampPoint = arg1; 10169 } 10170 this.setColor(color); 10171 this.setRampPoint(rampPoint); 10172 } 10173 }, 10174 10175 clone: function() { 10176 return new GradientStop(this._color.clone(), this._rampPoint); 10177 }, 10178 10179 _serialize: function(options, dictionary) { 10180 return Base.serialize([this._color, this._rampPoint], options, true, 10181 dictionary); 10182 }, 10183 10184 _changed: function() { 10185 if (this._owner) 10186 this._owner._changed(65); 10187 }, 10188 10189 getRampPoint: function() { 10190 return this._rampPoint; 10191 }, 10192 10193 setRampPoint: function(rampPoint) { 10194 this._defaultRamp = rampPoint == null; 10195 this._rampPoint = rampPoint || 0; 10196 this._changed(); 10197 }, 10198 10199 getColor: function() { 10200 return this._color; 10201 }, 10202 10203 setColor: function(color) { 10204 this._color = Color.read(arguments); 10205 if (this._color === color) 10206 this._color = color.clone(); 10207 this._color._owner = this; 10208 this._changed(); 10209 }, 10210 10211 equals: function(stop) { 10212 return stop === this || stop && this._class === stop._class 10213 && this._color.equals(stop._color) 10214 && this._rampPoint == stop._rampPoint 10215 || false; 10216 } 10217}); 10218 10219var Style = Base.extend(new function() { 10220 var defaults = { 10221 fillColor: undefined, 10222 strokeColor: undefined, 10223 strokeWidth: 1, 10224 strokeCap: 'butt', 10225 strokeJoin: 'miter', 10226 strokeScaling: true, 10227 miterLimit: 10, 10228 dashOffset: 0, 10229 dashArray: [], 10230 windingRule: 'nonzero', 10231 shadowColor: undefined, 10232 shadowBlur: 0, 10233 shadowOffset: new Point(), 10234 selectedColor: undefined, 10235 fontFamily: 'sans-serif', 10236 fontWeight: 'normal', 10237 fontSize: 12, 10238 font: 'sans-serif', 10239 leading: null, 10240 justification: 'left' 10241 }; 10242 10243 var flags = { 10244 strokeWidth: 97, 10245 strokeCap: 97, 10246 strokeJoin: 97, 10247 strokeScaling: 105, 10248 miterLimit: 97, 10249 fontFamily: 9, 10250 fontWeight: 9, 10251 fontSize: 9, 10252 font: 9, 10253 leading: 9, 10254 justification: 9 10255 }; 10256 10257 var item = { beans: true }, 10258 fields = { 10259 _defaults: defaults, 10260 _textDefaults: new Base(defaults, { 10261 fillColor: new Color() 10262 }), 10263 beans: true 10264 }; 10265 10266 Base.each(defaults, function(value, key) { 10267 var isColor = /Color$/.test(key), 10268 isPoint = key === 'shadowOffset', 10269 part = Base.capitalize(key), 10270 flag = flags[key], 10271 set = 'set' + part, 10272 get = 'get' + part;
10273 10274 fields[set] = function(value) { 10275 var owner = this._owner, 10276 children = owner && owner._children; 10277 if (children && children.length > 0 10278 && !(owner instanceof CompoundPath)) { 10279 for (var i = 0, l = children.length; i < l; i++) 10280 children[i]._style[set](value); 10281 } else { 10282 var old = this._values[key]; 10283 if (old !== value) { 10284 if (isColor) { 10285 if (old) 10286 old._owner = undefined; 10287 if (value && value.constructor === Color) { 10288 if (value._owner) 10289 value = value.clone(); 10290 value._owner = owner; 10291 } 10292 } 10293 this._values[key] = value; 10294 if (owner) 10295 owner._changed(flag || 65); 10296 } 10297 } 10298 }; 10299 10300 fields[get] = function(_dontMerge) { 10301 var owner = this._owner, 10302 children = owner && owner._children, 10303 value; 10304 if (!children || children.length === 0 || _dontMerge 10305 || owner instanceof CompoundPath) { 10306 var value = this._values[key]; 10307 if (value === undefined) { 10308 value = this._defaults[key]; 10309 if (value && value.clone) 10310 value = value.clone(); 10311 } else { 10312 var ctor = isColor ? Color : isPoint ? Point : null; 10313 if (ctor && !(value && value.constructor === ctor)) { 10314 this._values[key] = value = ctor.read([value], 0, 10315 { readNull: true, clone: true }); 10316 if (value && isColor) 10317 value._owner = owner; 10318 } 10319 } 10320 return value; 10321 } 10322 for (var i = 0, l = children.length; i < l; i++) { 10323 var childValue = children[i]._style[get](); 10324 if (i === 0) { 10325 value = childValue; 10326 } else if (!Base.equals(value, childValue)) { 10327 return undefined; 10328 } 10329 } 10330 return value; 10331 }; 10332 10333 item[get] = function(_dontMerge) { 10334 return this._style[get](_dontMerge); 10335 }; 10336 10337 item[set] = function(value) { 10338 this._style[set](value); 10339 }; 10340 }); 10341 10342 Item.inject(item); 10343 return fields; 10344}, { 10345 _class: 'Style', 10346 10347 initialize: function Style(style, _owner, _project) { 10348 this._values = {}; 10349 this._owner = _owner; 10350 this._project = _owner && _owner._project || _project || paper.project; 10351 if (_owner instanceof TextItem) 10352 this._defaults = this._textDefaults; 10353 if (style) 10354 this.set(style); 10355 }, 10356 10357 set: function(style) { 10358 var isStyle = style instanceof Style, 10359 values = isStyle ? style._values : style; 10360 if (values) { 10361 for (var key in values) { 10362 if (key in this._defaults) { 10363 var value = values[key]; 10364 this[key] = value && isStyle && value.clone 10365 ? value.clone() : value; 10366 } 10367 } 10368 } 10369 }, 10370 10371 equals: function(style) { 10372 return style === this || style && this._class === style._class 10373 && Base.equals(this._values, style._values) 10374 || false; 10375 }, 10376 10377 hasFill: function() { 10378 return !!this.getFillColor(); 10379 }, 10380 10381 hasStroke: function() {
vendor: 4,483 bytes, lines 10382-10563
10382 return !!this.getStrokeColor() && this.getStrokeWidth() > 0; 10383 }, 10384 10385 hasShadow: function() { 10386 return !!this.getShadowColor() && this.getShadowBlur() > 0; 10387 }, 10388 10389 getView: function() { 10390 return this._project.getView(); 10391 }, 10392 10393 getFontStyle: function() { 10394 var fontSize = this.getFontSize(); 10395 return this.getFontWeight() 10396 + ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ') 10397 + this.getFontFamily(); 10398 }, 10399 10400 getFont: '#getFontFamily', 10401 setFont: '#setFontFamily', 10402 10403 getLeading: function getLeading() { 10404 var leading = getLeading.base.call(this), 10405 fontSize = this.getFontSize(); 10406 if (/pt|em|%|px/.test(fontSize)) 10407 fontSize = this.getView().getPixelSize(fontSize); 10408 return leading != null ? leading : fontSize * 1.2; 10409 } 10410 10411}); 10412 10413var DomElement = new function() { 10414 function handlePrefix(el, name, set, value) { 10415 var prefixes = ['', 'webkit', 'moz', 'Moz', 'ms', 'o'], 10416 suffix = name[0].toUpperCase() + name.substring(1); 10417 for (var i = 0; i < 6; i++) { 10418 var prefix = prefixes[i], 10419 key = prefix ? prefix + suffix : name; 10420 if (key in el) { 10421 if (set) { 10422 el[key] = value; 10423 } else { 10424 return el[key]; 10425 } 10426 break; 10427 } 10428 } 10429 } 10430 10431 return { 10432 getStyles: function(el) { 10433 var doc = el && el.nodeType !== 9 ? el.ownerDocument : el, 10434 view = doc && doc.defaultView; 10435 return view && view.getComputedStyle(el, ''); 10436 }, 10437 10438 getBounds: function(el, viewport) { 10439 var doc = el.ownerDocument, 10440 body = doc.body, 10441 html = doc.documentElement, 10442 rect; 10443 try { 10444 rect = el.getBoundingClientRect(); 10445 } catch (e) { 10446 rect = { left: 0, top: 0, width: 0, height: 0 }; 10447 } 10448 var x = rect.left - (html.clientLeft || body.clientLeft || 0), 10449 y = rect.top - (html.clientTop || body.clientTop || 0); 10450 if (!viewport) { 10451 var view = doc.defaultView; 10452 x += view.pageXOffset || html.scrollLeft || body.scrollLeft; 10453 y += view.pageYOffset || html.scrollTop || body.scrollTop; 10454 } 10455 return new Rectangle(x, y, rect.width, rect.height); 10456 }, 10457 10458 getViewportBounds: function(el) { 10459 var doc = el.ownerDocument, 10460 view = doc.defaultView, 10461 html = doc.documentElement; 10462 return new Rectangle(0, 0, 10463 view.innerWidth || html.clientWidth, 10464 view.innerHeight || html.clientHeight 10465 ); 10466 }, 10467 10468 getOffset: function(el, viewport) { 10469 return DomElement.getBounds(el, viewport).getPoint(); 10470 }, 10471 10472 getSize: function(el) { 10473 return DomElement.getBounds(el, true).getSize(); 10474 }, 10475 10476 isInvisible: function(el) { 10477 return DomElement.getSize(el).equals(new Size(0, 0)); 10478 }, 10479 10480 isInView: function(el) { 10481 return !DomElement.isInvisible(el) 10482 && DomElement.getViewportBounds(el).intersects( 10483 DomElement.getBounds(el, true)); 10484 }, 10485 10486 getPrefixed: function(el, name) { 10487 return handlePrefix(el, name); 10488 }, 10489 10490 setPrefixed: function(el, name, value) { 10491 if (typeof name === 'object') { 10492 for (var key in name) 10493 handlePrefix(el, key, true, name[key]); 10494 } else { 10495 handlePrefix(el, name, true, value); 10496 } 10497 } 10498 }; 10499}; 10500 10501var DomEvent = { 10502 add: function(el, events) { 10503 for (var type in events) { 10504 var func = events[type], 10505 parts = type.split(/[\s,]+/g); 10506 for (var i = 0, l = parts.length; i < l; i++) 10507 el.addEventListener(parts[i], func, false); 10508 } 10509 }, 10510 10511 remove: function(el, events) { 10512 for (var type in events) { 10513 var func = events[type], 10514 parts = type.split(/[\s,]+/g); 10515 for (var i = 0, l = parts.length; i < l; i++) 10516 el.removeEventListener(parts[i], func, false); 10517 } 10518 }, 10519 10520 getPoint: function(event) { 10521 var pos = event.targetTouches 10522 ? event.targetTouches.length 10523 ? event.targetTouches[0] 10524 : event.changedTouches[0] 10525 : event; 10526 return new Point( 10527 pos.pageX || pos.clientX + document.documentElement.scrollLeft, 10528 pos.pageY || pos.clientY + document.documentElement.scrollTop 10529 ); 10530 }, 10531 10532 getTarget: function(event) { 10533 return event.target || event.srcElement; 10534 }, 10535 10536 getRelatedTarget: function(event) { 10537 return event.relatedTarget || event.toElement; 10538 }, 10539 10540 getOffset: function(event, target) { 10541 return DomEvent.getPoint(event).subtract(DomElement.getOffset( 10542 target || DomEvent.getTarget(event))); 10543 }, 10544 10545 stop: function(event) { 10546 event.stopPropagation(); 10547 event.preventDefault(); 10548 } 10549}; 10550 10551DomEvent.requestAnimationFrame = new function() { 10552 var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'), 10553 requested = false, 10554 callbacks = [], 10555 focused = true, 10556 timer; 10557 10558 DomEvent.add(window, { 10559 focus: function() { 10560 focused = true; 10561 }, 10562 blur: function() { 10563 focused = false;
10564 } 10565 }); 10566 10567 function handleCallbacks() { 10568 for (var i = callbacks.length - 1; i >= 0; i--) { 10569 var entry = callbacks[i], 10570 func = entry[0], 10571 el = entry[1]; 10572 if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true' 10573 || focused) && DomElement.isInView(el)) { 10574 callbacks.splice(i, 1); 10575 func(); 10576 } 10577 } 10578 if (nativeRequest) { 10579 if (callbacks.length) { 10580 nativeRequest(handleCallbacks); 10581 } else { 10582 requested = false; 10583 } 10584 } 10585 } 10586 10587 return function(callback, element) { 10588 callbacks.push([callback, element]); 10589 if (nativeRequest) { 10590 if (!requested) { 10591 nativeRequest(handleCallbacks); 10592 requested = true; 10593 } 10594 } else if (!timer) { 10595 timer = setInterval(handleCallbacks, 1000 / 60); 10596 } 10597 }; 10598}; 10599 10600var View = Base.extend(Emitter, { 10601 _class: 'View', 10602 10603 initialize: function View(project, element) { 10604 this._project = project; 10605 this._scope = project._scope; 10606 this._element = element; 10607 var size; 10608 if (!this._pixelRatio) 10609 this._pixelRatio = window.devicePixelRatio || 1; 10610 this._id = element.getAttribute('id'); 10611 if (this._id == null) 10612 element.setAttribute('id', this._id = 'view-' + View._id++); 10613 DomEvent.add(element, this._viewEvents); 10614 var none = 'none'; 10615 DomElement.setPrefixed(element.style, { 10616 userSelect: none, 10617 touchAction: none, 10618 touchCallout: none, 10619 contentZooming: none, 10620 userDrag: none, 10621 tapHighlightColor: 'rgba(0,0,0,0)' 10622 }); 10623 10624 function getSize(name) { 10625 return element[name] || parseInt(element.getAttribute(name), 10); 10626 }; 10627 10628 function getCanvasSize() { 10629 var size = DomElement.getSize(element); 10630 return size.isNaN() || size.isZero() 10631 ? new Size(getSize('width'), getSize('height')) 10632 : size; 10633 }; 10634 10635 if (PaperScope.hasAttribute(element, 'resize')) { 10636 var that = this; 10637 DomEvent.add(window, this._windowEvents = { 10638 resize: function() { 10639 that.setViewSize(getCanvasSize()); 10640 } 10641 }); 10642 } 10643 this._setViewSize(size = getCanvasSize()); 10644 if (PaperScope.hasAttribute(element, 'stats') 10645 && typeof Stats !== 'undefined') { 10646 this._stats = new Stats(); 10647 var stats = this._stats.domElement, 10648 style = stats.style, 10649 offset = DomElement.getOffset(element); 10650 style.position = 'absolute'; 10651 style.left = offset.x + 'px'; 10652 style.top = offset.y + 'px'; 10653 document.body.appendChild(stats); 10654 } 10655 View._views.push(this); 10656 View._viewsById[this._id] = this; 10657 this._viewSize = size; 10658 (this._matrix = new Matrix())._owner = this; 10659 this._zoom = 1; 10660 if (!View._focused) 10661 View._focused = this; 10662 this._frameItems = {}; 10663 this._frameItemCount = 0; 10664 }, 10665 10666 remove: function() { 10667 if (!this._project) 10668 return false; 10669 if (View._focused === this) 10670 View._focused = null; 10671 View._views.splice(View._views.indexOf(this), 1); 10672 delete View._viewsById[this._id]; 10673 if (this._project._view === this) 10674 this._project._view = null; 10675 DomEvent.remove(this._element, this._viewEvents); 10676 DomEvent.remove(window, this._windowEvents); 10677 this._element = this._project = null; 10678 this.off('frame'); 10679 this._animate = false; 10680 this._frameItems = {}; 10681 return true; 10682 }, 10683 10684 _events: { 10685 onFrame: { 10686 install: function() { 10687 this.play(); 10688 }, 10689 10690 uninstall: function() { 10691 this.pause(); 10692 } 10693 }, 10694 10695 onResize: {} 10696 }, 10697 10698 _animate: false, 10699 _time: 0, 10700 _count: 0, 10701 10702 _requestFrame: function() { 10703 var that = this; 10704 DomEvent.requestAnimationFrame(function() { 10705 that._requested = false; 10706 if (!that._animate) 10707 return; 10708 that._requestFrame(); 10709 that._handleFrame(); 10710 }, this._element); 10711 this._requested = true; 10712 }, 10713 10714 _handleFrame: function() { 10715 paper = this._scope; 10716 var now = Date.now() / 1000, 10717 delta = this._before ? now - this._before : 0; 10718 this._before = now; 10719 this._handlingFrame = true; 10720 this.emit('frame', new Base({ 10721 delta: delta, 10722 time: this._time += delta, 10723 count: this._count++ 10724 })); 10725 if (this._stats) 10726 this._stats.update(); 10727 this._handlingFrame = false; 10728 this.update(); 10729 }, 10730 10731 _animateItem: function(item, animate) { 10732 var items = this._frameItems; 10733 if (animate) { 10734 items[item._id] = { 10735 item: item, 10736 time: 0, 10737 count: 0 10738 }; 10739 if (++this._frameItemCount === 1) 10740 this.on('frame', this._handleFrameItems); 10741 } else { 10742 delete items[item._id]; 10743 if (--this._frameItemCount === 0) { 10744 this.off('frame', this._handleFrameItems); 10745 } 10746 } 10747 }, 10748 10749 _handleFrameItems: function(event) { 10750 for (var i in this._frameItems) { 10751 var entry = this._frameItems[i]; 10752 entry.item.emit('frame', new Base(event, { 10753 time: entry.time += event.delta, 10754 count: entry.count++ 10755 })); 10756 } 10757 }, 10758 10759 _update: function() { 10760 this._project._needsUpdate = true; 10761 if (this._handlingFrame) 10762 return; 10763 if (this._animate) { 10764 this._handleFrame(); 10765 } else { 10766 this.update(); 10767 } 10768 }, 10769 10770 _changed: function(flags) { 10771 if (flags & 1) 10772 this._project._needsUpdate = true; 10773 }, 10774 10775 _transform: function(matrix) { 10776 this._matrix.concatenate(matrix); 10777 this._bounds = null; 10778 this._update(); 10779 }, 10780 10781 getElement: function() { 10782 return this._element; 10783 }, 10784 10785 getPixelRatio: function() { 10786 return this._pixelRatio; 10787 }, 10788 10789 getResolution: function() { 10790 return this._pixelRatio * 72; 10791 }, 10792 10793 getViewSize: function() { 10794 var size = this._viewSize; 10795 return new LinkedSize(size.width, size.height, this, 'setViewSize'); 10796 }, 10797 10798 setViewSize: function() { 10799 var size = Size.read(arguments), 10800 delta = size.subtract(this._viewSize); 10801 if (delta.isZero()) 10802 return; 10803 this._viewSize.set(size.width, size.height);
10804 this._setViewSize(size); 10805 this._bounds = null; 10806 this.emit('resize', { 10807 size: size, 10808 delta: delta 10809 }); 10810 this._update(); 10811 }, 10812 10813 _setViewSize: function(size) { 10814 var element = this._element; 10815 element.width = size.width; 10816 element.height = size.height; 10817 }, 10818 10819 getBounds: function() { 10820 if (!this._bounds) 10821 this._bounds = this._matrix.inverted()._transformBounds( 10822 new Rectangle(new Point(), this._viewSize)); 10823 return this._bounds; 10824 }, 10825 10826 getSize: function() { 10827 return this.getBounds().getSize(); 10828 }, 10829 10830 getCenter: function() { 10831 return this.getBounds().getCenter(); 10832 }, 10833 10834 setCenter: function() { 10835 var center = Point.read(arguments); 10836 this.scrollBy(center.subtract(this.getCenter())); 10837 }, 10838 10839 getZoom: function() { 10840 return this._zoom; 10841 }, 10842 10843 setZoom: function(zoom) { 10844 this._transform(new Matrix().scale(zoom / this._zoom, 10845 this.getCenter())); 10846 this._zoom = zoom; 10847 }, 10848 10849 isVisible: function() { 10850 return DomElement.isInView(this._element); 10851 }, 10852 10853 scrollBy: function() { 10854 this._transform(new Matrix().translate(Point.read(arguments).negate())); 10855 }, 10856 10857 play: function() { 10858 this._animate = true; 10859 if (!this._requested) 10860 this._requestFrame(); 10861 }, 10862 10863 pause: function() { 10864 this._animate = false; 10865 }, 10866 10867 draw: function() { 10868 this.update(); 10869 }, 10870 10871 projectToView: function() { 10872 return this._matrix._transformPoint(Point.read(arguments)); 10873 }, 10874 10875 viewToProject: function() { 10876 return this._matrix._inverseTransform(Point.read(arguments)); 10877 } 10878 10879}, { 10880 statics: { 10881 _views: [], 10882 _viewsById: {}, 10883 _id: 0, 10884 10885 create: function(project, element) { 10886 if (typeof element === 'string') 10887 element = document.getElementById(element); 10888 return new CanvasView(project, element); 10889 } 10890 } 10891}, new function() { 10892 var tool, 10893 prevFocus, 10894 tempFocus, 10895 dragging = false; 10896 10897 function getView(event) { 10898 var target = DomEvent.getTarget(event); 10899 return target.getAttribute && View._viewsById[target.getAttribute('id')]; 10900 } 10901 10902 function viewToProject(view, event) { 10903 return view.viewToProject(DomEvent.getOffset(event, view._element)); 10904 } 10905 10906 function updateFocus() { 10907 if (!View._focused || !View._focused.isVisible()) { 10908 for (var i = 0, l = View._views.length; i < l; i++) { 10909 var view = View._views[i]; 10910 if (view && view.isVisible()) { 10911 View._focused = tempFocus = view; 10912 break; 10913 } 10914 } 10915 } 10916 } 10917 10918 function handleMouseMove(view, point, event) { 10919 view._handleEvent('mousemove', point, event); 10920 var tool = view._scope.tool; 10921 if (tool) { 10922 tool._handleEvent(dragging && tool.responds('mousedrag') 10923 ? 'mousedrag' : 'mousemove', point, event); 10924 } 10925 view.update(); 10926 return tool; 10927 } 10928 10929 var navigator = window.navigator, 10930 mousedown, mousemove, mouseup; 10931 if (navigator.pointerEnabled || navigator.msPointerEnabled) { 10932 mousedown = 'pointerdown MSPointerDown'; 10933 mousemove = 'pointermove MSPointerMove'; 10934 mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel'; 10935 } else { 10936 mousedown = 'touchstart'; 10937 mousemove = 'touchmove'; 10938 mouseup = 'touchend touchcancel'; 10939 if (!('ontouchstart' in window && navigator.userAgent.match( 10940 /mobile|tablet|ip(ad|hone|od)|android|silk/i))) { 10941 mousedown += ' mousedown'; 10942 mousemove += ' mousemove'; 10943 mouseup += ' mouseup'; 10944 } 10945 } 10946 10947 var viewEvents = { 10948 'selectstart dragstart': function(event) { 10949 if (dragging) 10950 event.preventDefault(); 10951 } 10952 }; 10953 10954 var docEvents = { 10955 mouseout: function(event) { 10956 var view = View._focused, 10957 target = DomEvent.getRelatedTarget(event); 10958 if (view && (!target || target.nodeName === 'HTML')) 10959 handleMouseMove(view, viewToProject(view, event), event); 10960 }, 10961 10962 scroll: updateFocus 10963 }; 10964 10965 viewEvents[mousedown] = function(event) { 10966 var view = View._focused = getView(event), 10967 point = viewToProject(view, event); 10968 dragging = true; 10969 view._handleEvent('mousedown', point, event); 10970 if (tool = view._scope.tool) 10971 tool._handleEvent('mousedown', point, event); 10972 view.update(); 10973 }; 10974 10975 docEvents[mousemove] = function(event) { 10976 var view = View._focused; 10977 if (!dragging) { 10978 var target = getView(event); 10979 if (target) { 10980 if (view !== target) 10981 handleMouseMove(view, viewToProject(view, event), event); 10982 prevFocus = view;
10983 view = View._focused = tempFocus = target; 10984 } else if (tempFocus && tempFocus === view) { 10985 view = View._focused = prevFocus; 10986 updateFocus(); 10987 } 10988 } 10989 if (view) { 10990 var point = viewToProject(view, event); 10991 if (dragging || view.getBounds().contains(point)) 10992 tool = handleMouseMove(view, point, event); 10993 } 10994 }; 10995 10996 docEvents[mouseup] = function(event) { 10997 var view = View._focused; 10998 if (!view || !dragging) 10999 return; 11000 var point = viewToProject(view, event); 11001 dragging = false; 11002 view._handleEvent('mouseup', point, event); 11003 if (tool) 11004 tool._handleEvent('mouseup', point, event); 11005 view.update(); 11006 }; 11007 11008 DomEvent.add(document, docEvents); 11009 11010 DomEvent.add(window, { 11011 load: updateFocus 11012 }); 11013 11014 return { 11015 _viewEvents: viewEvents, 11016 11017 _handleEvent: function() {}, 11018 11019 statics: { 11020 updateFocus: updateFocus 11021 } 11022 }; 11023}); 11024 11025var CanvasView = View.extend({ 11026 _class: 'CanvasView', 11027 11028 initialize: function CanvasView(project, canvas) { 11029 if (!(canvas instanceof HTMLCanvasElement)) { 11030 var size = Size.read(arguments, 1); 11031 if (size.isZero()) 11032 throw new Error( 11033 'Cannot create CanvasView with the provided argument: ' 11034 + [].slice.call(arguments, 1)); 11035 canvas = CanvasProvider.getCanvas(size); 11036 } 11037 this._context = canvas.getContext('2d'); 11038 this._eventCounters = {}; 11039 this._pixelRatio = 1; 11040 if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) { 11041 var deviceRatio = window.devicePixelRatio || 1, 11042 backingStoreRatio = DomElement.getPrefixed(this._context, 11043 'backingStorePixelRatio') || 1; 11044 this._pixelRatio = deviceRatio / backingStoreRatio; 11045 } 11046 View.call(this, project, canvas); 11047 }, 11048 11049 _setViewSize: function(size) { 11050 var element = this._element, 11051 pixelRatio = this._pixelRatio, 11052 width = size.width, 11053 height = size.height; 11054 element.width = width * pixelRatio; 11055 element.height = height * pixelRatio; 11056 if (pixelRatio !== 1) { 11057 if (!PaperScope.hasAttribute(element, 'resize')) { 11058 var style = element.style; 11059 style.width = width + 'px'; 11060 style.height = height + 'px'; 11061 } 11062 this._context.scale(pixelRatio, pixelRatio); 11063 } 11064 }, 11065 11066 getPixelSize: function(size) { 11067 var browser = paper.browser, 11068 pixels; 11069 if (browser && browser.firefox) { 11070 var parent = this._element.parentNode, 11071 temp = document.createElement('div'); 11072 temp.style.fontSize = size; 11073 parent.appendChild(temp); 11074 pixels = parseFloat(DomElement.getStyles(temp).fontSize); 11075 parent.removeChild(temp); 11076 } else { 11077 var ctx = this._context, 11078 prevFont = ctx.font; 11079 ctx.font = size + ' serif'; 11080 pixels = parseFloat(ctx.font); 11081 ctx.font = prevFont; 11082 } 11083 return pixels; 11084 }, 11085 11086 getTextWidth: function(font, lines) { 11087 var ctx = this._context, 11088 prevFont = ctx.font, 11089 width = 0; 11090 ctx.font = font; 11091 for (var i = 0, l = lines.length; i < l; i++) 11092 width = Math.max(width, ctx.measureText(lines[i]).width); 11093 ctx.font = prevFont; 11094 return width; 11095 }, 11096 11097 update: function(force) { 11098 var project = this._project; 11099 if (!project || !force && !project._needsUpdate) 11100 return false; 11101 var ctx = this._context, 11102 size = this._viewSize; 11103 ctx.clearRect(0, 0, size.width + 1, size.height + 1); 11104 project.draw(ctx, this._matrix, this._pixelRatio); 11105 project._needsUpdate = false; 11106 return true; 11107 } 11108}, new function() { 11109 11110 var downPoint, 11111 lastPoint, 11112 overPoint, 11113 downItem, 11114 lastItem, 11115 overItem, 11116 dragItem, 11117 dblClick, 11118 clickTime; 11119 11120 function callEvent(view, type, event, point, target, lastPoint) { 11121 var item = target, 11122 mouseEvent; 11123 11124 function call(obj) { 11125 if (obj.responds(type)) { 11126 if (!mouseEvent) { 11127 mouseEvent = new MouseEvent(type, event, point, target, 11128 lastPoint ? point.subtract(lastPoint) : null); 11129 } 11130 if (obj.emit(type, mouseEvent) && mouseEvent.isStopped) { 11131 event.preventDefault(); 11132 return true; 11133 } 11134 } 11135 } 11136 11137 while (item) { 11138 if (call(item)) 11139 return true; 11140 item = item.getParent(); 11141 } 11142 if (call(view)) 11143 return true; 11144 return false; 11145 } 11146 11147 return { 11148 _handleEvent: function(type, point, event) { 11149 if (!this._eventCounters[type]) 11150 return; 11151 var project = this._project, 11152 hit = project.hitTest(point, { 11153 tolerance: 0, 11154 fill: true, 11155 stroke: true 11156 }), 11157 item = hit && hit.item, 11158 stopped = false;
11159 switch (type) { 11160 case 'mousedown': 11161 stopped = callEvent(this, type, event, point, item); 11162 dblClick = lastItem == item && (Date.now() - clickTime < 300); 11163 downItem = lastItem = item; 11164 downPoint = lastPoint = overPoint = point; 11165 dragItem = !stopped && item; 11166 while (dragItem && !dragItem.responds('mousedrag')) 11167 dragItem = dragItem._parent; 11168 break; 11169 case 'mouseup': 11170 stopped = callEvent(this, type, event, point, item, downPoint); 11171 if (dragItem) { 11172 if (lastPoint && !lastPoint.equals(point)) 11173 callEvent(this, 'mousedrag', event, point, dragItem, 11174 lastPoint); 11175 if (item !== dragItem) { 11176 overPoint = point; 11177 callEvent(this, 'mousemove', event, point, item, 11178 overPoint); 11179 } 11180 } 11181 if (!stopped && item && item === downItem) { 11182 clickTime = Date.now(); 11183 callEvent(this, dblClick && downItem.responds('doubleclick') 11184 ? 'doubleclick' : 'click', event, downPoint, item); 11185 dblClick = false; 11186 } 11187 downItem = dragItem = null; 11188 break; 11189 case 'mousemove': 11190 if (dragItem) 11191 stopped = callEvent(this, 'mousedrag', event, point, 11192 dragItem, lastPoint); 11193 if (!stopped) { 11194 if (item !== overItem) 11195 overPoint = point; 11196 stopped = callEvent(this, type, event, point, item, 11197 overPoint); 11198 } 11199 lastPoint = overPoint = point; 11200 if (item !== overItem) { 11201 callEvent(this, 'mouseleave', event, point, overItem); 11202 overItem = item; 11203 callEvent(this, 'mouseenter', event, point, item); 11204 } 11205 break; 11206 } 11207 return stopped; 11208 } 11209 }; 11210}); 11211 11212var Event = Base.extend({ 11213 _class: 'Event', 11214 11215 initialize: function Event(event) { 11216 this.event = event; 11217 }, 11218 11219 isPrevented: false, 11220 isStopped: false, 11221 11222 preventDefault: function() { 11223 this.isPrevented = true; 11224 this.event.preventDefault(); 11225 }, 11226 11227 stopPropagation: function() { 11228 this.isStopped = true; 11229 this.event.stopPropagation(); 11230 }, 11231 11232 stop: function() { 11233 this.stopPropagation(); 11234 this.preventDefault(); 11235 }, 11236 11237 getModifiers: function() { 11238 return Key.modifiers; 11239 } 11240}); 11241 11242var KeyEvent = Event.extend({ 11243 _class: 'KeyEvent', 11244 11245 initialize: function KeyEvent(down, key, character, event) { 11246 Event.call(this, event); 11247 this.type = down ? 'keydown' : 'keyup'; 11248 this.key = key; 11249 this.character = character; 11250 }, 11251 11252 toString: function() { 11253 return "{ type: '" + this.type 11254 + "', key: '" + this.key 11255 + "', character: '" + this.character 11256 + "', modifiers: " + this.getModifiers() 11257 + " }"; 11258 } 11259}); 11260 11261var Key = new function() { 11262 11263 var specialKeys = { 11264 8: 'backspace', 11265 9: 'tab', 11266 13: 'enter', 11267 16: 'shift', 11268 17: 'control', 11269 18: 'option', 11270 19: 'pause', 11271 20: 'caps-lock', 11272 27: 'escape', 11273 32: 'space', 11274 35: 'end', 11275 36: 'home', 11276 37: 'left', 11277 38: 'up', 11278 39: 'right', 11279 40: 'down', 11280 46: 'delete', 11281 91: 'command', 11282 93: 'command', 11283 224: 'command' 11284 }, 11285 11286 specialChars = { 11287 9: true, 11288 13: true, 11289 32: true 11290 }, 11291 11292 modifiers = new Base({ 11293 shift: false, 11294 control: false, 11295 option: false, 11296 command: false, 11297 capsLock: false, 11298 space: false 11299 }), 11300 11301 charCodeMap = {}, 11302 keyMap = {}, 11303 commandFixMap, 11304 downCode; 11305 11306 function handleKey(down, keyCode, charCode, event) { 11307 var character = charCode ? String.fromCharCode(charCode) : '', 11308 specialKey = specialKeys[keyCode], 11309 key = specialKey || character.toLowerCase(), 11310 type = down ? 'keydown' : 'keyup', 11311 view = View._focused, 11312 scope = view && view.isVisible() && view._scope, 11313 tool = scope && scope.tool, 11314 name; 11315 keyMap[key] = down; 11316 if (down) { 11317 charCodeMap[keyCode] = charCode; 11318 } else { 11319 delete charCodeMap[keyCode]; 11320 } 11321 if (specialKey && (name = Base.camelize(specialKey)) in modifiers) { 11322 modifiers[name] = down; 11323 var browser = paper.browser; 11324 if (name === 'command' && browser && browser.mac) { 11325 if (down) { 11326 commandFixMap = {}; 11327 } else { 11328 for (var code in commandFixMap) { 11329 if (code in charCodeMap) 11330 handleKey(false, code, commandFixMap[code], event); 11331 } 11332 commandFixMap = null; 11333 } 11334 } 11335 } else if (down && commandFixMap) { 11336 commandFixMap[keyCode] = charCode; 11337 } 11338 if (tool && tool.responds(type)) { 11339 paper = scope;
11340 tool.emit(type, new KeyEvent(down, key, character, event)); 11341 if (view) 11342 view.update(); 11343 } 11344 } 11345 11346 DomEvent.add(document, { 11347 keydown: function(event) { 11348 var code = event.which || event.keyCode; 11349 if (code in specialKeys || modifiers.command) { 11350 handleKey(true, code, 11351 code in specialChars || modifiers.command ? code : 0, 11352 event); 11353 } else { 11354 downCode = code; 11355 } 11356 }, 11357 11358 keypress: function(event) { 11359 if (downCode != null) { 11360 handleKey(true, downCode, event.which || event.keyCode, event); 11361 downCode = null; 11362 } 11363 }, 11364 11365 keyup: function(event) { 11366 var code = event.which || event.keyCode; 11367 if (code in charCodeMap) 11368 handleKey(false, code, charCodeMap[code], event); 11369 } 11370 }); 11371 11372 DomEvent.add(window, { 11373 blur: function(event) { 11374 for (var code in charCodeMap) 11375 handleKey(false, code, charCodeMap[code], event); 11376 } 11377 }); 11378 11379 return { 11380 modifiers: modifiers, 11381 11382 isDown: function(key) { 11383 return !!keyMap[key]; 11384 } 11385 }; 11386}; 11387 11388var MouseEvent = Event.extend({ 11389 _class: 'MouseEvent', 11390 11391 initialize: function MouseEvent(type, event, point, target, delta) { 11392 Event.call(this, event); 11393 this.type = type; 11394 this.point = point; 11395 this.target = target; 11396 this.delta = delta; 11397 }, 11398 11399 toString: function() { 11400 return "{ type: '" + this.type 11401 + "', point: " + this.point 11402 + ', target: ' + this.target 11403 + (this.delta ? ', delta: ' + this.delta : '') 11404 + ', modifiers: ' + this.getModifiers() 11405 + ' }'; 11406 } 11407}); 11408 11409var ToolEvent = Event.extend({ 11410 _class: 'ToolEvent', 11411 _item: null, 11412 11413 initialize: function ToolEvent(tool, type, event) { 11414 this.tool = tool; 11415 this.type = type; 11416 this.event = event; 11417 }, 11418 11419 _choosePoint: function(point, toolPoint) { 11420 return point ? point : toolPoint ? toolPoint.clone() : null; 11421 }, 11422 11423 getPoint: function() { 11424 return this._choosePoint(this._point, this.tool._point); 11425 }, 11426 11427 setPoint: function(point) { 11428 this._point = point; 11429 }, 11430 11431 getLastPoint: function() { 11432 return this._choosePoint(this._lastPoint, this.tool._lastPoint); 11433 }, 11434 11435 setLastPoint: function(lastPoint) { 11436 this._lastPoint = lastPoint; 11437 }, 11438 11439 getDownPoint: function() { 11440 return this._choosePoint(this._downPoint, this.tool._downPoint); 11441 }, 11442 11443 setDownPoint: function(downPoint) { 11444 this._downPoint = downPoint; 11445 }, 11446 11447 getMiddlePoint: function() { 11448 if (!this._middlePoint && this.tool._lastPoint) { 11449 return this.tool._point.add(this.tool._lastPoint).divide(2); 11450 } 11451 return this._middlePoint; 11452 }, 11453 11454 setMiddlePoint: function(middlePoint) { 11455 this._middlePoint = middlePoint; 11456 }, 11457 11458 getDelta: function() { 11459 return !this._delta && this.tool._lastPoint 11460 ? this.tool._point.subtract(this.tool._lastPoint) 11461 : this._delta; 11462 }, 11463 11464 setDelta: function(delta) { 11465 this._delta = delta; 11466 }, 11467 11468 getCount: function() { 11469 return /^mouse(down|up)$/.test(this.type) 11470 ? this.tool._downCount 11471 : this.tool._count; 11472 }, 11473 11474 setCount: function(count) { 11475 this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count'] 11476 = count; 11477 }, 11478 11479 getItem: function() { 11480 if (!this._item) { 11481 var result = this.tool._scope.project.hitTest(this.getPoint()); 11482 if (result) { 11483 var item = result.item, 11484 parent = item._parent; 11485 while (/^(Group|CompoundPath)$/.test(parent._class)) { 11486 item = parent; 11487 parent = parent._parent; 11488 } 11489 this._item = item; 11490 } 11491 } 11492 return this._item; 11493 }, 11494 11495 setItem: function(item) { 11496 this._item = item; 11497 }, 11498 11499 toString: function() { 11500 return '{ type: ' + this.type 11501 + ', point: ' + this.getPoint() 11502 + ', count: ' + this.getCount() 11503 + ', modifiers: ' + this.getModifiers() 11504 + ' }'; 11505 } 11506}); 11507 11508var Tool = PaperScopeItem.extend({ 11509 _class: 'Tool', 11510 _list: 'tools', 11511 _reference: 'tool', 11512 _events: [ 'onActivate', 'onDeactivate', 'onEditOptions', 11513 'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove', 11514 'onKeyDown', 'onKeyUp' ], 11515 11516 initialize: function Tool(props) { 11517 PaperScopeItem.call(this); 11518 this._firstMove = true; 11519 this._count = 0; 11520 this._downCount = 0; 11521 this._set(props); 11522 }, 11523 11524 getMinDistance: function() { 11525 return this._minDistance; 11526 }, 11527 11528 setMinDistance: function(minDistance) { 11529 this._minDistance = minDistance; 11530 if (minDistance != null && this._maxDistance != null 11531 && minDistance > this._maxDistance) { 11532 this._maxDistance = minDistance; 11533 } 11534 }, 11535 11536 getMaxDistance: function() { 11537 return this._maxDistance; 11538 }, 11539 11540 setMaxDistance: function(maxDistance) { 11541 this._maxDistance = maxDistance; 11542 if (this._minDistance != null && maxDistance != null 11543 && maxDistance < this._minDistance) { 11544 this._minDistance = maxDistance; 11545 } 11546 }, 11547 11548 getFixedDistance: function() { 11549 return this._minDistance == this._maxDistance 11550 ? this._minDistance : null; 11551 }, 11552 11553 setFixedDistance: function(distance) { 11554 this._minDistance = distance; 11555 this._maxDistance = distance; 11556 }, 11557 11558 _updateEvent: function(type, point, minDistance, maxDistance, start, 11559 needsChange, matchMaxDistance) { 11560 if (!start) { 11561 if (minDistance != null || maxDistance != null) { 11562 var minDist = minDistance != null ? minDistance : 0, 11563 vector = point.subtract(this._point), 11564 distance = vector.getLength(); 11565 if (distance < minDist) 11566 return false;
11567 if (maxDistance != null && maxDistance != 0) { 11568 if (distance > maxDistance) { 11569 point = this._point.add(vector.normalize(maxDistance)); 11570 } else if (matchMaxDistance) { 11571 return false; 11572 } 11573 } 11574 } 11575 if (needsChange && point.equals(this._point)) 11576 return false; 11577 } 11578 this._lastPoint = start && type == 'mousemove' ? point : this._point; 11579 this._point = point; 11580 switch (type) { 11581 case 'mousedown': 11582 this._lastPoint = this._downPoint; 11583 this._downPoint = this._point; 11584 this._downCount++; 11585 break; 11586 case 'mouseup': 11587 this._lastPoint = this._downPoint; 11588 break; 11589 } 11590 this._count = start ? 0 : this._count + 1; 11591 return true; 11592 }, 11593 11594 _fireEvent: function(type, event) { 11595 var sets = paper.project._removeSets; 11596 if (sets) { 11597 if (type === 'mouseup') 11598 sets.mousedrag = null; 11599 var set = sets[type]; 11600 if (set) { 11601 for (var id in set) { 11602 var item = set[id]; 11603 for (var key in sets) { 11604 var other = sets[key]; 11605 if (other && other != set) 11606 delete other[item._id]; 11607 } 11608 item.remove(); 11609 }
11610 sets[type] = null; 11611 } 11612 } 11613 return this.responds(type) 11614 && this.emit(type, new ToolEvent(this, type, event)); 11615 }, 11616 11617 _handleEvent: function(type, point, event) { 11618 paper = this._scope; 11619 var called = false; 11620 switch (type) { 11621 case 'mousedown': 11622 this._updateEvent(type, point, null, null, true, false, false); 11623 called = this._fireEvent(type, event); 11624 break; 11625 case 'mousedrag': 11626 var needsChange = false, 11627 matchMaxDistance = false; 11628 while (this._updateEvent(type, point, this.minDistance, 11629 this.maxDistance, false, needsChange, matchMaxDistance)) { 11630 called = this._fireEvent(type, event) || called; 11631 needsChange = true; 11632 matchMaxDistance = true; 11633 } 11634 break; 11635 case 'mouseup': 11636 if (!point.equals(this._point) 11637 && this._updateEvent('mousedrag', point, this.minDistance, 11638 this.maxDistance, false, false, false)) { 11639 called = this._fireEvent('mousedrag', event); 11640 } 11641 this._updateEvent(type, point, null, this.maxDistance, false, 11642 false, false); 11643 called = this._fireEvent(type, event) || called; 11644 this._updateEvent(type, point, null, null, true, false, false); 11645 this._firstMove = true; 11646 break; 11647 case 'mousemove': 11648 while (this._updateEvent(type, point, this.minDistance, 11649 this.maxDistance, this._firstMove, true, false)) { 11650 called = this._fireEvent(type, event) || called; 11651 this._firstMove = false; 11652 } 11653 break; 11654 } 11655 if (called) 11656 event.preventDefault(); 11657 return called; 11658 } 11659 11660}); 11661 11662var Http = { 11663 request: function(method, url, callback, async) { 11664 async = (async === undefined) ? true : async; 11665 var xhr = new (window.ActiveXObject || XMLHttpRequest)( 11666 'Microsoft.XMLHTTP'); 11667 xhr.open(method.toUpperCase(), url, async); 11668 if ('overrideMimeType' in xhr) 11669 xhr.overrideMimeType('text/plain'); 11670 xhr.onreadystatechange = function() { 11671 if (xhr.readyState === 4) { 11672 var status = xhr.status; 11673 if (status === 0 || status === 200) { 11674 callback.call(xhr, xhr.responseText); 11675 } else { 11676 throw new Error('Could not load ' + url + ' (Error ' 11677 + status + ')'); 11678 } 11679 } 11680 }; 11681 return xhr.send(null); 11682 } 11683}; 11684 11685var CanvasProvider = { 11686 canvases: [], 11687 11688 getCanvas: function(width, height) { 11689 var canvas, 11690 clear = true; 11691 if (typeof width === 'object') { 11692 height = width.height; 11693 width = width.width; 11694 } 11695 if (this.canvases.length) { 11696 canvas = this.canvases.pop(); 11697 } else { 11698 canvas = document.createElement('canvas'); 11699 } 11700 var ctx = canvas.getContext('2d'); 11701 if (canvas.width === width && canvas.height === height) { 11702 if (clear) 11703 ctx.clearRect(0, 0, width + 1, height + 1); 11704 } else { 11705 canvas.width = width; 11706 canvas.height = height; 11707 } 11708 ctx.save(); 11709 return canvas; 11710 }, 11711 11712 getContext: function(width, height) { 11713 return this.getCanvas(width, height).getContext('2d'); 11714 }, 11715 11716 release: function(obj) { 11717 var canvas = obj.canvas ? obj.canvas : obj; 11718 canvas.getContext('2d').restore(); 11719 this.canvases.push(canvas); 11720 } 11721}; 11722 11723var BlendMode = new function() { 11724 var min = Math.min, 11725 max = Math.max, 11726 abs = Math.abs, 11727 sr, sg, sb, sa, 11728 br, bg, bb, ba, 11729 dr, dg, db; 11730 11731 function getLum(r, g, b) { 11732 return 0.2989 * r + 0.587 * g + 0.114 * b; 11733 } 11734 11735 function setLum(r, g, b, l) { 11736 var d = l - getLum(r, g, b); 11737 dr = r + d; 11738 dg = g + d; 11739 db = b + d; 11740 var l = getLum(dr, dg, db), 11741 mn = min(dr, dg, db), 11742 mx = max(dr, dg, db); 11743 if (mn < 0) { 11744 var lmn = l - mn; 11745 dr = l + (dr - l) * l / lmn; 11746 dg = l + (dg - l) * l / lmn; 11747 db = l + (db - l) * l / lmn; 11748 } 11749 if (mx > 255) { 11750 var ln = 255 - l, 11751 mxl = mx - l; 11752 dr = l + (dr - l) * ln / mxl; 11753 dg = l + (dg - l) * ln / mxl; 11754 db = l + (db - l) * ln / mxl; 11755 } 11756 } 11757 11758 function getSat(r, g, b) { 11759 return max(r, g, b) - min(r, g, b); 11760 } 11761 11762 function setSat(r, g, b, s) { 11763 var col = [r, g, b], 11764 mx = max(r, g, b), 11765 mn = min(r, g, b), 11766 md; 11767 mn = mn === r ? 0 : mn === g ? 1 : 2; 11768 mx = mx === r ? 0 : mx === g ? 1 : 2; 11769 md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0; 11770 if (col[mx] > col[mn]) { 11771 col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]); 11772 col[mx] = s; 11773 } else { 11774 col[md] = col[mx] = 0; 11775 } 11776 col[mn] = 0; 11777 dr = col[0]; 11778 dg = col[1]; 11779 db = col[2]; 11780 } 11781 11782 var modes = { 11783 multiply: function() { 11784 dr = br * sr / 255; 11785 dg = bg * sg / 255; 11786 db = bb * sb / 255; 11787 }, 11788 11789 screen: function() { 11790 dr = br + sr - (br * sr / 255); 11791 dg = bg + sg - (bg * sg / 255); 11792 db = bb + sb - (bb * sb / 255); 11793 }, 11794 11795 overlay: function() {
vendor: 4,622 bytes, lines 11796-11962
11796 dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255; 11797 dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255; 11798 db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255; 11799 }, 11800 11801 'soft-light': function() { 11802 var t = sr * br / 255; 11803 dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255; 11804 t = sg * bg / 255; 11805 dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255; 11806 t = sb * bb / 255; 11807 db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255; 11808 }, 11809 11810 'hard-light': function() { 11811 dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255; 11812 dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255; 11813 db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255; 11814 }, 11815 11816 'color-dodge': function() { 11817 dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr)); 11818 dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg)); 11819 db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb)); 11820 }, 11821 11822 'color-burn': function() { 11823 dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr); 11824 dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg); 11825 db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb); 11826 }, 11827 11828 darken: function() { 11829 dr = br < sr ? br : sr; 11830 dg = bg < sg ? bg : sg; 11831 db = bb < sb ? bb : sb; 11832 }, 11833 11834 lighten: function() { 11835 dr = br > sr ? br : sr; 11836 dg = bg > sg ? bg : sg; 11837 db = bb > sb ? bb : sb; 11838 }, 11839 11840 difference: function() { 11841 dr = br - sr; 11842 if (dr < 0) 11843 dr = -dr; 11844 dg = bg - sg; 11845 if (dg < 0) 11846 dg = -dg; 11847 db = bb - sb; 11848 if (db < 0) 11849 db = -db; 11850 }, 11851 11852 exclusion: function() { 11853 dr = br + sr * (255 - br - br) / 255; 11854 dg = bg + sg * (255 - bg - bg) / 255; 11855 db = bb + sb * (255 - bb - bb) / 255; 11856 }, 11857 11858 hue: function() { 11859 setSat(sr, sg, sb, getSat(br, bg, bb)); 11860 setLum(dr, dg, db, getLum(br, bg, bb)); 11861 }, 11862 11863 saturation: function() { 11864 setSat(br, bg, bb, getSat(sr, sg, sb)); 11865 setLum(dr, dg, db, getLum(br, bg, bb)); 11866 }, 11867 11868 luminosity: function() { 11869 setLum(br, bg, bb, getLum(sr, sg, sb)); 11870 }, 11871 11872 color: function() { 11873 setLum(sr, sg, sb, getLum(br, bg, bb)); 11874 }, 11875 11876 add: function() { 11877 dr = min(br + sr, 255); 11878 dg = min(bg + sg, 255); 11879 db = min(bb + sb, 255); 11880 }, 11881 11882 subtract: function() { 11883 dr = max(br - sr, 0); 11884 dg = max(bg - sg, 0); 11885 db = max(bb - sb, 0); 11886 }, 11887 11888 average: function() { 11889 dr = (br + sr) / 2; 11890 dg = (bg + sg) / 2; 11891 db = (bb + sb) / 2; 11892 }, 11893 11894 negation: function() { 11895 dr = 255 - abs(255 - sr - br); 11896 dg = 255 - abs(255 - sg - bg); 11897 db = 255 - abs(255 - sb - bb); 11898 } 11899 }; 11900 11901 var nativeModes = this.nativeModes = Base.each([ 11902 'source-over', 'source-in', 'source-out', 'source-atop', 11903 'destination-over', 'destination-in', 'destination-out', 11904 'destination-atop', 'lighter', 'darker', 'copy', 'xor' 11905 ], function(mode) { 11906 this[mode] = true; 11907 }, {}); 11908 11909 var ctx = CanvasProvider.getContext(1, 1); 11910 Base.each(modes, function(func, mode) { 11911 var darken = mode === 'darken', 11912 ok = false; 11913 ctx.save(); 11914 try { 11915 ctx.fillStyle = darken ? '#300' : '#a00'; 11916 ctx.fillRect(0, 0, 1, 1); 11917 ctx.globalCompositeOperation = mode; 11918 if (ctx.globalCompositeOperation === mode) { 11919 ctx.fillStyle = darken ? '#a00' : '#300'; 11920 ctx.fillRect(0, 0, 1, 1); 11921 ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51; 11922 } 11923 } catch (e) {} 11924 ctx.restore(); 11925 nativeModes[mode] = ok; 11926 }); 11927 CanvasProvider.release(ctx); 11928 11929 this.process = function(mode, srcContext, dstContext, alpha, offset) { 11930 var srcCanvas = srcContext.canvas, 11931 normal = mode === 'normal'; 11932 if (normal || nativeModes[mode]) { 11933 dstContext.save(); 11934 dstContext.setTransform(1, 0, 0, 1, 0, 0); 11935 dstContext.globalAlpha = alpha; 11936 if (!normal) 11937 dstContext.globalCompositeOperation = mode; 11938 dstContext.drawImage(srcCanvas, offset.x, offset.y); 11939 dstContext.restore(); 11940 } else { 11941 var process = modes[mode]; 11942 if (!process) 11943 return; 11944 var dstData = dstContext.getImageData(offset.x, offset.y, 11945 srcCanvas.width, srcCanvas.height), 11946 dst = dstData.data, 11947 src = srcContext.getImageData(0, 0, 11948 srcCanvas.width, srcCanvas.height).data; 11949 for (var i = 0, l = dst.length; i < l; i += 4) { 11950 sr = src[i]; 11951 br = dst[i]; 11952 sg = src[i + 1]; 11953 bg = dst[i + 1]; 11954 sb = src[i + 2]; 11955 bb = dst[i + 2]; 11956 sa = src[i + 3]; 11957 ba = dst[i + 3]; 11958 process(); 11959 var a1 = sa * alpha / 255, 11960 a2 = 1 - a1; 11961 dst[i] = a1 * dr + a2 * br; 11962 dst[i + 1] = a1 * dg + a2 * bg;
11963 dst[i + 2] = a1 * db + a2 * bb; 11964 dst[i + 3] = sa * alpha + a2 * ba; 11965 } 11966 dstContext.putImageData(dstData, offset.x, offset.y); 11967 } 11968 }; 11969}; 11970 11971var SVGStyles = Base.each({ 11972 fillColor: ['fill', 'color'], 11973 strokeColor: ['stroke', 'color'], 11974 strokeWidth: ['stroke-width', 'number'], 11975 strokeCap: ['stroke-linecap', 'string'], 11976 strokeJoin: ['stroke-linejoin', 'string'], 11977 strokeScaling: ['vector-effect', 'lookup', { 11978 true: 'none', 11979 false: 'non-scaling-stroke' 11980 }, function(item, value) { 11981 return !value 11982 && (item instanceof PathItem 11983 || item instanceof Shape 11984 || item instanceof TextItem); 11985 }], 11986 miterLimit: ['stroke-miterlimit', 'number'], 11987 dashArray: ['stroke-dasharray', 'array'], 11988 dashOffset: ['stroke-dashoffset', 'number'], 11989 fontFamily: ['font-family', 'string'], 11990 fontWeight: ['font-weight', 'string'], 11991 fontSize: ['font-size', 'number'], 11992 justification: ['text-anchor', 'lookup', { 11993 left: 'start', 11994 center: 'middle', 11995 right: 'end' 11996 }], 11997 opacity: ['opacity', 'number'], 11998 blendMode: ['mix-blend-mode', 'string'] 11999}, function(entry, key) { 12000 var part = Base.capitalize(key), 12001 lookup = entry[2]; 12002 this[key] = { 12003 type: entry[1], 12004 property: key, 12005 attribute: entry[0], 12006 toSVG: lookup, 12007 fromSVG: lookup && Base.each(lookup, function(value, name) { 12008 this[value] = name; 12009 }, {}), 12010 exportFilter: entry[3], 12011 get: 'get' + part, 12012 set: 'set' + part 12013 }; 12014}, {}); 12015 12016var SVGNamespaces = { 12017 href: 'http://www.w3.org/1999/xlink', 12018 xlink: 'http://www.w3.org/2000/xmlns' 12019}; 12020 12021new function() { 12022 var formatter; 12023 12024 function setAttributes(node, attrs) { 12025 for (var key in attrs) { 12026 var val = attrs[key], 12027 namespace = SVGNamespaces[key]; 12028 if (typeof val === 'number') 12029 val = formatter.number(val); 12030 if (namespace) { 12031 node.setAttributeNS(namespace, key, val); 12032 } else { 12033 node.setAttribute(key, val); 12034 } 12035 } 12036 return node; 12037 } 12038 12039 function createElement(tag, attrs) { 12040 return setAttributes( 12041 document.createElementNS('http://www.w3.org/2000/svg', tag), attrs); 12042 } 12043 12044 function getTransform(matrix, coordinates, center) { 12045 var attrs = new Base(), 12046 trans = matrix.getTranslation(); 12047 if (coordinates) { 12048 matrix = matrix.shiftless(); 12049 var point = matrix._inverseTransform(trans); 12050 attrs[center ? 'cx' : 'x'] = point.x;
12051 attrs[center ? 'cy' : 'y'] = point.y; 12052 trans = null; 12053 } 12054 if (!matrix.isIdentity()) { 12055 var decomposed = matrix.decompose(); 12056 if (decomposed && !decomposed.shearing) { 12057 var parts = [], 12058 angle = decomposed.rotation, 12059 scale = decomposed.scaling; 12060 if (trans && !trans.isZero()) 12061 parts.push('translate(' + formatter.point(trans) + ')'); 12062 if (!Numerical.isZero(scale.x - 1) 12063 || !Numerical.isZero(scale.y - 1)) 12064 parts.push('scale(' + formatter.point(scale) +')'); 12065 if (angle) 12066 parts.push('rotate(' + formatter.number(angle) + ')'); 12067 attrs.transform = parts.join(' '); 12068 } else { 12069 attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')'; 12070 } 12071 } 12072 return attrs; 12073 } 12074 12075 function exportGroup(item, options) { 12076 var attrs = getTransform(item._matrix), 12077 children = item._children; 12078 var node = createElement('g', attrs); 12079 for (var i = 0, l = children.length; i < l; i++) { 12080 var child = children[i]; 12081 var childNode = exportSVG(child, options); 12082 if (childNode) { 12083 if (child.isClipMask()) { 12084 var clip = createElement('clipPath'); 12085 clip.appendChild(childNode); 12086 setDefinition(child, clip, 'clip'); 12087 setAttributes(node, { 12088 'clip-path': 'url(#' + clip.id + ')' 12089 }); 12090 } else { 12091 node.appendChild(childNode); 12092 } 12093 } 12094 } 12095 return node; 12096 } 12097 12098 function exportRaster(item, options) { 12099 var attrs = getTransform(item._matrix, true), 12100 size = item.getSize(), 12101 image = item.getImage(); 12102 attrs.x -= size.width / 2; 12103 attrs.y -= size.height / 2; 12104 attrs.width = size.width; 12105 attrs.height = size.height; 12106 attrs.href = options.embedImages === false && image && image.src 12107 || item.toDataURL(); 12108 return createElement('image', attrs); 12109 } 12110 12111 function exportPath(item, options) { 12112 var matchShapes = options.matchShapes; 12113 if (matchShapes) { 12114 var shape = item.toShape(false); 12115 if (shape) 12116 return exportShape(shape, options); 12117 } 12118 var segments = item._segments, 12119 type, 12120 attrs = getTransform(item._matrix); 12121 if (segments.length === 0) 12122 return null; 12123 if (matchShapes && !item.hasHandles()) { 12124 if (segments.length >= 3) { 12125 type = item._closed ? 'polygon' : 'polyline'; 12126 var parts = []; 12127 for(var i = 0, l = segments.length; i < l; i++) 12128 parts.push(formatter.point(segments[i]._point)); 12129 attrs.points = parts.join(' '); 12130 } else { 12131 type = 'line'; 12132 var first = segments[0]._point, 12133 last = segments[segments.length - 1]._point; 12134 attrs.set({ 12135 x1: first.x, 12136 y1: first.y, 12137 x2: last.x, 12138 y2: last.y 12139 }); 12140 } 12141 } else { 12142 type = 'path'; 12143 attrs.d = item.getPathData(null, options.precision); 12144 } 12145 return createElement(type, attrs); 12146 } 12147 12148 function exportShape(item) { 12149 var type = item._type, 12150 radius = item._radius, 12151 attrs = getTransform(item._matrix, true, type !== 'rectangle'); 12152 if (type === 'rectangle') { 12153 type = 'rect'; 12154 var size = item._size, 12155 width = size.width, 12156 height = size.height; 12157 attrs.x -= width / 2; 12158 attrs.y -= height / 2; 12159 attrs.width = width; 12160 attrs.height = height; 12161 if (radius.isZero()) 12162 radius = null; 12163 } 12164 if (radius) { 12165 if (type === 'circle') { 12166 attrs.r = radius; 12167 } else { 12168 attrs.rx = radius.width; 12169 attrs.ry = radius.height; 12170 } 12171 } 12172 return createElement(type, attrs); 12173 } 12174 12175 function exportCompoundPath(item, options) { 12176 var attrs = getTransform(item._matrix); 12177 var data = item.getPathData(null, options.precision); 12178 if (data) 12179 attrs.d = data; 12180 return createElement('path', attrs); 12181 } 12182 12183 function exportPlacedSymbol(item, options) { 12184 var attrs = getTransform(item._matrix, true), 12185 symbol = item.getSymbol(), 12186 symbolNode = getDefinition(symbol, 'symbol'), 12187 definition = symbol.getDefinition(), 12188 bounds = definition.getBounds(); 12189 if (!symbolNode) { 12190 symbolNode = createElement('symbol', {
12191 viewBox: formatter.rectangle(bounds) 12192 }); 12193 symbolNode.appendChild(exportSVG(definition, options)); 12194 setDefinition(symbol, symbolNode, 'symbol'); 12195 } 12196 attrs.href = '#' + symbolNode.id; 12197 attrs.x += bounds.x; 12198 attrs.y += bounds.y; 12199 attrs.width = formatter.number(bounds.width); 12200 attrs.height = formatter.number(bounds.height); 12201 attrs.overflow = 'visible'; 12202 return createElement('use', attrs); 12203 } 12204 12205 function exportGradient(color) { 12206 var gradientNode = getDefinition(color, 'color'); 12207 if (!gradientNode) { 12208 var gradient = color.getGradient(), 12209 radial = gradient._radial, 12210 origin = color.getOrigin().transform(), 12211 destination = color.getDestination().transform(), 12212 attrs; 12213 if (radial) { 12214 attrs = { 12215 cx: origin.x, 12216 cy: origin.y, 12217 r: origin.getDistance(destination) 12218 }; 12219 var highlight = color.getHighlight(); 12220 if (highlight) { 12221 highlight = highlight.transform(); 12222 attrs.fx = highlight.x; 12223 attrs.fy = highlight.y; 12224 } 12225 } else { 12226 attrs = { 12227 x1: origin.x, 12228 y1: origin.y, 12229 x2: destination.x, 12230 y2: destination.y 12231 }; 12232 } 12233 attrs.gradientUnits = 'userSpaceOnUse'; 12234 gradientNode = createElement( 12235 (radial ? 'radial' : 'linear') + 'Gradient', attrs); 12236 var stops = gradient._stops; 12237 for (var i = 0, l = stops.length; i < l; i++) { 12238 var stop = stops[i], 12239 stopColor = stop._color, 12240 alpha = stopColor.getAlpha(); 12241 attrs = { 12242 offset: stop._rampPoint, 12243 'stop-color': stopColor.toCSS(true) 12244 }; 12245 if (alpha < 1) 12246 attrs['stop-opacity'] = alpha; 12247 gradientNode.appendChild(createElement('stop', attrs)); 12248 } 12249 setDefinition(color, gradientNode, 'color'); 12250 } 12251 return 'url(#' + gradientNode.id + ')'; 12252 } 12253 12254 function exportText(item) { 12255 var node = createElement('text', getTransform(item._matrix, true)); 12256 node.textContent = item._content; 12257 return node; 12258 } 12259 12260 var exporters = { 12261 Group: exportGroup, 12262 Layer: exportGroup, 12263 Raster: exportRaster, 12264 Path: exportPath, 12265 Shape: exportShape, 12266 CompoundPath: exportCompoundPath, 12267 PlacedSymbol: exportPlacedSymbol, 12268 PointText: exportText 12269 }; 12270 12271 function applyStyle(item, node, isRoot) { 12272 var attrs = {}, 12273 parent = !isRoot && item.getParent(); 12274 12275 if (item._name != null) 12276 attrs.id = item._name; 12277 12278 Base.each(SVGStyles, function(entry) { 12279 var get = entry.get, 12280 type = entry.type, 12281 value = item[get](); 12282 if (entry.exportFilter 12283 ? entry.exportFilter(item, value) 12284 : !parent || !Base.equals(parent[get](), value)) { 12285 if (type === 'color' && value != null) { 12286 var alpha = value.getAlpha(); 12287 if (alpha < 1) 12288 attrs[entry.attribute + '-opacity'] = alpha; 12289 } 12290 attrs[entry.attribute] = value == null 12291 ? 'none' 12292 : type === 'number' 12293 ? formatter.number(value) 12294 : type === 'color' 12295 ? value.gradient 12296 ? exportGradient(value, item) 12297 : value.toCSS(true) 12298 : type === 'array' 12299 ? value.join(',') 12300 : type === 'lookup' 12301 ? entry.toSVG[value] 12302 : value; 12303 } 12304 }); 12305 12306 if (attrs.opacity === 1) 12307 delete attrs.opacity; 12308 12309 if (!item._visible) 12310 attrs.visibility = 'hidden'; 12311 12312 return setAttributes(node, attrs); 12313 } 12314 12315 var definitions; 12316 function getDefinition(item, type) { 12317 if (!definitions) 12318 definitions = { ids: {}, svgs: {} }; 12319 return item && definitions.svgs[type + '-' + item._id]; 12320 } 12321 12322 function setDefinition(item, node, type) { 12323 if (!definitions) 12324 getDefinition(); 12325 var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1; 12326 node.id = type + '-' + id; 12327 definitions.svgs[type + '-' + item._id] = node; 12328 } 12329 12330 function exportDefinitions(node, options) { 12331 var svg = node, 12332 defs = null; 12333 if (definitions) { 12334 svg = node.nodeName.toLowerCase() === 'svg' && node; 12335 for (var i in definitions.svgs) { 12336 if (!defs) { 12337 if (!svg) { 12338 svg = createElement('svg'); 12339 svg.appendChild(node); 12340 } 12341 defs = svg.insertBefore(createElement('defs'), 12342 svg.firstChild); 12343 } 12344 defs.appendChild(definitions.svgs[i]); 12345 } 12346 definitions = null; 12347 } 12348 return options.asString 12349 ? new XMLSerializer().serializeToString(svg) 12350 : svg; 12351 } 12352 12353 function exportSVG(item, options, isRoot) { 12354 var exporter = exporters[item._class], 12355 node = exporter && exporter(item, options); 12356 if (node) { 12357 var onExport = options.onExport; 12358 if (onExport) 12359 node = onExport(item, node, options) || node; 12360 var data = JSON.stringify(item._data); 12361 if (data && data !== '{}' && data !== 'null') 12362 node.setAttribute('data-paper-data', data); 12363 } 12364 return node && applyStyle(item, node, isRoot); 12365 } 12366 12367 function setOptions(options) { 12368 if (!options) 12369 options = {}; 12370 formatter = new Formatter(options.precision); 12371 return options; 12372 } 12373 12374 Item.inject({ 12375 exportSVG: function(options) { 12376 options = setOptions(options); 12377 return exportDefinitions(exportSVG(this, options, true), options); 12378 } 12379 }); 12380 12381 Project.inject({ 12382 exportSVG: function(options) { 12383 options = setOptions(options); 12384 var layers = this.layers, 12385 view = this.getView(), 12386 size = view.getViewSize(), 12387 node = createElement('svg', { 12388 x: 0, 12389 y: 0, 12390 width: size.width, 12391 height: size.height, 12392 version: '1.1', 12393 xmlns: 'http://www.w3.org/2000/svg', 12394 'xmlns:xlink': 'http://www.w3.org/1999/xlink' 12395 }), 12396 parent = node, 12397 matrix = view._matrix; 12398 if (!matrix.isIdentity()) 12399 parent = node.appendChild( 12400 createElement('g', getTransform(matrix))); 12401 for (var i = 0, l = layers.length; i < l; i++) 12402 parent.appendChild(exportSVG(layers[i], options, true)); 12403 return exportDefinitions(node, options); 12404 } 12405 }); 12406}; 12407 12408new function() { 12409 12410 function getValue(node, name, isString, allowNull) { 12411 var namespace = SVGNamespaces[name], 12412 value = namespace 12413 ? node.getAttributeNS(namespace, name) 12414 : node.getAttribute(name); 12415 if (value === 'null') 12416 value = null; 12417 return value == null 12418 ? allowNull 12419 ? null 12420 : isString 12421 ? '' 12422 : 0 12423 : isString 12424 ? value 12425 : parseFloat(value); 12426 } 12427 12428 function getPoint(node, x, y, allowNull) { 12429 x = getValue(node, x, false, allowNull); 12430 y = getValue(node, y, false, allowNull); 12431 return allowNull && (x == null || y == null) ? null 12432 : new Point(x, y); 12433 } 12434 12435 function getSize(node, w, h, allowNull) { 12436 w = getValue(node, w, false, allowNull); 12437 h = getValue(node, h, false, allowNull); 12438 return allowNull && (w == null || h == null) ? null 12439 : new Size(w, h); 12440 } 12441 12442 function convertValue(value, type, lookup) { 12443 return value === 'none' 12444 ? null 12445 : type === 'number' 12446 ? parseFloat(value) 12447 : type === 'array' 12448 ? value ? value.split(/[\s,]+/g).map(parseFloat) : [] 12449 : type === 'color' 12450 ? getDefinition(value) || value 12451 : type === 'lookup' 12452 ? lookup[value] 12453 : value; 12454 } 12455 12456 function importGroup(node, type, options, isRoot) { 12457 var nodes = node.childNodes, 12458 isClip = type === 'clippath', 12459 item = new Group(), 12460 project = item._project, 12461 currentStyle = project._currentStyle, 12462 children = []; 12463 if (!isClip) { 12464 item = applyAttributes(item, node, isRoot); 12465 project._currentStyle = item._style.clone(); 12466 } 12467 if (isRoot) { 12468 var defs = node.querySelectorAll('defs'); 12469 for (var i = 0, l = defs.length; i < l; i++) { 12470 importSVG(defs[i], options, false); 12471 } 12472 } 12473 for (var i = 0, l = nodes.length; i < l; i++) { 12474 var childNode = nodes[i], 12475 child; 12476 if (childNode.nodeType === 1 12477 && childNode.nodeName.toLowerCase() !== 'defs'
12478 && (child = importSVG(childNode, options, false)) 12479 && !(child instanceof Symbol)) 12480 children.push(child); 12481 } 12482 item.addChildren(children); 12483 if (isClip) 12484 item = applyAttributes(item.reduce(), node, isRoot); 12485 project._currentStyle = currentStyle; 12486 if (isClip || type === 'defs') { 12487 item.remove(); 12488 item = null; 12489 } 12490 return item; 12491 } 12492 12493 function importPoly(node, type) { 12494 var coords = node.getAttribute('points').match( 12495 /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g), 12496 points = []; 12497 for (var i = 0, l = coords.length; i < l; i += 2) 12498 points.push(new Point( 12499 parseFloat(coords[i]), 12500 parseFloat(coords[i + 1]))); 12501 var path = new Path(points); 12502 if (type === 'polygon') 12503 path.closePath(); 12504 return path; 12505 } 12506 12507 function importPath(node) { 12508 var data = node.getAttribute('d'), 12509 param = { pathData: data }; 12510 return (data.match(/m/gi) || []).length > 1 || /z\S+/i.test(data) 12511 ? new CompoundPath(param) 12512 : new Path(param); 12513 } 12514 12515 function importGradient(node, type) { 12516 var id = (getValue(node, 'href', true) || '').substring(1), 12517 isRadial = type === 'radialgradient', 12518 gradient; 12519 if (id) { 12520 gradient = definitions[id].getGradient(); 12521 } else { 12522 var nodes = node.childNodes, 12523 stops = []; 12524 for (var i = 0, l = nodes.length; i < l; i++) { 12525 var child = nodes[i]; 12526 if (child.nodeType === 1) 12527 stops.push(applyAttributes(new GradientStop(), child)); 12528 } 12529 gradient = new Gradient(stops, isRadial); 12530 } 12531 var origin, destination, highlight; 12532 if (isRadial) { 12533 origin = getPoint(node, 'cx', 'cy'); 12534 destination = origin.add(getValue(node, 'r'), 0); 12535 highlight = getPoint(node, 'fx', 'fy', true); 12536 } else { 12537 origin = getPoint(node, 'x1', 'y1'); 12538 destination = getPoint(node, 'x2', 'y2'); 12539 } 12540 applyAttributes( 12541 new Color(gradient, origin, destination, highlight), node); 12542 return null; 12543 } 12544 12545 var importers = { 12546 '#document': function (node, type, options, isRoot) { 12547 var nodes = node.childNodes; 12548 for (var i = 0, l = nodes.length; i < l; i++) { 12549 var child = nodes[i]; 12550 if (child.nodeType === 1) { 12551 var next = child.nextSibling; 12552 document.body.appendChild(child); 12553 var item = importSVG(child, options, isRoot); 12554 if (next) { 12555 node.insertBefore(child, next); 12556 } else { 12557 node.appendChild(child); 12558 } 12559 return item; 12560 } 12561 } 12562 }, 12563 g: importGroup, 12564 svg: importGroup, 12565 clippath: importGroup, 12566 polygon: importPoly, 12567 polyline: importPoly, 12568 path: importPath, 12569 lineargradient: importGradient, 12570 radialgradient: importGradient, 12571 12572 image: function (node) { 12573 var raster = new Raster(getValue(node, 'href', true)); 12574 raster.on('load', function() { 12575 var size = getSize(node, 'width', 'height'); 12576 this.setSize(size); 12577 var center = this._matrix._transformPoint( 12578 getPoint(node, 'x', 'y').add(size.divide(2))); 12579 this.translate(center); 12580 }); 12581 return raster; 12582 }, 12583 12584 symbol: function(node, type, options, isRoot) { 12585 return new Symbol(importGroup(node, type, options, isRoot), true); 12586 }, 12587 12588 defs: importGroup, 12589 12590 use: function(node) { 12591 var id = (getValue(node, 'href', true) || '').substring(1), 12592 definition = definitions[id], 12593 point = getPoint(node, 'x', 'y'); 12594 return definition 12595 ? definition instanceof Symbol 12596 ? definition.place(point) 12597 : definition.clone().translate(point) 12598 : null; 12599 }, 12600 12601 circle: function(node) { 12602 return new Shape.Circle(getPoint(node, 'cx', 'cy'), 12603 getValue(node, 'r')); 12604 }, 12605 12606 ellipse: function(node) { 12607 return new Shape.Ellipse({ 12608 center: getPoint(node, 'cx', 'cy'), 12609 radius: getSize(node, 'rx', 'ry') 12610 }); 12611 }, 12612 12613 rect: function(node) { 12614 var point = getPoint(node, 'x', 'y'), 12615 size = getSize(node, 'width', 'height'), 12616 radius = getSize(node, 'rx', 'ry'); 12617 return new Shape.Rectangle(new Rectangle(point, size), radius); 12618 }, 12619 12620 line: function(node) { 12621 return new Path.Line(getPoint(node, 'x1', 'y1'), 12622 getPoint(node, 'x2', 'y2')); 12623 }, 12624 12625 text: function(node) { 12626 var text = new PointText(getPoint(node, 'x', 'y') 12627 .add(getPoint(node, 'dx', 'dy'))); 12628 text.setContent(node.textContent.trim() || ''); 12629 return text; 12630 } 12631 }; 12632 12633 function applyTransform(item, value, name, node) {
12634 var transforms = (node.getAttribute(name) || '').split(/\)\s*/g), 12635 matrix = new Matrix(); 12636 for (var i = 0, l = transforms.length; i < l; i++) { 12637 var transform = transforms[i]; 12638 if (!transform) 12639 break; 12640 var parts = transform.split(/\(\s*/), 12641 command = parts[0], 12642 v = parts[1].split(/[\s,]+/g); 12643 for (var j = 0, m = v.length; j < m; j++) 12644 v[j] = parseFloat(v[j]); 12645 switch (command) { 12646 case 'matrix': 12647 matrix.concatenate( 12648 new Matrix(v[0], v[1], v[2], v[3], v[4], v[5])); 12649 break; 12650 case 'rotate': 12651 matrix.rotate(v[0], v[1], v[2]); 12652 break; 12653 case 'translate': 12654 matrix.translate(v[0], v[1]); 12655 break; 12656 case 'scale': 12657 matrix.scale(v); 12658 break; 12659 case 'skewX': 12660 matrix.skew(v[0], 0); 12661 break; 12662 case 'skewY': 12663 matrix.skew(0, v[0]); 12664 break; 12665 } 12666 } 12667 item.transform(matrix); 12668 } 12669 12670 function applyOpacity(item, value, name) { 12671 var color = item[name === 'fill-opacity' ? 'getFillColor' 12672 : 'getStrokeColor'](); 12673 if (color) 12674 color.setAlpha(parseFloat(value)); 12675 } 12676 12677 var attributes = Base.set(Base.each(SVGStyles, function(entry) { 12678 this[entry.attribute] = function(item, value) { 12679 item[entry.set](convertValue(value, entry.type, entry.fromSVG)); 12680 if (entry.type === 'color' && item instanceof Shape) { 12681 var color = item[entry.get](); 12682 if (color) 12683 color.transform(new Matrix().translate( 12684 item.getPosition(true).negate())); 12685 } 12686 }; 12687 }, {}), { 12688 id: function(item, value) { 12689 definitions[value] = item; 12690 if (item.setName) 12691 item.setName(value); 12692 }, 12693 12694 'clip-path': function(item, value) { 12695 var clip = getDefinition(value); 12696 if (clip) { 12697 clip = clip.clone(); 12698 clip.setClipMask(true); 12699 if (item instanceof Group) { 12700 item.insertChild(0, clip); 12701 } else { 12702 return new Group(clip, item); 12703 } 12704 } 12705 }, 12706 12707 gradientTransform: applyTransform, 12708 transform: applyTransform, 12709 12710 'fill-opacity': applyOpacity, 12711 'stroke-opacity': applyOpacity, 12712 12713 visibility: function(item, value) { 12714 item.setVisible(value === 'visible'); 12715 }, 12716 12717 display: function(item, value) { 12718 item.setVisible(value !== null); 12719 }, 12720 12721 'stop-color': function(item, value) { 12722 if (item.setColor) 12723 item.setColor(value); 12724 }, 12725 12726 'stop-opacity': function(item, value) { 12727 if (item._color) 12728 item._color.setAlpha(parseFloat(value)); 12729 }, 12730 12731 offset: function(item, value) { 12732 var percentage = value.match(/(.*)%$/); 12733 item.setRampPoint(percentage 12734 ? percentage[1] / 100 12735 : parseFloat(value)); 12736 }, 12737 12738 viewBox: function(item, value, name, node, styles) { 12739 var rect = new Rectangle(convertValue(value, 'array')), 12740 size = getSize(node, 'width', 'height', true); 12741 if (item instanceof Group) { 12742 var scale = size ? rect.getSize().divide(size) : 1, 12743 matrix = new Matrix().translate(rect.getPoint()).scale(scale); 12744 item.transform(matrix.inverted()); 12745 } else if (item instanceof Symbol) { 12746 if (size) 12747 rect.setSize(size); 12748 var clip = getAttribute(node, 'overflow', styles) != 'visible', 12749 group = item._definition; 12750 if (clip && !rect.contains(group.getBounds())) { 12751 clip = new Shape.Rectangle(rect).transform(group._matrix); 12752 clip.setClipMask(true); 12753 group.addChild(clip); 12754 } 12755 } 12756 } 12757 }); 12758 12759 function getAttribute(node, name, styles) { 12760 var attr = node.attributes[name], 12761 value = attr && attr.value; 12762 if (!value) { 12763 var style = Base.camelize(name); 12764 value = node.style[style]; 12765 if (!value && styles.node[style] !== styles.parent[style]) 12766 value = styles.node[style]; 12767 } 12768 return !value 12769 ? undefined 12770 : value === 'none' 12771 ? null 12772 : value; 12773 } 12774 12775 function applyAttributes(item, node, isRoot) { 12776 var styles = { 12777 node: DomElement.getStyles(node) || {}, 12778 parent: !isRoot && DomElement.getStyles(node.parentNode) || {} 12779 }; 12780 Base.each(attributes, function(apply, name) { 12781 var value = getAttribute(node, name, styles); 12782 if (value !== undefined) 12783 item = Base.pick(apply(item, value, name, node, styles), item); 12784 }); 12785 return item; 12786 } 12787 12788 var definitions = {}; 12789 function getDefinition(value) { 12790 var match = value && value.match(/\((?:#|)([^)']+)/); 12791 return match && definitions[match[1]]; 12792 } 12793 12794 function importSVG(source, options, isRoot) { 12795 if (!source) 12796 return null; 12797 if (!options) { 12798 options = {}; 12799 } else if (typeof options === 'function') { 12800 options = { onLoad: options }; 12801 } 12802 12803 var node = source, 12804 scope = paper; 12805 12806 function onLoadCallback(svg) { 12807 paper = scope; 12808 var item = importSVG(svg, options, isRoot), 12809 onLoad = options.onLoad, 12810 view = scope.project && scope.getView(); 12811 if (onLoad) 12812 onLoad.call(this, item); 12813 view.update(); 12814 } 12815 12816 if (isRoot) { 12817 if (typeof source === 'string' && !/^.*</.test(source)) { 12818 node = document.getElementById(source); 12819 if (node) { 12820 source = null; 12821 } else { 12822 return Http.request('get', source, onLoadCallback); 12823 } 12824 } else if (typeof File !== 'undefined' && source instanceof File) { 12825 var reader = new FileReader(); 12826 reader.onload = function() { 12827 onLoadCallback(reader.result); 12828 }; 12829 return reader.readAsText(source); 12830 } 12831 } 12832 12833 if (typeof source === 'string') 12834 node = new DOMParser().parseFromString(source, 'image/svg+xml'); 12835 if (!node.nodeName) 12836 throw new Error('Unsupported SVG source: ' + source); 12837 var type = node.nodeName.toLowerCase(), 12838 importer = importers[type], 12839 item, 12840 data = node.getAttribute && node.getAttribute('data-paper-data'), 12841 settings = scope.settings, 12842 applyMatrix = settings.applyMatrix; 12843 settings.applyMatrix = false;
12844 item = importer && importer(node, type, options, isRoot) || null; 12845 settings.applyMatrix = applyMatrix; 12846 if (item) { 12847 if (type !== '#document' && !(item instanceof Group)) 12848 item = applyAttributes(item, node, isRoot); 12849 var onImport = options.onImport; 12850 if (onImport) 12851 item = onImport(node, item, options) || item; 12852 if (options.expandShapes && item instanceof Shape) { 12853 item.remove(); 12854 item = item.toPath(); 12855 } 12856 if (data) 12857 item._data = JSON.parse(data); 12858 } 12859 if (isRoot) { 12860 definitions = {}; 12861 if (item && Base.pick(options.applyMatrix, applyMatrix)) 12862 item.matrix.apply(true, true); 12863 } 12864 return item; 12865 } 12866 12867 Item.inject({ 12868 importSVG: function(node, options) { 12869 return this.addChild(importSVG(node, options, true)); 12870 } 12871 }); 12872 12873 Project.inject({ 12874 importSVG: function(node, options) { 12875 this.activate(); 12876 return importSVG(node, options, true); 12877 } 12878 }); 12879}; 12880 12881Base.exports.PaperScript = (function() { 12882 var exports, define, 12883 scope = this; 12884!function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in mr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=mr[r]);hr=fr.sourceFile||null}function t(e,r){var t=vr(dr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=dr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(dr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,dr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=dr.charCodeAt(br+=2);pr>br&&10!==t&&13!==t&&8232!==t&&8233!==t;)++br,t=dr.charCodeAt(br);fr.onComment&&fr.onComment(!1,dr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;pr>br;){var e=dr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=dr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e||8232===e||8233===e)++br,fr.locations&&(++Ar,Sr=br);else if(e>8&&14>e)++br;else if(47===e){var r=dr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=dr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=dr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function d(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Dt,1)}function p(e){var r=dr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Tt,1)}function h(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Vt,1)}function m(e){var r=dr.charCodeAt(br+1);return r===e?45==r&&62==dr.charCodeAt(br+2)&&Gt.test(dr.slice(Lr,br))?(br+=3,c(),u(),g()):x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=dr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===dr.charCodeAt(br+2)?3:2,61===dr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):33==r&&60==e&&45==dr.charCodeAt(br+2)&&45==dr.charCodeAt(br+3)?(br+=4,c(),u(),g()):(61===r&&(t=61===dr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=dr.charCodeAt(br+1);return 61===r?x(qt,61===dr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(mt);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(dt);case 123:return++br,i(pt);case 125:return++br,i(ht);case 58:return++br,i(gt);case 63:return++br,i(kt);
12884case 48:var r=dr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return d();case 124:case 38:return p(e);case 94:return h();case 43:case 45:return m(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=pr)return i(Br);var r=dr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=dr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=pr&&t(a,"Unterminated regular expression");var o=dr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=dr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=dr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===dr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===dr.charCodeAt(br)&&(++br,w(10),n=!0);var o=dr.charCodeAt(br);(69===o||101===o)&&(o=dr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=dr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Tr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=pr&&t(yr,"Unterminated string constant");var n=dr.charCodeAt(br);if(n===e)return++br,i(Dr,r);if(92===n){n=dr.charCodeAt(++br);var a=/^[0-7]+/.exec(dr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Tr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+=" ";break;case 98:r+="\b";break;case 118:r+="�";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===dr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8233===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=dr.charCodeAt(br);if(Yt(a))Bt&&(e+=dr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=dr.slice(n,br)),Bt=!0,117!=dr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:dr.slice(n,br)}function L(){var e=I(),r=Fr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Tr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){if(Tr=e,br=Lr,fr.locations)for(;Sr>br;)Sr=dr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function V(){this.type=null,this.start=yr,this.end=null}function T(){this.start=xr,this.end=null,null!==hr&&(this.source=hr)}function q(){var e=new V;return fr.locations&&(e.loc=new T),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new V;return r.start=e.start,fr.locations&&(r.loc=new T,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function D(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function F(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===ht||Gt.test(dr.slice(Lr,yr)))}function M(){F(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}
12884function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Tr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Tr=null,Vr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&D(n)&&R(!0),t=!1}return j(r,"Program")}function J(){(wr===wt||wr===Et&&"/="==Cr)&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;F(yt)||B()?r.label=null:wr!==Fr?X():(r.label=lr(),M());for(var a=0;a<Vr.length;++a){var o=Vr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Vr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Vr.push(Zt),r.body=J(),Vr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Vr.push(Zt),z(mt),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),j(i,"VariableDeclaration"),1===i.declarations.length&&F(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return F(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=F(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),F(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(pt),Vr.push(en);for(var s,c;wr!=ht;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Vr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(dr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(mt),l.param=lr(),Tr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=F($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),G(r),M(),j(r,"VariableDeclaration");case tt:return U(),r.test=P(),Vr.push(Zt),r.body=J(),Vr.pop(),j(r,"WhileStatement");case nt:return Tr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case pt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,d=K();if(e===Fr&&"Identifier"===d.type&&F(gt)){for(var a=0;a<Vr.length;++a)Vr[a].name===f&&t(d.start,"Label '"+f+"' is already declared");var p=wr.isLoop?"loop":wr===Yr?"switch":null;return Vr.push({name:f,kind:p}),r.body=J(),Vr.pop(),r.label=d,j(r,"LabeledStatement")}return r.expression=d,M(),j(r,"ExpressionStatement")}}function P(){z(mt);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(pt);!F(ht);){var o=J();t.body.push(o),n&&e&&D(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Tr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=F(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!F(bt))break}return e}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];F(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(F(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var o=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(o,r,t)}return e}
12884function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,Er=!0,U(),e.argument=rr(),r?N(e.argument):Tr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(F(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(F(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(dt),nr(j(t,"MemberExpression"),r)}if(!r&&F(mt)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Fr:return lr();case Or:case Dr:case jr:var e=q();return e.value=Cr,e.raw=dr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case mt:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(dt,!0,!0),j(e,"ArrayExpression");case pt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=F(mt)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!F(ht);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&F(ht))break;var a,o={key:sr()},i=!1;if(F(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==mt&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Tr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Tr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Dr?ar():lr(!0)}function cr(e,r){wr===Fr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(mt);!F(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Vr;if(Rr=!0,Vr=[],e.body=H(!0),Rr=a,Vr=o,Tr||e.body.body.length&&D(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!F(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&F(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Fr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),Er=!1,U(),j(r,"Identifier")}e.version="0.4.0";var fr,dr,pr,hr;e.parse=function(e,t){return dr=String(e),pr=dr.length,r(t),o(),W(fr.program)};var mr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}dr=String(e),pr=dr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(dr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Vr,Tr,qr=[],Or={type:"num"},jr={type:"regexp"},Dr={type:"string"},Fr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"}
12884,tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},dt={type:"]"},pt={type:"{",beforeExpr:!0},ht={type:"}"},mt={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Vt={binop:4,beforeExpr:!0},Tt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Dt={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:dt,braceL:pt,braceR:ht,parenL:mt,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Fr,eof:Br,num:Or,regexp:jr,string:Dr};for(var Ft in lt)e.tokTypes["_"+Ft]=lt[Ft];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u
1288419c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc",Ht="\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f",$t=new RegExp("["+Pt+"]"),_t=new RegExp("["+Pt+Ht+"]"),Gt=/[\n\r\u2028\u2029]/,Kt=/\r\n|[\n\r\u2028\u2029]/g,Qt=e.isIdentifierStart=function(e){return 65>e?36===e:91>e?!0:97>e?95===e:123>e?!0:e>=170&&$t.test(String.fromCharCode(e))},Yt=e.isIdentifierChar=function(e){return 48>e?36===e:58>e?!0:65>e?!1:91>e?!0:97>e?95===e:123>e?!0:e>=170&&_t.test(String.fromCharCode(e))},Zt={kind:"loop"},en={kind:"switch"}}); 12885 12886 var binaryOperators = { 12887 '+': '__add', 12888 '-': '__subtract', 12889 '*': '__multiply', 12890 '/': '__divide', 12891 '%': '__modulo', 12892 '==': 'equals', 12893 '!=': 'equals' 12894 }; 12895 12896 var unaryOperators = { 12897 '-': '__negate', 12898 '+': null 12899 }; 12900 12901 var fields = Base.each(
12902 ['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'], 12903 function(name) { 12904 this['__' + name] = '#' + name; 12905 }, 12906 {} 12907 ); 12908 Point.inject(fields); 12909 Size.inject(fields); 12910 Color.inject(fields); 12911 12912 function __$__(left, operator, right) { 12913 var handler = binaryOperators[operator]; 12914 if (left && left[handler]) { 12915 var res = left[handler](right); 12916 return operator === '!=' ? !res : res; 12917 } 12918 switch (operator) { 12919 case '+': return left + right; 12920 case '-': return left - right; 12921 case '*': return left * right; 12922 case '/': return left / right; 12923 case '%': return left % right; 12924 case '==': return left == right; 12925 case '!=': return left != right; 12926 } 12927 } 12928 12929 function $__(operator, value) { 12930 var handler = unaryOperators[operator]; 12931 if (handler && value && value[handler]) 12932 return value[handler](); 12933 switch (operator) { 12934 case '+': return +value; 12935 case '-': return -value; 12936 } 12937 } 12938 12939 function parse(code, options) { 12940 return scope.acorn.parse(code, options); 12941 } 12942 12943 function compile(code, url, options) { 12944 if (!code) 12945 return ''; 12946 options = options || {}; 12947 url = url || ''; 12948 12949 var insertions = []; 12950 12951 function getOffset(offset) { 12952 for (var i = 0, l = insertions.length; i < l; i++) { 12953 var insertion = insertions[i]; 12954 if (insertion[0] >= offset) 12955 break; 12956 offset += insertion[1]; 12957 } 12958 return offset; 12959 } 12960 12961 function getCode(node) { 12962 return code.substring(getOffset(node.range[0]), 12963 getOffset(node.range[1])); 12964 } 12965 12966 function getBetween(left, right) { 12967 return code.substring(getOffset(left.range[1]), 12968 getOffset(right.range[0])); 12969 } 12970 12971 function replaceCode(node, str) { 12972 var start = getOffset(node.range[0]), 12973 end = getOffset(node.range[1]), 12974 insert = 0; 12975 for (var i = insertions.length - 1; i >= 0; i--) { 12976 if (start > insertions[i][0]) { 12977 insert = i + 1; 12978 break; 12979 } 12980 } 12981 insertions.splice(insert, 0, [start, str.length - end + start]); 12982 code = code.substring(0, start) + str + code.substring(end); 12983 } 12984 12985 function walkAST(node, parent) { 12986 if (!node) 12987 return; 12988 for (var key in node) { 12989 if (key === 'range' || key === 'loc') 12990 continue; 12991 var value = node[key]; 12992 if (Array.isArray(value)) { 12993 for (var i = 0, l = value.length; i < l; i++) 12994 walkAST(value[i], node); 12995 } else if (value && typeof value === 'object') { 12996 walkAST(value, node); 12997 } 12998 } 12999 switch (node.type) { 13000 case 'UnaryExpression': 13001 if (node.operator in unaryOperators 13002 && node.argument.type !== 'Literal') { 13003 var arg = getCode(node.argument); 13004 replaceCode(node, '$__("' + node.operator + '", ' 13005 + arg + ')'); 13006 } 13007 break; 13008 case 'BinaryExpression': 13009 if (node.operator in binaryOperators 13010 && node.left.type !== 'Literal') { 13011 var left = getCode(node.left), 13012 right = getCode(node.right), 13013 between = getBetween(node.left, node.right), 13014 operator = node.operator; 13015 replaceCode(node, '__$__(' + left + ',' 13016 + between.replace(new RegExp('\\' + operator), 13017 '"' + operator + '"') 13018 + ', ' + right + ')'); 13019 } 13020 break; 13021 case 'UpdateExpression': 13022 case 'AssignmentExpression': 13023 var parentType = parent && parent.type; 13024 if (!( 13025 parentType === 'ForStatement' 13026 || parentType === 'BinaryExpression' 13027 && /^[=!<>]/.test(parent.operator) 13028 || parentType === 'MemberExpression' && parent.computed 13029 )) { 13030 if (node.type === 'UpdateExpression') { 13031 var arg = getCode(node.argument), 13032 exp = '__$__(' + arg + ', "' + node.operator[0] 13033 + '", 1)', 13034 str = arg + ' = ' + exp; 13035 if (!node.prefix 13036 && (parentType === 'AssignmentExpression' 13037 || parentType === 'VariableDeclarator')) { 13038 if (getCode(parent.left || parent.id) === arg) 13039 str = exp; 13040 str = arg + '; ' + str; 13041 } 13042 replaceCode(node, str); 13043 } else { 13044 if (/^.=$/.test(node.operator) 13045 && node.left.type !== 'Literal') { 13046 var left = getCode(node.left), 13047 right = getCode(node.right); 13048 replaceCode(node, left + ' = __$__(' + left + ', "' 13049 + node.operator[0] + '", ' + right + ')'); 13050 } 13051 } 13052 } 13053 break; 13054 } 13055 } 13056 var sourceMap = null, 13057 browser = paper.browser, 13058 version = browser.versionNumber, 13059 lineBreaks = /\r\n|\n|\r/mg; 13060 if (browser.chrome && version >= 30 13061 || browser.webkit && version >= 537.76 13062 || browser.firefox && version >= 23) { 13063 var offset = 0; 13064 if (window.location.href.indexOf(url) === 0) { 13065 var html = document.getElementsByTagName('html')[0].innerHTML; 13066 offset = html.substr(0, html.indexOf(code) + 1).match( 13067 lineBreaks).length + 1; 13068 } 13069 var mappings = ['AAAA']; 13070 mappings.length = (code.match(lineBreaks) || []).length + 1 + offset; 13071 sourceMap = { 13072 version: 3, 13073 file: url, 13074 names:[], 13075 mappings: mappings.join(';AACA'), 13076 sourceRoot: '', 13077 sources: [url] 13078 }; 13079 var source = options.source || !url && code; 13080 if (source) 13081 sourceMap.sourcesContent = [source]; 13082 } 13083 walkAST(parse(code, { ranges: true })); 13084 if (sourceMap) { 13085 code = new Array(offset + 1).join('\n') + code 13086 + "\n//# sourceMappingURL=data:application/json;base64," 13087 + (btoa(unescape(encodeURIComponent(
13088 JSON.stringify(sourceMap))))) 13089 + "\n//# sourceURL=" + (url || 'paperscript'); 13090 } 13091 return code; 13092 } 13093 13094 function execute(code, scope, url, options) { 13095 paper = scope; 13096 var view = scope.getView(), 13097 tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code) 13098 ? new Tool() 13099 : null, 13100 toolHandlers = tool ? tool._events : [], 13101 handlers = ['onFrame', 'onResize'].concat(toolHandlers), 13102 params = [], 13103 args = [], 13104 func; 13105 code = compile(code, url, options); 13106 function expose(scope, hidden) { 13107 for (var key in scope) { 13108 if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)' 13109 + key.replace(/\$/g, '\\$') + '\\b').test(code)) { 13110 params.push(key); 13111 args.push(scope[key]); 13112 } 13113 } 13114 } 13115 expose({ __$__: __$__, $__: $__, paper: scope, view: view, tool: tool }, 13116 true); 13117 expose(scope); 13118 handlers = Base.each(handlers, function(key) { 13119 if (new RegExp('\\s+' + key + '\\b').test(code)) { 13120 params.push(key); 13121 this.push(key + ': ' + key); 13122 } 13123 }, []).join(', '); 13124 if (handlers) 13125 code += '\nreturn { ' + handlers + ' };'; 13126 var browser = paper.browser; 13127 if (browser.chrome || browser.firefox) { 13128 var script = document.createElement('script'), 13129 head = document.head || document.getElementsByTagName('head')[0]; 13130 if (browser.firefox) 13131 code = '\n' + code; 13132 script.appendChild(document.createTextNode( 13133 'paper._execute = function(' + params + ') {' + code + '\n}' 13134 )); 13135 head.appendChild(script); 13136 func = paper._execute; 13137 delete paper._execute; 13138 head.removeChild(script); 13139 } else { 13140 func = Function(params, code); 13141 } 13142 var res = func.apply(scope, args) || {}; 13143 Base.each(toolHandlers, function(key) { 13144 var value = res[key]; 13145 if (value) 13146 tool[key] = value; 13147 }); 13148 if (view) { 13149 if (res.onResize) 13150 view.setOnResize(res.onResize); 13151 view.emit('resize', { 13152 size: view.size, 13153 delta: new Point() 13154 }); 13155 if (res.onFrame) 13156 view.setOnFrame(res.onFrame); 13157 view.update(); 13158 } 13159 } 13160 13161 function loadScript(script) { 13162 if (/^text\/(?:x-|)paperscript$/.test(script.type) 13163 && PaperScope.getAttribute(script, 'ignore') !== 'true') { 13164 var canvasId = PaperScope.getAttribute(script, 'canvas'), 13165 canvas = document.getElementById(canvasId), 13166 src = script.src || script.getAttribute('data-src'), 13167 async = PaperScope.hasAttribute(script, 'async'), 13168 scopeAttribute = 'data-paper-scope'; 13169 if (!canvas) 13170 throw new Error('Unable to find canvas with id "' 13171 + canvasId + '"'); 13172 var scope = PaperScope.get(canvas.getAttribute(scopeAttribute)) 13173 || new PaperScope().setup(canvas); 13174 canvas.setAttribute(scopeAttribute, scope._id); 13175 if (src) { 13176 Http.request('get', src, function(code) { 13177 execute(code, scope, src); 13178 }, async); 13179 } else { 13180 execute(script.innerHTML, scope, script.baseURI); 13181 } 13182 script.setAttribute('data-paper-ignore', 'true'); 13183 return scope; 13184 } 13185 } 13186 13187 function loadAll() { 13188 Base.each(document.getElementsByTagName('script'), loadScript); 13189 } 13190 13191 function load(script) { 13192 return script ? loadScript(script) : loadAll(); 13193 } 13194 13195 if (document.readyState === 'complete') { 13196 setTimeout(loadAll); 13197 } else { 13198 DomEvent.add(window, { load: loadAll }); 13199 } 13200 13201 return { 13202 compile: compile, 13203 execute: execute, 13204 load: load, 13205 parse: parse 13206 }; 13207 13208}).call(this); 13209 13210paper = new (PaperScope.inject(Base.exports, { 13211 enumerable: true, 13212 Base: Base, 13213 Numerical: Numerical, 13214 Key: Key 13215}))(); 13216 13217if (typeof define === 'function' && define.amd) { 13218 define('paper', paper); 13219} else if (typeof module === 'object' && module) { 13220 module.exports = paper; 13221} 13222 13223return paper; 13224};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.