Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
loeasy68
GitHub Repository: loeasy68/loeasy68.github.io
Path: blob/main/website/GAUSS/js/paper-full.js
2941 views
1
/*!
2
* Paper.js v0.9.18 - 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: Mon Apr 7 11:24:38 2014 +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
33
var paper = new function(undefined) {
34
35
var 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
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) {
138
for (var i in props)
139
if (props.hasOwnProperty(i))
140
obj[i] = props[i];
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
for (var i = 0, l = arguments.length; i < l; i++)
166
if (ctor = arguments[i].initialize)
167
break;
168
ctor = ctor || function() {
169
base.apply(this, arguments);
170
};
171
ctor.prototype = create(this.prototype);
172
ctor.base = base;
173
define(ctor.prototype, 'constructor',
174
{ value: ctor, writable: true, configurable: true });
175
inject(ctor, this, true);
176
return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
177
}
178
}, true).inject({
179
inject: function() {
180
for (var i = 0, l = arguments.length; i < l; i++) {
181
var src = arguments[i];
182
if (src)
183
inject(this, src, src.enumerable, src.beans, src.preserve);
184
}
185
return this;
186
},
187
188
extend: function() {
189
var res = create(this);
190
return res.inject.apply(res, arguments);
191
},
192
193
each: function(iter, bind) {
194
return each(this, iter, bind);
195
},
196
197
clone: function() {
198
return new this.constructor(this);
199
},
200
201
statics: {
202
each: each,
203
create: create,
204
define: define,
205
describe: describe,
206
set: set,
207
208
clone: function(obj) {
209
return set(new obj.constructor(), obj);
210
},
211
212
isPlainObject: function(obj) {
213
var ctor = obj != null && obj.constructor;
214
return ctor && (ctor === Object || ctor === Base
215
|| ctor.name === 'Object');
216
},
217
218
pick: function() {
219
for (var i = 0, l = arguments.length; i < l; i++)
220
if (arguments[i] !== undefined)
221
return arguments[i];
222
}
223
}
224
});
225
};
226
227
if (typeof module !== 'undefined')
228
module.exports = Base;
229
230
if (!Array.isArray) {
231
Array.isArray = function(obj) {
232
return Object.prototype.toString.call(obj) === '[object Array]';
233
};
234
}
235
236
if (!document.head) {
237
document.head = document.getElementsByTagName('head')[0];
238
}
239
240
Base.inject({
241
toString: function() {
242
return this._id != null
243
? (this._class || 'Object') + (this._name
244
? " '" + this._name + "'"
245
: ' @' + this._id)
246
: '{ ' + Base.each(this, function(value, key) {
247
if (!/^_/.test(key)) {
248
var type = typeof value;
249
this.push(key + ': ' + (type === 'number'
250
? Formatter.instance.number(value)
251
: type === 'string' ? "'" + value + "'" : value));
252
}
253
}, []).join(', ') + ' }';
254
},
255
256
exportJSON: function(options) {
257
return Base.exportJSON(this, options);
258
},
259
260
toJSON: function() {
261
return Base.serialize(this);
262
},
263
264
_set: function(props, exclude) {
265
if (props && Base.isPlainObject(props)) {
266
var orig = props._filtering || props;
267
for (var key in orig) {
268
if (key in this && orig.hasOwnProperty(key)
269
&& (!exclude || !exclude[key])) {
270
var value = props[key];
271
if (value !== undefined)
272
this[key] = value;
273
}
274
}
275
return true;
276
}
277
},
278
279
statics: {
280
281
exports: {
282
enumerable: true
283
},
284
285
extend: function extend() {
286
var res = extend.base.apply(this, arguments),
287
name = res.prototype._class;
288
if (name && !Base.exports[name])
289
Base.exports[name] = res;
290
return res;
291
},
292
293
equals: function(obj1, obj2) {
294
function checkKeys(o1, o2) {
295
for (var i in o1)
296
if (o1.hasOwnProperty(i) && !o2.hasOwnProperty(i))
297
return false;
298
return true;
299
}
300
if (obj1 === obj2)
301
return true;
302
if (obj1 && obj1.equals)
303
return obj1.equals(obj2);
304
if (obj2 && obj2.equals)
305
return obj2.equals(obj1);
306
if (Array.isArray(obj1) && Array.isArray(obj2)) {
307
if (obj1.length !== obj2.length)
308
return false;
309
for (var i = 0, l = obj1.length; i < l; i++) {
310
if (!Base.equals(obj1[i], obj2[i]))
311
return false;
312
}
313
return true;
314
}
315
if (obj1 && typeof obj1 === 'object'
316
&& obj2 && typeof obj2 === 'object') {
317
if (!checkKeys(obj1, obj2) || !checkKeys(obj2, obj1))
318
return false;
319
for (var i in obj1) {
320
if (obj1.hasOwnProperty(i)
321
&& !Base.equals(obj1[i], obj2[i]))
322
return false;
323
}
324
return true;
325
}
326
return false;
327
},
328
329
read: function(list, start, options, length) {
330
if (this === Base) {
331
var value = this.peek(list, start);
332
list.__index++;
333
return value;
334
}
335
var proto = this.prototype,
336
readIndex = proto._readIndex,
337
index = start || readIndex && list.__index || 0;
338
if (!length)
339
length = list.length - index;
340
var obj = list[index];
341
if (obj instanceof this
342
|| options && options.readNull && obj == null && length <= 1) {
343
if (readIndex)
344
list.__index = index + 1;
345
return obj && options && options.clone ? obj.clone() : obj;
346
}
347
obj = Base.create(this.prototype);
348
if (readIndex)
349
obj.__read = true;
350
obj = obj.initialize.apply(obj, index > 0 || length < list.length
351
? Array.prototype.slice.call(list, index, index + length)
352
: list) || obj;
353
if (readIndex) {
354
list.__index = index + obj.__read;
355
obj.__read = undefined;
356
}
357
return obj;
358
},
359
360
peek: function(list, start) {
361
return list[list.__index = start || list.__index || 0];
362
},
363
364
remain: function(list) {
365
return list.length - (list.__index || 0);
366
},
367
368
readAll: function(list, start, options) {
369
var res = [],
370
entry;
371
for (var i = start || 0, l = list.length; i < l; i++) {
372
res.push(Array.isArray(entry = list[i])
373
? this.read(entry, 0, options)
374
: this.read(list, i, options, 1));
375
}
376
return res;
377
},
378
379
readNamed: function(list, name, start, options, length) {
380
var value = this.getNamed(list, name),
381
hasObject = value !== undefined;
382
if (hasObject) {
383
var filtered = list._filtered;
384
if (!filtered) {
385
filtered = list._filtered = Base.create(list[0]);
386
filtered._filtering = list[0];
387
}
388
filtered[name] = undefined;
389
}
390
return this.read(hasObject ? [value] : list, start, options, length);
391
},
392
393
getNamed: function(list, name) {
394
var arg = list[0];
395
if (list._hasObject === undefined)
396
list._hasObject = list.length === 1 && Base.isPlainObject(arg);
397
if (list._hasObject)
398
return name ? arg[name] : list._filtered || arg;
399
},
400
401
hasNamed: function(list, name) {
402
return !!this.getNamed(list, name);
403
},
404
405
isPlainValue: function(obj) {
406
return this.isPlainObject(obj) || Array.isArray(obj);
407
},
408
409
serialize: function(obj, options, compact, dictionary) {
410
options = options || {};
411
412
var root = !dictionary,
413
res;
414
if (root) {
415
options.formatter = new Formatter(options.precision);
416
dictionary = {
417
length: 0,
418
definitions: {},
419
references: {},
420
add: function(item, create) {
421
var id = '#' + item._id,
422
ref = this.references[id];
423
if (!ref) {
424
this.length++;
425
var res = create.call(item),
426
name = item._class;
427
if (name && res[0] !== name)
428
res.unshift(name);
429
this.definitions[id] = res;
430
ref = this.references[id] = [id];
431
}
432
return ref;
433
}
434
};
435
}
436
if (obj && obj._serialize) {
437
res = obj._serialize(options, dictionary);
438
var name = obj._class;
439
if (name && !compact && !res._compact && res[0] !== name)
440
res.unshift(name);
441
} else if (Array.isArray(obj)) {
442
res = [];
443
for (var i = 0, l = obj.length; i < l; i++)
444
res[i] = Base.serialize(obj[i], options, compact,
445
dictionary);
446
if (compact)
447
res._compact = true;
448
} else if (Base.isPlainObject(obj)) {
449
res = {};
450
for (var i in obj)
451
if (obj.hasOwnProperty(i))
452
res[i] = Base.serialize(obj[i], options, compact,
453
dictionary);
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) {
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 (!isDictionary) {
472
if (_data.dictionary && json.length == 1 && /^#/.test(type))
473
return _data.dictionary[type];
474
type = Base.exports[type];
475
}
476
res = [];
477
for (var i = type ? 1 : 0, l = json.length; i < l; i++)
478
res.push(Base.deserialize(json[i], create, _data));
479
if (isDictionary) {
480
_data.dictionary = res[0];
481
} else if (type) {
482
var args = res;
483
if (create) {
484
res = create(type, args, isRoot);
485
} else {
486
res = Base.create(type.prototype);
487
type.apply(res, args);
488
}
489
}
490
} else if (Base.isPlainObject(json)) {
491
res = {};
492
for (var key in json)
493
res[key] = Base.deserialize(json[key], create, _data);
494
}
495
return res;
496
},
497
498
exportJSON: function(obj, options) {
499
var json = Base.serialize(obj, options);
500
return options && options.asString === false
501
? json
502
: JSON.stringify(json);
503
},
504
505
importJSON: function(json, target) {
506
return Base.deserialize(
507
typeof json === 'string' ? JSON.parse(json) : json,
508
function(type, args, isRoot) {
509
var obj = target && target.constructor === type
510
? target
511
: Base.create(type.prototype),
512
isTarget = obj === target;
513
if (!isRoot && args.length === 1 && obj instanceof Item
514
&& (!(obj instanceof Layer) || isTarget)) {
515
var arg = args[0];
516
if (Base.isPlainObject(arg))
517
arg.insert = false;
518
}
519
type.apply(obj, args);
520
if (isTarget)
521
target = null;
522
return obj;
523
});
524
},
525
526
splice: function(list, items, index, remove) {
527
var amount = items && items.length,
528
append = index === undefined;
529
index = append ? list.length : index;
530
if (index > list.length)
531
index = list.length;
532
for (var i = 0; i < amount; i++)
533
items[i]._index = index + i;
534
if (append) {
535
list.push.apply(list, items);
536
return [];
537
} else {
538
var args = [index, remove];
539
if (items)
540
args.push.apply(args, items);
541
var removed = list.splice.apply(list, args);
542
for (var i = 0, l = removed.length; i < l; i++)
543
removed[i]._index = undefined;
544
for (var i = index + amount, l = list.length; i < l; i++)
545
list[i]._index = i;
546
return removed;
547
}
548
},
549
550
capitalize: function(str) {
551
return str.replace(/\b[a-z]/g, function(match) {
552
return match.toUpperCase();
553
});
554
},
555
556
camelize: function(str) {
557
return str.replace(/-(.)/g, function(all, chr) {
558
return chr.toUpperCase();
559
});
560
},
561
562
hyphenate: function(str) {
563
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
564
}
565
}
566
});
567
568
var Callback = {
569
attach: function(type, func) {
570
if (typeof type !== 'string') {
571
Base.each(type, function(value, key) {
572
this.attach(key, value);
573
}, this);
574
return;
575
}
576
var entry = this._eventTypes[type];
577
if (entry) {
578
var handlers = this._handlers = this._handlers || {};
579
handlers = handlers[type] = handlers[type] || [];
580
if (handlers.indexOf(func) == -1) {
581
handlers.push(func);
582
if (entry.install && handlers.length == 1)
583
entry.install.call(this, type);
584
}
585
}
586
},
587
588
detach: function(type, func) {
589
if (typeof type !== 'string') {
590
Base.each(type, function(value, key) {
591
this.detach(key, value);
592
}, this);
593
return;
594
}
595
var entry = this._eventTypes[type],
596
handlers = this._handlers && this._handlers[type],
597
index;
598
if (entry && handlers) {
599
if (!func || (index = handlers.indexOf(func)) != -1
600
&& handlers.length == 1) {
601
if (entry.uninstall)
602
entry.uninstall.call(this, type);
603
delete this._handlers[type];
604
} else if (index != -1) {
605
handlers.splice(index, 1);
606
}
607
}
608
},
609
610
once: function(type, func) {
611
this.attach(type, function() {
612
func.apply(this, arguments);
613
this.detach(type, func);
614
});
615
},
616
617
fire: function(type, event) {
618
var handlers = this._handlers && this._handlers[type];
619
if (!handlers)
620
return false;
621
var args = [].slice.call(arguments, 1),
622
that = this;
623
for (var i = 0, l = handlers.length; i < l; i++) {
624
if (handlers[i].apply(that, args) === false
625
&& event && event.stop) {
626
event.stop();
627
break;
628
}
629
}
630
return true;
631
},
632
633
responds: function(type) {
634
return !!(this._handlers && this._handlers[type]);
635
},
636
637
on: '#attach',
638
off: '#detach',
639
trigger: '#fire',
640
641
_installEvents: function(install) {
642
var handlers = this._handlers,
643
key = install ? 'install' : 'uninstall';
644
for (var type in handlers) {
645
if (handlers[type].length > 0) {
646
var entry = this._eventTypes[type],
647
func = entry[key];
648
if (func)
649
func.call(this, type);
650
}
651
}
652
},
653
654
statics: {
655
inject: function inject() {
656
for (var i = 0, l = arguments.length; i < l; i++) {
657
var src = arguments[i],
658
events = src._events;
659
if (events) {
660
var types = {};
661
Base.each(events, function(entry, key) {
662
var isString = typeof entry === 'string',
663
name = isString ? entry : key,
664
part = Base.capitalize(name),
665
type = name.substring(2).toLowerCase();
666
types[type] = isString ? {} : entry;
667
name = '_' + name;
668
src['get' + part] = function() {
669
return this[name];
670
};
671
src['set' + part] = function(func) {
672
var prev = this[name];
673
if (prev)
674
this.detach(type, prev);
675
if (func)
676
this.attach(type, func);
677
this[name] = func;
678
};
679
});
680
src._eventTypes = types;
681
}
682
inject.base.call(this, src);
683
}
684
return this;
685
}
686
}
687
};
688
689
var PaperScope = Base.extend({
690
_class: 'PaperScope',
691
692
initialize: function PaperScope(script) {
693
paper = this;
694
this.settings = {
695
applyMatrix: true,
696
handleSize: 4,
697
hitTolerance: 0
698
};
699
this.project = null;
700
this.projects = [];
701
this.tools = [];
702
this.palettes = [];
703
this._id = script && (script.getAttribute('id') || script.src)
704
|| ('paperscope-' + (PaperScope._id++));
705
if (script)
706
script.setAttribute('id', this._id);
707
PaperScope._scopes[this._id] = this;
708
if (!this.support) {
709
var ctx = CanvasProvider.getContext(1, 1);
710
PaperScope.prototype.support = {
711
nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
712
nativeBlendModes: BlendMode.nativeModes
713
};
714
CanvasProvider.release(ctx);
715
}
716
},
717
718
version: '0.9.18',
719
720
getView: function() {
721
return this.project && this.project.getView();
722
},
723
724
getPaper: function() {
725
return this;
726
},
727
728
execute: function(code) {
729
paper.PaperScript.execute(code, this);
730
View.updateFocus();
731
},
732
733
install: function(scope) {
734
var that = this;
735
Base.each(['project', 'view', 'tool'], function(key) {
736
Base.define(scope, key, {
737
configurable: true,
738
get: function() {
739
return that[key];
740
}
741
});
742
});
743
for (var key in this)
744
if (!/^_/.test(key) && this[key])
745
scope[key] = this[key];
746
},
747
748
setup: function(canvas) {
749
paper = this;
750
this.project = new Project(canvas);
751
return this;
752
},
753
754
activate: function() {
755
paper = this;
756
},
757
758
clear: function() {
759
for (var i = this.projects.length - 1; i >= 0; i--)
760
this.projects[i].remove();
761
for (var i = this.tools.length - 1; i >= 0; i--)
762
this.tools[i].remove();
763
for (var i = this.palettes.length - 1; i >= 0; i--)
764
this.palettes[i].remove();
765
},
766
767
remove: function() {
768
this.clear();
769
delete PaperScope._scopes[this._id];
770
},
771
772
statics: new function() {
773
function handleAttribute(name) {
774
name += 'Attribute';
775
return function(el, attr) {
776
return el[name](attr) || el[name]('data-paper-' + attr);
777
};
778
}
779
780
return {
781
_scopes: {},
782
_id: 0,
783
784
get: function(id) {
785
if (id && id.getAttribute)
786
id = id.getAttribute('id');
787
return this._scopes[id] || null;
788
},
789
790
getAttribute: handleAttribute('get'),
791
hasAttribute: handleAttribute('has')
792
};
793
}
794
});
795
796
var PaperScopeItem = Base.extend(Callback, {
797
798
initialize: function(activate) {
799
this._scope = paper;
800
this._index = this._scope[this._list].push(this) - 1;
801
if (activate || !this._scope[this._reference])
802
this.activate();
803
},
804
805
activate: function() {
806
if (!this._scope)
807
return false;
808
var prev = this._scope[this._reference];
809
if (prev && prev !== this)
810
prev.fire('deactivate');
811
this._scope[this._reference] = this;
812
this.fire('activate', prev);
813
return true;
814
},
815
816
isActive: function() {
817
return this._scope[this._reference] === this;
818
},
819
820
remove: function() {
821
if (this._index == null)
822
return false;
823
Base.splice(this._scope[this._list], null, this._index, 1);
824
if (this._scope[this._reference] == this)
825
this._scope[this._reference] = null;
826
this._scope = null;
827
return true;
828
}
829
});
830
831
var Formatter = Base.extend({
832
initialize: function(precision) {
833
this.precision = precision || 5;
834
this.multiplier = Math.pow(10, this.precision);
835
},
836
837
number: function(val) {
838
return Math.round(val * this.multiplier) / this.multiplier;
839
},
840
841
point: function(val, separator) {
842
return this.number(val.x) + (separator || ',') + this.number(val.y);
843
},
844
845
size: function(val, separator) {
846
return this.number(val.width) + (separator || ',')
847
+ this.number(val.height);
848
},
849
850
rectangle: function(val, separator) {
851
return this.point(val, separator) + (separator || ',')
852
+ this.size(val, separator);
853
}
854
});
855
856
Formatter.instance = new Formatter();
857
858
var Numerical = new function() {
859
860
var abscissas = [
861
[ 0.5773502691896257645091488],
862
[0,0.7745966692414833770358531],
863
[ 0.3399810435848562648026658,0.8611363115940525752239465],
864
[0,0.5384693101056830910363144,0.9061798459386639927976269],
865
[ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016],
866
[0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897],
867
[ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609],
868
[0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762],
869
[ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640],
870
[0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380],
871
[ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491],
872
[0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294],
873
[ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973],
874
[0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657],
875
[ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542]
876
];
877
878
var weights = [
879
[1],
880
[0.8888888888888888888888889,0.5555555555555555555555556],
881
[0.6521451548625461426269361,0.3478548451374538573730639],
882
[0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640],
883
[0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],
884
[0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114],
885
[0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314],
886
[0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922],
887
[0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688],
888
[0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537],
889
[0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160],
890
[0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216],
891
[0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329],
892
[0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284],
893
[0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806]
894
];
895
896
var abs = Math.abs,
897
sqrt = Math.sqrt,
898
pow = Math.pow,
899
cos = Math.cos,
900
PI = Math.PI,
901
TOLERANCE = 10e-6,
902
EPSILON = 10e-12;
903
904
function setupRoots(roots, min, max) {
905
var unbound = min === undefined,
906
minE = min - EPSILON,
907
maxE = max + EPSILON,
908
count = 0;
909
return function(root) {
910
if (unbound || root > minE && root < maxE)
911
roots[count++] = root < min ? min : root > max ? max : root;
912
return count;
913
};
914
}
915
916
return {
917
TOLERANCE: TOLERANCE,
918
EPSILON: EPSILON,
919
KAPPA: 4 * (sqrt(2) - 1) / 3,
920
921
isZero: function(val) {
922
return abs(val) <= EPSILON;
923
},
924
925
integrate: function(f, a, b, n) {
926
var x = abscissas[n - 2],
927
w = weights[n - 2],
928
A = 0.5 * (b - a),
929
B = A + a,
930
i = 0,
931
m = (n + 1) >> 1,
932
sum = n & 1 ? w[i++] * f(B) : 0;
933
while (i < m) {
934
var Ax = A * x[i];
935
sum += w[i++] * (f(B + Ax) + f(B - Ax));
936
}
937
return A * sum;
938
},
939
940
findRoot: function(f, df, x, a, b, n, tolerance) {
941
for (var i = 0; i < n; i++) {
942
var fx = f(x),
943
dx = fx / df(x),
944
nx = x - dx;
945
if (abs(dx) < tolerance)
946
return nx;
947
if (fx > 0) {
948
b = x;
949
x = nx <= a ? 0.5 * (a + b) : nx;
950
} else {
951
a = x;
952
x = nx >= b ? 0.5 * (a + b) : nx;
953
}
954
}
955
return x;
956
},
957
958
solveQuadratic: function(a, b, c, roots, min, max) {
959
var add = setupRoots(roots, min, max);
960
961
if (abs(a) < EPSILON) {
962
if (abs(b) >= EPSILON)
963
return add(-c / b);
964
return abs(c) < EPSILON ? -1 : 0;
965
}
966
var p = b / (2 * a);
967
var q = c / a;
968
var p2 = p * p;
969
if (p2 < q - EPSILON)
970
return 0;
971
var s = p2 > q ? sqrt(p2 - q) : 0,
972
count = add(s - p);
973
if (s > 0)
974
count = add(-s - p);
975
return count;
976
},
977
978
solveCubic: function(a, b, c, d, roots, min, max) {
979
if (abs(a) < EPSILON)
980
return Numerical.solveQuadratic(b, c, d, roots, min, max);
981
982
b /= a;
983
c /= a;
984
d /= a;
985
var add = setupRoots(roots, min, max),
986
bb = b * b,
987
p = (bb - 3 * c) / 9,
988
q = (2 * bb * b - 9 * b * c + 27 * d) / 54,
989
ppp = p * p * p,
990
D = q * q - ppp;
991
b /= 3;
992
if (abs(D) < EPSILON) {
993
if (abs(q) < EPSILON)
994
return add(-b);
995
var sqp = sqrt(p),
996
snq = q > 0 ? 1 : -1;
997
add(-snq * 2 * sqp - b);
998
return add(snq * sqp - b);
999
}
1000
if (D < 0) {
1001
var sqp = sqrt(p),
1002
phi = Math.acos(q / (sqp * sqp * sqp)) / 3,
1003
t = -2 * sqp,
1004
o = 2 * PI / 3;
1005
add(t * cos(phi) - b);
1006
add(t * cos(phi + o) - b);
1007
return add(t * cos(phi - o) - b);
1008
}
1009
var A = (q > 0 ? -1 : 1) * pow(abs(q) + sqrt(D), 1 / 3);
1010
return add(A + p / A - b);
1011
}
1012
};
1013
};
1014
1015
var Point = Base.extend({
1016
_class: 'Point',
1017
_readIndex: true,
1018
1019
initialize: function Point(arg0, arg1) {
1020
var type = typeof arg0;
1021
if (type === 'number') {
1022
var hasY = typeof arg1 === 'number';
1023
this.x = arg0;
1024
this.y = hasY ? arg1 : arg0;
1025
if (this.__read)
1026
this.__read = hasY ? 2 : 1;
1027
} else if (type === 'undefined' || arg0 === null) {
1028
this.x = this.y = 0;
1029
if (this.__read)
1030
this.__read = arg0 === null ? 1 : 0;
1031
} else {
1032
if (Array.isArray(arg0)) {
1033
this.x = arg0[0];
1034
this.y = arg0.length > 1 ? arg0[1] : arg0[0];
1035
} else if (arg0.x != null) {
1036
this.x = arg0.x;
1037
this.y = arg0.y;
1038
} else if (arg0.width != null) {
1039
this.x = arg0.width;
1040
this.y = arg0.height;
1041
} else if (arg0.angle != null) {
1042
this.x = arg0.length;
1043
this.y = 0;
1044
this.setAngle(arg0.angle);
1045
} else {
1046
this.x = this.y = 0;
1047
if (this.__read)
1048
this.__read = 0;
1049
}
1050
if (this.__read)
1051
this.__read = 1;
1052
}
1053
},
1054
1055
set: function(x, y) {
1056
this.x = x;
1057
this.y = y;
1058
return this;
1059
},
1060
1061
equals: function(point) {
1062
return this === point || point
1063
&& (this.x === point.x && this.y === point.y
1064
|| Array.isArray(point)
1065
&& this.x === point[0] && this.y === point[1])
1066
|| false;
1067
},
1068
1069
clone: function() {
1070
return new Point(this.x, this.y);
1071
},
1072
1073
toString: function() {
1074
var f = Formatter.instance;
1075
return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';
1076
},
1077
1078
_serialize: function(options) {
1079
var f = options.formatter;
1080
return [f.number(this.x), f.number(this.y)];
1081
},
1082
1083
getLength: function() {
1084
return Math.sqrt(this.x * this.x + this.y * this.y);
1085
},
1086
1087
setLength: function(length) {
1088
if (this.isZero()) {
1089
var angle = this._angle || 0;
1090
this.set(
1091
Math.cos(angle) * length,
1092
Math.sin(angle) * length
1093
);
1094
} else {
1095
var scale = length / this.getLength();
1096
if (Numerical.isZero(scale))
1097
this.getAngle();
1098
this.set(
1099
this.x * scale,
1100
this.y * scale
1101
);
1102
}
1103
},
1104
getAngle: function() {
1105
return this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI;
1106
},
1107
1108
setAngle: function(angle) {
1109
this.setAngleInRadians.call(this, angle * Math.PI / 180);
1110
},
1111
1112
getAngleInDegrees: '#getAngle',
1113
setAngleInDegrees: '#setAngle',
1114
1115
getAngleInRadians: function() {
1116
if (!arguments.length) {
1117
return this.isZero()
1118
? this._angle || 0
1119
: this._angle = Math.atan2(this.y, this.x);
1120
} else {
1121
var point = Point.read(arguments),
1122
div = this.getLength() * point.getLength();
1123
if (Numerical.isZero(div)) {
1124
return NaN;
1125
} else {
1126
return Math.acos(this.dot(point) / div);
1127
}
1128
}
1129
},
1130
1131
setAngleInRadians: function(angle) {
1132
this._angle = angle;
1133
if (!this.isZero()) {
1134
var length = this.getLength();
1135
this.set(
1136
Math.cos(angle) * length,
1137
Math.sin(angle) * length
1138
);
1139
}
1140
},
1141
1142
getQuadrant: function() {
1143
return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;
1144
}
1145
}, {
1146
beans: false,
1147
1148
getDirectedAngle: function() {
1149
var point = Point.read(arguments);
1150
return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;
1151
},
1152
1153
getDistance: function() {
1154
var point = Point.read(arguments),
1155
x = point.x - this.x,
1156
y = point.y - this.y,
1157
d = x * x + y * y,
1158
squared = Base.read(arguments);
1159
return squared ? d : Math.sqrt(d);
1160
},
1161
1162
normalize: function(length) {
1163
if (length === undefined)
1164
length = 1;
1165
var current = this.getLength(),
1166
scale = current !== 0 ? length / current : 0,
1167
point = new Point(this.x * scale, this.y * scale);
1168
if (scale >= 0)
1169
point._angle = this._angle;
1170
return point;
1171
},
1172
1173
rotate: function(angle, center) {
1174
if (angle === 0)
1175
return this.clone();
1176
angle = angle * Math.PI / 180;
1177
var point = center ? this.subtract(center) : this,
1178
s = Math.sin(angle),
1179
c = Math.cos(angle);
1180
point = new Point(
1181
point.x * c - point.y * s,
1182
point.x * s + point.y * c
1183
);
1184
return center ? point.add(center) : point;
1185
},
1186
1187
transform: function(matrix) {
1188
return matrix ? matrix._transformPoint(this) : this;
1189
},
1190
1191
add: function() {
1192
var point = Point.read(arguments);
1193
return new Point(this.x + point.x, this.y + point.y);
1194
},
1195
1196
subtract: function() {
1197
var point = Point.read(arguments);
1198
return new Point(this.x - point.x, this.y - point.y);
1199
},
1200
1201
multiply: function() {
1202
var point = Point.read(arguments);
1203
return new Point(this.x * point.x, this.y * point.y);
1204
},
1205
1206
divide: function() {
1207
var point = Point.read(arguments);
1208
return new Point(this.x / point.x, this.y / point.y);
1209
},
1210
1211
modulo: function() {
1212
var point = Point.read(arguments);
1213
return new Point(this.x % point.x, this.y % point.y);
1214
},
1215
1216
negate: function() {
1217
return new Point(-this.x, -this.y);
1218
},
1219
1220
isInside: function(rect) {
1221
return rect.contains(this);
1222
},
1223
1224
isClose: function(point, tolerance) {
1225
return this.getDistance(point) < tolerance;
1226
},
1227
1228
isColinear: function(point) {
1229
return Math.abs(this.cross(point)) < 0.00001;
1230
},
1231
1232
isOrthogonal: function(point) {
1233
return Math.abs(this.dot(point)) < 0.00001;
1234
},
1235
1236
isZero: function() {
1237
return Numerical.isZero(this.x) && Numerical.isZero(this.y);
1238
},
1239
1240
isNaN: function() {
1241
return isNaN(this.x) || isNaN(this.y);
1242
},
1243
1244
dot: function() {
1245
var point = Point.read(arguments);
1246
return this.x * point.x + this.y * point.y;
1247
},
1248
1249
cross: function() {
1250
var point = Point.read(arguments);
1251
return this.x * point.y - this.y * point.x;
1252
},
1253
1254
project: function() {
1255
var point = Point.read(arguments);
1256
if (point.isZero()) {
1257
return new Point(0, 0);
1258
} else {
1259
var scale = this.dot(point) / point.dot(point);
1260
return new Point(
1261
point.x * scale,
1262
point.y * scale
1263
);
1264
}
1265
},
1266
1267
statics: {
1268
min: function() {
1269
var point1 = Point.read(arguments),
1270
point2 = Point.read(arguments);
1271
return new Point(
1272
Math.min(point1.x, point2.x),
1273
Math.min(point1.y, point2.y)
1274
);
1275
},
1276
1277
max: function() {
1278
var point1 = Point.read(arguments),
1279
point2 = Point.read(arguments);
1280
return new Point(
1281
Math.max(point1.x, point2.x),
1282
Math.max(point1.y, point2.y)
1283
);
1284
},
1285
1286
random: function() {
1287
return new Point(Math.random(), Math.random());
1288
}
1289
}
1290
}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
1291
var op = Math[name];
1292
this[name] = function() {
1293
return new Point(op(this.x), op(this.y));
1294
};
1295
}, {}));
1296
1297
var LinkedPoint = Point.extend({
1298
initialize: function Point(x, y, owner, setter) {
1299
this._x = x;
1300
this._y = y;
1301
this._owner = owner;
1302
this._setter = setter;
1303
},
1304
1305
set: function(x, y, _dontNotify) {
1306
this._x = x;
1307
this._y = y;
1308
if (!_dontNotify)
1309
this._owner[this._setter](this);
1310
return this;
1311
},
1312
1313
getX: function() {
1314
return this._x;
1315
},
1316
1317
setX: function(x) {
1318
this._x = x;
1319
this._owner[this._setter](this);
1320
},
1321
1322
getY: function() {
1323
return this._y;
1324
},
1325
1326
setY: function(y) {
1327
this._y = y;
1328
this._owner[this._setter](this);
1329
}
1330
});
1331
1332
var Size = Base.extend({
1333
_class: 'Size',
1334
_readIndex: true,
1335
1336
initialize: function Size(arg0, arg1) {
1337
var type = typeof arg0;
1338
if (type === 'number') {
1339
var hasHeight = typeof arg1 === 'number';
1340
this.width = arg0;
1341
this.height = hasHeight ? arg1 : arg0;
1342
if (this.__read)
1343
this.__read = hasHeight ? 2 : 1;
1344
} else if (type === 'undefined' || arg0 === null) {
1345
this.width = this.height = 0;
1346
if (this.__read)
1347
this.__read = arg0 === null ? 1 : 0;
1348
} else {
1349
if (Array.isArray(arg0)) {
1350
this.width = arg0[0];
1351
this.height = arg0.length > 1 ? arg0[1] : arg0[0];
1352
} else if (arg0.width != null) {
1353
this.width = arg0.width;
1354
this.height = arg0.height;
1355
} else if (arg0.x != null) {
1356
this.width = arg0.x;
1357
this.height = arg0.y;
1358
} else {
1359
this.width = this.height = 0;
1360
if (this.__read)
1361
this.__read = 0;
1362
}
1363
if (this.__read)
1364
this.__read = 1;
1365
}
1366
},
1367
1368
set: function(width, height) {
1369
this.width = width;
1370
this.height = height;
1371
return this;
1372
},
1373
1374
equals: function(size) {
1375
return size === this || size && (this.width === size.width
1376
&& this.height === size.height
1377
|| Array.isArray(size) && this.width === size[0]
1378
&& this.height === size[1]) || false;
1379
},
1380
1381
clone: function() {
1382
return new Size(this.width, this.height);
1383
},
1384
1385
toString: function() {
1386
var f = Formatter.instance;
1387
return '{ width: ' + f.number(this.width)
1388
+ ', height: ' + f.number(this.height) + ' }';
1389
},
1390
1391
_serialize: function(options) {
1392
var f = options.formatter;
1393
return [f.number(this.width),
1394
f.number(this.height)];
1395
},
1396
1397
add: function() {
1398
var size = Size.read(arguments);
1399
return new Size(this.width + size.width, this.height + size.height);
1400
},
1401
1402
subtract: function() {
1403
var size = Size.read(arguments);
1404
return new Size(this.width - size.width, this.height - size.height);
1405
},
1406
1407
multiply: function() {
1408
var size = Size.read(arguments);
1409
return new Size(this.width * size.width, this.height * size.height);
1410
},
1411
1412
divide: function() {
1413
var size = Size.read(arguments);
1414
return new Size(this.width / size.width, this.height / size.height);
1415
},
1416
1417
modulo: function() {
1418
var size = Size.read(arguments);
1419
return new Size(this.width % size.width, this.height % size.height);
1420
},
1421
1422
negate: function() {
1423
return new Size(-this.width, -this.height);
1424
},
1425
1426
isZero: function() {
1427
return Numerical.isZero(this.width) && Numerical.isZero(this.height);
1428
},
1429
1430
isNaN: function() {
1431
return isNaN(this.width) || isNaN(this.height);
1432
},
1433
1434
statics: {
1435
min: function(size1, size2) {
1436
return new Size(
1437
Math.min(size1.width, size2.width),
1438
Math.min(size1.height, size2.height));
1439
},
1440
1441
max: function(size1, size2) {
1442
return new Size(
1443
Math.max(size1.width, size2.width),
1444
Math.max(size1.height, size2.height));
1445
},
1446
1447
random: function() {
1448
return new Size(Math.random(), Math.random());
1449
}
1450
}
1451
}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
1452
var op = Math[name];
1453
this[name] = function() {
1454
return new Size(op(this.width), op(this.height));
1455
};
1456
}, {}));
1457
1458
var LinkedSize = Size.extend({
1459
initialize: function Size(width, height, owner, setter) {
1460
this._width = width;
1461
this._height = height;
1462
this._owner = owner;
1463
this._setter = setter;
1464
},
1465
1466
set: function(width, height, _dontNotify) {
1467
this._width = width;
1468
this._height = height;
1469
if (!_dontNotify)
1470
this._owner[this._setter](this);
1471
return this;
1472
},
1473
1474
getWidth: function() {
1475
return this._width;
1476
},
1477
1478
setWidth: function(width) {
1479
this._width = width;
1480
this._owner[this._setter](this);
1481
},
1482
1483
getHeight: function() {
1484
return this._height;
1485
},
1486
1487
setHeight: function(height) {
1488
this._height = height;
1489
this._owner[this._setter](this);
1490
}
1491
});
1492
1493
var Rectangle = Base.extend({
1494
_class: 'Rectangle',
1495
_readIndex: true,
1496
beans: true,
1497
1498
initialize: function Rectangle(arg0, arg1, arg2, arg3) {
1499
var type = typeof arg0,
1500
read = 0;
1501
if (type === 'number') {
1502
this.x = arg0;
1503
this.y = arg1;
1504
this.width = arg2;
1505
this.height = arg3;
1506
read = 4;
1507
} else if (type === 'undefined' || arg0 === null) {
1508
this.x = this.y = this.width = this.height = 0;
1509
read = arg0 === null ? 1 : 0;
1510
} else if (arguments.length === 1) {
1511
if (Array.isArray(arg0)) {
1512
this.x = arg0[0];
1513
this.y = arg0[1];
1514
this.width = arg0[2];
1515
this.height = arg0[3];
1516
read = 1;
1517
} else if (arg0.x !== undefined || arg0.width !== undefined) {
1518
this.x = arg0.x || 0;
1519
this.y = arg0.y || 0;
1520
this.width = arg0.width || 0;
1521
this.height = arg0.height || 0;
1522
read = 1;
1523
} else if (arg0.from === undefined && arg0.to === undefined) {
1524
this.x = this.y = this.width = this.height = 0;
1525
this._set(arg0);
1526
read = 1;
1527
}
1528
}
1529
if (!read) {
1530
var point = Point.readNamed(arguments, 'from'),
1531
next = Base.peek(arguments);
1532
this.x = point.x;
1533
this.y = point.y;
1534
if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
1535
var to = Point.readNamed(arguments, 'to');
1536
this.width = to.x - point.x;
1537
this.height = to.y - point.y;
1538
if (this.width < 0) {
1539
this.x = to.x;
1540
this.width = -this.width;
1541
}
1542
if (this.height < 0) {
1543
this.y = to.y;
1544
this.height = -this.height;
1545
}
1546
} else {
1547
var size = Size.read(arguments);
1548
this.width = size.width;
1549
this.height = size.height;
1550
}
1551
read = arguments.__index;
1552
}
1553
if (this.__read)
1554
this.__read = read;
1555
},
1556
1557
set: function(x, y, width, height) {
1558
this.x = x;
1559
this.y = y;
1560
this.width = width;
1561
this.height = height;
1562
return this;
1563
},
1564
1565
clone: function() {
1566
return new Rectangle(this.x, this.y, this.width, this.height);
1567
},
1568
1569
equals: function(rect) {
1570
var rt = Base.isPlainValue(rect)
1571
? Rectangle.read(arguments)
1572
: rect;
1573
return rt === this
1574
|| rt && this.x === rt.x && this.y === rt.y
1575
&& this.width === rt.width && this.height === rt.height
1576
|| false;
1577
},
1578
1579
toString: function() {
1580
var f = Formatter.instance;
1581
return '{ x: ' + f.number(this.x)
1582
+ ', y: ' + f.number(this.y)
1583
+ ', width: ' + f.number(this.width)
1584
+ ', height: ' + f.number(this.height)
1585
+ ' }';
1586
},
1587
1588
_serialize: function(options) {
1589
var f = options.formatter;
1590
return [f.number(this.x),
1591
f.number(this.y),
1592
f.number(this.width),
1593
f.number(this.height)];
1594
},
1595
1596
getPoint: function(_dontLink) {
1597
var ctor = _dontLink ? Point : LinkedPoint;
1598
return new ctor(this.x, this.y, this, 'setPoint');
1599
},
1600
1601
setPoint: function() {
1602
var point = Point.read(arguments);
1603
this.x = point.x;
1604
this.y = point.y;
1605
},
1606
1607
getSize: function(_dontLink) {
1608
var ctor = _dontLink ? Size : LinkedSize;
1609
return new ctor(this.width, this.height, this, 'setSize');
1610
},
1611
1612
setSize: function() {
1613
var size = Size.read(arguments);
1614
if (this._fixX)
1615
this.x += (this.width - size.width) * this._fixX;
1616
if (this._fixY)
1617
this.y += (this.height - size.height) * this._fixY;
1618
this.width = size.width;
1619
this.height = size.height;
1620
this._fixW = 1;
1621
this._fixH = 1;
1622
},
1623
1624
getLeft: function() {
1625
return this.x;
1626
},
1627
1628
setLeft: function(left) {
1629
if (!this._fixW)
1630
this.width -= left - this.x;
1631
this.x = left;
1632
this._fixX = 0;
1633
},
1634
1635
getTop: function() {
1636
return this.y;
1637
},
1638
1639
setTop: function(top) {
1640
if (!this._fixH)
1641
this.height -= top - this.y;
1642
this.y = top;
1643
this._fixY = 0;
1644
},
1645
1646
getRight: function() {
1647
return this.x + this.width;
1648
},
1649
1650
setRight: function(right) {
1651
if (this._fixX !== undefined && this._fixX !== 1)
1652
this._fixW = 0;
1653
if (this._fixW)
1654
this.x = right - this.width;
1655
else
1656
this.width = right - this.x;
1657
this._fixX = 1;
1658
},
1659
1660
getBottom: function() {
1661
return this.y + this.height;
1662
},
1663
1664
setBottom: function(bottom) {
1665
if (this._fixY !== undefined && this._fixY !== 1)
1666
this._fixH = 0;
1667
if (this._fixH)
1668
this.y = bottom - this.height;
1669
else
1670
this.height = bottom - this.y;
1671
this._fixY = 1;
1672
},
1673
1674
getCenterX: function() {
1675
return this.x + this.width * 0.5;
1676
},
1677
1678
setCenterX: function(x) {
1679
this.x = x - this.width * 0.5;
1680
this._fixX = 0.5;
1681
},
1682
1683
getCenterY: function() {
1684
return this.y + this.height * 0.5;
1685
},
1686
1687
setCenterY: function(y) {
1688
this.y = y - this.height * 0.5;
1689
this._fixY = 0.5;
1690
},
1691
1692
getCenter: function(_dontLink) {
1693
var ctor = _dontLink ? Point : LinkedPoint;
1694
return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');
1695
},
1696
1697
setCenter: function() {
1698
var point = Point.read(arguments);
1699
this.setCenterX(point.x);
1700
this.setCenterY(point.y);
1701
return this;
1702
},
1703
1704
getArea: function() {
1705
return this.width * this.height;
1706
},
1707
1708
isEmpty: function() {
1709
return this.width === 0 || this.height === 0;
1710
},
1711
1712
contains: function(arg) {
1713
return arg && arg.width !== undefined
1714
|| (Array.isArray(arg) ? arg : arguments).length == 4
1715
? this._containsRectangle(Rectangle.read(arguments))
1716
: this._containsPoint(Point.read(arguments));
1717
},
1718
1719
_containsPoint: function(point) {
1720
var x = point.x,
1721
y = point.y;
1722
return x >= this.x && y >= this.y
1723
&& x <= this.x + this.width
1724
&& y <= this.y + this.height;
1725
},
1726
1727
_containsRectangle: function(rect) {
1728
var x = rect.x,
1729
y = rect.y;
1730
return x >= this.x && y >= this.y
1731
&& x + rect.width <= this.x + this.width
1732
&& y + rect.height <= this.y + this.height;
1733
},
1734
1735
intersects: function() {
1736
var rect = Rectangle.read(arguments);
1737
return rect.x + rect.width > this.x
1738
&& rect.y + rect.height > this.y
1739
&& rect.x < this.x + this.width
1740
&& rect.y < this.y + this.height;
1741
},
1742
1743
touches: function() {
1744
var rect = Rectangle.read(arguments);
1745
return rect.x + rect.width >= this.x
1746
&& rect.y + rect.height >= this.y
1747
&& rect.x <= this.x + this.width
1748
&& rect.y <= this.y + this.height;
1749
},
1750
1751
intersect: function() {
1752
var rect = Rectangle.read(arguments),
1753
x1 = Math.max(this.x, rect.x),
1754
y1 = Math.max(this.y, rect.y),
1755
x2 = Math.min(this.x + this.width, rect.x + rect.width),
1756
y2 = Math.min(this.y + this.height, rect.y + rect.height);
1757
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
1758
},
1759
1760
unite: function() {
1761
var rect = Rectangle.read(arguments),
1762
x1 = Math.min(this.x, rect.x),
1763
y1 = Math.min(this.y, rect.y),
1764
x2 = Math.max(this.x + this.width, rect.x + rect.width),
1765
y2 = Math.max(this.y + this.height, rect.y + rect.height);
1766
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
1767
},
1768
1769
include: function() {
1770
var point = Point.read(arguments);
1771
var x1 = Math.min(this.x, point.x),
1772
y1 = Math.min(this.y, point.y),
1773
x2 = Math.max(this.x + this.width, point.x),
1774
y2 = Math.max(this.y + this.height, point.y);
1775
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
1776
},
1777
1778
expand: function() {
1779
var amount = Size.read(arguments),
1780
hor = amount.width,
1781
ver = amount.height;
1782
return new Rectangle(this.x - hor / 2, this.y - ver / 2,
1783
this.width + hor, this.height + ver);
1784
},
1785
1786
scale: function(hor, ver) {
1787
return this.expand(this.width * hor - this.width,
1788
this.height * (ver === undefined ? hor : ver) - this.height);
1789
}
1790
}, new function() {
1791
return Base.each([
1792
['Top', 'Left'], ['Top', 'Right'],
1793
['Bottom', 'Left'], ['Bottom', 'Right'],
1794
['Left', 'Center'], ['Top', 'Center'],
1795
['Right', 'Center'], ['Bottom', 'Center']
1796
],
1797
function(parts, index) {
1798
var part = parts.join('');
1799
var xFirst = /^[RL]/.test(part);
1800
if (index >= 4)
1801
parts[1] += xFirst ? 'Y' : 'X';
1802
var x = parts[xFirst ? 0 : 1],
1803
y = parts[xFirst ? 1 : 0],
1804
getX = 'get' + x,
1805
getY = 'get' + y,
1806
setX = 'set' + x,
1807
setY = 'set' + y,
1808
get = 'get' + part,
1809
set = 'set' + part;
1810
this[get] = function(_dontLink) {
1811
var ctor = _dontLink ? Point : LinkedPoint;
1812
return new ctor(this[getX](), this[getY](), this, set);
1813
};
1814
this[set] = function() {
1815
var point = Point.read(arguments);
1816
this[setX](point.x);
1817
this[setY](point.y);
1818
};
1819
}, {
1820
beans: true
1821
});
1822
});
1823
1824
var LinkedRectangle = Rectangle.extend({
1825
initialize: function Rectangle(x, y, width, height, owner, setter) {
1826
this.set(x, y, width, height, true);
1827
this._owner = owner;
1828
this._setter = setter;
1829
},
1830
1831
set: function(x, y, width, height, _dontNotify) {
1832
this._x = x;
1833
this._y = y;
1834
this._width = width;
1835
this._height = height;
1836
if (!_dontNotify)
1837
this._owner[this._setter](this);
1838
return this;
1839
}
1840
}, new function() {
1841
var proto = Rectangle.prototype;
1842
1843
return Base.each(['x', 'y', 'width', 'height'], function(key) {
1844
var part = Base.capitalize(key);
1845
var internal = '_' + key;
1846
this['get' + part] = function() {
1847
return this[internal];
1848
};
1849
1850
this['set' + part] = function(value) {
1851
this[internal] = value;
1852
if (!this._dontNotify)
1853
this._owner[this._setter](this);
1854
};
1855
}, Base.each(['Point', 'Size', 'Center',
1856
'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',
1857
'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
1858
'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],
1859
function(key) {
1860
var name = 'set' + key;
1861
this[name] = function() {
1862
this._dontNotify = true;
1863
proto[name].apply(this, arguments);
1864
this._dontNotify = false;
1865
this._owner[this._setter](this);
1866
};
1867
}, {
1868
isSelected: function() {
1869
return this._owner._boundsSelected;
1870
},
1871
1872
setSelected: function(selected) {
1873
var owner = this._owner;
1874
if (owner.setSelected) {
1875
owner._boundsSelected = selected;
1876
owner.setSelected(selected || owner._selectedSegmentState > 0);
1877
}
1878
}
1879
})
1880
);
1881
});
1882
1883
var Matrix = Base.extend({
1884
_class: 'Matrix',
1885
1886
initialize: function Matrix(arg) {
1887
var count = arguments.length,
1888
ok = true;
1889
if (count === 6) {
1890
this.set.apply(this, arguments);
1891
} else if (count === 1) {
1892
if (arg instanceof Matrix) {
1893
this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
1894
} else if (Array.isArray(arg)) {
1895
this.set.apply(this, arg);
1896
} else {
1897
ok = false;
1898
}
1899
} else if (count === 0) {
1900
this.reset();
1901
} else {
1902
ok = false;
1903
}
1904
if (!ok)
1905
throw new Error('Unsupported matrix parameters');
1906
},
1907
1908
set: function(a, c, b, d, tx, ty, _dontNotify) {
1909
this._a = a;
1910
this._c = c;
1911
this._b = b;
1912
this._d = d;
1913
this._tx = tx;
1914
this._ty = ty;
1915
if (!_dontNotify)
1916
this._changed();
1917
return this;
1918
},
1919
1920
_serialize: function(options) {
1921
return Base.serialize(this.getValues(), options);
1922
},
1923
1924
_changed: function() {
1925
var owner = this._owner;
1926
if (owner) {
1927
if (owner._applyMatrix) {
1928
owner.transform(null, true);
1929
} else {
1930
owner._changed(9);
1931
}
1932
}
1933
},
1934
1935
clone: function() {
1936
return new Matrix(this._a, this._c, this._b, this._d,
1937
this._tx, this._ty);
1938
},
1939
1940
equals: function(mx) {
1941
return mx === this || mx && this._a === mx._a && this._b === mx._b
1942
&& this._c === mx._c && this._d === mx._d
1943
&& this._tx === mx._tx && this._ty === mx._ty
1944
|| false;
1945
},
1946
1947
toString: function() {
1948
var f = Formatter.instance;
1949
return '[[' + [f.number(this._a), f.number(this._b),
1950
f.number(this._tx)].join(', ') + '], ['
1951
+ [f.number(this._c), f.number(this._d),
1952
f.number(this._ty)].join(', ') + ']]';
1953
},
1954
1955
reset: function(_dontNotify) {
1956
this._a = this._d = 1;
1957
this._c = this._b = this._tx = this._ty = 0;
1958
if (!_dontNotify)
1959
this._changed();
1960
return this;
1961
},
1962
1963
apply: function() {
1964
var owner = this._owner;
1965
if (owner) {
1966
owner.transform(null, true);
1967
return this.isIdentity();
1968
}
1969
return false;
1970
},
1971
1972
translate: function() {
1973
var point = Point.read(arguments),
1974
x = point.x,
1975
y = point.y;
1976
this._tx += x * this._a + y * this._b;
1977
this._ty += x * this._c + y * this._d;
1978
this._changed();
1979
return this;
1980
},
1981
1982
scale: function() {
1983
var scale = Point.read(arguments),
1984
center = Point.read(arguments, 0, { readNull: true });
1985
if (center)
1986
this.translate(center);
1987
this._a *= scale.x;
1988
this._c *= scale.x;
1989
this._b *= scale.y;
1990
this._d *= scale.y;
1991
if (center)
1992
this.translate(center.negate());
1993
this._changed();
1994
return this;
1995
},
1996
1997
rotate: function(angle ) {
1998
angle *= Math.PI / 180;
1999
var center = Point.read(arguments, 1),
2000
x = center.x,
2001
y = center.y,
2002
cos = Math.cos(angle),
2003
sin = Math.sin(angle),
2004
tx = x - x * cos + y * sin,
2005
ty = y - x * sin - y * cos,
2006
a = this._a,
2007
b = this._b,
2008
c = this._c,
2009
d = this._d;
2010
this._a = cos * a + sin * b;
2011
this._b = -sin * a + cos * b;
2012
this._c = cos * c + sin * d;
2013
this._d = -sin * c + cos * d;
2014
this._tx += tx * a + ty * b;
2015
this._ty += tx * c + ty * d;
2016
this._changed();
2017
return this;
2018
},
2019
2020
shear: function() {
2021
var shear = Point.read(arguments),
2022
center = Point.read(arguments, 0, { readNull: true });
2023
if (center)
2024
this.translate(center);
2025
var a = this._a,
2026
c = this._c;
2027
this._a += shear.y * this._b;
2028
this._c += shear.y * this._d;
2029
this._b += shear.x * a;
2030
this._d += shear.x * c;
2031
if (center)
2032
this.translate(center.negate());
2033
this._changed();
2034
return this;
2035
},
2036
2037
skew: function() {
2038
var skew = Point.read(arguments),
2039
center = Point.read(arguments, 0, { readNull: true }),
2040
toRadians = Math.PI / 180,
2041
shear = new Point(Math.tan(skew.x * toRadians),
2042
Math.tan(skew.y * toRadians));
2043
return this.shear(shear, center);
2044
},
2045
2046
concatenate: function(mx) {
2047
var a = this._a,
2048
b = this._b,
2049
c = this._c,
2050
d = this._d;
2051
this._a = mx._a * a + mx._c * b;
2052
this._b = mx._b * a + mx._d * b;
2053
this._c = mx._a * c + mx._c * d;
2054
this._d = mx._b * c + mx._d * d;
2055
this._tx += mx._tx * a + mx._ty * b;
2056
this._ty += mx._tx * c + mx._ty * d;
2057
this._changed();
2058
return this;
2059
},
2060
2061
preConcatenate: function(mx) {
2062
var a = this._a,
2063
b = this._b,
2064
c = this._c,
2065
d = this._d,
2066
tx = this._tx,
2067
ty = this._ty;
2068
this._a = mx._a * a + mx._b * c;
2069
this._b = mx._a * b + mx._b * d;
2070
this._c = mx._c * a + mx._d * c;
2071
this._d = mx._c * b + mx._d * d;
2072
this._tx = mx._a * tx + mx._b * ty + mx._tx;
2073
this._ty = mx._c * tx + mx._d * ty + mx._ty;
2074
this._changed();
2075
return this;
2076
},
2077
2078
isIdentity: function() {
2079
return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
2080
&& this._tx === 0 && this._ty === 0;
2081
},
2082
2083
orNullIfIdentity: function() {
2084
return this.isIdentity() ? null : this;
2085
},
2086
2087
isInvertible: function() {
2088
return !!this._getDeterminant();
2089
},
2090
2091
isSingular: function() {
2092
return !this._getDeterminant();
2093
},
2094
2095
transform: function( src, srcOffset, dst, dstOffset, count) {
2096
return arguments.length < 5
2097
? this._transformPoint(Point.read(arguments))
2098
: this._transformCoordinates(src, srcOffset, dst, dstOffset, count);
2099
},
2100
2101
_transformPoint: function(point, dest, _dontNotify) {
2102
var x = point.x,
2103
y = point.y;
2104
if (!dest)
2105
dest = new Point();
2106
return dest.set(
2107
x * this._a + y * this._b + this._tx,
2108
x * this._c + y * this._d + this._ty,
2109
_dontNotify
2110
);
2111
},
2112
2113
_transformCoordinates: function(src, srcOffset, dst, dstOffset, count) {
2114
var i = srcOffset,
2115
j = dstOffset,
2116
max = i + 2 * count;
2117
while (i < max) {
2118
var x = src[i++],
2119
y = src[i++];
2120
dst[j++] = x * this._a + y * this._b + this._tx;
2121
dst[j++] = x * this._c + y * this._d + this._ty;
2122
}
2123
return dst;
2124
},
2125
2126
_transformCorners: function(rect) {
2127
var x1 = rect.x,
2128
y1 = rect.y,
2129
x2 = x1 + rect.width,
2130
y2 = y1 + rect.height,
2131
coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
2132
return this._transformCoordinates(coords, 0, coords, 0, 4);
2133
},
2134
2135
_transformBounds: function(bounds, dest, _dontNotify) {
2136
var coords = this._transformCorners(bounds),
2137
min = coords.slice(0, 2),
2138
max = coords.slice();
2139
for (var i = 2; i < 8; i++) {
2140
var val = coords[i],
2141
j = i & 1;
2142
if (val < min[j])
2143
min[j] = val;
2144
else if (val > max[j])
2145
max[j] = val;
2146
}
2147
if (!dest)
2148
dest = new Rectangle();
2149
return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
2150
_dontNotify);
2151
},
2152
2153
inverseTransform: function() {
2154
return this._inverseTransform(Point.read(arguments));
2155
},
2156
2157
_getDeterminant: function() {
2158
var det = this._a * this._d - this._b * this._c;
2159
return isFinite(det) && !Numerical.isZero(det)
2160
&& isFinite(this._tx) && isFinite(this._ty)
2161
? det : null;
2162
},
2163
2164
_inverseTransform: function(point, dest, _dontNotify) {
2165
var det = this._getDeterminant();
2166
if (!det)
2167
return null;
2168
var x = point.x - this._tx,
2169
y = point.y - this._ty;
2170
if (!dest)
2171
dest = new Point();
2172
return dest.set(
2173
(x * this._d - y * this._b) / det,
2174
(y * this._a - x * this._c) / det,
2175
_dontNotify
2176
);
2177
},
2178
2179
decompose: function() {
2180
var a = this._a, b = this._b, c = this._c, d = this._d;
2181
if (Numerical.isZero(a * d - b * c))
2182
return null;
2183
2184
var scaleX = Math.sqrt(a * a + b * b);
2185
a /= scaleX;
2186
b /= scaleX;
2187
2188
var shear = a * c + b * d;
2189
c -= a * shear;
2190
d -= b * shear;
2191
2192
var scaleY = Math.sqrt(c * c + d * d);
2193
c /= scaleY;
2194
d /= scaleY;
2195
shear /= scaleY;
2196
2197
if (a * d < b * c) {
2198
a = -a;
2199
b = -b;
2200
shear = -shear;
2201
scaleX = -scaleX;
2202
}
2203
2204
return {
2205
scaling: new Point(scaleX, scaleY),
2206
rotation: -Math.atan2(b, a) * 180 / Math.PI,
2207
shearing: shear
2208
};
2209
},
2210
2211
getValues: function() {
2212
return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
2213
},
2214
2215
getTranslation: function() {
2216
return new Point(this._tx, this._ty);
2217
},
2218
2219
getScaling: function() {
2220
return (this.decompose() || {}).scaling;
2221
},
2222
2223
getRotation: function() {
2224
return (this.decompose() || {}).rotation;
2225
},
2226
2227
inverted: function() {
2228
var det = this._getDeterminant();
2229
return det && new Matrix(
2230
this._d / det,
2231
-this._c / det,
2232
-this._b / det,
2233
this._a / det,
2234
(this._b * this._ty - this._d * this._tx) / det,
2235
(this._c * this._tx - this._a * this._ty) / det);
2236
},
2237
2238
shiftless: function() {
2239
return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
2240
},
2241
2242
applyToContext: function(ctx) {
2243
ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
2244
}
2245
}, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) {
2246
var part = Base.capitalize(name),
2247
prop = '_' + name;
2248
this['get' + part] = function() {
2249
return this[prop];
2250
};
2251
this['set' + part] = function(value) {
2252
this[prop] = value;
2253
this._changed();
2254
};
2255
}, {}));
2256
2257
var Line = Base.extend({
2258
_class: 'Line',
2259
2260
initialize: function Line(arg0, arg1, arg2, arg3, arg4) {
2261
var asVector = false;
2262
if (arguments.length >= 4) {
2263
this._px = arg0;
2264
this._py = arg1;
2265
this._vx = arg2;
2266
this._vy = arg3;
2267
asVector = arg4;
2268
} else {
2269
this._px = arg0.x;
2270
this._py = arg0.y;
2271
this._vx = arg1.x;
2272
this._vy = arg1.y;
2273
asVector = arg2;
2274
}
2275
if (!asVector) {
2276
this._vx -= this._px;
2277
this._vy -= this._py;
2278
}
2279
},
2280
2281
getPoint: function() {
2282
return new Point(this._px, this._py);
2283
},
2284
2285
getVector: function() {
2286
return new Point(this._vx, this._vy);
2287
},
2288
2289
getLength: function() {
2290
return this.getVector().getLength();
2291
},
2292
2293
intersect: function(line, isInfinite) {
2294
return Line.intersect(
2295
this._px, this._py, this._vx, this._vy,
2296
line._px, line._py, line._vx, line._vy,
2297
true, isInfinite);
2298
},
2299
2300
getSide: function(point) {
2301
return Line.getSide(
2302
this._px, this._py, this._vx, this._vy,
2303
point.x, point.y, true);
2304
},
2305
2306
getDistance: function(point) {
2307
return Math.abs(Line.getSignedDistance(
2308
this._px, this._py, this._vx, this._vy,
2309
point.x, point.y, true));
2310
},
2311
2312
statics: {
2313
intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector,
2314
isInfinite) {
2315
if (!asVector) {
2316
avx -= apx;
2317
avy -= apy;
2318
bvx -= bpx;
2319
bvy -= bpy;
2320
}
2321
var cross = bvy * avx - bvx * avy;
2322
if (!Numerical.isZero(cross)) {
2323
var dx = apx - bpx,
2324
dy = apy - bpy,
2325
ta = (bvx * dy - bvy * dx) / cross,
2326
tb = (avx * dy - avy * dx) / cross;
2327
if ((isInfinite || 0 <= ta && ta <= 1)
2328
&& (isInfinite || 0 <= tb && tb <= 1))
2329
return new Point(
2330
apx + ta * avx,
2331
apy + ta * avy);
2332
}
2333
},
2334
2335
getSide: function(px, py, vx, vy, x, y, asVector) {
2336
if (!asVector) {
2337
vx -= px;
2338
vy -= py;
2339
}
2340
var v2x = x - px,
2341
v2y = y - py,
2342
ccw = v2x * vy - v2y * vx;
2343
if (ccw === 0) {
2344
ccw = v2x * vx + v2y * vy;
2345
if (ccw > 0) {
2346
v2x -= vx;
2347
v2y -= vy;
2348
ccw = v2x * vx + v2y * vy;
2349
if (ccw < 0)
2350
ccw = 0;
2351
}
2352
}
2353
return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
2354
},
2355
2356
getSignedDistance: function(px, py, vx, vy, x, y, asVector) {
2357
if (!asVector) {
2358
vx -= px;
2359
vy -= py;
2360
}
2361
var m = vy / vx,
2362
b = py - m * px;
2363
return (y - (m * x) - b) / Math.sqrt(m * m + 1);
2364
}
2365
}
2366
});
2367
2368
var Project = PaperScopeItem.extend({
2369
_class: 'Project',
2370
_list: 'projects',
2371
_reference: 'project',
2372
2373
initialize: function Project(element) {
2374
PaperScopeItem.call(this, true);
2375
this.layers = [];
2376
this.symbols = [];
2377
this._currentStyle = new Style(null, null, this);
2378
this.activeLayer = new Layer();
2379
this._view = View.create(this,
2380
element || CanvasProvider.getCanvas(1, 1));
2381
this._selectedItems = {};
2382
this._selectedItemCount = 0;
2383
this._updateVersion = 0;
2384
},
2385
2386
_serialize: function(options, dictionary) {
2387
return Base.serialize(this.layers, options, true, dictionary);
2388
},
2389
2390
clear: function() {
2391
for (var i = this.layers.length - 1; i >= 0; i--)
2392
this.layers[i].remove();
2393
this.symbols = [];
2394
},
2395
2396
isEmpty: function() {
2397
return this.layers.length <= 1
2398
&& (!this.activeLayer || this.activeLayer.isEmpty());
2399
},
2400
2401
remove: function remove() {
2402
if (!remove.base.call(this))
2403
return false;
2404
if (this._view)
2405
this._view.remove();
2406
return true;
2407
},
2408
2409
getView: function() {
2410
return this._view;
2411
},
2412
2413
getCurrentStyle: function() {
2414
return this._currentStyle;
2415
},
2416
2417
setCurrentStyle: function(style) {
2418
this._currentStyle.initialize(style);
2419
},
2420
2421
getIndex: function() {
2422
return this._index;
2423
},
2424
2425
addChild: function(child) {
2426
if (child instanceof Layer) {
2427
Base.splice(this.layers, [child]);
2428
if (!this.activeLayer)
2429
this.activeLayer = child;
2430
} else if (child instanceof Item) {
2431
(this.activeLayer
2432
|| this.addChild(new Layer(Item.NO_INSERT))).addChild(child);
2433
} else {
2434
child = null;
2435
}
2436
return child;
2437
},
2438
2439
getSelectedItems: function() {
2440
var items = [];
2441
for (var id in this._selectedItems) {
2442
var item = this._selectedItems[id];
2443
if (item.isInserted())
2444
items.push(item);
2445
}
2446
return items;
2447
},
2448
2449
getOptions: function() {
2450
return this._scope.settings;
2451
},
2452
2453
_updateSelection: function(item) {
2454
var id = item._id,
2455
selectedItems = this._selectedItems;
2456
if (item._selected) {
2457
if (selectedItems[id] !== item) {
2458
this._selectedItemCount++;
2459
selectedItems[id] = item;
2460
}
2461
} else if (selectedItems[id] === item) {
2462
this._selectedItemCount--;
2463
delete selectedItems[id];
2464
}
2465
},
2466
2467
selectAll: function() {
2468
var layers = this.layers;
2469
for (var i = 0, l = layers.length; i < l; i++)
2470
layers[i].setFullySelected(true);
2471
},
2472
2473
deselectAll: function() {
2474
var selectedItems = this._selectedItems;
2475
for (var i in selectedItems)
2476
selectedItems[i].setFullySelected(false);
2477
},
2478
2479
hitTest: function() {
2480
var point = Point.read(arguments),
2481
options = HitResult.getOptions(Base.read(arguments));
2482
for (var i = this.layers.length - 1; i >= 0; i--) {
2483
var res = this.layers[i].hitTest(point, options);
2484
if (res) return res;
2485
}
2486
return null;
2487
},
2488
2489
getItems: function(match) {
2490
return Item._getItems(this.layers, match, true);
2491
},
2492
2493
getItem: function(match) {
2494
return Item._getItems(this.layers, match, false);
2495
},
2496
2497
importJSON: function(json) {
2498
this.activate();
2499
var layer = this.activeLayer;
2500
return Base.importJSON(json, layer && layer.isEmpty() && layer);
2501
},
2502
2503
draw: function(ctx, matrix, pixelRatio) {
2504
this._updateVersion++;
2505
ctx.save();
2506
matrix.applyToContext(ctx);
2507
var param = new Base({
2508
offset: new Point(0, 0),
2509
pixelRatio: pixelRatio,
2510
trackTransforms: true,
2511
transforms: [matrix]
2512
});
2513
for (var i = 0, l = this.layers.length; i < l; i++)
2514
this.layers[i].draw(ctx, param);
2515
ctx.restore();
2516
2517
if (this._selectedItemCount > 0) {
2518
ctx.save();
2519
ctx.strokeWidth = 1;
2520
for (var id in this._selectedItems) {
2521
var item = this._selectedItems[id],
2522
globalMatrix = item._globalMatrix,
2523
size = this._scope.settings.handleSize,
2524
half = size / 2;
2525
if (item._updateVersion === this._updateVersion
2526
&& (item._drawSelected || item._boundsSelected)
2527
&& globalMatrix) {
2528
var color = item.getSelectedColor()
2529
|| item.getLayer().getSelectedColor();
2530
ctx.strokeStyle = ctx.fillStyle = color
2531
? color.toCanvasStyle(ctx) : '#009dec';
2532
if (item._drawSelected)
2533
item._drawSelected(ctx, globalMatrix);
2534
if (item._boundsSelected) {
2535
var coords = globalMatrix._transformCorners(
2536
item.getInternalBounds());
2537
ctx.beginPath();
2538
for (var i = 0; i < 8; i++)
2539
ctx[i === 0 ? 'moveTo' : 'lineTo'](
2540
coords[i], coords[++i]);
2541
ctx.closePath();
2542
ctx.stroke();
2543
for (var i = 0; i < 8; i++)
2544
ctx.fillRect(coords[i] - half, coords[++i] - half,
2545
size, size);
2546
}
2547
}
2548
}
2549
ctx.restore();
2550
}
2551
}
2552
});
2553
2554
var Symbol = Base.extend({
2555
_class: 'Symbol',
2556
2557
initialize: function Symbol(item, dontCenter) {
2558
this._id = Symbol._id = (Symbol._id || 0) + 1;
2559
this.project = paper.project;
2560
this.project.symbols.push(this);
2561
if (item)
2562
this.setDefinition(item, dontCenter);
2563
},
2564
2565
_serialize: function(options, dictionary) {
2566
return dictionary.add(this, function() {
2567
return Base.serialize([this._class, this._definition],
2568
options, false, dictionary);
2569
});
2570
},
2571
2572
_changed: function(flags) {
2573
if (flags & 8) {
2574
Item._clearBoundsCache(this);
2575
}
2576
if (flags & 1) {
2577
this.project._needsUpdate = true;
2578
}
2579
},
2580
2581
getDefinition: function() {
2582
return this._definition;
2583
},
2584
2585
setDefinition: function(item, _dontCenter) {
2586
if (item._parentSymbol)
2587
item = item.clone();
2588
if (this._definition)
2589
this._definition._parentSymbol = null;
2590
this._definition = item;
2591
item.remove();
2592
item.setSelected(false);
2593
if (!_dontCenter)
2594
item.setPosition(new Point());
2595
item._parentSymbol = this;
2596
this._changed(9);
2597
},
2598
2599
place: function(position) {
2600
return new PlacedSymbol(this, position);
2601
},
2602
2603
clone: function() {
2604
return new Symbol(this._definition.clone(false));
2605
}
2606
});
2607
2608
var Item = Base.extend(Callback, {
2609
statics: {
2610
extend: function extend(src) {
2611
if (src._serializeFields)
2612
src._serializeFields = new Base(
2613
this.prototype._serializeFields, src._serializeFields);
2614
return extend.base.apply(this, arguments);
2615
},
2616
2617
NO_INSERT: { insert: false }
2618
},
2619
2620
_class: 'Item',
2621
_applyMatrix: true,
2622
_canApplyMatrix: true,
2623
_boundsSelected: false,
2624
_selectChildren: false,
2625
_serializeFields: {
2626
name: null,
2627
matrix: new Matrix(),
2628
pivot: null,
2629
locked: false,
2630
visible: true,
2631
blendMode: 'normal',
2632
opacity: 1,
2633
guide: false,
2634
selected: false,
2635
clipMask: false,
2636
applyMatrix: null,
2637
data: {}
2638
},
2639
2640
initialize: function Item() {
2641
},
2642
2643
_initialize: function(props, point) {
2644
var internal = props && props.internal === true,
2645
matrix = this._matrix = new Matrix(),
2646
project = paper.project;
2647
if (!internal)
2648
this._id = Item._id = (Item._id || 0) + 1;
2649
this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix;
2650
if (point)
2651
matrix.translate(point);
2652
matrix._owner = this;
2653
this._style = new Style(project._currentStyle, this, project);
2654
if (!this._project) {
2655
if (internal || props && props.insert === false) {
2656
this._setProject(project);
2657
} else {
2658
(project.activeLayer || new Layer()).addChild(this);
2659
}
2660
}
2661
return props && props !== Item.NO_INSERT
2662
? this._set(props, { insert: true })
2663
: true;
2664
},
2665
2666
_events: new function() {
2667
2668
var mouseFlags = {
2669
mousedown: {
2670
mousedown: 1,
2671
mousedrag: 1,
2672
click: 1,
2673
doubleclick: 1
2674
},
2675
mouseup: {
2676
mouseup: 1,
2677
mousedrag: 1,
2678
click: 1,
2679
doubleclick: 1
2680
},
2681
mousemove: {
2682
mousedrag: 1,
2683
mousemove: 1,
2684
mouseenter: 1,
2685
mouseleave: 1
2686
}
2687
};
2688
2689
var mouseEvent = {
2690
install: function(type) {
2691
var counters = this.getView()._eventCounters;
2692
if (counters) {
2693
for (var key in mouseFlags) {
2694
counters[key] = (counters[key] || 0)
2695
+ (mouseFlags[key][type] || 0);
2696
}
2697
}
2698
},
2699
uninstall: function(type) {
2700
var counters = this.getView()._eventCounters;
2701
if (counters) {
2702
for (var key in mouseFlags)
2703
counters[key] -= mouseFlags[key][type] || 0;
2704
}
2705
}
2706
};
2707
2708
return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
2709
'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
2710
function(name) {
2711
this[name] = mouseEvent;
2712
}, {
2713
onFrame: {
2714
install: function() {
2715
this._animateItem(true);
2716
},
2717
uninstall: function() {
2718
this._animateItem(false);
2719
}
2720
},
2721
2722
onLoad: {}
2723
}
2724
);
2725
},
2726
2727
_animateItem: function(animate) {
2728
this.getView()._animateItem(this, animate);
2729
},
2730
2731
_serialize: function(options, dictionary) {
2732
var props = {},
2733
that = this;
2734
2735
function serialize(fields) {
2736
for (var key in fields) {
2737
var value = that[key];
2738
if (!Base.equals(value, key === 'leading'
2739
? fields.fontSize * 1.2 : fields[key])) {
2740
props[key] = Base.serialize(value, options,
2741
key !== 'data', dictionary);
2742
}
2743
}
2744
}
2745
2746
serialize(this._serializeFields);
2747
if (!(this instanceof Group))
2748
serialize(this._style._defaults);
2749
return [ this._class, props ];
2750
},
2751
2752
_changed: function(flags) {
2753
var symbol = this._parentSymbol,
2754
cacheParent = this._parent || symbol,
2755
project = this._project;
2756
if (flags & 8) {
2757
this._bounds = this._position = this._decomposed =
2758
this._globalMatrix = this._currentPath = undefined;
2759
}
2760
if (cacheParent && (flags
2761
& (8 | 32))) {
2762
Item._clearBoundsCache(cacheParent);
2763
}
2764
if (flags & 2) {
2765
Item._clearBoundsCache(this);
2766
}
2767
if (project) {
2768
if (flags & 1) {
2769
project._needsUpdate = true;
2770
}
2771
if (project._changes) {
2772
var entry = project._changesById[this._id];
2773
if (entry) {
2774
entry.flags |= flags;
2775
} else {
2776
entry = { item: this, flags: flags };
2777
project._changesById[this._id] = entry;
2778
project._changes.push(entry);
2779
}
2780
}
2781
}
2782
if (symbol)
2783
symbol._changed(flags);
2784
},
2785
2786
set: function(props) {
2787
if (props)
2788
this._set(props, { insert: true });
2789
return this;
2790
},
2791
2792
getId: function() {
2793
return this._id;
2794
},
2795
2796
getClassName: function() {
2797
return this._class;
2798
},
2799
2800
getName: function() {
2801
return this._name;
2802
},
2803
2804
setName: function(name, unique) {
2805
2806
if (this._name)
2807
this._removeNamed();
2808
if (name === (+name) + '')
2809
throw new Error(
2810
'Names consisting only of numbers are not supported.');
2811
if (name && this._parent) {
2812
var children = this._parent._children,
2813
namedChildren = this._parent._namedChildren,
2814
orig = name,
2815
i = 1;
2816
while (unique && children[name])
2817
name = orig + ' ' + (i++);
2818
(namedChildren[name] = namedChildren[name] || []).push(this);
2819
children[name] = this;
2820
}
2821
this._name = name || undefined;
2822
this._changed(128);
2823
},
2824
2825
getStyle: function() {
2826
return this._style;
2827
},
2828
2829
setStyle: function(style) {
2830
this.getStyle().set(style);
2831
},
2832
2833
hasFill: function() {
2834
return this.getStyle().hasFill();
2835
},
2836
2837
hasStroke: function() {
2838
return this.getStyle().hasStroke();
2839
},
2840
2841
hasShadow: function() {
2842
return this.getStyle().hasShadow();
2843
}
2844
}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
2845
function(name) {
2846
var part = Base.capitalize(name),
2847
name = '_' + name;
2848
this['get' + part] = function() {
2849
return this[name];
2850
};
2851
this['set' + part] = function(value) {
2852
if (value != this[name]) {
2853
this[name] = value;
2854
this._changed(name === '_locked'
2855
? 128 : 129);
2856
}
2857
};
2858
}, {}), {
2859
beans: true,
2860
2861
_locked: false,
2862
2863
_visible: true,
2864
2865
_blendMode: 'normal',
2866
2867
_opacity: 1,
2868
2869
_guide: false,
2870
2871
isSelected: function() {
2872
if (this._selectChildren) {
2873
for (var i = 0, l = this._children.length; i < l; i++)
2874
if (this._children[i].isSelected())
2875
return true;
2876
}
2877
return this._selected;
2878
},
2879
2880
setSelected: function(selected, noChildren) {
2881
if (!noChildren && this._selectChildren) {
2882
for (var i = 0, l = this._children.length; i < l; i++)
2883
this._children[i].setSelected(selected);
2884
}
2885
if ((selected = !!selected) ^ this._selected) {
2886
this._selected = selected;
2887
this._project._updateSelection(this);
2888
this._changed(129);
2889
}
2890
},
2891
2892
_selected: false,
2893
2894
isFullySelected: function() {
2895
if (this._children && this._selected) {
2896
for (var i = 0, l = this._children.length; i < l; i++)
2897
if (!this._children[i].isFullySelected())
2898
return false;
2899
return true;
2900
}
2901
return this._selected;
2902
},
2903
2904
setFullySelected: function(selected) {
2905
if (this._children) {
2906
for (var i = 0, l = this._children.length; i < l; i++)
2907
this._children[i].setFullySelected(selected);
2908
}
2909
this.setSelected(selected, true);
2910
},
2911
2912
isClipMask: function() {
2913
return this._clipMask;
2914
},
2915
2916
setClipMask: function(clipMask) {
2917
if (this._clipMask != (clipMask = !!clipMask)) {
2918
this._clipMask = clipMask;
2919
if (clipMask) {
2920
this.setFillColor(null);
2921
this.setStrokeColor(null);
2922
}
2923
this._changed(129);
2924
if (this._parent)
2925
this._parent._changed(1024);
2926
}
2927
},
2928
2929
_clipMask: false,
2930
2931
getData: function() {
2932
if (!this._data)
2933
this._data = {};
2934
return this._data;
2935
},
2936
2937
setData: function(data) {
2938
this._data = data;
2939
},
2940
2941
getPosition: function(_dontLink) {
2942
var position = this._position,
2943
ctor = _dontLink ? Point : LinkedPoint;
2944
if (!position) {
2945
var pivot = this._pivot;
2946
position = this._position = pivot
2947
? this._matrix._transformPoint(pivot)
2948
: this.getBounds().getCenter(true);
2949
}
2950
return new ctor(position.x, position.y, this, 'setPosition');
2951
},
2952
2953
setPosition: function() {
2954
this.translate(Point.read(arguments).subtract(this.getPosition(true)));
2955
},
2956
2957
getPivot: function(_dontLink) {
2958
var pivot = this._pivot;
2959
if (pivot) {
2960
var ctor = _dontLink ? Point : LinkedPoint;
2961
pivot = new ctor(pivot.x, pivot.y, this, 'setAnchor');
2962
}
2963
return pivot;
2964
},
2965
2966
setPivot: function() {
2967
this._pivot = Point.read(arguments);
2968
this._position = undefined;
2969
},
2970
2971
_pivot: null,
2972
2973
getRegistration: '#getPivot',
2974
setRegistration: '#setPivot'
2975
}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds',
2976
'internalBounds', 'internalRoughBounds'],
2977
function(key) {
2978
var getter = 'get' + Base.capitalize(key),
2979
match = key.match(/^internal(.*)$/),
2980
internalGetter = match ? 'get' + match[1] : null;
2981
this[getter] = function(_matrix) {
2982
var boundsGetter = this._boundsGetter,
2983
name = !internalGetter && (typeof boundsGetter === 'string'
2984
? boundsGetter : boundsGetter && boundsGetter[getter])
2985
|| getter,
2986
bounds = this._getCachedBounds(name, _matrix, null,
2987
internalGetter);
2988
return key === 'bounds'
2989
? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
2990
bounds.height, this, 'setBounds')
2991
: bounds;
2992
};
2993
},
2994
{
2995
beans: true,
2996
2997
_getBounds: function(getter, matrix, cacheItem) {
2998
var children = this._children;
2999
if (!children || children.length == 0)
3000
return new Rectangle();
3001
var x1 = Infinity,
3002
x2 = -x1,
3003
y1 = x1,
3004
y2 = x2;
3005
for (var i = 0, l = children.length; i < l; i++) {
3006
var child = children[i];
3007
if (child._visible && !child.isEmpty()) {
3008
var rect = child._getCachedBounds(getter, matrix, cacheItem);
3009
x1 = Math.min(rect.x, x1);
3010
y1 = Math.min(rect.y, y1);
3011
x2 = Math.max(rect.x + rect.width, x2);
3012
y2 = Math.max(rect.y + rect.height, y2);
3013
}
3014
}
3015
return isFinite(x1)
3016
? new Rectangle(x1, y1, x2 - x1, y2 - y1)
3017
: new Rectangle();
3018
},
3019
3020
setBounds: function() {
3021
var rect = Rectangle.read(arguments),
3022
bounds = this.getBounds(),
3023
matrix = new Matrix(),
3024
center = rect.getCenter();
3025
matrix.translate(center);
3026
if (rect.width != bounds.width || rect.height != bounds.height) {
3027
matrix.scale(
3028
bounds.width != 0 ? rect.width / bounds.width : 1,
3029
bounds.height != 0 ? rect.height / bounds.height : 1);
3030
}
3031
center = bounds.getCenter();
3032
matrix.translate(-center.x, -center.y);
3033
this.transform(matrix);
3034
},
3035
3036
_getCachedBounds: function(getter, matrix, cacheItem, internalGetter) {
3037
matrix = matrix && matrix.orNullIfIdentity();
3038
var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(),
3039
cache = (!matrix || matrix.equals(_matrix)) && getter;
3040
var cacheParent = this._parent || this._parentSymbol;
3041
if (cacheItem && cacheParent) {
3042
var id = cacheItem._id,
3043
ref = cacheParent._boundsCache = cacheParent._boundsCache || {
3044
ids: {},
3045
list: []
3046
};
3047
if (!ref.ids[id]) {
3048
ref.list.push(cacheItem);
3049
ref.ids[id] = cacheItem;
3050
}
3051
}
3052
if (cache && this._bounds && this._bounds[cache])
3053
return this._bounds[cache].clone();
3054
matrix = !matrix
3055
? _matrix
3056
: _matrix
3057
? matrix.clone().concatenate(_matrix)
3058
: matrix;
3059
var bounds = this._getBounds(internalGetter || getter, matrix,
3060
cache ? this : cacheItem);
3061
if (cache) {
3062
if (!this._bounds)
3063
this._bounds = {};
3064
var cached = this._bounds[cache] = bounds.clone();
3065
cached._internal = !!internalGetter;
3066
}
3067
return bounds;
3068
},
3069
3070
statics: {
3071
_clearBoundsCache: function(item) {
3072
if (item._boundsCache) {
3073
for (var i = 0, list = item._boundsCache.list, l = list.length;
3074
i < l; i++) {
3075
var child = list[i];
3076
child._bounds = child._position = undefined;
3077
if (child !== item && child._boundsCache)
3078
Item._clearBoundsCache(child);
3079
}
3080
item._boundsCache = undefined;
3081
}
3082
}
3083
}
3084
3085
}), {
3086
beans: true,
3087
3088
_decompose: function() {
3089
return this._decomposed = this._matrix.decompose();
3090
},
3091
3092
getRotation: function() {
3093
var decomposed = this._decomposed || this._decompose();
3094
return decomposed && decomposed.rotation;
3095
},
3096
3097
setRotation: function(rotation) {
3098
var current = this.getRotation();
3099
if (current != null && rotation != null) {
3100
var decomposed = this._decomposed;
3101
this.rotate(rotation - current);
3102
decomposed.rotation = rotation;
3103
this._decomposed = decomposed;
3104
}
3105
},
3106
3107
getScaling: function() {
3108
var decomposed = this._decomposed || this._decompose();
3109
return decomposed && decomposed.scaling;
3110
},
3111
3112
setScaling: function() {
3113
var current = this.getScaling();
3114
if (current != null) {
3115
var scaling = Point.read(arguments, 0, { clone: true }),
3116
decomposed = this._decomposed;
3117
this.scale(scaling.x / current.x, scaling.y / current.y);
3118
decomposed.scaling = scaling;
3119
this._decomposed = decomposed;
3120
}
3121
},
3122
3123
getMatrix: function() {
3124
return this._matrix;
3125
},
3126
3127
setMatrix: function(matrix) {
3128
this._matrix.initialize(matrix);
3129
if (this._applyMatrix) {
3130
this.transform(null, true);
3131
} else {
3132
this._changed(9);
3133
}
3134
},
3135
3136
getGlobalMatrix: function(_internal) {
3137
var matrix = this._globalMatrix,
3138
updateVersion = this._project._updateVersion,
3139
viewMatrix = this.getView()._matrix;
3140
if (matrix && matrix._updateVersion !== updateVersion)
3141
matrix = null;
3142
if (!matrix) {
3143
matrix = this._globalMatrix = this._matrix.clone();
3144
matrix.preConcatenate(this._parent
3145
? this._parent.getGlobalMatrix(true)
3146
: viewMatrix);
3147
matrix._updateVersion = updateVersion;
3148
}
3149
return _internal ? matrix : viewMatrix.inverted().concatenate(matrix);
3150
},
3151
3152
getApplyMatrix: function() {
3153
return this._applyMatrix;
3154
},
3155
3156
setApplyMatrix: function(transform) {
3157
if (this._applyMatrix = this._canApplyMatrix && !!transform)
3158
this.transform(null, true);
3159
},
3160
3161
getTransformContent: '#getApplyMatrix',
3162
setTransformContent: '#setApplyMatrix',
3163
}, {
3164
getProject: function() {
3165
return this._project;
3166
},
3167
3168
_setProject: function(project, installEvents) {
3169
if (this._project !== project) {
3170
if (this._project)
3171
this._installEvents(false);
3172
this._project = project;
3173
var children = this._children;
3174
for (var i = 0, l = children && children.length; i < l; i++)
3175
children[i]._setProject(project);
3176
installEvents = true;
3177
}
3178
if (installEvents)
3179
this._installEvents(true);
3180
},
3181
3182
getView: function() {
3183
return this._project.getView();
3184
},
3185
3186
_installEvents: function _installEvents(install) {
3187
_installEvents.base.call(this, install);
3188
var children = this._children;
3189
for (var i = 0, l = children && children.length; i < l; i++)
3190
children[i]._installEvents(install);
3191
},
3192
3193
getLayer: function() {
3194
var parent = this;
3195
while (parent = parent._parent) {
3196
if (parent instanceof Layer)
3197
return parent;
3198
}
3199
return null;
3200
},
3201
3202
getParent: function() {
3203
return this._parent;
3204
},
3205
3206
setParent: function(item) {
3207
return item.addChild(this);
3208
},
3209
3210
getChildren: function() {
3211
return this._children;
3212
},
3213
3214
setChildren: function(items) {
3215
this.removeChildren();
3216
this.addChildren(items);
3217
},
3218
3219
getFirstChild: function() {
3220
return this._children && this._children[0] || null;
3221
},
3222
3223
getLastChild: function() {
3224
return this._children && this._children[this._children.length - 1]
3225
|| null;
3226
},
3227
3228
getNextSibling: function() {
3229
return this._parent && this._parent._children[this._index + 1] || null;
3230
},
3231
3232
getPreviousSibling: function() {
3233
return this._parent && this._parent._children[this._index - 1] || null;
3234
},
3235
3236
getIndex: function() {
3237
return this._index;
3238
},
3239
3240
isInserted: function() {
3241
return this._parent ? this._parent.isInserted() : false;
3242
},
3243
3244
equals: function(item) {
3245
return item === this || item && this._class === item._class
3246
&& this._style.equals(item._style)
3247
&& this._matrix.equals(item._matrix)
3248
&& this._locked === item._locked
3249
&& this._visible === item._visible
3250
&& this._blendMode === item._blendMode
3251
&& this._opacity === item._opacity
3252
&& this._clipMask === item._clipMask
3253
&& this._guide === item._guide
3254
&& this._equals(item)
3255
|| false;
3256
},
3257
3258
_equals: function(item) {
3259
return Base.equals(this._children, item._children);
3260
},
3261
3262
clone: function(insert) {
3263
return this._clone(new this.constructor(Item.NO_INSERT), insert);
3264
},
3265
3266
_clone: function(copy, insert) {
3267
copy.setStyle(this._style);
3268
if (this._children) {
3269
for (var i = 0, l = this._children.length; i < l; i++)
3270
copy.addChild(this._children[i].clone(false), true);
3271
}
3272
if (insert || insert === undefined)
3273
copy.insertAbove(this);
3274
var keys = ['_locked', '_visible', '_blendMode', '_opacity',
3275
'_clipMask', '_guide', '_applyMatrix'];
3276
for (var i = 0, l = keys.length; i < l; i++) {
3277
var key = keys[i];
3278
if (this.hasOwnProperty(key))
3279
copy[key] = this[key];
3280
}
3281
copy._matrix.initialize(this._matrix);
3282
copy._data = this._data ? Base.clone(this._data) : null;
3283
copy.setSelected(this._selected);
3284
if (this._name)
3285
copy.setName(this._name, true);
3286
return copy;
3287
},
3288
3289
copyTo: function(itemOrProject) {
3290
return itemOrProject.addChild(this.clone(false));
3291
},
3292
3293
rasterize: function(resolution) {
3294
var bounds = this.getStrokeBounds(),
3295
scale = (resolution || this.getView().getResolution()) / 72,
3296
topLeft = bounds.getTopLeft().floor(),
3297
bottomRight = bounds.getBottomRight().ceil(),
3298
size = new Size(bottomRight.subtract(topLeft)),
3299
canvas = CanvasProvider.getCanvas(size.multiply(scale)),
3300
ctx = canvas.getContext('2d'),
3301
matrix = new Matrix().scale(scale).translate(topLeft.negate());
3302
ctx.save();
3303
matrix.applyToContext(ctx);
3304
this.draw(ctx, new Base({ transforms: [matrix] }));
3305
ctx.restore();
3306
var raster = new Raster(Item.NO_INSERT);
3307
raster.setCanvas(canvas);
3308
raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
3309
.scale(1 / scale));
3310
raster.insertAbove(this);
3311
return raster;
3312
},
3313
3314
contains: function() {
3315
return !!this._contains(
3316
this._matrix._inverseTransform(Point.read(arguments)));
3317
},
3318
3319
_contains: function(point) {
3320
if (this._children) {
3321
for (var i = this._children.length - 1; i >= 0; i--) {
3322
if (this._children[i].contains(point))
3323
return true;
3324
}
3325
return false;
3326
}
3327
return point.isInside(this.getInternalBounds());
3328
},
3329
3330
hitTest: function(point, options) {
3331
point = Point.read(arguments);
3332
options = HitResult.getOptions(Base.read(arguments));
3333
if (this._locked || !this._visible || this._guide && !options.guides
3334
|| this.isEmpty())
3335
return null;
3336
3337
var matrix = this._matrix,
3338
parentTotalMatrix = options._totalMatrix,
3339
view = this.getView(),
3340
totalMatrix = options._totalMatrix = parentTotalMatrix
3341
? parentTotalMatrix.clone().concatenate(matrix)
3342
: this.getGlobalMatrix().clone().preConcatenate(
3343
view._matrix),
3344
tolerancePadding = options._tolerancePadding = new Size(
3345
Path._getPenPadding(1, totalMatrix.inverted())
3346
).multiply(
3347
Math.max(options.tolerance, 0.00001)
3348
);
3349
point = matrix._inverseTransform(point);
3350
3351
if (!this._children && !this.getInternalRoughBounds()
3352
.expand(tolerancePadding.multiply(2))._containsPoint(point))
3353
return null;
3354
var type,
3355
checkSelf = !(options.guides && !this._guide
3356
|| options.selected && !this._selected
3357
|| (type = options.type) && (typeof type === 'string'
3358
? type !== Base.hyphenate(this._class)
3359
: !(this instanceof type))),
3360
that = this,
3361
res;
3362
3363
function checkBounds(type, part) {
3364
var pt = bounds['get' + part]();
3365
if (point.subtract(pt).divide(tolerancePadding).length <= 1)
3366
return new HitResult(type, that,
3367
{ name: Base.hyphenate(part), point: pt });
3368
}
3369
3370
if (checkSelf && (options.center || options.bounds) && this._parent) {
3371
var bounds = this.getInternalBounds();
3372
if (options.center)
3373
res = checkBounds('center', 'Center');
3374
if (!res && options.bounds) {
3375
var points = [
3376
'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
3377
'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
3378
];
3379
for (var i = 0; i < 8 && !res; i++)
3380
res = checkBounds('bounds', points[i]);
3381
}
3382
}
3383
3384
var children = !res && this._children;
3385
if (children) {
3386
var opts = this._getChildHitTestOptions(options);
3387
for (var i = children.length - 1; i >= 0 && !res; i--)
3388
res = children[i].hitTest(point, opts);
3389
}
3390
if (!res && checkSelf)
3391
res = this._hitTest(point, options);
3392
if (res && res.point)
3393
res.point = matrix.transform(res.point);
3394
options._totalMatrix = parentTotalMatrix;
3395
return res;
3396
},
3397
3398
_getChildHitTestOptions: function(options) {
3399
return options;
3400
},
3401
3402
_hitTest: function(point, options) {
3403
if (options.fill && this.hasFill() && this._contains(point))
3404
return new HitResult('fill', this);
3405
}
3406
}, {
3407
matches: function(match) {
3408
function matchObject(obj1, obj2) {
3409
for (var i in obj1) {
3410
if (obj1.hasOwnProperty(i)) {
3411
var val1 = obj1[i],
3412
val2 = obj2[i];
3413
if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {
3414
if (!matchObject(val1, val2))
3415
return false;
3416
} else if (!Base.equals(val1, val2)) {
3417
return false;
3418
}
3419
}
3420
}
3421
return true;
3422
}
3423
for (var key in match) {
3424
if (match.hasOwnProperty(key)) {
3425
var value = this[key],
3426
compare = match[key];
3427
if (value === undefined && key === 'type')
3428
value = Base.hyphenate(this._class);
3429
if (/^(constructor|class)$/.test(key)) {
3430
if (!(this instanceof compare))
3431
return false;
3432
} else if (compare instanceof RegExp) {
3433
if (!compare.test(value))
3434
return false;
3435
} else if (typeof compare === 'function') {
3436
if (!compare(value))
3437
return false;
3438
} else if (Base.isPlainObject(compare)) {
3439
if (!matchObject(compare, value))
3440
return false;
3441
} else if (!Base.equals(value, compare)) {
3442
return false;
3443
}
3444
}
3445
}
3446
return true;
3447
},
3448
3449
getItems: function(match) {
3450
return Item._getItems(this._children, match, true);
3451
},
3452
3453
getItem: function(match) {
3454
return Item._getItems(this._children, match, false);
3455
},
3456
3457
statics: {
3458
_getItems: function _getItems(children, match, list) {
3459
var items = list && [];
3460
for (var i = 0, l = children && children.length; i < l; i++) {
3461
var child = children[i];
3462
if (child.matches(match)) {
3463
if (list) {
3464
items.push(child);
3465
} else {
3466
return child;
3467
}
3468
}
3469
var res = _getItems(child._children, match, list);
3470
if (list) {
3471
items.push.apply(items, res);
3472
} else if (res) {
3473
return res;
3474
}
3475
}
3476
return list ? items : null;
3477
}
3478
}
3479
}, {
3480
3481
importJSON: function(json) {
3482
var res = Base.importJSON(json, this);
3483
return res !== this
3484
? this.addChild(res)
3485
: res;
3486
},
3487
3488
addChild: function(item, _preserve) {
3489
return this.insertChild(undefined, item, _preserve);
3490
},
3491
3492
insertChild: function(index, item, _preserve) {
3493
var res = this.insertChildren(index, [item], _preserve);
3494
return res && res[0];
3495
},
3496
3497
addChildren: function(items, _preserve) {
3498
return this.insertChildren(this._children.length, items, _preserve);
3499
},
3500
3501
insertChildren: function(index, items, _preserve, _proto) {
3502
var children = this._children;
3503
if (children && items && items.length > 0) {
3504
items = Array.prototype.slice.apply(items);
3505
for (var i = items.length - 1; i >= 0; i--) {
3506
var item = items[i];
3507
if (_proto && !(item instanceof _proto)) {
3508
items.splice(i, 1);
3509
} else {
3510
item._remove(false, true);
3511
}
3512
}
3513
Base.splice(children, items, index, 0);
3514
var project = this._project,
3515
notifySelf = project && project._changes;
3516
for (var i = 0, l = items.length; i < l; i++) {
3517
var item = items[i];
3518
item._parent = this;
3519
item._setProject(this._project, true);
3520
if (item._name)
3521
item.setName(item._name);
3522
if (notifySelf)
3523
this._changed(5);
3524
}
3525
this._changed(11);
3526
} else {
3527
items = null;
3528
}
3529
return items;
3530
},
3531
3532
_insert: function(above, item, _preserve) {
3533
if (!item._parent)
3534
return null;
3535
var index = item._index + (above ? 1 : 0);
3536
if (item._parent === this._parent && index > this._index)
3537
index--;
3538
return item._parent.insertChild(index, this, _preserve);
3539
},
3540
3541
insertAbove: function(item, _preserve) {
3542
return this._insert(true, item, _preserve);
3543
},
3544
3545
insertBelow: function(item, _preserve) {
3546
return this._insert(false, item, _preserve);
3547
},
3548
3549
sendToBack: function() {
3550
return this._parent.insertChild(0, this);
3551
},
3552
3553
bringToFront: function() {
3554
return this._parent.addChild(this);
3555
},
3556
3557
appendTop: '#addChild',
3558
3559
appendBottom: function(item) {
3560
return this.insertChild(0, item);
3561
},
3562
3563
moveAbove: '#insertAbove',
3564
3565
moveBelow: '#insertBelow',
3566
3567
reduce: function() {
3568
if (this._children && this._children.length === 1) {
3569
var child = this._children[0].reduce();
3570
child.insertAbove(this);
3571
child.setStyle(this._style);
3572
this.remove();
3573
return child;
3574
}
3575
return this;
3576
},
3577
3578
_removeNamed: function() {
3579
var children = this._parent._children,
3580
namedChildren = this._parent._namedChildren,
3581
name = this._name,
3582
namedArray = namedChildren[name],
3583
index = namedArray ? namedArray.indexOf(this) : -1;
3584
if (index == -1)
3585
return;
3586
if (children[name] == this)
3587
delete children[name];
3588
namedArray.splice(index, 1);
3589
if (namedArray.length) {
3590
children[name] = namedArray[namedArray.length - 1];
3591
} else {
3592
delete namedChildren[name];
3593
}
3594
},
3595
3596
_remove: function(notifySelf, notifyParent) {
3597
var parent = this._parent;
3598
if (parent) {
3599
if (this._name)
3600
this._removeNamed();
3601
if (this._index != null)
3602
Base.splice(parent._children, null, this._index, 1);
3603
this._installEvents(false);
3604
if (notifySelf) {
3605
var project = this._project;
3606
if (project && project._changes)
3607
this._changed(5);
3608
}
3609
if (notifyParent)
3610
parent._changed(11);
3611
this._parent = null;
3612
return true;
3613
}
3614
return false;
3615
},
3616
3617
remove: function() {
3618
return this._remove(true, true);
3619
},
3620
3621
removeChildren: function(from, to) {
3622
if (!this._children)
3623
return null;
3624
from = from || 0;
3625
to = Base.pick(to, this._children.length);
3626
var removed = Base.splice(this._children, null, from, to - from);
3627
for (var i = removed.length - 1; i >= 0; i--) {
3628
removed[i]._remove(true, false);
3629
}
3630
if (removed.length > 0)
3631
this._changed(11);
3632
return removed;
3633
},
3634
3635
clear: '#removeChildren',
3636
3637
reverseChildren: function() {
3638
if (this._children) {
3639
this._children.reverse();
3640
for (var i = 0, l = this._children.length; i < l; i++)
3641
this._children[i]._index = i;
3642
this._changed(11);
3643
}
3644
},
3645
3646
isEmpty: function() {
3647
return !this._children || this._children.length == 0;
3648
},
3649
3650
isEditable: function() {
3651
var item = this;
3652
while (item) {
3653
if (!item._visible || item._locked)
3654
return false;
3655
item = item._parent;
3656
}
3657
return true;
3658
},
3659
3660
_getOrder: function(item) {
3661
function getList(item) {
3662
var list = [];
3663
do {
3664
list.unshift(item);
3665
} while (item = item._parent);
3666
return list;
3667
}
3668
var list1 = getList(this),
3669
list2 = getList(item);
3670
for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {
3671
if (list1[i] != list2[i]) {
3672
return list1[i]._index < list2[i]._index ? 1 : -1;
3673
}
3674
}
3675
return 0;
3676
},
3677
3678
hasChildren: function() {
3679
return this._children && this._children.length > 0;
3680
},
3681
3682
isAbove: function(item) {
3683
return this._getOrder(item) === -1;
3684
},
3685
3686
isBelow: function(item) {
3687
return this._getOrder(item) === 1;
3688
},
3689
3690
isParent: function(item) {
3691
return this._parent === item;
3692
},
3693
3694
isChild: function(item) {
3695
return item && item._parent === this;
3696
},
3697
3698
isDescendant: function(item) {
3699
var parent = this;
3700
while (parent = parent._parent) {
3701
if (parent == item)
3702
return true;
3703
}
3704
return false;
3705
},
3706
3707
isAncestor: function(item) {
3708
return item ? item.isDescendant(this) : false;
3709
},
3710
3711
isGroupedWith: function(item) {
3712
var parent = this._parent;
3713
while (parent) {
3714
if (parent._parent
3715
&& /^(Group|Layer|CompoundPath)$/.test(parent._class)
3716
&& item.isDescendant(parent))
3717
return true;
3718
parent = parent._parent;
3719
}
3720
return false;
3721
},
3722
3723
translate: function() {
3724
var mx = new Matrix();
3725
return this.transform(mx.translate.apply(mx, arguments));
3726
},
3727
3728
rotate: function(angle ) {
3729
return this.transform(new Matrix().rotate(angle,
3730
Point.read(arguments, 1, { readNull: true })
3731
|| this.getPosition(true)));
3732
}
3733
}, Base.each(['scale', 'shear', 'skew'], function(name) {
3734
this[name] = function() {
3735
var point = Point.read(arguments),
3736
center = Point.read(arguments, 0, { readNull: true });
3737
return this.transform(new Matrix()[name](point,
3738
center || this.getPosition(true)));
3739
};
3740
}, {
3741
3742
}), {
3743
transform: function(matrix, _applyMatrix) {
3744
if (matrix && matrix.isIdentity())
3745
matrix = null;
3746
var _matrix = this._matrix,
3747
applyMatrix = (_applyMatrix || this._applyMatrix)
3748
&& (!_matrix.isIdentity() || matrix);
3749
if (!matrix && !applyMatrix)
3750
return this;
3751
if (matrix)
3752
_matrix.preConcatenate(matrix);
3753
if (applyMatrix = applyMatrix && this._transformContent(_matrix)) {
3754
var pivot = this._pivot,
3755
style = this._style,
3756
fillColor = style.getFillColor(true),
3757
strokeColor = style.getStrokeColor(true);
3758
if (pivot)
3759
pivot.transform(_matrix);
3760
if (fillColor)
3761
fillColor.transform(_matrix);
3762
if (strokeColor)
3763
strokeColor.transform(_matrix);
3764
_matrix.reset(true);
3765
}
3766
var bounds = this._bounds,
3767
position = this._position;
3768
this._changed(9);
3769
var decomp = bounds && matrix && matrix.decompose();
3770
if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
3771
for (var key in bounds) {
3772
var rect = bounds[key];
3773
if (applyMatrix || !rect._internal)
3774
matrix._transformBounds(rect, rect);
3775
}
3776
var getter = this._boundsGetter,
3777
rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
3778
if (rect)
3779
this._position = rect.getCenter(true);
3780
this._bounds = bounds;
3781
} else if (matrix && position) {
3782
this._position = matrix._transformPoint(position, position);
3783
}
3784
return this;
3785
},
3786
3787
_transformContent: function(matrix) {
3788
var children = this._children;
3789
if (children) {
3790
for (var i = 0, l = children.length; i < l; i++)
3791
children[i].transform(matrix, true);
3792
return true;
3793
}
3794
},
3795
3796
globalToLocal: function() {
3797
var matrix = this.getGlobalMatrix();
3798
return matrix && matrix._inverseTransform(Point.read(arguments));
3799
},
3800
3801
localToGlobal: function() {
3802
var matrix = this.getGlobalMatrix();
3803
return matrix && matrix._transformPoint(Point.read(arguments));
3804
},
3805
3806
fitBounds: function(rectangle, fill) {
3807
rectangle = Rectangle.read(arguments);
3808
var bounds = this.getBounds(),
3809
itemRatio = bounds.height / bounds.width,
3810
rectRatio = rectangle.height / rectangle.width,
3811
scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
3812
? rectangle.width / bounds.width
3813
: rectangle.height / bounds.height,
3814
newBounds = new Rectangle(new Point(),
3815
new Size(bounds.width * scale, bounds.height * scale));
3816
newBounds.setCenter(rectangle.getCenter());
3817
this.setBounds(newBounds);
3818
},
3819
3820
_setStyles: function(ctx) {
3821
var style = this._style,
3822
fillColor = style.getFillColor(),
3823
strokeColor = style.getStrokeColor(),
3824
shadowColor = style.getShadowColor();
3825
if (fillColor)
3826
ctx.fillStyle = fillColor.toCanvasStyle(ctx);
3827
if (strokeColor) {
3828
var strokeWidth = style.getStrokeWidth();
3829
if (strokeWidth > 0) {
3830
ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
3831
ctx.lineWidth = strokeWidth;
3832
var strokeJoin = style.getStrokeJoin(),
3833
strokeCap = style.getStrokeCap(),
3834
miterLimit = style.getMiterLimit();
3835
if (strokeJoin)
3836
ctx.lineJoin = strokeJoin;
3837
if (strokeCap)
3838
ctx.lineCap = strokeCap;
3839
if (miterLimit)
3840
ctx.miterLimit = miterLimit;
3841
if (paper.support.nativeDash) {
3842
var dashArray = style.getDashArray(),
3843
dashOffset = style.getDashOffset();
3844
if (dashArray && dashArray.length) {
3845
if ('setLineDash' in ctx) {
3846
ctx.setLineDash(dashArray);
3847
ctx.lineDashOffset = dashOffset;
3848
} else {
3849
ctx.mozDash = dashArray;
3850
ctx.mozDashOffset = dashOffset;
3851
}
3852
}
3853
}
3854
}
3855
}
3856
if (shadowColor) {
3857
var shadowBlur = style.getShadowBlur();
3858
if (shadowBlur > 0) {
3859
ctx.shadowColor = shadowColor.toCanvasStyle(ctx);
3860
ctx.shadowBlur = shadowBlur;
3861
var offset = this.getShadowOffset();
3862
ctx.shadowOffsetX = offset.x;
3863
ctx.shadowOffsetY = offset.y;
3864
}
3865
}
3866
},
3867
3868
draw: function(ctx, param) {
3869
if (!this._visible || this._opacity === 0)
3870
return;
3871
var updateVersion = this._updateVersion = this._project._updateVersion;
3872
var trackTransforms = param.trackTransforms,
3873
transforms = param.transforms,
3874
matrix = this._matrix,
3875
parentMatrix = transforms[transforms.length - 1],
3876
globalMatrix = parentMatrix.clone().concatenate(matrix);
3877
if (!globalMatrix.isInvertible())
3878
return;
3879
if (trackTransforms) {
3880
transforms.push(this._globalMatrix = globalMatrix);
3881
globalMatrix._updateVersion = updateVersion;
3882
}
3883
3884
var blendMode = this._blendMode,
3885
opacity = this._opacity,
3886
normalBlend = blendMode === 'normal',
3887
nativeBlend = BlendMode.nativeModes[blendMode],
3888
direct = normalBlend && opacity === 1
3889
|| param.clip
3890
|| (nativeBlend || normalBlend && opacity < 1)
3891
&& this._canComposite(),
3892
mainCtx, itemOffset, prevOffset;
3893
if (!direct) {
3894
var bounds = this.getStrokeBounds(parentMatrix);
3895
if (!bounds.width || !bounds.height)
3896
return;
3897
prevOffset = param.offset;
3898
itemOffset = param.offset = bounds.getTopLeft().floor();
3899
mainCtx = ctx;
3900
ctx = CanvasProvider.getContext(
3901
bounds.getSize().ceil().add(new Size(1, 1)),
3902
param.pixelRatio);
3903
}
3904
ctx.save();
3905
if (direct) {
3906
ctx.globalAlpha = opacity;
3907
if (nativeBlend)
3908
ctx.globalCompositeOperation = blendMode;
3909
} else {
3910
ctx.translate(-itemOffset.x, -itemOffset.y);
3911
}
3912
(direct ? matrix : globalMatrix).applyToContext(ctx);
3913
if (!direct && param.clipItem)
3914
param.clipItem.draw(ctx, param.extend({ clip: true }));
3915
this._draw(ctx, param);
3916
ctx.restore();
3917
if (trackTransforms)
3918
transforms.pop();
3919
if (param.clip && !param.dontFinish)
3920
ctx.clip();
3921
if (!direct) {
3922
BlendMode.process(blendMode, ctx, mainCtx, opacity,
3923
itemOffset.subtract(prevOffset).multiply(param.pixelRatio));
3924
CanvasProvider.release(ctx);
3925
param.offset = prevOffset;
3926
}
3927
},
3928
3929
_canComposite: function() {
3930
return false;
3931
}
3932
}, Base.each(['down', 'drag', 'up', 'move'], function(name) {
3933
this['removeOn' + Base.capitalize(name)] = function() {
3934
var hash = {};
3935
hash[name] = true;
3936
return this.removeOn(hash);
3937
};
3938
}, {
3939
3940
removeOn: function(obj) {
3941
for (var name in obj) {
3942
if (obj[name]) {
3943
var key = 'mouse' + name,
3944
project = this._project,
3945
sets = project._removeSets = project._removeSets || {};
3946
sets[key] = sets[key] || {};
3947
sets[key][this._id] = this;
3948
}
3949
}
3950
return this;
3951
}
3952
}));
3953
3954
var Group = Item.extend({
3955
_class: 'Group',
3956
_selectChildren: true,
3957
_serializeFields: {
3958
children: []
3959
},
3960
3961
initialize: function Group(arg) {
3962
this._children = [];
3963
this._namedChildren = {};
3964
if (!this._initialize(arg))
3965
this.addChildren(Array.isArray(arg) ? arg : arguments);
3966
},
3967
3968
_changed: function _changed(flags) {
3969
_changed.base.call(this, flags);
3970
if (flags & (2 | 1024)) {
3971
this._clipItem = undefined;
3972
}
3973
},
3974
3975
_getClipItem: function() {
3976
var clipItem = this._clipItem;
3977
if (clipItem === undefined) {
3978
clipItem = null;
3979
for (var i = 0, l = this._children.length; i < l; i++) {
3980
var child = this._children[i];
3981
if (child._clipMask) {
3982
clipItem = child;
3983
break;
3984
}
3985
}
3986
this._clipItem = clipItem;
3987
}
3988
return clipItem;
3989
},
3990
3991
isClipped: function() {
3992
return !!this._getClipItem();
3993
},
3994
3995
setClipped: function(clipped) {
3996
var child = this.getFirstChild();
3997
if (child)
3998
child.setClipMask(clipped);
3999
},
4000
4001
_draw: function(ctx, param) {
4002
var clip = param.clip,
4003
clipItem = !clip && this._getClipItem(),
4004
draw = true;
4005
param = param.extend({ clipItem: clipItem, clip: false });
4006
if (clip) {
4007
if (this._currentPath) {
4008
ctx.currentPath = this._currentPath;
4009
draw = false;
4010
} else {
4011
ctx.beginPath();
4012
param.dontStart = param.dontFinish = true;
4013
}
4014
} else if (clipItem) {
4015
clipItem.draw(ctx, param.extend({ clip: true }));
4016
}
4017
if (draw) {
4018
for (var i = 0, l = this._children.length; i < l; i++) {
4019
var item = this._children[i];
4020
if (item !== clipItem)
4021
item.draw(ctx, param);
4022
}
4023
}
4024
if (clip) {
4025
this._currentPath = ctx.currentPath;
4026
}
4027
}
4028
});
4029
4030
var Layer = Group.extend({
4031
_class: 'Layer',
4032
4033
initialize: function Layer(arg) {
4034
var props = Base.isPlainObject(arg)
4035
? new Base(arg)
4036
: { children: Array.isArray(arg) ? arg : arguments },
4037
insert = props.insert;
4038
props.insert = false;
4039
Group.call(this, props);
4040
if (insert || insert === undefined) {
4041
this._project.addChild(this);
4042
this.activate();
4043
}
4044
},
4045
4046
_remove: function _remove(notify) {
4047
if (this._parent)
4048
return _remove.base.call(this, notify);
4049
if (this._index != null) {
4050
if (this._project.activeLayer === this)
4051
this._project.activeLayer = this.getNextSibling()
4052
|| this.getPreviousSibling();
4053
Base.splice(this._project.layers, null, this._index, 1);
4054
this._installEvents(false);
4055
this._project._needsUpdate = true;
4056
return true;
4057
}
4058
return false;
4059
},
4060
4061
getNextSibling: function getNextSibling() {
4062
return this._parent ? getNextSibling.base.call(this)
4063
: this._project.layers[this._index + 1] || null;
4064
},
4065
4066
getPreviousSibling: function getPreviousSibling() {
4067
return this._parent ? getPreviousSibling.base.call(this)
4068
: this._project.layers[this._index - 1] || null;
4069
},
4070
4071
isInserted: function isInserted() {
4072
return this._parent ? isInserted.base.call(this) : this._index != null;
4073
},
4074
4075
activate: function() {
4076
this._project.activeLayer = this;
4077
},
4078
4079
_insert: function _insert(above, item, _preserve) {
4080
if (item instanceof Layer && !item._parent) {
4081
this._remove(true, true);
4082
Base.splice(item._project.layers, [this],
4083
item._index + (above ? 1 : 0), 0);
4084
this._setProject(item._project, true);
4085
return this;
4086
}
4087
return _insert.base.call(this, above, item, _preserve);
4088
}
4089
});
4090
4091
var Shape = Item.extend({
4092
_class: 'Shape',
4093
_applyMatrix: false,
4094
_canApplyMatrix: false,
4095
_boundsSelected: true,
4096
_serializeFields: {
4097
type: null,
4098
size: null,
4099
radius: null
4100
},
4101
4102
initialize: function Shape(props) {
4103
this._initialize(props);
4104
},
4105
4106
_equals: function(item) {
4107
return this._type === item._type
4108
&& this._size.equals(item._size)
4109
&& Base.equals(this._radius, item._radius);
4110
},
4111
4112
clone: function(insert) {
4113
var copy = new Shape(Item.NO_INSERT);
4114
copy.setType(this._type);
4115
copy.setSize(this._size);
4116
copy.setRadius(this._radius);
4117
return this._clone(copy, insert);
4118
},
4119
4120
getType: function() {
4121
return this._type;
4122
},
4123
4124
setType: function(type) {
4125
this._type = type;
4126
},
4127
4128
getShape: '#getType',
4129
setShape: '#setType',
4130
4131
getSize: function() {
4132
var size = this._size;
4133
return new LinkedSize(size.width, size.height, this, 'setSize');
4134
},
4135
4136
setSize: function() {
4137
var size = Size.read(arguments);
4138
if (!this._size) {
4139
this._size = size.clone();
4140
} else if (!this._size.equals(size)) {
4141
var type = this._type,
4142
width = size.width,
4143
height = size.height;
4144
if (type === 'rectangle') {
4145
var radius = Size.min(this._radius, size.divide(2));
4146
this._radius.set(radius.width, radius.height);
4147
} else if (type === 'circle') {
4148
width = height = (width + height) / 2;
4149
this._radius = width / 2;
4150
} else if (type === 'ellipse') {
4151
this._radius.set(width / 2, height / 2);
4152
}
4153
this._size.set(width, height);
4154
this._changed(9);
4155
}
4156
},
4157
4158
getRadius: function() {
4159
var rad = this._radius;
4160
return this._type === 'circle'
4161
? rad
4162
: new LinkedSize(rad.width, rad.height, this, 'setRadius');
4163
},
4164
4165
setRadius: function(radius) {
4166
var type = this._type;
4167
if (type === 'circle') {
4168
if (radius === this._radius)
4169
return;
4170
var size = radius * 2;
4171
this._radius = radius;
4172
this._size.set(size, size);
4173
} else {
4174
radius = Size.read(arguments);
4175
if (!this._radius) {
4176
this._radius = radius.clone();
4177
} else {
4178
if (this._radius.equals(radius))
4179
return;
4180
this._radius.set(radius.width, radius.height);
4181
if (type === 'rectangle') {
4182
var size = Size.max(this._size, radius.multiply(2));
4183
this._size.set(size.width, size.height);
4184
} else if (type === 'ellipse') {
4185
this._size.set(radius.width * 2, radius.height * 2);
4186
}
4187
}
4188
}
4189
this._changed(9);
4190
},
4191
4192
isEmpty: function() {
4193
return false;
4194
},
4195
4196
toPath: function(insert) {
4197
var path = new Path[Base.capitalize(this._type)]({
4198
center: new Point(),
4199
size: this._size,
4200
radius: this._radius,
4201
insert: false
4202
});
4203
path.setStyle(this._style);
4204
path.transform(this._matrix);
4205
if (insert || insert === undefined)
4206
path.insertAbove(this);
4207
return path;
4208
},
4209
4210
_draw: function(ctx, param) {
4211
var style = this._style,
4212
hasFill = style.hasFill(),
4213
hasStroke = style.hasStroke(),
4214
dontPaint = param.dontFinish || param.clip;
4215
if (hasFill || hasStroke || dontPaint) {
4216
var radius = this._radius,
4217
type = this._type;
4218
if (!param.dontStart)
4219
ctx.beginPath();
4220
if (type === 'circle') {
4221
ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
4222
} else {
4223
var rx = radius.width,
4224
ry = radius.height,
4225
kappa = 0.5522847498307936;
4226
if (type === 'ellipse') {
4227
var cx = rx * kappa,
4228
cy = ry * kappa;
4229
ctx.moveTo(-rx, 0);
4230
ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry);
4231
ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0);
4232
ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry);
4233
ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0);
4234
} else {
4235
var size = this._size,
4236
width = size.width,
4237
height = size.height;
4238
if (rx === 0 && ry === 0) {
4239
ctx.rect(-width / 2, -height / 2, width, height);
4240
} else {
4241
kappa = 1 - kappa;
4242
var x = width / 2,
4243
y = height / 2,
4244
cx = rx * kappa,
4245
cy = ry * kappa;
4246
ctx.moveTo(-x, -y + ry);
4247
ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y);
4248
ctx.lineTo(x - rx, -y);
4249
ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry);
4250
ctx.lineTo(x, y - ry);
4251
ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y);
4252
ctx.lineTo(-x + rx, y);
4253
ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry);
4254
}
4255
}
4256
}
4257
ctx.closePath();
4258
}
4259
if (!dontPaint && (hasFill || hasStroke)) {
4260
this._setStyles(ctx);
4261
if (hasFill) {
4262
ctx.fill(style.getWindingRule());
4263
ctx.shadowColor = 'rgba(0,0,0,0)';
4264
}
4265
if (hasStroke)
4266
ctx.stroke();
4267
}
4268
},
4269
4270
_canComposite: function() {
4271
return !(this.hasFill() && this.hasStroke());
4272
},
4273
4274
_getBounds: function(getter, matrix) {
4275
var rect = new Rectangle(this._size).setCenter(0, 0);
4276
if (getter !== 'getBounds' && this.hasStroke())
4277
rect = rect.expand(this.getStrokeWidth());
4278
return matrix ? matrix._transformBounds(rect) : rect;
4279
}
4280
},
4281
new function() {
4282
4283
function getCornerCenter(that, point, expand) {
4284
var radius = that._radius;
4285
if (!radius.isZero()) {
4286
var halfSize = that._size.divide(2);
4287
for (var i = 0; i < 4; i++) {
4288
var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1),
4289
corner = dir.multiply(halfSize),
4290
center = corner.subtract(dir.multiply(radius)),
4291
rect = new Rectangle(corner, center);
4292
if ((expand ? rect.expand(expand) : rect).contains(point))
4293
return center;
4294
}
4295
}
4296
}
4297
4298
function getEllipseRadius(point, radius) {
4299
var angle = point.getAngleInRadians(),
4300
width = radius.width * 2,
4301
height = radius.height * 2,
4302
x = width * Math.sin(angle),
4303
y = height * Math.cos(angle);
4304
return width * height / (2 * Math.sqrt(x * x + y * y));
4305
}
4306
4307
return {
4308
_contains: function _contains(point) {
4309
if (this._type === 'rectangle') {
4310
var center = getCornerCenter(this, point);
4311
return center
4312
? point.subtract(center).divide(this._radius)
4313
.getLength() <= 1
4314
: _contains.base.call(this, point);
4315
} else {
4316
return point.divide(this.size).getLength() <= 0.5;
4317
}
4318
},
4319
4320
_hitTest: function _hitTest(point, options) {
4321
var hit = false;
4322
if (this.hasStroke()) {
4323
var type = this._type,
4324
radius = this._radius,
4325
strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
4326
if (type === 'rectangle') {
4327
var center = getCornerCenter(this, point, strokeWidth);
4328
if (center) {
4329
var pt = point.subtract(center);
4330
hit = 2 * Math.abs(pt.getLength()
4331
- getEllipseRadius(pt, radius)) <= strokeWidth;
4332
} else {
4333
var rect = new Rectangle(this._size).setCenter(0, 0),
4334
outer = rect.expand(strokeWidth),
4335
inner = rect.expand(-strokeWidth);
4336
hit = outer._containsPoint(point)
4337
&& !inner._containsPoint(point);
4338
}
4339
} else {
4340
if (type === 'ellipse')
4341
radius = getEllipseRadius(point, radius);
4342
hit = 2 * Math.abs(point.getLength() - radius)
4343
<= strokeWidth;
4344
}
4345
}
4346
return hit
4347
? new HitResult('stroke', this)
4348
: _hitTest.base.apply(this, arguments);
4349
}
4350
};
4351
}, {
4352
4353
statics: new function() {
4354
function createShape(type, point, size, radius, args) {
4355
var item = new Shape(Base.getNamed(args));
4356
item._type = type;
4357
item._size = size;
4358
item._radius = radius;
4359
return item.translate(point);
4360
}
4361
4362
return {
4363
Circle: function() {
4364
var center = Point.readNamed(arguments, 'center'),
4365
radius = Base.readNamed(arguments, 'radius');
4366
return createShape('circle', center, new Size(radius * 2), radius,
4367
arguments);
4368
},
4369
4370
Rectangle: function() {
4371
var rect = Rectangle.readNamed(arguments, 'rectangle'),
4372
radius = Size.min(Size.readNamed(arguments, 'radius'),
4373
rect.getSize(true).divide(2));
4374
return createShape('rectangle', rect.getCenter(true),
4375
rect.getSize(true), radius, arguments);
4376
},
4377
4378
Ellipse: function() {
4379
var ellipse = Shape._readEllipse(arguments),
4380
radius = ellipse.radius;
4381
return createShape('ellipse', ellipse.center, radius.multiply(2),
4382
radius, arguments);
4383
},
4384
4385
_readEllipse: function(args) {
4386
var center,
4387
radius;
4388
if (Base.hasNamed(args, 'radius')) {
4389
center = Point.readNamed(args, 'center');
4390
radius = Size.readNamed(args, 'radius');
4391
} else {
4392
var rect = Rectangle.readNamed(args, 'rectangle');
4393
center = rect.getCenter(true);
4394
radius = rect.getSize(true).divide(2);
4395
}
4396
return { center: center, radius: radius };
4397
}
4398
};
4399
}});
4400
4401
var Raster = Item.extend({
4402
_class: 'Raster',
4403
_applyMatrix: false,
4404
_canApplyMatrix: false,
4405
_boundsGetter: 'getBounds',
4406
_boundsSelected: true,
4407
_serializeFields: {
4408
source: null
4409
},
4410
4411
initialize: function Raster(object, position) {
4412
if (!this._initialize(object,
4413
position !== undefined && Point.read(arguments, 1))) {
4414
if (typeof object === 'string') {
4415
this.setSource(object);
4416
} else {
4417
this.setImage(object);
4418
}
4419
}
4420
if (!this._size)
4421
this._size = new Size();
4422
},
4423
4424
_equals: function(item) {
4425
return this.getSource() === item.getSource();
4426
},
4427
4428
clone: function(insert) {
4429
var copy = new Raster(Item.NO_INSERT),
4430
image = this._image,
4431
canvas = this._canvas;
4432
if (image) {
4433
copy.setImage(image);
4434
} else if (canvas) {
4435
var copyCanvas = CanvasProvider.getCanvas(this._size);
4436
copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
4437
copy.setCanvas(copyCanvas);
4438
}
4439
return this._clone(copy, insert);
4440
},
4441
4442
getSize: function() {
4443
var size = this._size;
4444
return new LinkedSize(size.width, size.height, this, 'setSize');
4445
},
4446
4447
setSize: function() {
4448
var size = Size.read(arguments);
4449
if (!this._size.equals(size)) {
4450
var element = this.getElement();
4451
this.setCanvas(CanvasProvider.getCanvas(size));
4452
if (element)
4453
this.getContext(true).drawImage(element, 0, 0,
4454
size.width, size.height);
4455
}
4456
},
4457
4458
getWidth: function() {
4459
return this._size.width;
4460
},
4461
4462
getHeight: function() {
4463
return this._size.height;
4464
},
4465
4466
isEmpty: function() {
4467
return this._size.width == 0 && this._size.height == 0;
4468
},
4469
4470
getPpi: function() {
4471
var matrix = this._matrix,
4472
orig = new Point(0, 0).transform(matrix),
4473
u = new Point(1, 0).transform(matrix).subtract(orig),
4474
v = new Point(0, 1).transform(matrix).subtract(orig);
4475
return new Size(
4476
72 / u.getLength(),
4477
72 / v.getLength()
4478
);
4479
},
4480
4481
getImage: function() {
4482
return this._image;
4483
},
4484
4485
setImage: function(image) {
4486
if (this._canvas)
4487
CanvasProvider.release(this._canvas);
4488
if (image.getContext) {
4489
this._image = null;
4490
this._canvas = image;
4491
} else {
4492
this._image = image;
4493
this._canvas = null;
4494
}
4495
this._size = new Size(
4496
image.naturalWidth || image.width,
4497
image.naturalHeight || image.height);
4498
this._context = null;
4499
this._changed(9 | 513);
4500
},
4501
4502
getCanvas: function() {
4503
if (!this._canvas) {
4504
var ctx = CanvasProvider.getContext(this._size);
4505
try {
4506
if (this._image)
4507
ctx.drawImage(this._image, 0, 0);
4508
this._canvas = ctx.canvas;
4509
} catch (e) {
4510
CanvasProvider.release(ctx);
4511
}
4512
}
4513
return this._canvas;
4514
},
4515
4516
setCanvas: '#setImage',
4517
4518
getContext: function(modify) {
4519
if (!this._context)
4520
this._context = this.getCanvas().getContext('2d');
4521
if (modify) {
4522
this._image = null;
4523
this._changed(513);
4524
}
4525
return this._context;
4526
},
4527
4528
setContext: function(context) {
4529
this._context = context;
4530
},
4531
4532
getSource: function() {
4533
return this._image && this._image.src || this.toDataURL();
4534
},
4535
4536
setSource: function(src) {
4537
var that = this,
4538
image;
4539
4540
function loaded() {
4541
var view = that.getView();
4542
if (view) {
4543
paper = view._scope;
4544
that.setImage(image);
4545
that.fire('load');
4546
view.update();
4547
}
4548
}
4549
4550
image = document.getElementById(src) || new Image();
4551
4552
if (image.naturalWidth && image.naturalHeight) {
4553
setTimeout(loaded, 0);
4554
} else {
4555
DomEvent.add(image, {
4556
load: loaded
4557
});
4558
if (!image.src)
4559
image.src = src;
4560
}
4561
this.setImage(image);
4562
},
4563
4564
getElement: function() {
4565
return this._canvas || this._image;
4566
},
4567
4568
getSubCanvas: function(rect) {
4569
var rect = Rectangle.read(arguments),
4570
ctx = CanvasProvider.getContext(rect.getSize());
4571
ctx.drawImage(this.getCanvas(), rect.x, rect.y,
4572
rect.width, rect.height, 0, 0, rect.width, rect.height);
4573
return ctx.canvas;
4574
},
4575
4576
getSubRaster: function(rect) {
4577
var rect = Rectangle.read(arguments),
4578
raster = new Raster(Item.NO_INSERT);
4579
raster.setCanvas(this.getSubCanvas(rect));
4580
raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
4581
raster._matrix.preConcatenate(this._matrix);
4582
raster.insertAbove(this);
4583
return raster;
4584
},
4585
4586
toDataURL: function() {
4587
var src = this._image && this._image.src;
4588
if (/^data:/.test(src))
4589
return src;
4590
var canvas = this.getCanvas();
4591
return canvas ? canvas.toDataURL() : null;
4592
},
4593
4594
drawImage: function(image ) {
4595
var point = Point.read(arguments, 1);
4596
this.getContext(true).drawImage(image, point.x, point.y);
4597
},
4598
4599
getAverageColor: function(object) {
4600
var bounds, path;
4601
if (!object) {
4602
bounds = this.getBounds();
4603
} else if (object instanceof PathItem) {
4604
path = object;
4605
bounds = object.getBounds();
4606
} else if (object.width) {
4607
bounds = new Rectangle(object);
4608
} else if (object.x) {
4609
bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
4610
}
4611
var sampleSize = 32,
4612
width = Math.min(bounds.width, sampleSize),
4613
height = Math.min(bounds.height, sampleSize);
4614
var ctx = Raster._sampleContext;
4615
if (!ctx) {
4616
ctx = Raster._sampleContext = CanvasProvider.getContext(
4617
new Size(sampleSize));
4618
} else {
4619
ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);
4620
}
4621
ctx.save();
4622
var matrix = new Matrix()
4623
.scale(width / bounds.width, height / bounds.height)
4624
.translate(-bounds.x, -bounds.y);
4625
matrix.applyToContext(ctx);
4626
if (path)
4627
path.draw(ctx, new Base({ clip: true, transforms: [matrix] }));
4628
this._matrix.applyToContext(ctx);
4629
ctx.drawImage(this.getElement(),
4630
-this._size.width / 2, -this._size.height / 2);
4631
ctx.restore();
4632
var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
4633
Math.ceil(height)).data,
4634
channels = [0, 0, 0],
4635
total = 0;
4636
for (var i = 0, l = pixels.length; i < l; i += 4) {
4637
var alpha = pixels[i + 3];
4638
total += alpha;
4639
alpha /= 255;
4640
channels[0] += pixels[i] * alpha;
4641
channels[1] += pixels[i + 1] * alpha;
4642
channels[2] += pixels[i + 2] * alpha;
4643
}
4644
for (var i = 0; i < 3; i++)
4645
channels[i] /= total;
4646
return total ? Color.read(channels) : null;
4647
},
4648
4649
getPixel: function(point) {
4650
var point = Point.read(arguments);
4651
var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
4652
return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
4653
data[3] / 255);
4654
},
4655
4656
setPixel: function() {
4657
var point = Point.read(arguments),
4658
color = Color.read(arguments),
4659
components = color._convert('rgb'),
4660
alpha = color._alpha,
4661
ctx = this.getContext(true),
4662
imageData = ctx.createImageData(1, 1),
4663
data = imageData.data;
4664
data[0] = components[0] * 255;
4665
data[1] = components[1] * 255;
4666
data[2] = components[2] * 255;
4667
data[3] = alpha != null ? alpha * 255 : 255;
4668
ctx.putImageData(imageData, point.x, point.y);
4669
},
4670
4671
createImageData: function() {
4672
var size = Size.read(arguments);
4673
return this.getContext().createImageData(size.width, size.height);
4674
},
4675
4676
getImageData: function(rect) {
4677
var rect = Rectangle.read(arguments);
4678
if (rect.isEmpty())
4679
rect = new Rectangle(this._size);
4680
return this.getContext().getImageData(rect.x, rect.y,
4681
rect.width, rect.height);
4682
},
4683
4684
setImageData: function(data ) {
4685
var point = Point.read(arguments, 1);
4686
this.getContext(true).putImageData(data, point.x, point.y);
4687
},
4688
4689
_getBounds: function(getter, matrix) {
4690
var rect = new Rectangle(this._size).setCenter(0, 0);
4691
return matrix ? matrix._transformBounds(rect) : rect;
4692
},
4693
4694
_hitTest: function(point) {
4695
if (this._contains(point)) {
4696
var that = this;
4697
return new HitResult('pixel', that, {
4698
offset: point.add(that._size.divide(2)).round(),
4699
color: {
4700
get: function() {
4701
return that.getPixel(this.offset);
4702
}
4703
}
4704
});
4705
}
4706
},
4707
4708
_draw: function(ctx) {
4709
var element = this.getElement();
4710
if (element) {
4711
ctx.globalAlpha = this._opacity;
4712
ctx.drawImage(element,
4713
-this._size.width / 2, -this._size.height / 2);
4714
}
4715
},
4716
4717
_canComposite: function() {
4718
return true;
4719
}
4720
});
4721
4722
var PlacedSymbol = Item.extend({
4723
_class: 'PlacedSymbol',
4724
_applyMatrix: false,
4725
_canApplyMatrix: false,
4726
_boundsGetter: { getBounds: 'getStrokeBounds' },
4727
_boundsSelected: true,
4728
_serializeFields: {
4729
symbol: null
4730
},
4731
4732
initialize: function PlacedSymbol(arg0, arg1) {
4733
if (!this._initialize(arg0,
4734
arg1 !== undefined && Point.read(arguments, 1)))
4735
this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
4736
},
4737
4738
_equals: function(item) {
4739
return this._symbol === item._symbol;
4740
},
4741
4742
getSymbol: function() {
4743
return this._symbol;
4744
},
4745
4746
setSymbol: function(symbol) {
4747
this._symbol = symbol;
4748
this._changed(9);
4749
},
4750
4751
clone: function(insert) {
4752
var copy = new PlacedSymbol(Item.NO_INSERT);
4753
copy.setSymbol(this._symbol);
4754
return this._clone(copy, insert);
4755
},
4756
4757
isEmpty: function() {
4758
return this._symbol._definition.isEmpty();
4759
},
4760
4761
_getBounds: function(getter, matrix, cacheItem) {
4762
return this.symbol._definition._getCachedBounds(getter, matrix,
4763
cacheItem);
4764
},
4765
4766
_hitTest: function(point, options) {
4767
var res = this._symbol._definition.hitTest(point, options);
4768
if (res)
4769
res.item = this;
4770
return res;
4771
},
4772
4773
_draw: function(ctx, param) {
4774
this.symbol._definition.draw(ctx, param);
4775
}
4776
4777
});
4778
4779
var HitResult = Base.extend({
4780
_class: 'HitResult',
4781
4782
initialize: function HitResult(type, item, values) {
4783
this.type = type;
4784
this.item = item;
4785
if (values) {
4786
values.enumerable = true;
4787
this.inject(values);
4788
}
4789
},
4790
4791
statics: {
4792
getOptions: function(options) {
4793
return options && options._merged ? options : new Base({
4794
type: null,
4795
tolerance: paper.settings.hitTolerance,
4796
fill: !options,
4797
stroke: !options,
4798
segments: !options,
4799
handles: false,
4800
ends: false,
4801
center: false,
4802
bounds: false,
4803
guides: false,
4804
selected: false,
4805
_merged: true
4806
}, options);
4807
}
4808
}
4809
});
4810
4811
var Segment = Base.extend({
4812
_class: 'Segment',
4813
beans: true,
4814
4815
initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
4816
var count = arguments.length,
4817
point, handleIn, handleOut;
4818
if (count === 0) {
4819
} else if (count === 1) {
4820
if (arg0.point) {
4821
point = arg0.point;
4822
handleIn = arg0.handleIn;
4823
handleOut = arg0.handleOut;
4824
} else {
4825
point = arg0;
4826
}
4827
} else if (count === 2 && typeof arg0 === 'number') {
4828
point = arguments;
4829
} else if (count <= 3) {
4830
point = arg0;
4831
handleIn = arg1;
4832
handleOut = arg2;
4833
} else {
4834
point = arg0 !== undefined ? [ arg0, arg1 ] : null;
4835
handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
4836
handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
4837
}
4838
new SegmentPoint(point, this, '_point');
4839
new SegmentPoint(handleIn, this, '_handleIn');
4840
new SegmentPoint(handleOut, this, '_handleOut');
4841
},
4842
4843
_serialize: function(options) {
4844
return Base.serialize(this.isLinear() ? this._point
4845
: [this._point, this._handleIn, this._handleOut],
4846
options, true);
4847
},
4848
4849
_changed: function(point) {
4850
var path = this._path;
4851
if (!path)
4852
return;
4853
var curves = path._curves,
4854
index = this._index,
4855
curveIn, curveOut;
4856
if (curves) {
4857
if ((!point || point === this._point || point === this._handleIn)
4858
&& (curveIn = curves[index - 1]
4859
|| path._closed && curves[curves.length - 1]))
4860
curveIn._changed();
4861
if ((!point || point === this._point || point === this._handleOut)
4862
&& (curveOut = curves[index]))
4863
curveOut._changed();
4864
}
4865
path._changed(25);
4866
},
4867
4868
getPoint: function() {
4869
return this._point;
4870
},
4871
4872
setPoint: function() {
4873
var point = Point.read(arguments);
4874
this._point.set(point.x, point.y);
4875
},
4876
4877
getHandleIn: function() {
4878
return this._handleIn;
4879
},
4880
4881
setHandleIn: function() {
4882
var point = Point.read(arguments);
4883
this._handleIn.set(point.x, point.y);
4884
},
4885
4886
getHandleOut: function() {
4887
return this._handleOut;
4888
},
4889
4890
setHandleOut: function() {
4891
var point = Point.read(arguments);
4892
this._handleOut.set(point.x, point.y);
4893
},
4894
4895
isLinear: function() {
4896
return this._handleIn.isZero() && this._handleOut.isZero();
4897
},
4898
4899
setLinear: function() {
4900
this._handleIn.set(0, 0);
4901
this._handleOut.set(0, 0);
4902
},
4903
4904
isColinear: function(segment) {
4905
var next1 = this.getNext(),
4906
next2 = segment.getNext();
4907
return this._handleOut.isZero() && next1._handleIn.isZero()
4908
&& segment._handleOut.isZero() && next2._handleIn.isZero()
4909
&& next1._point.subtract(this._point).isColinear(
4910
next2._point.subtract(segment._point));
4911
},
4912
4913
isOrthogonal: function() {
4914
var prev = this.getPrevious(),
4915
next = this.getNext();
4916
return prev._handleOut.isZero() && this._handleIn.isZero()
4917
&& this._handleOut.isZero() && next._handleIn.isZero()
4918
&& this._point.subtract(prev._point).isOrthogonal(
4919
next._point.subtract(this._point));
4920
},
4921
4922
isArc: function() {
4923
var next = this.getNext(),
4924
handle1 = this._handleOut,
4925
handle2 = next._handleIn,
4926
kappa = 0.5522847498307936;
4927
if (handle1.isOrthogonal(handle2)) {
4928
var from = this._point,
4929
to = next._point,
4930
corner = new Line(from, handle1, true).intersect(
4931
new Line(to, handle2, true), true);
4932
return corner && Numerical.isZero(handle1.getLength() /
4933
corner.subtract(from).getLength() - kappa)
4934
&& Numerical.isZero(handle2.getLength() /
4935
corner.subtract(to).getLength() - kappa);
4936
}
4937
return false;
4938
},
4939
4940
_selectionState: 0,
4941
4942
isSelected: function(_point) {
4943
var state = this._selectionState;
4944
return !_point ? !!(state & 7)
4945
: _point === this._point ? !!(state & 4)
4946
: _point === this._handleIn ? !!(state & 1)
4947
: _point === this._handleOut ? !!(state & 2)
4948
: false;
4949
},
4950
4951
setSelected: function(selected, _point) {
4952
var path = this._path,
4953
selected = !!selected,
4954
state = this._selectionState,
4955
oldState = state,
4956
flag = !_point ? 7
4957
: _point === this._point ? 4
4958
: _point === this._handleIn ? 1
4959
: _point === this._handleOut ? 2
4960
: 0;
4961
if (selected) {
4962
state |= flag;
4963
} else {
4964
state &= ~flag;
4965
}
4966
this._selectionState = state;
4967
if (path && state !== oldState) {
4968
path._updateSelection(this, oldState, state);
4969
path._changed(129);
4970
}
4971
},
4972
4973
getIndex: function() {
4974
return this._index !== undefined ? this._index : null;
4975
},
4976
4977
getPath: function() {
4978
return this._path || null;
4979
},
4980
4981
getCurve: function() {
4982
var path = this._path,
4983
index = this._index;
4984
if (path) {
4985
if (index > 0 && !path._closed
4986
&& index === path._segments.length - 1)
4987
index--;
4988
return path.getCurves()[index] || null;
4989
}
4990
return null;
4991
},
4992
4993
getLocation: function() {
4994
var curve = this.getCurve();
4995
return curve
4996
? new CurveLocation(curve, this === curve._segment1 ? 0 : 1)
4997
: null;
4998
},
4999
5000
getNext: function() {
5001
var segments = this._path && this._path._segments;
5002
return segments && (segments[this._index + 1]
5003
|| this._path._closed && segments[0]) || null;
5004
},
5005
5006
getPrevious: function() {
5007
var segments = this._path && this._path._segments;
5008
return segments && (segments[this._index - 1]
5009
|| this._path._closed && segments[segments.length - 1]) || null;
5010
},
5011
5012
reverse: function() {
5013
return new Segment(this._point, this._handleOut, this._handleIn);
5014
},
5015
5016
remove: function() {
5017
return this._path ? !!this._path.removeSegment(this._index) : false;
5018
},
5019
5020
clone: function() {
5021
return new Segment(this._point, this._handleIn, this._handleOut);
5022
},
5023
5024
equals: function(segment) {
5025
return segment === this || segment && this._class === segment._class
5026
&& this._point.equals(segment._point)
5027
&& this._handleIn.equals(segment._handleIn)
5028
&& this._handleOut.equals(segment._handleOut)
5029
|| false;
5030
},
5031
5032
toString: function() {
5033
var parts = [ 'point: ' + this._point ];
5034
if (!this._handleIn.isZero())
5035
parts.push('handleIn: ' + this._handleIn);
5036
if (!this._handleOut.isZero())
5037
parts.push('handleOut: ' + this._handleOut);
5038
return '{ ' + parts.join(', ') + ' }';
5039
},
5040
5041
transform: function(matrix) {
5042
this._transformCoordinates(matrix, new Array(6), true);
5043
this._changed();
5044
},
5045
5046
_transformCoordinates: function(matrix, coords, change) {
5047
var point = this._point,
5048
handleIn = !change || !this._handleIn.isZero()
5049
? this._handleIn : null,
5050
handleOut = !change || !this._handleOut.isZero()
5051
? this._handleOut : null,
5052
x = point._x,
5053
y = point._y,
5054
i = 2;
5055
coords[0] = x;
5056
coords[1] = y;
5057
if (handleIn) {
5058
coords[i++] = handleIn._x + x;
5059
coords[i++] = handleIn._y + y;
5060
}
5061
if (handleOut) {
5062
coords[i++] = handleOut._x + x;
5063
coords[i++] = handleOut._y + y;
5064
}
5065
if (matrix) {
5066
matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
5067
x = coords[0];
5068
y = coords[1];
5069
if (change) {
5070
point._x = x;
5071
point._y = y;
5072
i = 2;
5073
if (handleIn) {
5074
handleIn._x = coords[i++] - x;
5075
handleIn._y = coords[i++] - y;
5076
}
5077
if (handleOut) {
5078
handleOut._x = coords[i++] - x;
5079
handleOut._y = coords[i++] - y;
5080
}
5081
} else {
5082
if (!handleIn) {
5083
coords[i++] = x;
5084
coords[i++] = y;
5085
}
5086
if (!handleOut) {
5087
coords[i++] = x;
5088
coords[i++] = y;
5089
}
5090
}
5091
}
5092
return coords;
5093
}
5094
});
5095
5096
var SegmentPoint = Point.extend({
5097
initialize: function SegmentPoint(point, owner, key) {
5098
var x, y, selected;
5099
if (!point) {
5100
x = y = 0;
5101
} else if ((x = point[0]) !== undefined) {
5102
y = point[1];
5103
} else {
5104
var pt = point;
5105
if ((x = pt.x) === undefined) {
5106
pt = Point.read(arguments);
5107
x = pt.x;
5108
}
5109
y = pt.y;
5110
selected = pt.selected;
5111
}
5112
this._x = x;
5113
this._y = y;
5114
this._owner = owner;
5115
owner[key] = this;
5116
if (selected)
5117
this.setSelected(true);
5118
},
5119
5120
set: function(x, y) {
5121
this._x = x;
5122
this._y = y;
5123
this._owner._changed(this);
5124
return this;
5125
},
5126
5127
_serialize: function(options) {
5128
var f = options.formatter,
5129
x = f.number(this._x),
5130
y = f.number(this._y);
5131
return this.isSelected()
5132
? { x: x, y: y, selected: true }
5133
: [x, y];
5134
},
5135
5136
getX: function() {
5137
return this._x;
5138
},
5139
5140
setX: function(x) {
5141
this._x = x;
5142
this._owner._changed(this);
5143
},
5144
5145
getY: function() {
5146
return this._y;
5147
},
5148
5149
setY: function(y) {
5150
this._y = y;
5151
this._owner._changed(this);
5152
},
5153
5154
isZero: function() {
5155
return Numerical.isZero(this._x) && Numerical.isZero(this._y);
5156
},
5157
5158
setSelected: function(selected) {
5159
this._owner.setSelected(selected, this);
5160
},
5161
5162
isSelected: function() {
5163
return this._owner.isSelected(this);
5164
}
5165
});
5166
5167
var Curve = Base.extend({
5168
_class: 'Curve',
5169
initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
5170
var count = arguments.length;
5171
if (count === 3) {
5172
this._path = arg0;
5173
this._segment1 = arg1;
5174
this._segment2 = arg2;
5175
} else if (count === 0) {
5176
this._segment1 = new Segment();
5177
this._segment2 = new Segment();
5178
} else if (count === 1) {
5179
this._segment1 = new Segment(arg0.segment1);
5180
this._segment2 = new Segment(arg0.segment2);
5181
} else if (count === 2) {
5182
this._segment1 = new Segment(arg0);
5183
this._segment2 = new Segment(arg1);
5184
} else {
5185
var point1, handle1, handle2, point2;
5186
if (count === 4) {
5187
point1 = arg0;
5188
handle1 = arg1;
5189
handle2 = arg2;
5190
point2 = arg3;
5191
} else if (count === 8) {
5192
point1 = [arg0, arg1];
5193
point2 = [arg6, arg7];
5194
handle1 = [arg2 - arg0, arg3 - arg1];
5195
handle2 = [arg4 - arg6, arg5 - arg7];
5196
}
5197
this._segment1 = new Segment(point1, null, handle1);
5198
this._segment2 = new Segment(point2, handle2, null);
5199
}
5200
},
5201
5202
_changed: function() {
5203
this._length = this._bounds = undefined;
5204
},
5205
5206
getPoint1: function() {
5207
return this._segment1._point;
5208
},
5209
5210
setPoint1: function() {
5211
var point = Point.read(arguments);
5212
this._segment1._point.set(point.x, point.y);
5213
},
5214
5215
getPoint2: function() {
5216
return this._segment2._point;
5217
},
5218
5219
setPoint2: function() {
5220
var point = Point.read(arguments);
5221
this._segment2._point.set(point.x, point.y);
5222
},
5223
5224
getHandle1: function() {
5225
return this._segment1._handleOut;
5226
},
5227
5228
setHandle1: function() {
5229
var point = Point.read(arguments);
5230
this._segment1._handleOut.set(point.x, point.y);
5231
},
5232
5233
getHandle2: function() {
5234
return this._segment2._handleIn;
5235
},
5236
5237
setHandle2: function() {
5238
var point = Point.read(arguments);
5239
this._segment2._handleIn.set(point.x, point.y);
5240
},
5241
5242
getSegment1: function() {
5243
return this._segment1;
5244
},
5245
5246
getSegment2: function() {
5247
return this._segment2;
5248
},
5249
5250
getPath: function() {
5251
return this._path;
5252
},
5253
5254
getIndex: function() {
5255
return this._segment1._index;
5256
},
5257
5258
getNext: function() {
5259
var curves = this._path && this._path._curves;
5260
return curves && (curves[this._segment1._index + 1]
5261
|| this._path._closed && curves[0]) || null;
5262
},
5263
5264
getPrevious: function() {
5265
var curves = this._path && this._path._curves;
5266
return curves && (curves[this._segment1._index - 1]
5267
|| this._path._closed && curves[curves.length - 1]) || null;
5268
},
5269
5270
isSelected: function() {
5271
return this.getPoint1().isSelected()
5272
&& this.getHandle2().isSelected()
5273
&& this.getHandle2().isSelected()
5274
&& this.getPoint2().isSelected();
5275
},
5276
5277
setSelected: function(selected) {
5278
this.getPoint1().setSelected(selected);
5279
this.getHandle1().setSelected(selected);
5280
this.getHandle2().setSelected(selected);
5281
this.getPoint2().setSelected(selected);
5282
},
5283
5284
getValues: function(matrix) {
5285
return Curve.getValues(this._segment1, this._segment2, matrix);
5286
},
5287
5288
getPoints: function() {
5289
var coords = this.getValues(),
5290
points = [];
5291
for (var i = 0; i < 8; i += 2)
5292
points.push(new Point(coords[i], coords[i + 1]));
5293
return points;
5294
},
5295
5296
getLength: function() {
5297
if (this._length == null) {
5298
this._length = this.isLinear()
5299
? this._segment2._point.getDistance(this._segment1._point)
5300
: Curve.getLength(this.getValues(), 0, 1);
5301
}
5302
return this._length;
5303
},
5304
5305
getArea: function() {
5306
return Curve.getArea(this.getValues());
5307
},
5308
5309
getPart: function(from, to) {
5310
return new Curve(Curve.getPart(this.getValues(), from, to));
5311
},
5312
5313
getPartLength: function(from, to) {
5314
return Curve.getLength(this.getValues(), from, to);
5315
},
5316
5317
isLinear: function() {
5318
return this._segment1._handleOut.isZero()
5319
&& this._segment2._handleIn.isZero();
5320
},
5321
5322
isHorizontal: function() {
5323
return this.isLinear() && Numerical.isZero(
5324
this._segment1._point._y - this._segment2._point._y);
5325
},
5326
5327
getIntersections: function(curve) {
5328
return Curve.getIntersections(this.getValues(), curve.getValues(),
5329
this, curve, []);
5330
},
5331
5332
_getParameter: function(offset, isParameter) {
5333
return isParameter
5334
? offset
5335
: offset && offset.curve === this
5336
? offset.parameter
5337
: offset === undefined && isParameter === undefined
5338
? 0.5
5339
: this.getParameterAt(offset, 0);
5340
},
5341
5342
divide: function(offset, isParameter, ignoreLinear) {
5343
var parameter = this._getParameter(offset, isParameter),
5344
tolerance = 0.00001,
5345
res = null;
5346
if (parameter > tolerance && parameter < 1 - tolerance) {
5347
var parts = Curve.subdivide(this.getValues(), parameter),
5348
isLinear = ignoreLinear ? false : this.isLinear(),
5349
left = parts[0],
5350
right = parts[1];
5351
5352
if (!isLinear) {
5353
this._segment1._handleOut.set(left[2] - left[0],
5354
left[3] - left[1]);
5355
this._segment2._handleIn.set(right[4] - right[6],
5356
right[5] - right[7]);
5357
}
5358
5359
var x = left[6], y = left[7],
5360
segment = new Segment(new Point(x, y),
5361
!isLinear && new Point(left[4] - x, left[5] - y),
5362
!isLinear && new Point(right[2] - x, right[3] - y));
5363
5364
if (this._path) {
5365
if (this._segment1._index > 0 && this._segment2._index === 0) {
5366
this._path.add(segment);
5367
} else {
5368
this._path.insert(this._segment2._index, segment);
5369
}
5370
res = this;
5371
} else {
5372
var end = this._segment2;
5373
this._segment2 = segment;
5374
res = new Curve(segment, end);
5375
}
5376
}
5377
return res;
5378
},
5379
5380
split: function(offset, isParameter) {
5381
return this._path
5382
? this._path.split(this._segment1._index,
5383
this._getParameter(offset, isParameter))
5384
: null;
5385
},
5386
5387
reverse: function() {
5388
return new Curve(this._segment2.reverse(), this._segment1.reverse());
5389
},
5390
5391
remove: function() {
5392
var removed = false;
5393
if (this._path) {
5394
var segment2 = this._segment2,
5395
handleOut = segment2._handleOut;
5396
removed = segment2.remove();
5397
if (removed)
5398
this._segment1._handleOut.set(handleOut.x, handleOut.y);
5399
}
5400
return removed;
5401
},
5402
5403
clone: function() {
5404
return new Curve(this._segment1, this._segment2);
5405
},
5406
5407
toString: function() {
5408
var parts = [ 'point1: ' + this._segment1._point ];
5409
if (!this._segment1._handleOut.isZero())
5410
parts.push('handle1: ' + this._segment1._handleOut);
5411
if (!this._segment2._handleIn.isZero())
5412
parts.push('handle2: ' + this._segment2._handleIn);
5413
parts.push('point2: ' + this._segment2._point);
5414
return '{ ' + parts.join(', ') + ' }';
5415
},
5416
5417
statics: {
5418
getValues: function(segment1, segment2, matrix) {
5419
var p1 = segment1._point,
5420
h1 = segment1._handleOut,
5421
h2 = segment2._handleIn,
5422
p2 = segment2._point,
5423
values = [
5424
p1._x, p1._y,
5425
p1._x + h1._x, p1._y + h1._y,
5426
p2._x + h2._x, p2._y + h2._y,
5427
p2._x, p2._y
5428
];
5429
if (matrix)
5430
matrix._transformCoordinates(values, 0, values, 0, 6);
5431
return values;
5432
},
5433
5434
evaluate: function(v, t, type) {
5435
var p1x = v[0], p1y = v[1],
5436
c1x = v[2], c1y = v[3],
5437
c2x = v[4], c2y = v[5],
5438
p2x = v[6], p2y = v[7],
5439
tolerance = 0.00001,
5440
x, y;
5441
5442
if (type === 0 && (t < tolerance || t > 1 - tolerance)) {
5443
var isZero = t < tolerance;
5444
x = isZero ? p1x : p2x;
5445
y = isZero ? p1y : p2y;
5446
} else {
5447
var cx = 3 * (c1x - p1x),
5448
bx = 3 * (c2x - c1x) - cx,
5449
ax = p2x - p1x - cx - bx,
5450
5451
cy = 3 * (c1y - p1y),
5452
by = 3 * (c2y - c1y) - cy,
5453
ay = p2y - p1y - cy - by;
5454
if (type === 0) {
5455
x = ((ax * t + bx) * t + cx) * t + p1x;
5456
y = ((ay * t + by) * t + cy) * t + p1y;
5457
} else {
5458
if (t < tolerance && c1x === p1x && c1y === p1y
5459
|| t > 1 - tolerance && c2x === p2x && c2y === p2y) {
5460
x = p2x - p1x;
5461
y = p2y - p1y;
5462
} else if (t < tolerance) {
5463
x = cx;
5464
y = cy;
5465
} else if (t > 1 - tolerance) {
5466
x = 3 * (p2x - c2x);
5467
y = 3 * (p2y - c2y);
5468
} else {
5469
x = (3 * ax * t + 2 * bx) * t + cx;
5470
y = (3 * ay * t + 2 * by) * t + cy;
5471
}
5472
if (type === 3) {
5473
var x2 = 6 * ax * t + 2 * bx,
5474
y2 = 6 * ay * t + 2 * by;
5475
return (x * y2 - y * x2) / Math.pow(x * x + y * y, 3 / 2);
5476
}
5477
}
5478
}
5479
return type === 2 ? new Point(y, -x) : new Point(x, y);
5480
},
5481
5482
subdivide: function(v, t) {
5483
var p1x = v[0], p1y = v[1],
5484
c1x = v[2], c1y = v[3],
5485
c2x = v[4], c2y = v[5],
5486
p2x = v[6], p2y = v[7];
5487
if (t === undefined)
5488
t = 0.5;
5489
var u = 1 - t,
5490
p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y,
5491
p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y,
5492
p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y,
5493
p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y,
5494
p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
5495
p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
5496
return [
5497
[p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
5498
[p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
5499
];
5500
},
5501
5502
solveCubic: function (v, coord, val, roots, min, max) {
5503
var p1 = v[coord],
5504
c1 = v[coord + 2],
5505
c2 = v[coord + 4],
5506
p2 = v[coord + 6],
5507
c = 3 * (c1 - p1),
5508
b = 3 * (c2 - c1) - c,
5509
a = p2 - p1 - c - b;
5510
return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
5511
},
5512
5513
getParameterOf: function(v, x, y) {
5514
var tolerance = 0.00001;
5515
if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance)
5516
return 0;
5517
if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance)
5518
return 1;
5519
var txs = [],
5520
tys = [],
5521
sx = Curve.solveCubic(v, 0, x, txs),
5522
sy = Curve.solveCubic(v, 1, y, tys),
5523
tx, ty;
5524
for (var cx = 0; sx == -1 || cx < sx;) {
5525
if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) {
5526
for (var cy = 0; sy == -1 || cy < sy;) {
5527
if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) {
5528
if (sx == -1) tx = ty;
5529
else if (sy == -1) ty = tx;
5530
if (Math.abs(tx - ty) < tolerance)
5531
return (tx + ty) * 0.5;
5532
}
5533
}
5534
if (sx == -1)
5535
break;
5536
}
5537
}
5538
return null;
5539
},
5540
5541
getPart: function(v, from, to) {
5542
if (from > 0)
5543
v = Curve.subdivide(v, from)[1];
5544
if (to < 1)
5545
v = Curve.subdivide(v, (to - from) / (1 - from))[0];
5546
return v;
5547
},
5548
5549
isLinear: function(v) {
5550
var isZero = Numerical.isZero;
5551
return isZero(v[0] - v[2]) && isZero(v[1] - v[3])
5552
&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]);
5553
},
5554
5555
isFlatEnough: function(v, tolerance) {
5556
var p1x = v[0], p1y = v[1],
5557
c1x = v[2], c1y = v[3],
5558
c2x = v[4], c2y = v[5],
5559
p2x = v[6], p2y = v[7],
5560
ux = 3 * c1x - 2 * p1x - p2x,
5561
uy = 3 * c1y - 2 * p1y - p2y,
5562
vx = 3 * c2x - 2 * p2x - p1x,
5563
vy = 3 * c2y - 2 * p2y - p1y;
5564
return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
5565
< 10 * tolerance * tolerance;
5566
},
5567
5568
getArea: function(v) {
5569
var p1x = v[0], p1y = v[1],
5570
c1x = v[2], c1y = v[3],
5571
c2x = v[4], c2y = v[5],
5572
p2x = v[6], p2y = v[7];
5573
return ( 3.0 * c1y * p1x - 1.5 * c1y * c2x
5574
- 1.5 * c1y * p2x - 3.0 * p1y * c1x
5575
- 1.5 * p1y * c2x - 0.5 * p1y * p2x
5576
+ 1.5 * c2y * p1x + 1.5 * c2y * c1x
5577
- 3.0 * c2y * p2x + 0.5 * p2y * p1x
5578
+ 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10;
5579
},
5580
5581
getBounds: function(v) {
5582
var min = v.slice(0, 2),
5583
max = min.slice(),
5584
roots = [0, 0];
5585
for (var i = 0; i < 2; i++)
5586
Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],
5587
i, 0, min, max, roots);
5588
return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
5589
},
5590
5591
_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {
5592
function add(value, padding) {
5593
var left = value - padding,
5594
right = value + padding;
5595
if (left < min[coord])
5596
min[coord] = left;
5597
if (right > max[coord])
5598
max[coord] = right;
5599
}
5600
var a = 3 * (v1 - v2) - v0 + v3,
5601
b = 2 * (v0 + v2) - 4 * v1,
5602
c = v1 - v0,
5603
count = Numerical.solveQuadratic(a, b, c, roots),
5604
tMin = 0.00001,
5605
tMax = 1 - tMin;
5606
add(v3, 0);
5607
for (var i = 0; i < count; i++) {
5608
var t = roots[i],
5609
u = 1 - t;
5610
if (tMin < t && t < tMax)
5611
add(u * u * u * v0
5612
+ 3 * u * u * t * v1
5613
+ 3 * u * t * t * v2
5614
+ t * t * t * v3,
5615
padding);
5616
}
5617
}
5618
}}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
5619
function(name) {
5620
this[name] = function() {
5621
if (!this._bounds)
5622
this._bounds = {};
5623
var bounds = this._bounds[name];
5624
if (!bounds) {
5625
bounds = this._bounds[name] = Path[name]([this._segment1,
5626
this._segment2], false, this._path.getStyle());
5627
}
5628
return bounds.clone();
5629
};
5630
},
5631
{
5632
5633
}), Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'],
5634
function(name, index) {
5635
this[name + 'At'] = function(offset, isParameter) {
5636
var values = this.getValues();
5637
return Curve.evaluate(values, isParameter
5638
? offset : Curve.getParameterAt(values, offset, 0), index);
5639
};
5640
this[name] = function(parameter) {
5641
return Curve.evaluate(this.getValues(), parameter, index);
5642
};
5643
},
5644
{
5645
getParameterAt: function(offset, start) {
5646
return Curve.getParameterAt(this.getValues(), offset,
5647
start !== undefined ? start : offset < 0 ? 1 : 0);
5648
},
5649
5650
getParameterOf: function(point) {
5651
var point = Point.read(arguments);
5652
return Curve.getParameterOf(this.getValues(), point.x, point.y);
5653
},
5654
5655
getLocationAt: function(offset, isParameter) {
5656
if (!isParameter)
5657
offset = this.getParameterAt(offset);
5658
return new CurveLocation(this, offset);
5659
},
5660
5661
getLocationOf: function(point) {
5662
var point = Point.read(arguments),
5663
t = this.getParameterOf(point);
5664
return t != null ? new CurveLocation(this, t) : null;
5665
},
5666
5667
getNearestLocation: function(point) {
5668
var point = Point.read(arguments),
5669
values = this.getValues(),
5670
count = 100,
5671
minDist = Infinity,
5672
minT = 0;
5673
5674
function refine(t) {
5675
if (t >= 0 && t <= 1) {
5676
var dist = point.getDistance(
5677
Curve.evaluate(values, t, 0), true);
5678
if (dist < minDist) {
5679
minDist = dist;
5680
minT = t;
5681
return true;
5682
}
5683
}
5684
}
5685
5686
for (var i = 0; i <= count; i++)
5687
refine(i / count);
5688
5689
var step = 1 / (count * 2);
5690
while (step > 0.00001) {
5691
if (!refine(minT - step) && !refine(minT + step))
5692
step /= 2;
5693
}
5694
var pt = Curve.evaluate(values, minT, 0);
5695
return new CurveLocation(this, minT, pt, null, null, null,
5696
point.getDistance(pt));
5697
},
5698
5699
getNearestPoint: function(point) {
5700
var point = Point.read(arguments);
5701
return this.getNearestLocation(point).getPoint();
5702
}
5703
5704
}),
5705
new function() {
5706
5707
function getLengthIntegrand(v) {
5708
var p1x = v[0], p1y = v[1],
5709
c1x = v[2], c1y = v[3],
5710
c2x = v[4], c2y = v[5],
5711
p2x = v[6], p2y = v[7],
5712
5713
ax = 9 * (c1x - c2x) + 3 * (p2x - p1x),
5714
bx = 6 * (p1x + c2x) - 12 * c1x,
5715
cx = 3 * (c1x - p1x),
5716
5717
ay = 9 * (c1y - c2y) + 3 * (p2y - p1y),
5718
by = 6 * (p1y + c2y) - 12 * c1y,
5719
cy = 3 * (c1y - p1y);
5720
5721
return function(t) {
5722
var dx = (ax * t + bx) * t + cx,
5723
dy = (ay * t + by) * t + cy;
5724
return Math.sqrt(dx * dx + dy * dy);
5725
};
5726
}
5727
5728
function getIterations(a, b) {
5729
return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));
5730
}
5731
5732
return {
5733
statics: true,
5734
5735
getLength: function(v, a, b) {
5736
if (a === undefined)
5737
a = 0;
5738
if (b === undefined)
5739
b = 1;
5740
var isZero = Numerical.isZero;
5741
if (a === 0 && b === 1
5742
&& isZero(v[0] - v[2]) && isZero(v[1] - v[3])
5743
&& isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
5744
var dx = v[6] - v[0],
5745
dy = v[7] - v[1];
5746
return Math.sqrt(dx * dx + dy * dy);
5747
}
5748
var ds = getLengthIntegrand(v);
5749
return Numerical.integrate(ds, a, b, getIterations(a, b));
5750
},
5751
5752
getParameterAt: function(v, offset, start) {
5753
if (offset === 0)
5754
return start;
5755
var forward = offset > 0,
5756
a = forward ? start : 0,
5757
b = forward ? 1 : start,
5758
offset = Math.abs(offset),
5759
ds = getLengthIntegrand(v),
5760
rangeLength = Numerical.integrate(ds, a, b,
5761
getIterations(a, b));
5762
if (offset >= rangeLength)
5763
return forward ? b : a;
5764
var guess = offset / rangeLength,
5765
length = 0;
5766
function f(t) {
5767
var count = getIterations(start, t);
5768
length += start < t
5769
? Numerical.integrate(ds, start, t, count)
5770
: -Numerical.integrate(ds, t, start, count);
5771
start = t;
5772
return length - offset;
5773
}
5774
return Numerical.findRoot(f, ds,
5775
forward ? a + guess : b - guess,
5776
a, b, 16, 0.00001);
5777
}
5778
};
5779
}, new function() {
5780
function addLocation(locations, include, curve1, t1, point1, curve2, t2,
5781
point2) {
5782
var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2);
5783
if (!include || include(loc))
5784
locations.push(loc);
5785
}
5786
5787
function addCurveIntersections(v1, v2, curve1, curve2, locations, include,
5788
tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) {
5789
if (recursion > 20)
5790
return;
5791
var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
5792
tolerance = 0.00001,
5793
hullEpsilon = 1e-9,
5794
getSignedDistance = Line.getSignedDistance,
5795
d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0,
5796
d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0,
5797
factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
5798
dMin = factor * Math.min(0, d1, d2),
5799
dMax = factor * Math.max(0, d1, d2),
5800
dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]),
5801
dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),
5802
dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),
5803
dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),
5804
tMinNew, tMaxNew, tDiff;
5805
if (q0x === q3x && uMax - uMin <= hullEpsilon && recursion > 3) {
5806
tMinNew = (tMax + tMin) / 2;
5807
tMaxNew = tMinNew;
5808
tDiff = 0;
5809
} else {
5810
var hull = getConvexHull(dp0, dp1, dp2, dp3),
5811
top = hull[0],
5812
bottom = hull[1],
5813
tMinClip, tMaxClip;
5814
tMinClip = clipConvexHull(top, bottom, dMin, dMax);
5815
top.reverse();
5816
bottom.reverse();
5817
tMaxClip = clipConvexHull(top, bottom, dMin, dMax);
5818
if (tMinClip == null || tMaxClip == null)
5819
return false;
5820
v1 = Curve.getPart(v1, tMinClip, tMaxClip);
5821
tDiff = tMaxClip - tMinClip;
5822
tMinNew = tMax * tMinClip + tMin * (1 - tMinClip);
5823
tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip);
5824
}
5825
if (oldTDiff > 0.8 && tDiff > 0.8) {
5826
if (tMaxNew - tMinNew > uMax - uMin) {
5827
var parts = Curve.subdivide(v1, 0.5),
5828
t = tMinNew + (tMaxNew - tMinNew) / 2;
5829
addCurveIntersections(
5830
v2, parts[0], curve2, curve1, locations, include,
5831
uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion);
5832
addCurveIntersections(
5833
v2, parts[1], curve2, curve1, locations, include,
5834
uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion);
5835
} else {
5836
var parts = Curve.subdivide(v2, 0.5),
5837
t = uMin + (uMax - uMin) / 2;
5838
addCurveIntersections(
5839
parts[0], v1, curve2, curve1, locations, include,
5840
uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
5841
addCurveIntersections(
5842
parts[1], v1, curve2, curve1, locations, include,
5843
t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion);
5844
}
5845
} else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) {
5846
var t1 = tMinNew + (tMaxNew - tMinNew) / 2,
5847
t2 = uMin + (uMax - uMin) / 2;
5848
if (reverse) {
5849
addLocation(locations, include,
5850
curve2, t2, Curve.evaluate(v2, t2, 0),
5851
curve1, t1, Curve.evaluate(v1, t1, 0));
5852
} else {
5853
addLocation(locations, include,
5854
curve1, t1, Curve.evaluate(v1, t1, 0),
5855
curve2, t2, Curve.evaluate(v2, t2, 0));
5856
}
5857
} else {
5858
addCurveIntersections(v2, v1, curve2, curve1, locations, include,
5859
uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
5860
}
5861
}
5862
5863
function getConvexHull(dq0, dq1, dq2, dq3) {
5864
var p0 = [ 0, dq0 ],
5865
p1 = [ 1 / 3, dq1 ],
5866
p2 = [ 2 / 3, dq2 ],
5867
p3 = [ 1, dq3 ],
5868
getSignedDistance = Line.getSignedDistance,
5869
dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
5870
dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2),
5871
flip = false,
5872
hull;
5873
if (dist1 * dist2 < 0) {
5874
hull = [[p0, p1, p3], [p0, p2, p3]];
5875
flip = dist1 < 0;
5876
} else {
5877
var pmax, cross = 0,
5878
distZero = dist1 === 0 || dist2 === 0;
5879
if (Math.abs(dist1) > Math.abs(dist2)) {
5880
pmax = p1;
5881
cross = (dq3 - dq2 - (dq3 - dq0) / 3)
5882
* (2 * (dq3 - dq2) - dq3 + dq1) / 3;
5883
} else {
5884
pmax = p2;
5885
cross = (dq1 - dq0 + (dq0 - dq3) / 3)
5886
* (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
5887
}
5888
hull = cross < 0 || distZero
5889
? [[p0, pmax, p3], [p0, p3]]
5890
: [[p0, p1, p2, p3], [p0, p3]];
5891
flip = dist1 ? dist1 < 0 : dist2 < 0;
5892
}
5893
return flip ? hull.reverse() : hull;
5894
}
5895
5896
function clipConvexHull(hullTop, hullBottom, dMin, dMax) {
5897
var tProxy,
5898
tVal = null,
5899
px, py,
5900
qx, qy;
5901
for (var i = 0, l = hullBottom.length - 1; i < l; i++) {
5902
py = hullBottom[i][1];
5903
qy = hullBottom[i + 1][1];
5904
if (py < qy) {
5905
tProxy = null;
5906
} else if (qy <= dMax) {
5907
px = hullBottom[i][0];
5908
qx = hullBottom[i + 1][0];
5909
tProxy = px + (dMax - py) * (qx - px) / (qy - py);
5910
} else {
5911
continue;
5912
}
5913
break;
5914
}
5915
if (hullTop[0][1] <= dMax)
5916
tProxy = hullTop[0][0];
5917
for (var i = 0, l = hullTop.length - 1; i < l; i++) {
5918
py = hullTop[i][1];
5919
qy = hullTop[i + 1][1];
5920
if (py >= dMin) {
5921
tVal = tProxy;
5922
} else if (py > qy) {
5923
tVal = null;
5924
} else if (qy >= dMin) {
5925
px = hullTop[i][0];
5926
qx = hullTop[i + 1][0];
5927
tVal = px + (dMin - py) * (qx - px) / (qy - py);
5928
} else {
5929
continue;
5930
}
5931
break;
5932
}
5933
return tVal;
5934
}
5935
5936
function addCurveLineIntersections(v1, v2, curve1, curve2, locations,
5937
include) {
5938
var flip = Curve.isLinear(v1),
5939
vc = flip ? v2 : v1,
5940
vl = flip ? v1 : v2,
5941
lx1 = vl[0], ly1 = vl[1],
5942
lx2 = vl[6], ly2 = vl[7],
5943
ldx = lx2 - lx1,
5944
ldy = ly2 - ly1,
5945
angle = Math.atan2(-ldy, ldx),
5946
sin = Math.sin(angle),
5947
cos = Math.cos(angle),
5948
rlx2 = ldx * cos - ldy * sin,
5949
rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0],
5950
rvc = [];
5951
for(var i = 0; i < 8; i += 2) {
5952
var x = vc[i] - lx1,
5953
y = vc[i + 1] - ly1;
5954
rvc.push(
5955
x * cos - y * sin,
5956
y * cos + x * sin);
5957
}
5958
var roots = [],
5959
count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1);
5960
for (var i = 0; i < count; i++) {
5961
var tc = roots[i],
5962
x = Curve.evaluate(rvc, tc, 0).x;
5963
if (x >= 0 && x <= rlx2) {
5964
var tl = Curve.getParameterOf(rvl, x, 0),
5965
t1 = flip ? tl : tc,
5966
t2 = flip ? tc : tl;
5967
addLocation(locations, include,
5968
curve1, t1, Curve.evaluate(v1, t1, 0),
5969
curve2, t2, Curve.evaluate(v2, t2, 0));
5970
}
5971
}
5972
}
5973
5974
function addLineIntersection(v1, v2, curve1, curve2, locations, include) {
5975
var point = Line.intersect(
5976
v1[0], v1[1], v1[6], v1[7],
5977
v2[0], v2[1], v2[6], v2[7]);
5978
if (point) {
5979
var x = point.x,
5980
y = point.y;
5981
addLocation(locations, include,
5982
curve1, Curve.getParameterOf(v1, x, y), point,
5983
curve2, Curve.getParameterOf(v2, x, y), point);
5984
}
5985
}
5986
5987
return { statics: {
5988
getIntersections: function(v1, v2, curve1, curve2, locations, include) {
5989
var linear1 = Curve.isLinear(v1),
5990
linear2 = Curve.isLinear(v2);
5991
(linear1 && linear2
5992
? addLineIntersection
5993
: linear1 || linear2
5994
? addCurveLineIntersections
5995
: addCurveIntersections)(
5996
v1, v2, curve1, curve2, locations, include,
5997
0, 1, 0, 1, 0, false, 0);
5998
return locations;
5999
}
6000
}};
6001
});
6002
6003
var CurveLocation = Base.extend({
6004
_class: 'CurveLocation',
6005
beans: true,
6006
6007
initialize: function CurveLocation(curve, parameter, point, _curve2,
6008
_parameter2, _point2, _distance) {
6009
this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
6010
this._curve = curve;
6011
this._segment1 = curve._segment1;
6012
this._segment2 = curve._segment2;
6013
this._parameter = parameter;
6014
this._point = point;
6015
this._curve2 = _curve2;
6016
this._parameter2 = _parameter2;
6017
this._point2 = _point2;
6018
this._distance = _distance;
6019
},
6020
6021
getSegment: function(_preferFirst) {
6022
if (!this._segment) {
6023
var curve = this.getCurve(),
6024
parameter = this.getParameter();
6025
if (parameter === 1) {
6026
this._segment = curve._segment2;
6027
} else if (parameter === 0 || _preferFirst) {
6028
this._segment = curve._segment1;
6029
} else if (parameter == null) {
6030
return null;
6031
} else {
6032
this._segment = curve.getPartLength(0, parameter)
6033
< curve.getPartLength(parameter, 1)
6034
? curve._segment1
6035
: curve._segment2;
6036
}
6037
}
6038
return this._segment;
6039
},
6040
6041
getCurve: function(_uncached) {
6042
if (!this._curve || _uncached) {
6043
this._curve = this._segment1.getCurve();
6044
if (this._curve.getParameterOf(this._point) == null)
6045
this._curve = this._segment2.getPrevious().getCurve();
6046
}
6047
return this._curve;
6048
},
6049
6050
getIntersection: function() {
6051
var intersection = this._intersection;
6052
if (!intersection && this._curve2) {
6053
var param = this._parameter2;
6054
this._intersection = intersection = new CurveLocation(
6055
this._curve2, param, this._point2 || this._point, this);
6056
intersection._intersection = this;
6057
}
6058
return intersection;
6059
},
6060
6061
getPath: function() {
6062
var curve = this.getCurve();
6063
return curve && curve._path;
6064
},
6065
6066
getIndex: function() {
6067
var curve = this.getCurve();
6068
return curve && curve.getIndex();
6069
},
6070
6071
getOffset: function() {
6072
var path = this.getPath();
6073
return path && path._getOffset(this);
6074
},
6075
6076
getCurveOffset: function() {
6077
var curve = this.getCurve(),
6078
parameter = this.getParameter();
6079
return parameter != null && curve && curve.getPartLength(0, parameter);
6080
},
6081
6082
getParameter: function(_uncached) {
6083
if ((this._parameter == null || _uncached) && this._point) {
6084
var curve = this.getCurve(_uncached && this._point);
6085
this._parameter = curve && curve.getParameterOf(this._point);
6086
}
6087
return this._parameter;
6088
},
6089
6090
getPoint: function(_uncached) {
6091
if ((!this._point || _uncached) && this._parameter != null) {
6092
var curve = this.getCurve();
6093
this._point = curve && curve.getPointAt(this._parameter, true);
6094
}
6095
return this._point;
6096
},
6097
6098
getTangent: function() {
6099
var parameter = this.getParameter(),
6100
curve = this.getCurve();
6101
return parameter != null && curve && curve.getTangentAt(parameter, true);
6102
},
6103
6104
getNormal: function() {
6105
var parameter = this.getParameter(),
6106
curve = this.getCurve();
6107
return parameter != null && curve && curve.getNormalAt(parameter, true);
6108
},
6109
6110
getDistance: function() {
6111
return this._distance;
6112
},
6113
6114
divide: function() {
6115
var curve = this.getCurve(true);
6116
return curve && curve.divide(this.getParameter(true), true);
6117
},
6118
6119
split: function() {
6120
var curve = this.getCurve(true);
6121
return curve && curve.split(this.getParameter(true), true);
6122
},
6123
6124
equals: function(loc) {
6125
var isZero = Numerical.isZero;
6126
return this === loc
6127
|| loc
6128
&& this._curve === loc._curve
6129
&& this._curve2 === loc._curve2
6130
&& isZero(this._parameter - loc._parameter)
6131
&& isZero(this._parameter2 - loc._parameter2)
6132
|| false;
6133
},
6134
6135
toString: function() {
6136
var parts = [],
6137
point = this.getPoint(),
6138
f = Formatter.instance;
6139
if (point)
6140
parts.push('point: ' + point);
6141
var index = this.getIndex();
6142
if (index != null)
6143
parts.push('index: ' + index);
6144
var parameter = this.getParameter();
6145
if (parameter != null)
6146
parts.push('parameter: ' + f.number(parameter));
6147
if (this._distance != null)
6148
parts.push('distance: ' + f.number(this._distance));
6149
return '{ ' + parts.join(', ') + ' }';
6150
}
6151
});
6152
6153
var PathItem = Item.extend({
6154
_class: 'PathItem',
6155
6156
initialize: function PathItem() {
6157
},
6158
6159
getIntersections: function(path, _expand) {
6160
if (this === path)
6161
path = null;
6162
if (path && !this.getBounds().touches(path.getBounds()))
6163
return [];
6164
var locations = [],
6165
curves1 = this.getCurves(),
6166
curves2 = path ? path.getCurves() : curves1,
6167
matrix1 = this._matrix.orNullIfIdentity(),
6168
matrix2 = path ? path._matrix.orNullIfIdentity() : matrix1,
6169
length1 = curves1.length,
6170
length2 = path ? curves2.length : length1,
6171
values2 = [],
6172
MIN = 1e-11,
6173
MAX = 1 - 1e-11;
6174
for (var i = 0; i < length2; i++)
6175
values2[i] = curves2[i].getValues(matrix2);
6176
for (var i = 0; i < length1; i++) {
6177
var curve1 = curves1[i],
6178
values1 = path ? curve1.getValues(matrix1) : values2[i];
6179
if (!path) {
6180
var seg1 = curve1.getSegment1(),
6181
seg2 = curve1.getSegment2(),
6182
h1 = seg1._handleOut,
6183
h2 = seg2._handleIn;
6184
if (new Line(seg1._point.subtract(h1), h1.multiply(2), true)
6185
.intersect(new Line(seg2._point.subtract(h2),
6186
h2.multiply(2), true), false)) {
6187
var parts = Curve.subdivide(values1);
6188
Curve.getIntersections(
6189
parts[0], parts[1], curve1, curve1, locations,
6190
function(loc) {
6191
if (loc._parameter <= MAX) {
6192
loc._parameter /= 2;
6193
loc._parameter2 = 0.5 + loc._parameter2 / 2;
6194
return true;
6195
}
6196
}
6197
);
6198
}
6199
}
6200
for (var j = path ? 0 : i + 1; j < length2; j++) {
6201
Curve.getIntersections(
6202
values1, values2[j], curve1, curves2[j], locations,
6203
!path && (j === i + 1 || j === length2 - 1 && i === 0)
6204
&& function(loc) {
6205
var t = loc._parameter;
6206
return t >= MIN && t <= MAX;
6207
}
6208
);
6209
}
6210
}
6211
var last = locations.length - 1;
6212
for (var i = last; i >= 0; i--) {
6213
var loc = locations[i],
6214
next = loc._curve.getNext(),
6215
next2 = loc._curve2.getNext();
6216
if (next && loc._parameter >= MAX) {
6217
loc._parameter = 0;
6218
loc._curve = next;
6219
}
6220
if (next2 && loc._parameter2 >= MAX) {
6221
loc._parameter2 = 0;
6222
loc._curve2 = next2;
6223
}
6224
}
6225
6226
function compare(loc1, loc2) {
6227
var path1 = loc1.getPath(),
6228
path2 = loc2.getPath();
6229
return path1 === path2
6230
? (loc1.getIndex() + loc1.getParameter())
6231
- (loc2.getIndex() + loc2.getParameter())
6232
: path1._id - path2._id;
6233
}
6234
6235
if (last > 0) {
6236
locations.sort(compare);
6237
for (var i = last; i >= 0; i--) {
6238
if (locations[i].equals(locations[i === 0 ? last : i - 1])) {
6239
locations.splice(i, 1);
6240
last--;
6241
}
6242
}
6243
}
6244
if (_expand) {
6245
for (var i = last; i >= 0; i--)
6246
locations.push(locations[i].getIntersection());
6247
locations.sort(compare);
6248
}
6249
return locations;
6250
},
6251
6252
setPathData: function(data) {
6253
6254
var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
6255
coords,
6256
relative = false,
6257
previous,
6258
control,
6259
current = new Point(),
6260
start = new Point();
6261
6262
function getCoord(index, coord) {
6263
var val = +coords[index];
6264
if (relative)
6265
val += current[coord];
6266
return val;
6267
}
6268
6269
function getPoint(index) {
6270
return new Point(
6271
getCoord(index, 'x'),
6272
getCoord(index + 1, 'y')
6273
);
6274
}
6275
6276
this.clear();
6277
6278
for (var i = 0, l = parts.length; i < l; i++) {
6279
var part = parts[i],
6280
command = part[0],
6281
lower = command.toLowerCase();
6282
coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
6283
var length = coords && coords.length;
6284
relative = command === lower;
6285
if (previous === 'z' && !/[mz]/.test(lower))
6286
this.moveTo(current = start);
6287
switch (lower) {
6288
case 'm':
6289
case 'l':
6290
var move = lower === 'm';
6291
if (move && previous && previous !== 'z')
6292
this.closePath(true);
6293
for (var j = 0; j < length; j += 2)
6294
this[j === 0 && move ? 'moveTo' : 'lineTo'](
6295
current = getPoint(j));
6296
control = current;
6297
if (move)
6298
start = current;
6299
break;
6300
case 'h':
6301
case 'v':
6302
var coord = lower === 'h' ? 'x' : 'y';
6303
for (var j = 0; j < length; j++) {
6304
current[coord] = getCoord(j, coord);
6305
this.lineTo(current);
6306
}
6307
control = current;
6308
break;
6309
case 'c':
6310
for (var j = 0; j < length; j += 6) {
6311
this.cubicCurveTo(
6312
getPoint(j),
6313
control = getPoint(j + 2),
6314
current = getPoint(j + 4));
6315
}
6316
break;
6317
case 's':
6318
for (var j = 0; j < length; j += 4) {
6319
this.cubicCurveTo(
6320
/[cs]/.test(previous)
6321
? current.multiply(2).subtract(control)
6322
: current,
6323
control = getPoint(j),
6324
current = getPoint(j + 2));
6325
previous = lower;
6326
}
6327
break;
6328
case 'q':
6329
for (var j = 0; j < length; j += 4) {
6330
this.quadraticCurveTo(
6331
control = getPoint(j),
6332
current = getPoint(j + 2));
6333
}
6334
break;
6335
case 't':
6336
for (var j = 0; j < length; j += 2) {
6337
this.quadraticCurveTo(
6338
control = (/[qt]/.test(previous)
6339
? current.multiply(2).subtract(control)
6340
: current),
6341
current = getPoint(j));
6342
previous = lower;
6343
}
6344
break;
6345
case 'a':
6346
for (var j = 0; j < length; j += 7) {
6347
this.arcTo(current = getPoint(j + 5),
6348
new Size(+coords[0], +coords[1]),
6349
+coords[2], +coords[4], +coords[3]);
6350
}
6351
break;
6352
case 'z':
6353
this.closePath(true);
6354
break;
6355
}
6356
previous = lower;
6357
}
6358
},
6359
6360
_canComposite: function() {
6361
return !(this.hasFill() && this.hasStroke());
6362
},
6363
6364
_contains: function(point) {
6365
var winding = this._getWinding(point, false, true);
6366
return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
6367
}
6368
6369
});
6370
6371
var Path = PathItem.extend({
6372
_class: 'Path',
6373
_serializeFields: {
6374
segments: [],
6375
closed: false
6376
},
6377
6378
initialize: function Path(arg) {
6379
this._closed = false;
6380
this._segments = [];
6381
var segments = Array.isArray(arg)
6382
? typeof arg[0] === 'object'
6383
? arg
6384
: arguments
6385
: arg && (arg.size === undefined && (arg.x !== undefined
6386
|| arg.point !== undefined))
6387
? arguments
6388
: null;
6389
if (segments && segments.length > 0) {
6390
this.setSegments(segments);
6391
} else {
6392
this._curves = undefined;
6393
this._selectedSegmentState = 0;
6394
if (!segments && typeof arg === 'string') {
6395
this.setPathData(arg);
6396
arg = null;
6397
}
6398
}
6399
this._initialize(!segments && arg);
6400
},
6401
6402
_equals: function(item) {
6403
return Base.equals(this._segments, item._segments);
6404
},
6405
6406
clone: function(insert) {
6407
var copy = new Path(Item.NO_INSERT);
6408
copy.setSegments(this._segments);
6409
copy._closed = this._closed;
6410
if (this._clockwise !== undefined)
6411
copy._clockwise = this._clockwise;
6412
return this._clone(copy, insert);
6413
},
6414
6415
_changed: function _changed(flags) {
6416
_changed.base.call(this, flags);
6417
if (flags & 8) {
6418
var parent = this._parent;
6419
if (parent)
6420
parent._currentPath = undefined;
6421
this._length = this._clockwise = undefined;
6422
if (this._curves && !(flags & 16)) {
6423
for (var i = 0, l = this._curves.length; i < l; i++)
6424
this._curves[i]._changed();
6425
}
6426
this._monoCurves = undefined;
6427
} else if (flags & 32) {
6428
this._bounds = undefined;
6429
}
6430
},
6431
6432
getStyle: function() {
6433
var parent = this._parent;
6434
return (parent instanceof CompoundPath ? parent : this)._style;
6435
},
6436
6437
getSegments: function() {
6438
return this._segments;
6439
},
6440
6441
setSegments: function(segments) {
6442
var fullySelected = this.isFullySelected();
6443
this._segments.length = 0;
6444
this._selectedSegmentState = 0;
6445
this._curves = undefined;
6446
if (segments && segments.length > 0)
6447
this._add(Segment.readAll(segments));
6448
if (fullySelected)
6449
this.setFullySelected(true);
6450
},
6451
6452
getFirstSegment: function() {
6453
return this._segments[0];
6454
},
6455
6456
getLastSegment: function() {
6457
return this._segments[this._segments.length - 1];
6458
},
6459
6460
getCurves: function() {
6461
var curves = this._curves,
6462
segments = this._segments;
6463
if (!curves) {
6464
var length = this._countCurves();
6465
curves = this._curves = new Array(length);
6466
for (var i = 0; i < length; i++)
6467
curves[i] = new Curve(this, segments[i],
6468
segments[i + 1] || segments[0]);
6469
}
6470
return curves;
6471
},
6472
6473
getFirstCurve: function() {
6474
return this.getCurves()[0];
6475
},
6476
6477
getLastCurve: function() {
6478
var curves = this.getCurves();
6479
return curves[curves.length - 1];
6480
},
6481
6482
isClosed: function() {
6483
return this._closed;
6484
},
6485
6486
setClosed: function(closed) {
6487
if (this._closed != (closed = !!closed)) {
6488
this._closed = closed;
6489
if (this._curves) {
6490
var length = this._curves.length = this._countCurves();
6491
if (closed)
6492
this._curves[length - 1] = new Curve(this,
6493
this._segments[length - 1], this._segments[0]);
6494
}
6495
this._changed(25);
6496
}
6497
}
6498
}, {
6499
beans: true,
6500
6501
getPathData: function(_precision) {
6502
var segments = this._segments,
6503
f = Formatter.instance,
6504
parts = [];
6505
6506
function addCurve(seg1, seg2, skipLine) {
6507
var point1 = seg1._point,
6508
point2 = seg2._point,
6509
handle1 = seg1._handleOut,
6510
handle2 = seg2._handleIn;
6511
if (handle1.isZero() && handle2.isZero()) {
6512
if (!skipLine) {
6513
parts.push('L' + f.point(point2, _precision));
6514
}
6515
} else {
6516
var end = point2.subtract(point1);
6517
parts.push('c' + f.point(handle1, _precision)
6518
+ ' ' + f.point(end.add(handle2), _precision)
6519
+ ' ' + f.point(end, _precision));
6520
}
6521
}
6522
6523
if (segments.length === 0)
6524
return '';
6525
parts.push('M' + f.point(segments[0]._point));
6526
for (var i = 0, l = segments.length - 1; i < l; i++)
6527
addCurve(segments[i], segments[i + 1], false);
6528
if (this._closed) {
6529
addCurve(segments[segments.length - 1], segments[0], true);
6530
parts.push('z');
6531
}
6532
return parts.join('');
6533
}
6534
}, {
6535
6536
isEmpty: function() {
6537
return this._segments.length === 0;
6538
},
6539
6540
isPolygon: function() {
6541
for (var i = 0, l = this._segments.length; i < l; i++) {
6542
if (!this._segments[i].isLinear())
6543
return false;
6544
}
6545
return true;
6546
},
6547
6548
_transformContent: function(matrix) {
6549
var coords = new Array(6);
6550
for (var i = 0, l = this._segments.length; i < l; i++)
6551
this._segments[i]._transformCoordinates(matrix, coords, true);
6552
return true;
6553
},
6554
6555
_add: function(segs, index) {
6556
var segments = this._segments,
6557
curves = this._curves,
6558
amount = segs.length,
6559
append = index == null,
6560
index = append ? segments.length : index;
6561
for (var i = 0; i < amount; i++) {
6562
var segment = segs[i];
6563
if (segment._path)
6564
segment = segs[i] = segment.clone();
6565
segment._path = this;
6566
segment._index = index + i;
6567
if (segment._selectionState)
6568
this._updateSelection(segment, 0, segment._selectionState);
6569
}
6570
if (append) {
6571
segments.push.apply(segments, segs);
6572
} else {
6573
segments.splice.apply(segments, [index, 0].concat(segs));
6574
for (var i = index + amount, l = segments.length; i < l; i++)
6575
segments[i]._index = i;
6576
}
6577
if (curves || segs._curves) {
6578
if (!curves)
6579
curves = this._curves = [];
6580
var from = index > 0 ? index - 1 : index,
6581
start = from,
6582
to = Math.min(from + amount, this._countCurves());
6583
if (segs._curves) {
6584
curves.splice.apply(curves, [from, 0].concat(segs._curves));
6585
start += segs._curves.length;
6586
}
6587
for (var i = start; i < to; i++)
6588
curves.splice(i, 0, new Curve(this, null, null));
6589
this._adjustCurves(from, to);
6590
}
6591
this._changed(25);
6592
return segs;
6593
},
6594
6595
_adjustCurves: function(from, to) {
6596
var segments = this._segments,
6597
curves = this._curves,
6598
curve;
6599
for (var i = from; i < to; i++) {
6600
curve = curves[i];
6601
curve._path = this;
6602
curve._segment1 = segments[i];
6603
curve._segment2 = segments[i + 1] || segments[0];
6604
}
6605
if (curve = curves[this._closed && from === 0 ? segments.length - 1
6606
: from - 1])
6607
curve._segment2 = segments[from] || segments[0];
6608
if (curve = curves[to])
6609
curve._segment1 = segments[to];
6610
},
6611
6612
_countCurves: function() {
6613
var length = this._segments.length;
6614
return !this._closed && length > 0 ? length - 1 : length;
6615
},
6616
6617
add: function(segment1 ) {
6618
return arguments.length > 1 && typeof segment1 !== 'number'
6619
? this._add(Segment.readAll(arguments))
6620
: this._add([ Segment.read(arguments) ])[0];
6621
},
6622
6623
insert: function(index, segment1 ) {
6624
return arguments.length > 2 && typeof segment1 !== 'number'
6625
? this._add(Segment.readAll(arguments, 1), index)
6626
: this._add([ Segment.read(arguments, 1) ], index)[0];
6627
},
6628
6629
addSegment: function() {
6630
return this._add([ Segment.read(arguments) ])[0];
6631
},
6632
6633
insertSegment: function(index ) {
6634
return this._add([ Segment.read(arguments, 1) ], index)[0];
6635
},
6636
6637
addSegments: function(segments) {
6638
return this._add(Segment.readAll(segments));
6639
},
6640
6641
insertSegments: function(index, segments) {
6642
return this._add(Segment.readAll(segments), index);
6643
},
6644
6645
removeSegment: function(index) {
6646
return this.removeSegments(index, index + 1)[0] || null;
6647
},
6648
6649
removeSegments: function(from, to, _includeCurves) {
6650
from = from || 0;
6651
to = Base.pick(to, this._segments.length);
6652
var segments = this._segments,
6653
curves = this._curves,
6654
count = segments.length,
6655
removed = segments.splice(from, to - from),
6656
amount = removed.length;
6657
if (!amount)
6658
return removed;
6659
for (var i = 0; i < amount; i++) {
6660
var segment = removed[i];
6661
if (segment._selectionState)
6662
this._updateSelection(segment, segment._selectionState, 0);
6663
segment._index = segment._path = null;
6664
}
6665
for (var i = from, l = segments.length; i < l; i++)
6666
segments[i]._index = i;
6667
if (curves) {
6668
var index = from > 0 && to === count + (this._closed ? 1 : 0)
6669
? from - 1
6670
: from,
6671
curves = curves.splice(index, amount);
6672
if (_includeCurves)
6673
removed._curves = curves.slice(1);
6674
this._adjustCurves(index, index);
6675
}
6676
this._changed(25);
6677
return removed;
6678
},
6679
6680
clear: '#removeSegments',
6681
6682
isFullySelected: function() {
6683
var length = this._segments.length;
6684
return this._selected && length > 0 && this._selectedSegmentState
6685
=== length * 7;
6686
},
6687
6688
setFullySelected: function(selected) {
6689
if (selected)
6690
this._selectSegments(true);
6691
this.setSelected(selected);
6692
},
6693
6694
setSelected: function setSelected(selected) {
6695
if (!selected)
6696
this._selectSegments(false);
6697
setSelected.base.call(this, selected);
6698
},
6699
6700
_selectSegments: function(selected) {
6701
var length = this._segments.length;
6702
this._selectedSegmentState = selected
6703
? length * 7 : 0;
6704
for (var i = 0; i < length; i++)
6705
this._segments[i]._selectionState = selected
6706
? 7 : 0;
6707
},
6708
6709
_updateSelection: function(segment, oldState, newState) {
6710
segment._selectionState = newState;
6711
var total = this._selectedSegmentState += newState - oldState;
6712
if (total > 0)
6713
this.setSelected(true);
6714
},
6715
6716
flatten: function(maxDistance) {
6717
var flattener = new PathFlattener(this),
6718
pos = 0,
6719
step = flattener.length / Math.ceil(flattener.length / maxDistance),
6720
end = flattener.length + (this._closed ? -step : step) / 2;
6721
var segments = [];
6722
while (pos <= end) {
6723
segments.push(new Segment(flattener.evaluate(pos, 0)));
6724
pos += step;
6725
}
6726
this.setSegments(segments);
6727
},
6728
6729
reduce: function() {
6730
var curves = this.getCurves();
6731
for (var i = curves.length - 1; i >= 0; i--) {
6732
var curve = curves[i];
6733
if (curve.isLinear() && curve.getLength() === 0)
6734
curve.remove();
6735
}
6736
return this;
6737
},
6738
6739
simplify: function(tolerance) {
6740
if (this._segments.length > 2) {
6741
var fitter = new PathFitter(this, tolerance || 2.5);
6742
this.setSegments(fitter.fit());
6743
}
6744
},
6745
6746
split: function(index, parameter) {
6747
if (parameter === null)
6748
return;
6749
if (arguments.length === 1) {
6750
var arg = index;
6751
if (typeof arg === 'number')
6752
arg = this.getLocationAt(arg);
6753
index = arg.index;
6754
parameter = arg.parameter;
6755
}
6756
var tolerance = 0.00001;
6757
if (parameter >= 1 - tolerance) {
6758
index++;
6759
parameter--;
6760
}
6761
var curves = this.getCurves();
6762
if (index >= 0 && index < curves.length) {
6763
if (parameter > tolerance) {
6764
curves[index++].divide(parameter, true);
6765
}
6766
var segs = this.removeSegments(index, this._segments.length, true),
6767
path;
6768
if (this._closed) {
6769
this.setClosed(false);
6770
path = this;
6771
} else if (index > 0) {
6772
path = this._clone(new Path().insertAbove(this, true));
6773
}
6774
path._add(segs, 0);
6775
this.addSegment(segs[0]);
6776
return path;
6777
}
6778
return null;
6779
},
6780
6781
isClockwise: function() {
6782
if (this._clockwise !== undefined)
6783
return this._clockwise;
6784
return Path.isClockwise(this._segments);
6785
},
6786
6787
setClockwise: function(clockwise) {
6788
if (this.isClockwise() != (clockwise = !!clockwise))
6789
this.reverse();
6790
this._clockwise = clockwise;
6791
},
6792
6793
reverse: function() {
6794
this._segments.reverse();
6795
for (var i = 0, l = this._segments.length; i < l; i++) {
6796
var segment = this._segments[i];
6797
var handleIn = segment._handleIn;
6798
segment._handleIn = segment._handleOut;
6799
segment._handleOut = handleIn;
6800
segment._index = i;
6801
}
6802
this._curves = null;
6803
if (this._clockwise !== undefined)
6804
this._clockwise = !this._clockwise;
6805
},
6806
6807
join: function(path) {
6808
if (path) {
6809
var segments = path._segments,
6810
last1 = this.getLastSegment(),
6811
last2 = path.getLastSegment();
6812
if (last1._point.equals(last2._point))
6813
path.reverse();
6814
var first1,
6815
first2 = path.getFirstSegment();
6816
if (last1._point.equals(first2._point)) {
6817
last1.setHandleOut(first2._handleOut);
6818
this._add(segments.slice(1));
6819
} else {
6820
first1 = this.getFirstSegment();
6821
if (first1._point.equals(first2._point))
6822
path.reverse();
6823
last2 = path.getLastSegment();
6824
if (first1._point.equals(last2._point)) {
6825
first1.setHandleIn(last2._handleIn);
6826
this._add(segments.slice(0, segments.length - 1), 0);
6827
} else {
6828
this._add(segments.slice());
6829
}
6830
}
6831
if (path.closed)
6832
this._add([segments[0]]);
6833
path.remove();
6834
}
6835
var first = this.getFirstSegment(),
6836
last = this.getLastSegment();
6837
if (first !== last && first._point.equals(last._point)) {
6838
first.setHandleIn(last._handleIn);
6839
last.remove();
6840
this.setClosed(true);
6841
}
6842
},
6843
6844
getLength: function() {
6845
if (this._length == null) {
6846
var curves = this.getCurves();
6847
this._length = 0;
6848
for (var i = 0, l = curves.length; i < l; i++)
6849
this._length += curves[i].getLength();
6850
}
6851
return this._length;
6852
},
6853
6854
getArea: function() {
6855
var curves = this.getCurves();
6856
var area = 0;
6857
for (var i = 0, l = curves.length; i < l; i++)
6858
area += curves[i].getArea();
6859
return area;
6860
},
6861
6862
_getOffset: function(location) {
6863
var index = location && location.getIndex();
6864
if (index != null) {
6865
var curves = this.getCurves(),
6866
offset = 0;
6867
for (var i = 0; i < index; i++)
6868
offset += curves[i].getLength();
6869
var curve = curves[index],
6870
parameter = location.getParameter();
6871
if (parameter > 0)
6872
offset += curve.getPartLength(0, parameter);
6873
return offset;
6874
}
6875
return null;
6876
},
6877
6878
getLocationOf: function(point) {
6879
var point = Point.read(arguments),
6880
curves = this.getCurves();
6881
for (var i = 0, l = curves.length; i < l; i++) {
6882
var loc = curves[i].getLocationOf(point);
6883
if (loc)
6884
return loc;
6885
}
6886
return null;
6887
},
6888
6889
getLocationAt: function(offset, isParameter) {
6890
var curves = this.getCurves(),
6891
length = 0;
6892
if (isParameter) {
6893
var index = ~~offset;
6894
return curves[index].getLocationAt(offset - index, true);
6895
}
6896
for (var i = 0, l = curves.length; i < l; i++) {
6897
var start = length,
6898
curve = curves[i];
6899
length += curve.getLength();
6900
if (length > offset) {
6901
return curve.getLocationAt(offset - start);
6902
}
6903
}
6904
if (offset <= this.getLength())
6905
return new CurveLocation(curves[curves.length - 1], 1);
6906
return null;
6907
},
6908
6909
getPointAt: function(offset, isParameter) {
6910
var loc = this.getLocationAt(offset, isParameter);
6911
return loc && loc.getPoint();
6912
},
6913
6914
getTangentAt: function(offset, isParameter) {
6915
var loc = this.getLocationAt(offset, isParameter);
6916
return loc && loc.getTangent();
6917
},
6918
6919
getNormalAt: function(offset, isParameter) {
6920
var loc = this.getLocationAt(offset, isParameter);
6921
return loc && loc.getNormal();
6922
},
6923
6924
getNearestLocation: function(point) {
6925
var point = Point.read(arguments),
6926
curves = this.getCurves(),
6927
minDist = Infinity,
6928
minLoc = null;
6929
for (var i = 0, l = curves.length; i < l; i++) {
6930
var loc = curves[i].getNearestLocation(point);
6931
if (loc._distance < minDist) {
6932
minDist = loc._distance;
6933
minLoc = loc;
6934
}
6935
}
6936
return minLoc;
6937
},
6938
6939
getNearestPoint: function(point) {
6940
var point = Point.read(arguments);
6941
return this.getNearestLocation(point).getPoint();
6942
},
6943
6944
toShape: function(insert) {
6945
if (!this._closed)
6946
return null;
6947
6948
var segments = this._segments,
6949
type,
6950
size,
6951
radius,
6952
topCenter;
6953
6954
function isColinear(i, j) {
6955
return segments[i].isColinear(segments[j]);
6956
}
6957
6958
function isOrthogonal(i) {
6959
return segments[i].isOrthogonal();
6960
}
6961
6962
function isArc(i) {
6963
return segments[i].isArc();
6964
}
6965
6966
function getDistance(i, j) {
6967
return segments[i]._point.getDistance(segments[j]._point);
6968
}
6969
6970
if (this.isPolygon() && segments.length === 4
6971
&& isColinear(0, 2) && isColinear(1, 3) && isOrthogonal(1)) {
6972
type = Shape.Rectangle;
6973
size = new Size(getDistance(0, 3), getDistance(0, 1));
6974
topCenter = segments[1]._point.add(segments[2]._point).divide(2);
6975
} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)
6976
&& isArc(6) && isColinear(1, 5) && isColinear(3, 7)) {
6977
type = Shape.Rectangle;
6978
size = new Size(getDistance(1, 6), getDistance(0, 3));
6979
radius = size.subtract(new Size(getDistance(0, 7),
6980
getDistance(1, 2))).divide(2);
6981
topCenter = segments[3]._point.add(segments[4]._point).divide(2);
6982
} else if (segments.length === 4
6983
&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
6984
if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {
6985
type = Shape.Circle;
6986
radius = getDistance(0, 2) / 2;
6987
} else {
6988
type = Shape.Ellipse;
6989
radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);
6990
}
6991
topCenter = segments[1]._point;
6992
}
6993
6994
if (type) {
6995
var center = this.getPosition(true),
6996
shape = new type({
6997
center: center,
6998
size: size,
6999
radius: radius,
7000
insert: false
7001
});
7002
shape.rotate(topCenter.subtract(center).getAngle() + 90);
7003
shape.setStyle(this._style);
7004
if (insert || insert === undefined)
7005
shape.insertAbove(this);
7006
return shape;
7007
}
7008
return null;
7009
},
7010
7011
_hitTest: function(point, options) {
7012
var that = this,
7013
style = this.getStyle(),
7014
segments = this._segments,
7015
numSegments = segments.length,
7016
closed = this._closed,
7017
tolerancePadding = options._tolerancePadding,
7018
strokePadding = tolerancePadding,
7019
join, cap, miterLimit,
7020
area, loc, res,
7021
hasStroke = options.stroke && style.hasStroke(),
7022
hasFill = options.fill && style.hasFill(),
7023
radius = hasStroke ? style.getStrokeWidth() / 2
7024
: hasFill ? 0 : null;
7025
if (radius != null) {
7026
if (radius > 0) {
7027
join = style.getStrokeJoin();
7028
cap = style.getStrokeCap();
7029
miterLimit = radius * style.getMiterLimit();
7030
strokePadding = tolerancePadding.add(new Point(radius, radius));
7031
} else {
7032
join = cap = 'round';
7033
}
7034
}
7035
7036
function isCloseEnough(pt, padding) {
7037
return point.subtract(pt).divide(padding).length <= 1;
7038
}
7039
7040
function checkSegmentPoint(seg, pt, name) {
7041
if (!options.selected || pt.isSelected()) {
7042
var anchor = seg._point;
7043
if (pt !== anchor)
7044
pt = pt.add(anchor);
7045
if (isCloseEnough(pt, strokePadding)) {
7046
return new HitResult(name, that, {
7047
segment: seg,
7048
point: pt
7049
});
7050
}
7051
}
7052
}
7053
7054
function checkSegmentPoints(seg, ends) {
7055
return (ends || options.segments)
7056
&& checkSegmentPoint(seg, seg._point, 'segment')
7057
|| (!ends && options.handles) && (
7058
checkSegmentPoint(seg, seg._handleIn, 'handle-in') ||
7059
checkSegmentPoint(seg, seg._handleOut, 'handle-out'));
7060
}
7061
7062
function addToArea(point) {
7063
area.add(point);
7064
}
7065
7066
function checkSegmentStroke(segment) {
7067
if (join !== 'round' || cap !== 'round') {
7068
area = new Path({ internal: true, closed: true });
7069
if (closed || segment._index > 0
7070
&& segment._index < numSegments - 1) {
7071
if (join !== 'round' && (segment._handleIn.isZero()
7072
|| segment._handleOut.isZero()))
7073
Path._addBevelJoin(segment, join, radius, miterLimit,
7074
addToArea, true);
7075
} else if (cap !== 'round') {
7076
Path._addSquareCap(segment, cap, radius, addToArea, true);
7077
}
7078
if (!area.isEmpty()) {
7079
var loc;
7080
return area.contains(point)
7081
|| (loc = area.getNearestLocation(point))
7082
&& isCloseEnough(loc.getPoint(), tolerancePadding);
7083
}
7084
}
7085
return isCloseEnough(segment._point, strokePadding);
7086
}
7087
7088
if (options.ends && !options.segments && !closed) {
7089
if (res = checkSegmentPoints(segments[0], true)
7090
|| checkSegmentPoints(segments[numSegments - 1], true))
7091
return res;
7092
} else if (options.segments || options.handles) {
7093
for (var i = 0; i < numSegments; i++)
7094
if (res = checkSegmentPoints(segments[i]))
7095
return res;
7096
}
7097
if (radius != null) {
7098
loc = this.getNearestLocation(point);
7099
if (loc) {
7100
var parameter = loc.getParameter();
7101
if (parameter === 0 || parameter === 1 && numSegments > 1) {
7102
if (!checkSegmentStroke(loc.getSegment()))
7103
loc = null;
7104
} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
7105
loc = null;
7106
}
7107
}
7108
if (!loc && join === 'miter' && numSegments > 1) {
7109
for (var i = 0; i < numSegments; i++) {
7110
var segment = segments[i];
7111
if (point.getDistance(segment._point) <= miterLimit
7112
&& checkSegmentStroke(segment)) {
7113
loc = segment.getLocation();
7114
break;
7115
}
7116
}
7117
}
7118
}
7119
return !loc && hasFill && this._contains(point) || loc && !hasStroke
7120
? new HitResult('fill', this)
7121
: loc
7122
? new HitResult('stroke', this, {
7123
location: loc,
7124
point: loc.getPoint()
7125
})
7126
: null;
7127
}
7128
7129
}, new function() {
7130
7131
function drawHandles(ctx, segments, matrix, size) {
7132
var half = size / 2;
7133
7134
function drawHandle(index) {
7135
var hX = coords[index],
7136
hY = coords[index + 1];
7137
if (pX != hX || pY != hY) {
7138
ctx.beginPath();
7139
ctx.moveTo(pX, pY);
7140
ctx.lineTo(hX, hY);
7141
ctx.stroke();
7142
ctx.beginPath();
7143
ctx.arc(hX, hY, half, 0, Math.PI * 2, true);
7144
ctx.fill();
7145
}
7146
}
7147
7148
var coords = new Array(6);
7149
for (var i = 0, l = segments.length; i < l; i++) {
7150
var segment = segments[i];
7151
segment._transformCoordinates(matrix, coords, false);
7152
var state = segment._selectionState,
7153
pX = coords[0],
7154
pY = coords[1];
7155
if (state & 1)
7156
drawHandle(2);
7157
if (state & 2)
7158
drawHandle(4);
7159
ctx.fillRect(pX - half, pY - half, size, size);
7160
if (!(state & 4)) {
7161
var fillStyle = ctx.fillStyle;
7162
ctx.fillStyle = '#ffffff';
7163
ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2);
7164
ctx.fillStyle = fillStyle;
7165
}
7166
}
7167
}
7168
7169
function drawSegments(ctx, path, matrix) {
7170
var segments = path._segments,
7171
length = segments.length,
7172
coords = new Array(6),
7173
first = true,
7174
curX, curY,
7175
prevX, prevY,
7176
inX, inY,
7177
outX, outY;
7178
7179
function drawSegment(i) {
7180
var segment = segments[i];
7181
if (matrix) {
7182
segment._transformCoordinates(matrix, coords, false);
7183
curX = coords[0];
7184
curY = coords[1];
7185
} else {
7186
var point = segment._point;
7187
curX = point._x;
7188
curY = point._y;
7189
}
7190
if (first) {
7191
ctx.moveTo(curX, curY);
7192
first = false;
7193
} else {
7194
if (matrix) {
7195
inX = coords[2];
7196
inY = coords[3];
7197
} else {
7198
var handle = segment._handleIn;
7199
inX = curX + handle._x;
7200
inY = curY + handle._y;
7201
}
7202
if (inX == curX && inY == curY && outX == prevX && outY == prevY) {
7203
ctx.lineTo(curX, curY);
7204
} else {
7205
ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
7206
}
7207
}
7208
prevX = curX;
7209
prevY = curY;
7210
if (matrix) {
7211
outX = coords[4];
7212
outY = coords[5];
7213
} else {
7214
var handle = segment._handleOut;
7215
outX = prevX + handle._x;
7216
outY = prevY + handle._y;
7217
}
7218
}
7219
7220
for (var i = 0; i < length; i++)
7221
drawSegment(i);
7222
if (path._closed && length > 0)
7223
drawSegment(0);
7224
}
7225
7226
return {
7227
_draw: function(ctx, param) {
7228
var dontStart = param.dontStart,
7229
dontPaint = param.dontFinish || param.clip;
7230
if (!dontStart)
7231
ctx.beginPath();
7232
7233
var style = this.getStyle(),
7234
hasFill = style.hasFill(),
7235
hasStroke = style.hasStroke(),
7236
dashArray = style.getDashArray(),
7237
dashLength = !paper.support.nativeDash && hasStroke
7238
&& dashArray && dashArray.length;
7239
7240
function getOffset(i) {
7241
return dashArray[((i % dashLength) + dashLength) % dashLength];
7242
}
7243
7244
if (!dontStart && this._currentPath) {
7245
ctx.currentPath = this._currentPath;
7246
} else if (hasFill || hasStroke && !dashLength || dontPaint) {
7247
drawSegments(ctx, this);
7248
if (this._closed)
7249
ctx.closePath();
7250
if (!dontStart)
7251
this._currentPath = ctx.currentPath;
7252
}
7253
7254
if (!dontPaint && (hasFill || hasStroke)) {
7255
this._setStyles(ctx);
7256
if (hasFill) {
7257
ctx.fill(style.getWindingRule());
7258
ctx.shadowColor = 'rgba(0,0,0,0)';
7259
}
7260
if (hasStroke) {
7261
if (dashLength) {
7262
if (!dontStart)
7263
ctx.beginPath();
7264
var flattener = new PathFlattener(this),
7265
length = flattener.length,
7266
from = -style.getDashOffset(), to,
7267
i = 0;
7268
from = from % length;
7269
while (from > 0) {
7270
from -= getOffset(i--) + getOffset(i--);
7271
}
7272
while (from < length) {
7273
to = from + getOffset(i++);
7274
if (from > 0 || to > 0)
7275
flattener.drawPart(ctx,
7276
Math.max(from, 0), Math.max(to, 0));
7277
from = to + getOffset(i++);
7278
}
7279
}
7280
ctx.stroke();
7281
}
7282
}
7283
},
7284
7285
_drawSelected: function(ctx, matrix) {
7286
ctx.beginPath();
7287
drawSegments(ctx, this, matrix);
7288
ctx.stroke();
7289
drawHandles(ctx, this._segments, matrix, paper.settings.handleSize);
7290
}
7291
};
7292
}, new function() {
7293
7294
function getFirstControlPoints(rhs) {
7295
var n = rhs.length,
7296
x = [],
7297
tmp = [],
7298
b = 2;
7299
x[0] = rhs[0] / b;
7300
for (var i = 1; i < n; i++) {
7301
tmp[i] = 1 / b;
7302
b = (i < n - 1 ? 4 : 2) - tmp[i];
7303
x[i] = (rhs[i] - x[i - 1]) / b;
7304
}
7305
for (var i = 1; i < n; i++) {
7306
x[n - i - 1] -= tmp[n - i] * x[n - i];
7307
}
7308
return x;
7309
}
7310
7311
return {
7312
smooth: function() {
7313
var segments = this._segments,
7314
size = segments.length,
7315
closed = this._closed,
7316
n = size,
7317
overlap = 0;
7318
if (size <= 2)
7319
return;
7320
if (closed) {
7321
overlap = Math.min(size, 4);
7322
n += Math.min(size, overlap) * 2;
7323
}
7324
var knots = [];
7325
for (var i = 0; i < size; i++)
7326
knots[i + overlap] = segments[i]._point;
7327
if (closed) {
7328
for (var i = 0; i < overlap; i++) {
7329
knots[i] = segments[i + size - overlap]._point;
7330
knots[i + size + overlap] = segments[i]._point;
7331
}
7332
} else {
7333
n--;
7334
}
7335
var rhs = [];
7336
7337
for (var i = 1; i < n - 1; i++)
7338
rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
7339
rhs[0] = knots[0]._x + 2 * knots[1]._x;
7340
rhs[n - 1] = 3 * knots[n - 1]._x;
7341
var x = getFirstControlPoints(rhs);
7342
7343
for (var i = 1; i < n - 1; i++)
7344
rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
7345
rhs[0] = knots[0]._y + 2 * knots[1]._y;
7346
rhs[n - 1] = 3 * knots[n - 1]._y;
7347
var y = getFirstControlPoints(rhs);
7348
7349
if (closed) {
7350
for (var i = 0, j = size; i < overlap; i++, j++) {
7351
var f1 = i / overlap,
7352
f2 = 1 - f1,
7353
ie = i + overlap,
7354
je = j + overlap;
7355
x[j] = x[i] * f1 + x[j] * f2;
7356
y[j] = y[i] * f1 + y[j] * f2;
7357
x[je] = x[ie] * f2 + x[je] * f1;
7358
y[je] = y[ie] * f2 + y[je] * f1;
7359
}
7360
n--;
7361
}
7362
var handleIn = null;
7363
for (var i = overlap; i <= n - overlap; i++) {
7364
var segment = segments[i - overlap];
7365
if (handleIn)
7366
segment.setHandleIn(handleIn.subtract(segment._point));
7367
if (i < n) {
7368
segment.setHandleOut(
7369
new Point(x[i], y[i]).subtract(segment._point));
7370
handleIn = i < n - 1
7371
? new Point(
7372
2 * knots[i + 1]._x - x[i + 1],
7373
2 * knots[i + 1]._y - y[i + 1])
7374
: new Point(
7375
(knots[n]._x + x[n - 1]) / 2,
7376
(knots[n]._y + y[n - 1]) / 2);
7377
}
7378
}
7379
if (closed && handleIn) {
7380
var segment = this._segments[0];
7381
segment.setHandleIn(handleIn.subtract(segment._point));
7382
}
7383
}
7384
};
7385
}, new function() {
7386
function getCurrentSegment(that) {
7387
var segments = that._segments;
7388
if (segments.length === 0)
7389
throw new Error('Use a moveTo() command first');
7390
return segments[segments.length - 1];
7391
}
7392
7393
return {
7394
moveTo: function() {
7395
var segments = this._segments;
7396
if (segments.length === 1)
7397
this.removeSegment(0);
7398
if (!segments.length)
7399
this._add([ new Segment(Point.read(arguments)) ]);
7400
},
7401
7402
moveBy: function() {
7403
throw new Error('moveBy() is unsupported on Path items.');
7404
},
7405
7406
lineTo: function() {
7407
this._add([ new Segment(Point.read(arguments)) ]);
7408
},
7409
7410
cubicCurveTo: function() {
7411
var handle1 = Point.read(arguments),
7412
handle2 = Point.read(arguments),
7413
to = Point.read(arguments),
7414
current = getCurrentSegment(this);
7415
current.setHandleOut(handle1.subtract(current._point));
7416
this._add([ new Segment(to, handle2.subtract(to)) ]);
7417
},
7418
7419
quadraticCurveTo: function() {
7420
var handle = Point.read(arguments),
7421
to = Point.read(arguments),
7422
current = getCurrentSegment(this)._point;
7423
this.cubicCurveTo(
7424
handle.add(current.subtract(handle).multiply(1 / 3)),
7425
handle.add(to.subtract(handle).multiply(1 / 3)),
7426
to
7427
);
7428
},
7429
7430
curveTo: function() {
7431
var through = Point.read(arguments),
7432
to = Point.read(arguments),
7433
t = Base.pick(Base.read(arguments), 0.5),
7434
t1 = 1 - t,
7435
current = getCurrentSegment(this)._point,
7436
handle = through.subtract(current.multiply(t1 * t1))
7437
.subtract(to.multiply(t * t)).divide(2 * t * t1);
7438
if (handle.isNaN())
7439
throw new Error(
7440
'Cannot put a curve through points with parameter = ' + t);
7441
this.quadraticCurveTo(handle, to);
7442
},
7443
7444
arcTo: function() {
7445
var current = getCurrentSegment(this),
7446
from = current._point,
7447
to = Point.read(arguments),
7448
through,
7449
peek = Base.peek(arguments),
7450
clockwise = Base.pick(peek, true),
7451
center, extent, vector, matrix;
7452
if (typeof clockwise === 'boolean') {
7453
var middle = from.add(to).divide(2),
7454
through = middle.add(middle.subtract(from).rotate(
7455
clockwise ? -90 : 90));
7456
} else if (Base.remain(arguments) <= 2) {
7457
through = to;
7458
to = Point.read(arguments);
7459
} else {
7460
var radius = Size.read(arguments);
7461
if (radius.isZero())
7462
return this.lineTo(to);
7463
var rotation = Base.read(arguments),
7464
clockwise = !!Base.read(arguments),
7465
large = !!Base.read(arguments),
7466
middle = from.add(to).divide(2),
7467
pt = from.subtract(middle).rotate(-rotation),
7468
x = pt.x,
7469
y = pt.y,
7470
abs = Math.abs,
7471
EPSILON = 1e-11,
7472
rx = abs(radius.width),
7473
ry = abs(radius.height),
7474
rxSq = rx * rx,
7475
rySq = ry * ry,
7476
xSq = x * x,
7477
ySq = y * y;
7478
var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
7479
if (factor > 1) {
7480
rx *= factor;
7481
ry *= factor;
7482
rxSq = rx * rx;
7483
rySq = ry * ry;
7484
}
7485
factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
7486
(rxSq * ySq + rySq * xSq);
7487
if (abs(factor) < EPSILON)
7488
factor = 0;
7489
if (factor < 0)
7490
throw new Error(
7491
'Cannot create an arc with the given arguments');
7492
center = new Point(rx * y / ry, -ry * x / rx)
7493
.multiply((large === clockwise ? -1 : 1)
7494
* Math.sqrt(factor))
7495
.rotate(rotation).add(middle);
7496
matrix = new Matrix().translate(center).rotate(rotation)
7497
.scale(rx, ry);
7498
vector = matrix._inverseTransform(from);
7499
extent = vector.getDirectedAngle(matrix._inverseTransform(to));
7500
if (!clockwise && extent > 0)
7501
extent -= 360;
7502
else if (clockwise && extent < 0)
7503
extent += 360;
7504
}
7505
if (through) {
7506
var l1 = new Line(from.add(through).divide(2),
7507
through.subtract(from).rotate(90), true),
7508
l2 = new Line(through.add(to).divide(2),
7509
to.subtract(through).rotate(90), true),
7510
line = new Line(from, to),
7511
throughSide = line.getSide(through);
7512
center = l1.intersect(l2, true);
7513
if (!center) {
7514
if (!throughSide)
7515
return this.lineTo(to);
7516
throw new Error(
7517
'Cannot create an arc with the given arguments');
7518
}
7519
vector = from.subtract(center);
7520
extent = vector.getDirectedAngle(to.subtract(center));
7521
var centerSide = line.getSide(center);
7522
if (centerSide === 0) {
7523
extent = throughSide * Math.abs(extent);
7524
} else if (throughSide === centerSide) {
7525
extent += extent < 0 ? 360 : -360;
7526
}
7527
}
7528
var ext = Math.abs(extent),
7529
count = ext >= 360 ? 4 : Math.ceil(ext / 90),
7530
inc = extent / count,
7531
half = inc * Math.PI / 360,
7532
z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
7533
segments = [];
7534
for (var i = 0; i <= count; i++) {
7535
var pt = to,
7536
out = null;
7537
if (i < count) {
7538
out = vector.rotate(90).multiply(z);
7539
if (matrix) {
7540
pt = matrix._transformPoint(vector);
7541
out = matrix._transformPoint(vector.add(out))
7542
.subtract(pt);
7543
} else {
7544
pt = center.add(vector);
7545
}
7546
}
7547
if (i === 0) {
7548
current.setHandleOut(out);
7549
} else {
7550
var _in = vector.rotate(-90).multiply(z);
7551
if (matrix) {
7552
_in = matrix._transformPoint(vector.add(_in))
7553
.subtract(pt);
7554
}
7555
segments.push(new Segment(pt, _in, out));
7556
}
7557
vector = vector.rotate(inc);
7558
}
7559
this._add(segments);
7560
},
7561
7562
lineBy: function() {
7563
var to = Point.read(arguments),
7564
current = getCurrentSegment(this)._point;
7565
this.lineTo(current.add(to));
7566
},
7567
7568
curveBy: function() {
7569
var through = Point.read(arguments),
7570
to = Point.read(arguments),
7571
parameter = Base.read(arguments),
7572
current = getCurrentSegment(this)._point;
7573
this.curveTo(current.add(through), current.add(to), parameter);
7574
},
7575
7576
cubicCurveBy: function() {
7577
var handle1 = Point.read(arguments),
7578
handle2 = Point.read(arguments),
7579
to = Point.read(arguments),
7580
current = getCurrentSegment(this)._point;
7581
this.cubicCurveTo(current.add(handle1), current.add(handle2),
7582
current.add(to));
7583
},
7584
7585
quadraticCurveBy: function() {
7586
var handle = Point.read(arguments),
7587
to = Point.read(arguments),
7588
current = getCurrentSegment(this)._point;
7589
this.quadraticCurveTo(current.add(handle), current.add(to));
7590
},
7591
7592
arcBy: function() {
7593
var current = getCurrentSegment(this)._point,
7594
point = current.add(Point.read(arguments)),
7595
clockwise = Base.pick(Base.peek(arguments), true);
7596
if (typeof clockwise === 'boolean') {
7597
this.arcTo(point, clockwise);
7598
} else {
7599
this.arcTo(point, current.add(Point.read(arguments)));
7600
}
7601
},
7602
7603
closePath: function(join) {
7604
this.setClosed(true);
7605
if (join)
7606
this.join();
7607
}
7608
};
7609
}, {
7610
7611
_getBounds: function(getter, matrix) {
7612
return Path[getter](this._segments, this._closed, this.getStyle(),
7613
matrix);
7614
},
7615
7616
statics: {
7617
isClockwise: function(segments) {
7618
var sum = 0;
7619
for (var i = 0, l = segments.length; i < l; i++) {
7620
var v = Curve.getValues(
7621
segments[i], segments[i + 1 < l ? i + 1 : 0]);
7622
for (var j = 2; j < 8; j += 2)
7623
sum += (v[j - 2] - v[j]) * (v[j + 1] + v[j - 1]);
7624
}
7625
return sum > 0;
7626
},
7627
7628
getBounds: function(segments, closed, style, matrix, strokePadding) {
7629
var first = segments[0];
7630
if (!first)
7631
return new Rectangle();
7632
var coords = new Array(6),
7633
prevCoords = first._transformCoordinates(matrix, new Array(6), false),
7634
min = prevCoords.slice(0, 2),
7635
max = min.slice(),
7636
roots = new Array(2);
7637
7638
function processSegment(segment) {
7639
segment._transformCoordinates(matrix, coords, false);
7640
for (var i = 0; i < 2; i++) {
7641
Curve._addBounds(
7642
prevCoords[i],
7643
prevCoords[i + 4],
7644
coords[i + 2],
7645
coords[i],
7646
i, strokePadding ? strokePadding[i] : 0, min, max, roots);
7647
}
7648
var tmp = prevCoords;
7649
prevCoords = coords;
7650
coords = tmp;
7651
}
7652
7653
for (var i = 1, l = segments.length; i < l; i++)
7654
processSegment(segments[i]);
7655
if (closed)
7656
processSegment(first);
7657
return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
7658
},
7659
7660
getStrokeBounds: function(segments, closed, style, matrix) {
7661
if (!style.hasStroke())
7662
return Path.getBounds(segments, closed, style, matrix);
7663
var length = segments.length - (closed ? 0 : 1),
7664
radius = style.getStrokeWidth() / 2,
7665
padding = Path._getPenPadding(radius, matrix),
7666
bounds = Path.getBounds(segments, closed, style, matrix, padding),
7667
join = style.getStrokeJoin(),
7668
cap = style.getStrokeCap(),
7669
miterLimit = radius * style.getMiterLimit();
7670
var joinBounds = new Rectangle(new Size(padding).multiply(2));
7671
7672
function add(point) {
7673
bounds = bounds.include(matrix
7674
? matrix._transformPoint(point, point) : point);
7675
}
7676
7677
function addRound(segment) {
7678
bounds = bounds.unite(joinBounds.setCenter(matrix
7679
? matrix._transformPoint(segment._point) : segment._point));
7680
}
7681
7682
function addJoin(segment, join) {
7683
var handleIn = segment._handleIn,
7684
handleOut = segment._handleOut
7685
if (join === 'round' || !handleIn.isZero() && !handleOut.isZero()
7686
&& handleIn.isColinear(handleOut)) {
7687
addRound(segment);
7688
} else {
7689
Path._addBevelJoin(segment, join, radius, miterLimit, add);
7690
}
7691
}
7692
7693
function addCap(segment, cap) {
7694
if (cap === 'round') {
7695
addRound(segment);
7696
} else {
7697
Path._addSquareCap(segment, cap, radius, add);
7698
}
7699
}
7700
7701
for (var i = 1; i < length; i++)
7702
addJoin(segments[i], join);
7703
if (closed) {
7704
addJoin(segments[0], join);
7705
} else if (length > 0) {
7706
addCap(segments[0], cap);
7707
addCap(segments[segments.length - 1], cap);
7708
}
7709
return bounds;
7710
},
7711
7712
_getPenPadding: function(radius, matrix) {
7713
if (!matrix)
7714
return [radius, radius];
7715
var mx = matrix.shiftless(),
7716
hor = mx.transform(new Point(radius, 0)),
7717
ver = mx.transform(new Point(0, radius)),
7718
phi = hor.getAngleInRadians(),
7719
a = hor.getLength(),
7720
b = ver.getLength();
7721
var sin = Math.sin(phi),
7722
cos = Math.cos(phi),
7723
tan = Math.tan(phi),
7724
tx = -Math.atan(b * tan / a),
7725
ty = Math.atan(b / (tan * a));
7726
return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
7727
Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
7728
},
7729
7730
_addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) {
7731
var curve2 = segment.getCurve(),
7732
curve1 = curve2.getPrevious(),
7733
point = curve2.getPointAt(0, true),
7734
normal1 = curve1.getNormalAt(1, true),
7735
normal2 = curve2.getNormalAt(0, true),
7736
step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
7737
normal1.setLength(step);
7738
normal2.setLength(step);
7739
if (area) {
7740
addPoint(point);
7741
addPoint(point.add(normal1));
7742
}
7743
if (join === 'miter') {
7744
var corner = new Line(
7745
point.add(normal1),
7746
new Point(-normal1.y, normal1.x), true
7747
).intersect(new Line(
7748
point.add(normal2),
7749
new Point(-normal2.y, normal2.x), true
7750
), true);
7751
if (corner && point.getDistance(corner) <= miterLimit) {
7752
addPoint(corner);
7753
if (!area)
7754
return;
7755
}
7756
}
7757
if (!area)
7758
addPoint(point.add(normal1));
7759
addPoint(point.add(normal2));
7760
},
7761
7762
_addSquareCap: function(segment, cap, radius, addPoint, area) {
7763
var point = segment._point,
7764
loc = segment.getLocation(),
7765
normal = loc.getNormal().normalize(radius);
7766
if (area) {
7767
addPoint(point.subtract(normal));
7768
addPoint(point.add(normal));
7769
}
7770
if (cap === 'square')
7771
point = point.add(normal.rotate(loc.getParameter() == 0 ? -90 : 90));
7772
addPoint(point.add(normal));
7773
addPoint(point.subtract(normal));
7774
},
7775
7776
getHandleBounds: function(segments, closed, style, matrix, strokePadding,
7777
joinPadding) {
7778
var coords = new Array(6),
7779
x1 = Infinity,
7780
x2 = -x1,
7781
y1 = x1,
7782
y2 = x2;
7783
for (var i = 0, l = segments.length; i < l; i++) {
7784
var segment = segments[i];
7785
segment._transformCoordinates(matrix, coords, false);
7786
for (var j = 0; j < 6; j += 2) {
7787
var padding = j == 0 ? joinPadding : strokePadding,
7788
paddingX = padding ? padding[0] : 0,
7789
paddingY = padding ? padding[1] : 0,
7790
x = coords[j],
7791
y = coords[j + 1],
7792
xn = x - paddingX,
7793
xx = x + paddingX,
7794
yn = y - paddingY,
7795
yx = y + paddingY;
7796
if (xn < x1) x1 = xn;
7797
if (xx > x2) x2 = xx;
7798
if (yn < y1) y1 = yn;
7799
if (yx > y2) y2 = yx;
7800
}
7801
}
7802
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
7803
},
7804
7805
getRoughBounds: function(segments, closed, style, matrix) {
7806
var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
7807
joinRadius = strokeRadius;
7808
if (strokeRadius > 0) {
7809
if (style.getStrokeJoin() === 'miter')
7810
joinRadius = strokeRadius * style.getMiterLimit();
7811
if (style.getStrokeCap() === 'square')
7812
joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
7813
}
7814
return Path.getHandleBounds(segments, closed, style, matrix,
7815
Path._getPenPadding(strokeRadius, matrix),
7816
Path._getPenPadding(joinRadius, matrix));
7817
}
7818
}});
7819
7820
Path.inject({ statics: new function() {
7821
7822
var kappa = 0.5522847498307936,
7823
ellipseSegments = [
7824
new Segment([-1, 0], [0, kappa ], [0, -kappa]),
7825
new Segment([0, -1], [-kappa, 0], [kappa, 0 ]),
7826
new Segment([1, 0], [0, -kappa], [0, kappa ]),
7827
new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
7828
];
7829
7830
function createPath(segments, closed, args) {
7831
var props = Base.getNamed(args),
7832
path = new Path(props && props.insert === false && Item.NO_INSERT);
7833
path._add(segments);
7834
path._closed = closed;
7835
return path.set(props);
7836
}
7837
7838
function createEllipse(center, radius, args) {
7839
var segments = new Array(4);
7840
for (var i = 0; i < 4; i++) {
7841
var segment = ellipseSegments[i];
7842
segments[i] = new Segment(
7843
segment._point.multiply(radius).add(center),
7844
segment._handleIn.multiply(radius),
7845
segment._handleOut.multiply(radius)
7846
);
7847
}
7848
return createPath(segments, true, args);
7849
}
7850
7851
return {
7852
Line: function() {
7853
return createPath([
7854
new Segment(Point.readNamed(arguments, 'from')),
7855
new Segment(Point.readNamed(arguments, 'to'))
7856
], false, arguments);
7857
},
7858
7859
Circle: function() {
7860
var center = Point.readNamed(arguments, 'center'),
7861
radius = Base.readNamed(arguments, 'radius');
7862
return createEllipse(center, new Size(radius), arguments);
7863
},
7864
7865
Rectangle: function() {
7866
var rect = Rectangle.readNamed(arguments, 'rectangle'),
7867
radius = Size.readNamed(arguments, 'radius', 0,
7868
{ readNull: true }),
7869
bl = rect.getBottomLeft(true),
7870
tl = rect.getTopLeft(true),
7871
tr = rect.getTopRight(true),
7872
br = rect.getBottomRight(true),
7873
segments;
7874
if (!radius || radius.isZero()) {
7875
segments = [
7876
new Segment(bl),
7877
new Segment(tl),
7878
new Segment(tr),
7879
new Segment(br)
7880
];
7881
} else {
7882
radius = Size.min(radius, rect.getSize(true).divide(2));
7883
var rx = radius.width,
7884
ry = radius.height,
7885
hx = rx * kappa,
7886
hy = ry * kappa;
7887
segments = [
7888
new Segment(bl.add(rx, 0), null, [-hx, 0]),
7889
new Segment(bl.subtract(0, ry), [0, hy]),
7890
new Segment(tl.add(0, ry), null, [0, -hy]),
7891
new Segment(tl.add(rx, 0), [-hx, 0], null),
7892
new Segment(tr.subtract(rx, 0), null, [hx, 0]),
7893
new Segment(tr.add(0, ry), [0, -hy], null),
7894
new Segment(br.subtract(0, ry), null, [0, hy]),
7895
new Segment(br.subtract(rx, 0), [hx, 0])
7896
];
7897
}
7898
return createPath(segments, true, arguments);
7899
},
7900
7901
RoundRectangle: '#Rectangle',
7902
7903
Ellipse: function() {
7904
var ellipse = Shape._readEllipse(arguments);
7905
return createEllipse(ellipse.center, ellipse.radius, arguments);
7906
},
7907
7908
Oval: '#Ellipse',
7909
7910
Arc: function() {
7911
var from = Point.readNamed(arguments, 'from'),
7912
through = Point.readNamed(arguments, 'through'),
7913
to = Point.readNamed(arguments, 'to'),
7914
props = Base.getNamed(arguments),
7915
path = new Path(props && props.insert === false
7916
&& Item.NO_INSERT);
7917
path.moveTo(from);
7918
path.arcTo(through, to);
7919
return path.set(props);
7920
},
7921
7922
RegularPolygon: function() {
7923
var center = Point.readNamed(arguments, 'center'),
7924
sides = Base.readNamed(arguments, 'sides'),
7925
radius = Base.readNamed(arguments, 'radius'),
7926
step = 360 / sides,
7927
three = !(sides % 3),
7928
vector = new Point(0, three ? -radius : radius),
7929
offset = three ? -1 : 0.5,
7930
segments = new Array(sides);
7931
for (var i = 0; i < sides; i++)
7932
segments[i] = new Segment(center.add(
7933
vector.rotate((i + offset) * step)));
7934
return createPath(segments, true, arguments);
7935
},
7936
7937
Star: function() {
7938
var center = Point.readNamed(arguments, 'center'),
7939
points = Base.readNamed(arguments, 'points') * 2,
7940
radius1 = Base.readNamed(arguments, 'radius1'),
7941
radius2 = Base.readNamed(arguments, 'radius2'),
7942
step = 360 / points,
7943
vector = new Point(0, -1),
7944
segments = new Array(points);
7945
for (var i = 0; i < points; i++)
7946
segments[i] = new Segment(center.add(vector.rotate(step * i)
7947
.multiply(i % 2 ? radius2 : radius1)));
7948
return createPath(segments, true, arguments);
7949
}
7950
};
7951
}});
7952
7953
var CompoundPath = PathItem.extend({
7954
_class: 'CompoundPath',
7955
_serializeFields: {
7956
children: []
7957
},
7958
7959
initialize: function CompoundPath(arg) {
7960
this._children = [];
7961
this._namedChildren = {};
7962
if (!this._initialize(arg)) {
7963
if (typeof arg === 'string') {
7964
this.setPathData(arg);
7965
} else {
7966
this.addChildren(Array.isArray(arg) ? arg : arguments);
7967
}
7968
}
7969
},
7970
7971
insertChildren: function insertChildren(index, items, _preserve) {
7972
items = insertChildren.base.call(this, index, items, _preserve, Path);
7973
for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
7974
var item = items[i];
7975
if (item._clockwise === undefined)
7976
item.setClockwise(item._index === 0);
7977
}
7978
return items;
7979
},
7980
7981
reverse: function() {
7982
var children = this._children;
7983
for (var i = 0, l = children.length; i < l; i++)
7984
children[i].reverse();
7985
},
7986
7987
smooth: function() {
7988
for (var i = 0, l = this._children.length; i < l; i++)
7989
this._children[i].smooth();
7990
},
7991
7992
isClockwise: function() {
7993
var child = this.getFirstChild();
7994
return child && child.isClockwise();
7995
},
7996
7997
setClockwise: function(clockwise) {
7998
if (this.isClockwise() !== !!clockwise)
7999
this.reverse();
8000
},
8001
8002
getFirstSegment: function() {
8003
var first = this.getFirstChild();
8004
return first && first.getFirstSegment();
8005
},
8006
8007
getLastSegment: function() {
8008
var last = this.getLastChild();
8009
return last && last.getLastSegment();
8010
},
8011
8012
getCurves: function() {
8013
var children = this._children,
8014
curves = [];
8015
for (var i = 0, l = children.length; i < l; i++)
8016
curves.push.apply(curves, children[i].getCurves());
8017
return curves;
8018
},
8019
8020
getFirstCurve: function() {
8021
var first = this.getFirstChild();
8022
return first && first.getFirstCurve();
8023
},
8024
8025
getLastCurve: function() {
8026
var last = this.getLastChild();
8027
return last && last.getFirstCurve();
8028
},
8029
8030
getArea: function() {
8031
var children = this._children,
8032
area = 0;
8033
for (var i = 0, l = children.length; i < l; i++)
8034
area += children[i].getArea();
8035
return area;
8036
}
8037
}, {
8038
beans: true,
8039
8040
getPathData: function(_precision) {
8041
var children = this._children,
8042
paths = [];
8043
for (var i = 0, l = children.length; i < l; i++)
8044
paths.push(children[i].getPathData(_precision));
8045
return paths.join(' ');
8046
}
8047
}, {
8048
_getChildHitTestOptions: function(options) {
8049
return options.type === 'path'
8050
? options
8051
: new Base(options, { fill: false });
8052
},
8053
8054
_draw: function(ctx, param) {
8055
var children = this._children;
8056
if (children.length === 0)
8057
return;
8058
8059
if (this._currentPath) {
8060
ctx.currentPath = this._currentPath;
8061
} else {
8062
param = param.extend({ dontStart: true, dontFinish: true });
8063
ctx.beginPath();
8064
for (var i = 0, l = children.length; i < l; i++)
8065
children[i].draw(ctx, param);
8066
this._currentPath = ctx.currentPath;
8067
}
8068
8069
if (!param.clip) {
8070
this._setStyles(ctx);
8071
var style = this._style;
8072
if (style.hasFill()) {
8073
ctx.fill(style.getWindingRule());
8074
ctx.shadowColor = 'rgba(0,0,0,0)';
8075
}
8076
if (style.hasStroke())
8077
ctx.stroke();
8078
}
8079
},
8080
8081
_drawSelected: function(ctx, matrix) {
8082
var children = this._children;
8083
for (var i = 0, l = children.length; i < l; i++) {
8084
var child = children[i],
8085
mx = child._matrix;
8086
child._drawSelected(ctx, mx.isIdentity() ? matrix
8087
: matrix.clone().concatenate(child._matrix));
8088
}
8089
}
8090
}, new function() {
8091
function getCurrentPath(that, check) {
8092
var children = that._children;
8093
if (check && children.length === 0)
8094
throw new Error('Use a moveTo() command first');
8095
return children[children.length - 1];
8096
}
8097
8098
var fields = {
8099
moveTo: function() {
8100
var current = getCurrentPath(this),
8101
path = current && current.isEmpty() ? current : new Path();
8102
if (path !== current)
8103
this.addChild(path);
8104
path.moveTo.apply(path, arguments);
8105
},
8106
8107
moveBy: function() {
8108
var current = getCurrentPath(this, true),
8109
last = current && current.getLastSegment(),
8110
point = Point.read(arguments);
8111
this.moveTo(last ? point.add(last._point) : point);
8112
},
8113
8114
closePath: function(join) {
8115
getCurrentPath(this, true).closePath(join);
8116
}
8117
};
8118
8119
Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
8120
'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
8121
function(key) {
8122
fields[key] = function() {
8123
var path = getCurrentPath(this, true);
8124
path[key].apply(path, arguments);
8125
};
8126
}
8127
);
8128
8129
return fields;
8130
});
8131
8132
PathItem.inject(new function() {
8133
function computeBoolean(path1, path2, operator, subtract) {
8134
function preparePath(path) {
8135
return path.clone(false).reduce().reorient().transform(null, true);
8136
}
8137
8138
var _path1 = preparePath(path1),
8139
_path2 = path2 && path1 !== path2 && preparePath(path2);
8140
if (!_path1.isClockwise())
8141
_path1.reverse();
8142
if (_path2 && !(subtract ^ _path2.isClockwise()))
8143
_path2.reverse();
8144
splitPath(_path1.getIntersections(_path2, true));
8145
8146
var chain = [],
8147
windings = [],
8148
lengths = [],
8149
segments = [],
8150
monoCurves = [];
8151
8152
function collect(paths) {
8153
for (var i = 0, l = paths.length; i < l; i++) {
8154
var path = paths[i];
8155
segments.push.apply(segments, path._segments);
8156
monoCurves.push.apply(monoCurves, path._getMonoCurves());
8157
}
8158
}
8159
8160
collect(_path1._children || [_path1]);
8161
if (_path2)
8162
collect(_path2._children || [_path2]);
8163
segments.sort(function(a, b) {
8164
var _a = a._intersection,
8165
_b = b._intersection;
8166
return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1;
8167
});
8168
for (var i = 0, l = segments.length; i < l; i++) {
8169
var segment = segments[i];
8170
if (segment._winding != null)
8171
continue;
8172
chain.length = windings.length = lengths.length = 0;
8173
var totalLength = 0,
8174
startSeg = segment;
8175
do {
8176
chain.push(segment);
8177
lengths.push(totalLength += segment.getCurve().getLength());
8178
segment = segment.getNext();
8179
} while (segment && !segment._intersection && segment !== startSeg);
8180
for (var j = 0; j < 3; j++) {
8181
var length = totalLength * Math.random(),
8182
amount = lengths.length,
8183
k = 0;
8184
do {
8185
if (lengths[k] >= length) {
8186
if (k > 0)
8187
length -= lengths[k - 1];
8188
break;
8189
}
8190
} while (++k < amount);
8191
var curve = chain[k].getCurve(),
8192
point = curve.getPointAt(length),
8193
hor = curve.isHorizontal(),
8194
path = curve._path;
8195
if (path._parent instanceof CompoundPath)
8196
path = path._parent;
8197
windings[j] = subtract && _path2
8198
&& (path === _path1 && _path2._getWinding(point, hor)
8199
|| path === _path2 && !_path1._getWinding(point, hor))
8200
? 0
8201
: getWinding(point, monoCurves, hor);
8202
}
8203
windings.sort();
8204
var winding = windings[1];
8205
for (var j = chain.length - 1; j >= 0; j--)
8206
chain[j]._winding = winding;
8207
}
8208
var result = new CompoundPath();
8209
result.addChildren(tracePaths(segments, operator), true);
8210
_path1.remove();
8211
if (_path2)
8212
_path2.remove();
8213
return result.reduce();
8214
}
8215
8216
function splitPath(intersections) {
8217
var TOLERANCE = 0.00001,
8218
linearSegments;
8219
8220
function resetLinear() {
8221
for (var i = 0, l = linearSegments.length; i < l; i++) {
8222
var segment = linearSegments[i];
8223
segment._handleOut.set(0, 0);
8224
segment._handleIn.set(0, 0);
8225
}
8226
}
8227
8228
for (var i = intersections.length - 1, curve, prevLoc; i >= 0; i--) {
8229
var loc = intersections[i],
8230
t = loc._parameter;
8231
if (prevLoc && prevLoc._curve === loc._curve
8232
&& prevLoc._parameter > 0) {
8233
t /= prevLoc._parameter;
8234
} else {
8235
if (linearSegments)
8236
resetLinear();
8237
curve = loc._curve;
8238
linearSegments = curve.isLinear() && [];
8239
}
8240
var newCurve,
8241
segment;
8242
if (newCurve = curve.divide(t, true, true)) {
8243
segment = newCurve._segment1;
8244
curve = newCurve.getPrevious();
8245
} else {
8246
segment = t < TOLERANCE
8247
? curve._segment1
8248
: t > 1 - TOLERANCE
8249
? curve._segment2
8250
: curve.getPartLength(0, t) < curve.getPartLength(t, 1)
8251
? curve._segment1
8252
: curve._segment2;
8253
}
8254
segment._intersection = loc.getIntersection();
8255
loc._segment = segment;
8256
if (linearSegments)
8257
linearSegments.push(segment);
8258
prevLoc = loc;
8259
}
8260
if (linearSegments)
8261
resetLinear();
8262
}
8263
8264
function getWinding(point, curves, horizontal, testContains) {
8265
var TOLERANCE = 0.00001,
8266
x = point.x,
8267
y = point.y,
8268
windLeft = 0,
8269
windRight = 0,
8270
roots = [],
8271
abs = Math.abs,
8272
MAX = 1 - TOLERANCE;
8273
if (horizontal) {
8274
var yTop = -Infinity,
8275
yBottom = Infinity,
8276
yBefore = y - TOLERANCE,
8277
yAfter = y + TOLERANCE;
8278
for (var i = 0, l = curves.length; i < l; i++) {
8279
var values = curves[i].values;
8280
if (Curve.solveCubic(values, 0, x, roots, 0, 1) > 0) {
8281
for (var j = roots.length - 1; j >= 0; j--) {
8282
var y0 = Curve.evaluate(values, roots[j], 0).y;
8283
if (y0 < yBefore && y0 > yTop) {
8284
yTop = y0;
8285
} else if (y0 > yAfter && y0 < yBottom) {
8286
yBottom = y0;
8287
}
8288
}
8289
}
8290
}
8291
yTop = (yTop + y) / 2;
8292
yBottom = (yBottom + y) / 2;
8293
if (yTop > -Infinity)
8294
windLeft = getWinding(new Point(x, yTop), curves);
8295
if (yBottom < Infinity)
8296
windRight = getWinding(new Point(x, yBottom), curves);
8297
} else {
8298
var xBefore = x - TOLERANCE,
8299
xAfter = x + TOLERANCE;
8300
for (var i = 0, l = curves.length; i < l; i++) {
8301
var curve = curves[i],
8302
values = curve.values,
8303
winding = curve.winding,
8304
next = curve.next;
8305
if (winding && (winding === 1
8306
&& y >= values[1] && y <= values[7]
8307
|| y >= values[7] && y <= values[1])
8308
&& Curve.solveCubic(values, 1, y, roots, 0,
8309
!next.winding && next.values[1] === y ? 1 : MAX) === 1){
8310
var t = roots[0],
8311
x0 = Curve.evaluate(values, t, 0).x,
8312
slope = Curve.evaluate(values, t, 1).y;
8313
if (abs(slope) < TOLERANCE && !Curve.isLinear(values)
8314
|| t < TOLERANCE && slope * Curve.evaluate(
8315
curve.previous.values, t, 1).y < 0) {
8316
if (testContains && x0 >= xBefore && x0 <= xAfter) {
8317
++windLeft;
8318
++windRight;
8319
}
8320
} else if (x0 <= xBefore) {
8321
windLeft += winding;
8322
} else if (x0 >= xAfter) {
8323
windRight += winding;
8324
}
8325
}
8326
}
8327
}
8328
return Math.max(abs(windLeft), abs(windRight));
8329
}
8330
8331
function tracePaths(segments, operator, selfOp) {
8332
operator = operator || function() {
8333
return true;
8334
};
8335
var paths = [],
8336
ZERO = 1e-3,
8337
ONE = 1 - 1e-3;
8338
for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) {
8339
seg = startSeg = segments[i];
8340
if (seg._visited || !operator(seg._winding))
8341
continue;
8342
var path = new Path(Item.NO_INSERT),
8343
inter = seg._intersection,
8344
startInterSeg = inter && inter._segment,
8345
added = false,
8346
dir = 1;
8347
do {
8348
var handleIn = dir > 0 ? seg._handleIn : seg._handleOut,
8349
handleOut = dir > 0 ? seg._handleOut : seg._handleIn,
8350
interSeg;
8351
if (added && (!operator(seg._winding) || selfOp)
8352
&& (inter = seg._intersection)
8353
&& (interSeg = inter._segment)
8354
&& interSeg !== startSeg) {
8355
if (selfOp) {
8356
seg._visited = interSeg._visited;
8357
seg = interSeg;
8358
dir = 1;
8359
} else {
8360
var c1 = seg.getCurve();
8361
if (dir > 0)
8362
c1 = c1.getPrevious();
8363
var t1 = c1.getTangentAt(dir < 1 ? ZERO : ONE, true),
8364
c4 = interSeg.getCurve(),
8365
c3 = c4.getPrevious(),
8366
t3 = c3.getTangentAt(ONE, true),
8367
t4 = c4.getTangentAt(ZERO, true),
8368
w3 = t1.cross(t3),
8369
w4 = t1.cross(t4);
8370
if (w3 * w4 !== 0) {
8371
var curve = w3 < w4 ? c3 : c4,
8372
nextCurve = operator(curve._segment1._winding)
8373
? curve
8374
: w3 < w4 ? c4 : c3,
8375
nextSeg = nextCurve._segment1;
8376
dir = nextCurve === c3 ? -1 : 1;
8377
if (nextSeg._visited && seg._path !== nextSeg._path
8378
|| !operator(nextSeg._winding)) {
8379
dir = 1;
8380
} else {
8381
seg._visited = interSeg._visited;
8382
seg = interSeg;
8383
if (nextSeg._visited)
8384
dir = 1;
8385
}
8386
} else {
8387
dir = 1;
8388
}
8389
}
8390
handleOut = dir > 0 ? seg._handleOut : seg._handleIn;
8391
}
8392
path.add(new Segment(seg._point, added && handleIn, handleOut));
8393
added = true;
8394
seg._visited = true;
8395
seg = dir > 0 ? seg.getNext() : seg. getPrevious();
8396
} while (seg && !seg._visited
8397
&& seg !== startSeg && seg !== startInterSeg
8398
&& (seg._intersection || operator(seg._winding)));
8399
if (seg && (seg === startSeg || seg === startInterSeg)) {
8400
path.firstSegment.setHandleIn((seg === startInterSeg
8401
? startInterSeg : seg)._handleIn);
8402
path.setClosed(true);
8403
} else {
8404
path.lastSegment._handleOut.set(0, 0);
8405
}
8406
if (path._segments.length >
8407
(path._closed ? path.isPolygon() ? 2 : 0 : 1))
8408
paths.push(path);
8409
}
8410
return paths;
8411
}
8412
8413
return {
8414
_getWinding: function(point, horizontal, testContains) {
8415
return getWinding(point, this._getMonoCurves(),
8416
horizontal, testContains);
8417
},
8418
8419
unite: function(path) {
8420
return computeBoolean(this, path, function(w) {
8421
return w === 1 || w === 0;
8422
}, false);
8423
},
8424
8425
intersect: function(path) {
8426
return computeBoolean(this, path, function(w) {
8427
return w === 2;
8428
}, false);
8429
},
8430
8431
subtract: function(path) {
8432
return computeBoolean(this, path, function(w) {
8433
return w === 1;
8434
}, true);
8435
},
8436
8437
exclude: function(path) {
8438
return new Group([this.subtract(path), path.subtract(this)]);
8439
},
8440
8441
divide: function(path) {
8442
return new Group([this.subtract(path), this.intersect(path)]);
8443
}
8444
};
8445
});
8446
8447
Path.inject({
8448
_getMonoCurves: function() {
8449
var monoCurves = this._monoCurves,
8450
prevCurve;
8451
8452
function insertCurve(v) {
8453
var y0 = v[1],
8454
y1 = v[7],
8455
curve = {
8456
values: v,
8457
winding: y0 === y1
8458
? 0
8459
: y0 > y1
8460
? -1
8461
: 1,
8462
previous: prevCurve,
8463
next: null
8464
};
8465
if (prevCurve)
8466
prevCurve.next = curve;
8467
monoCurves.push(curve);
8468
prevCurve = curve;
8469
}
8470
8471
function handleCurve(v) {
8472
if (Curve.getLength(v) === 0)
8473
return;
8474
var y0 = v[1],
8475
y1 = v[3],
8476
y2 = v[5],
8477
y3 = v[7];
8478
if (Curve.isLinear(v)) {
8479
insertCurve(v);
8480
} else {
8481
var a = 3 * (y1 - y2) - y0 + y3,
8482
b = 2 * (y0 + y2) - 4 * y1,
8483
c = y1 - y0,
8484
TOLERANCE = 0.00001,
8485
roots = [];
8486
var count = Numerical.solveQuadratic(a, b, c, roots, TOLERANCE,
8487
1 - TOLERANCE);
8488
if (count === 0) {
8489
insertCurve(v);
8490
} else {
8491
roots.sort();
8492
var t = roots[0],
8493
parts = Curve.subdivide(v, t);
8494
insertCurve(parts[0]);
8495
if (count > 1) {
8496
t = (roots[1] - t) / (1 - t);
8497
parts = Curve.subdivide(parts[1], t);
8498
insertCurve(parts[0]);
8499
}
8500
insertCurve(parts[1]);
8501
}
8502
}
8503
}
8504
8505
if (!monoCurves) {
8506
monoCurves = this._monoCurves = [];
8507
var curves = this.getCurves(),
8508
segments = this._segments;
8509
for (var i = 0, l = curves.length; i < l; i++)
8510
handleCurve(curves[i].getValues());
8511
if (!this._closed && segments.length > 1) {
8512
var p1 = segments[segments.length - 1]._point,
8513
p2 = segments[0]._point,
8514
p1x = p1._x, p1y = p1._y,
8515
p2x = p2._x, p2y = p2._y;
8516
handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
8517
}
8518
if (monoCurves.length > 0) {
8519
var first = monoCurves[0],
8520
last = monoCurves[monoCurves.length - 1];
8521
first.previous = last;
8522
last.next = first;
8523
}
8524
}
8525
return monoCurves;
8526
},
8527
8528
getInteriorPoint: function() {
8529
var bounds = this.getBounds(),
8530
point = bounds.getCenter(true);
8531
if (!this.contains(point)) {
8532
var curves = this._getMonoCurves(),
8533
roots = [],
8534
y = point.y,
8535
xIntercepts = [];
8536
for (var i = 0, l = curves.length; i < l; i++) {
8537
var values = curves[i].values;
8538
if ((curves[i].winding === 1
8539
&& y >= values[1] && y <= values[7]
8540
|| y >= values[7] && y <= values[1])
8541
&& Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
8542
for (var j = roots.length - 1; j >= 0; j--)
8543
xIntercepts.push(Curve.evaluate(values, roots[j], 0).x);
8544
}
8545
if (xIntercepts.length > 1)
8546
break;
8547
}
8548
point.x = (xIntercepts[0] + xIntercepts[1]) / 2;
8549
}
8550
return point;
8551
},
8552
8553
reorient: function() {
8554
this.setClockwise(true);
8555
return this;
8556
}
8557
});
8558
8559
CompoundPath.inject({
8560
_getMonoCurves: function() {
8561
var children = this._children,
8562
monoCurves = [];
8563
for (var i = 0, l = children.length; i < l; i++)
8564
monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
8565
return monoCurves;
8566
},
8567
8568
reorient: function() {
8569
var children = this.removeChildren().sort(function(a, b) {
8570
return b.getBounds().getArea() - a.getBounds().getArea();
8571
});
8572
this.addChildren(children);
8573
var clockwise = children[0].isClockwise();
8574
for (var i = 1, l = children.length; i < l; i++) {
8575
var point = children[i].getInteriorPoint(),
8576
counters = 0;
8577
for (var j = i - 1; j >= 0; j--) {
8578
if (children[j].contains(point))
8579
counters++;
8580
}
8581
children[i].setClockwise(counters % 2 === 0 && clockwise);
8582
}
8583
return this;
8584
}
8585
});
8586
8587
var PathFlattener = Base.extend({
8588
initialize: function(path) {
8589
this.curves = [];
8590
this.parts = [];
8591
this.length = 0;
8592
this.index = 0;
8593
8594
var segments = path._segments,
8595
segment1 = segments[0],
8596
segment2,
8597
that = this;
8598
8599
function addCurve(segment1, segment2) {
8600
var curve = Curve.getValues(segment1, segment2);
8601
that.curves.push(curve);
8602
that._computeParts(curve, segment1._index, 0, 1);
8603
}
8604
8605
for (var i = 1, l = segments.length; i < l; i++) {
8606
segment2 = segments[i];
8607
addCurve(segment1, segment2);
8608
segment1 = segment2;
8609
}
8610
if (path._closed)
8611
addCurve(segment2, segments[0]);
8612
},
8613
8614
_computeParts: function(curve, index, minT, maxT) {
8615
if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough(curve, 0.25)) {
8616
var curves = Curve.subdivide(curve);
8617
var halfT = (minT + maxT) / 2;
8618
this._computeParts(curves[0], index, minT, halfT);
8619
this._computeParts(curves[1], index, halfT, maxT);
8620
} else {
8621
var x = curve[6] - curve[0],
8622
y = curve[7] - curve[1],
8623
dist = Math.sqrt(x * x + y * y);
8624
if (dist > 0.00001) {
8625
this.length += dist;
8626
this.parts.push({
8627
offset: this.length,
8628
value: maxT,
8629
index: index
8630
});
8631
}
8632
}
8633
},
8634
8635
getParameterAt: function(offset) {
8636
var i, j = this.index;
8637
for (;;) {
8638
i = j;
8639
if (j == 0 || this.parts[--j].offset < offset)
8640
break;
8641
}
8642
for (var l = this.parts.length; i < l; i++) {
8643
var part = this.parts[i];
8644
if (part.offset >= offset) {
8645
this.index = i;
8646
var prev = this.parts[i - 1];
8647
var prevVal = prev && prev.index == part.index ? prev.value : 0,
8648
prevLen = prev ? prev.offset : 0;
8649
return {
8650
value: prevVal + (part.value - prevVal)
8651
* (offset - prevLen) / (part.offset - prevLen),
8652
index: part.index
8653
};
8654
}
8655
}
8656
var part = this.parts[this.parts.length - 1];
8657
return {
8658
value: 1,
8659
index: part.index
8660
};
8661
},
8662
8663
evaluate: function(offset, type) {
8664
var param = this.getParameterAt(offset);
8665
return Curve.evaluate(this.curves[param.index], param.value, type);
8666
},
8667
8668
drawPart: function(ctx, from, to) {
8669
from = this.getParameterAt(from);
8670
to = this.getParameterAt(to);
8671
for (var i = from.index; i <= to.index; i++) {
8672
var curve = Curve.getPart(this.curves[i],
8673
i == from.index ? from.value : 0,
8674
i == to.index ? to.value : 1);
8675
if (i == from.index)
8676
ctx.moveTo(curve[0], curve[1]);
8677
ctx.bezierCurveTo.apply(ctx, curve.slice(2));
8678
}
8679
}
8680
});
8681
8682
var PathFitter = Base.extend({
8683
initialize: function(path, error) {
8684
this.points = [];
8685
var segments = path._segments,
8686
prev;
8687
for (var i = 0, l = segments.length; i < l; i++) {
8688
var point = segments[i].point.clone();
8689
if (!prev || !prev.equals(point)) {
8690
this.points.push(point);
8691
prev = point;
8692
}
8693
}
8694
this.error = error;
8695
},
8696
8697
fit: function() {
8698
var points = this.points,
8699
length = points.length;
8700
this.segments = length > 0 ? [new Segment(points[0])] : [];
8701
if (length > 1)
8702
this.fitCubic(0, length - 1,
8703
points[1].subtract(points[0]).normalize(),
8704
points[length - 2].subtract(points[length - 1]).normalize());
8705
return this.segments;
8706
},
8707
8708
fitCubic: function(first, last, tan1, tan2) {
8709
if (last - first == 1) {
8710
var pt1 = this.points[first],
8711
pt2 = this.points[last],
8712
dist = pt1.getDistance(pt2) / 3;
8713
this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
8714
pt2.add(tan2.normalize(dist)), pt2]);
8715
return;
8716
}
8717
var uPrime = this.chordLengthParameterize(first, last),
8718
maxError = Math.max(this.error, this.error * this.error),
8719
split;
8720
for (var i = 0; i <= 4; i++) {
8721
var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
8722
var max = this.findMaxError(first, last, curve, uPrime);
8723
if (max.error < this.error) {
8724
this.addCurve(curve);
8725
return;
8726
}
8727
split = max.index;
8728
if (max.error >= maxError)
8729
break;
8730
this.reparameterize(first, last, uPrime, curve);
8731
maxError = max.error;
8732
}
8733
var V1 = this.points[split - 1].subtract(this.points[split]),
8734
V2 = this.points[split].subtract(this.points[split + 1]),
8735
tanCenter = V1.add(V2).divide(2).normalize();
8736
this.fitCubic(first, split, tan1, tanCenter);
8737
this.fitCubic(split, last, tanCenter.negate(), tan2);
8738
},
8739
8740
addCurve: function(curve) {
8741
var prev = this.segments[this.segments.length - 1];
8742
prev.setHandleOut(curve[1].subtract(curve[0]));
8743
this.segments.push(
8744
new Segment(curve[3], curve[2].subtract(curve[3])));
8745
},
8746
8747
generateBezier: function(first, last, uPrime, tan1, tan2) {
8748
var epsilon = 1e-11,
8749
pt1 = this.points[first],
8750
pt2 = this.points[last],
8751
C = [[0, 0], [0, 0]],
8752
X = [0, 0];
8753
8754
for (var i = 0, l = last - first + 1; i < l; i++) {
8755
var u = uPrime[i],
8756
t = 1 - u,
8757
b = 3 * u * t,
8758
b0 = t * t * t,
8759
b1 = b * t,
8760
b2 = b * u,
8761
b3 = u * u * u,
8762
a1 = tan1.normalize(b1),
8763
a2 = tan2.normalize(b2),
8764
tmp = this.points[first + i]
8765
.subtract(pt1.multiply(b0 + b1))
8766
.subtract(pt2.multiply(b2 + b3));
8767
C[0][0] += a1.dot(a1);
8768
C[0][1] += a1.dot(a2);
8769
C[1][0] = C[0][1];
8770
C[1][1] += a2.dot(a2);
8771
X[0] += a1.dot(tmp);
8772
X[1] += a2.dot(tmp);
8773
}
8774
8775
var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
8776
alpha1, alpha2;
8777
if (Math.abs(detC0C1) > epsilon) {
8778
var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
8779
detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
8780
alpha1 = detXC1 / detC0C1;
8781
alpha2 = detC0X / detC0C1;
8782
} else {
8783
var c0 = C[0][0] + C[0][1],
8784
c1 = C[1][0] + C[1][1];
8785
if (Math.abs(c0) > epsilon) {
8786
alpha1 = alpha2 = X[0] / c0;
8787
} else if (Math.abs(c1) > epsilon) {
8788
alpha1 = alpha2 = X[1] / c1;
8789
} else {
8790
alpha1 = alpha2 = 0;
8791
}
8792
}
8793
8794
var segLength = pt2.getDistance(pt1);
8795
epsilon *= segLength;
8796
if (alpha1 < epsilon || alpha2 < epsilon) {
8797
alpha1 = alpha2 = segLength / 3;
8798
}
8799
8800
return [pt1, pt1.add(tan1.normalize(alpha1)),
8801
pt2.add(tan2.normalize(alpha2)), pt2];
8802
},
8803
8804
reparameterize: function(first, last, u, curve) {
8805
for (var i = first; i <= last; i++) {
8806
u[i - first] = this.findRoot(curve, this.points[i], u[i - first]);
8807
}
8808
},
8809
8810
findRoot: function(curve, point, u) {
8811
var curve1 = [],
8812
curve2 = [];
8813
for (var i = 0; i <= 2; i++) {
8814
curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3);
8815
}
8816
for (var i = 0; i <= 1; i++) {
8817
curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2);
8818
}
8819
var pt = this.evaluate(3, curve, u),
8820
pt1 = this.evaluate(2, curve1, u),
8821
pt2 = this.evaluate(1, curve2, u),
8822
diff = pt.subtract(point),
8823
df = pt1.dot(pt1) + diff.dot(pt2);
8824
if (Math.abs(df) < 0.00001)
8825
return u;
8826
return u - diff.dot(pt1) / df;
8827
},
8828
8829
evaluate: function(degree, curve, t) {
8830
var tmp = curve.slice();
8831
for (var i = 1; i <= degree; i++) {
8832
for (var j = 0; j <= degree - i; j++) {
8833
tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t));
8834
}
8835
}
8836
return tmp[0];
8837
},
8838
8839
chordLengthParameterize: function(first, last) {
8840
var u = [0];
8841
for (var i = first + 1; i <= last; i++) {
8842
u[i - first] = u[i - first - 1]
8843
+ this.points[i].getDistance(this.points[i - 1]);
8844
}
8845
for (var i = 1, m = last - first; i <= m; i++) {
8846
u[i] /= u[m];
8847
}
8848
return u;
8849
},
8850
8851
findMaxError: function(first, last, curve, u) {
8852
var index = Math.floor((last - first + 1) / 2),
8853
maxDist = 0;
8854
for (var i = first + 1; i < last; i++) {
8855
var P = this.evaluate(3, curve, u[i - first]);
8856
var v = P.subtract(this.points[i]);
8857
var dist = v.x * v.x + v.y * v.y;
8858
if (dist >= maxDist) {
8859
maxDist = dist;
8860
index = i;
8861
}
8862
}
8863
return {
8864
error: maxDist,
8865
index: index
8866
};
8867
}
8868
});
8869
8870
var TextItem = Item.extend({
8871
_class: 'TextItem',
8872
_boundsSelected: true,
8873
_applyMatrix: false,
8874
_canApplyMatrix: false,
8875
_serializeFields: {
8876
content: null
8877
},
8878
_boundsGetter: 'getBounds',
8879
8880
initialize: function TextItem(arg) {
8881
this._content = '';
8882
this._lines = [];
8883
var hasProps = arg && Base.isPlainObject(arg)
8884
&& arg.x === undefined && arg.y === undefined;
8885
this._initialize(hasProps && arg, !hasProps && Point.read(arguments));
8886
},
8887
8888
_equals: function(item) {
8889
return this._content === item._content;
8890
},
8891
8892
_clone: function _clone(copy) {
8893
copy.setContent(this._content);
8894
return _clone.base.call(this, copy);
8895
},
8896
8897
getContent: function() {
8898
return this._content;
8899
},
8900
8901
setContent: function(content) {
8902
this._content = '' + content;
8903
this._lines = this._content.split(/\r\n|\n|\r/mg);
8904
this._changed(265);
8905
},
8906
8907
isEmpty: function() {
8908
return !this._content;
8909
},
8910
8911
getCharacterStyle: '#getStyle',
8912
setCharacterStyle: '#setStyle',
8913
8914
getParagraphStyle: '#getStyle',
8915
setParagraphStyle: '#setStyle'
8916
});
8917
8918
var PointText = TextItem.extend({
8919
_class: 'PointText',
8920
8921
initialize: function PointText() {
8922
TextItem.apply(this, arguments);
8923
},
8924
8925
clone: function(insert) {
8926
return this._clone(new PointText(Item.NO_INSERT), insert);
8927
},
8928
8929
getPoint: function() {
8930
var point = this._matrix.getTranslation();
8931
return new LinkedPoint(point.x, point.y, this, 'setPoint');
8932
},
8933
8934
setPoint: function() {
8935
var point = Point.read(arguments);
8936
this.translate(point.subtract(this._matrix.getTranslation()));
8937
},
8938
8939
_draw: function(ctx) {
8940
if (!this._content)
8941
return;
8942
this._setStyles(ctx);
8943
var style = this._style,
8944
lines = this._lines,
8945
leading = style.getLeading(),
8946
shadowColor = ctx.shadowColor;
8947
ctx.font = style.getFontStyle();
8948
ctx.textAlign = style.getJustification();
8949
for (var i = 0, l = lines.length; i < l; i++) {
8950
ctx.shadowColor = shadowColor;
8951
var line = lines[i];
8952
if (style.hasFill()) {
8953
ctx.fillText(line, 0, 0);
8954
ctx.shadowColor = 'rgba(0,0,0,0)';
8955
}
8956
if (style.hasStroke())
8957
ctx.strokeText(line, 0, 0);
8958
ctx.translate(0, leading);
8959
}
8960
},
8961
8962
_getBounds: function(getter, matrix) {
8963
var style = this._style,
8964
lines = this._lines,
8965
numLines = lines.length,
8966
justification = style.getJustification(),
8967
leading = style.getLeading(),
8968
width = this.getView().getTextWidth(style.getFontStyle(), lines),
8969
x = 0;
8970
if (justification !== 'left')
8971
x -= width / (justification === 'center' ? 2: 1);
8972
var bounds = new Rectangle(x,
8973
numLines ? - 0.75 * leading : 0,
8974
width, numLines * leading);
8975
return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
8976
}
8977
});
8978
8979
var Color = Base.extend(new function() {
8980
8981
var types = {
8982
gray: ['gray'],
8983
rgb: ['red', 'green', 'blue'],
8984
hsb: ['hue', 'saturation', 'brightness'],
8985
hsl: ['hue', 'saturation', 'lightness'],
8986
gradient: ['gradient', 'origin', 'destination', 'highlight']
8987
};
8988
8989
var componentParsers = {},
8990
colorCache = {},
8991
colorCtx;
8992
8993
function fromCSS(string) {
8994
var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
8995
components;
8996
if (match) {
8997
components = [0, 0, 0];
8998
for (var i = 0; i < 3; i++) {
8999
var value = match[i + 1];
9000
components[i] = parseInt(value.length == 1
9001
? value + value : value, 16) / 255;
9002
}
9003
} else if (match = string.match(/^rgba?\((.*)\)$/)) {
9004
components = match[1].split(',');
9005
for (var i = 0, l = components.length; i < l; i++) {
9006
var value = +components[i];
9007
components[i] = i < 3 ? value / 255 : value;
9008
}
9009
} else {
9010
var cached = colorCache[string];
9011
if (!cached) {
9012
if (!colorCtx) {
9013
colorCtx = CanvasProvider.getContext(1, 1);
9014
colorCtx.globalCompositeOperation = 'copy';
9015
}
9016
colorCtx.fillStyle = 'rgba(0,0,0,0)';
9017
colorCtx.fillStyle = string;
9018
colorCtx.fillRect(0, 0, 1, 1);
9019
var data = colorCtx.getImageData(0, 0, 1, 1).data;
9020
cached = colorCache[string] = [
9021
data[0] / 255,
9022
data[1] / 255,
9023
data[2] / 255
9024
];
9025
}
9026
components = cached.slice();
9027
}
9028
return components;
9029
}
9030
9031
var hsbIndices = [
9032
[0, 3, 1],
9033
[2, 0, 1],
9034
[1, 0, 3],
9035
[1, 2, 0],
9036
[3, 1, 0],
9037
[0, 1, 2]
9038
];
9039
9040
var converters = {
9041
'rgb-hsb': function(r, g, b) {
9042
var max = Math.max(r, g, b),
9043
min = Math.min(r, g, b),
9044
delta = max - min,
9045
h = delta === 0 ? 0
9046
: ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
9047
: max == g ? (b - r) / delta + 2
9048
: (r - g) / delta + 4) * 60;
9049
return [h, max === 0 ? 0 : delta / max, max];
9050
},
9051
9052
'hsb-rgb': function(h, s, b) {
9053
h = (((h / 60) % 6) + 6) % 6;
9054
var i = Math.floor(h),
9055
f = h - i,
9056
i = hsbIndices[i],
9057
v = [
9058
b,
9059
b * (1 - s),
9060
b * (1 - s * f),
9061
b * (1 - s * (1 - f))
9062
];
9063
return [v[i[0]], v[i[1]], v[i[2]]];
9064
},
9065
9066
'rgb-hsl': function(r, g, b) {
9067
var max = Math.max(r, g, b),
9068
min = Math.min(r, g, b),
9069
delta = max - min,
9070
achromatic = delta === 0,
9071
h = achromatic ? 0
9072
: ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
9073
: max == g ? (b - r) / delta + 2
9074
: (r - g) / delta + 4) * 60,
9075
l = (max + min) / 2,
9076
s = achromatic ? 0 : l < 0.5
9077
? delta / (max + min)
9078
: delta / (2 - max - min);
9079
return [h, s, l];
9080
},
9081
9082
'hsl-rgb': function(h, s, l) {
9083
h = (((h / 360) % 1) + 1) % 1;
9084
if (s === 0)
9085
return [l, l, l];
9086
var t3s = [ h + 1 / 3, h, h - 1 / 3 ],
9087
t2 = l < 0.5 ? l * (1 + s) : l + s - l * s,
9088
t1 = 2 * l - t2,
9089
c = [];
9090
for (var i = 0; i < 3; i++) {
9091
var t3 = t3s[i];
9092
if (t3 < 0) t3 += 1;
9093
if (t3 > 1) t3 -= 1;
9094
c[i] = 6 * t3 < 1
9095
? t1 + (t2 - t1) * 6 * t3
9096
: 2 * t3 < 1
9097
? t2
9098
: 3 * t3 < 2
9099
? t1 + (t2 - t1) * ((2 / 3) - t3) * 6
9100
: t1;
9101
}
9102
return c;
9103
},
9104
9105
'rgb-gray': function(r, g, b) {
9106
return [r * 0.2989 + g * 0.587 + b * 0.114];
9107
},
9108
9109
'gray-rgb': function(g) {
9110
return [g, g, g];
9111
},
9112
9113
'gray-hsb': function(g) {
9114
return [0, 0, g];
9115
},
9116
9117
'gray-hsl': function(g) {
9118
return [0, 0, g];
9119
},
9120
9121
'gradient-rgb': function() {
9122
return [];
9123
},
9124
9125
'rgb-gradient': function() {
9126
return [];
9127
}
9128
9129
};
9130
9131
return Base.each(types, function(properties, type) {
9132
componentParsers[type] = [];
9133
Base.each(properties, function(name, index) {
9134
var part = Base.capitalize(name),
9135
hasOverlap = /^(hue|saturation)$/.test(name),
9136
parser = componentParsers[type][index] = name === 'gradient'
9137
? function(value) {
9138
var current = this._components[0];
9139
value = Gradient.read(Array.isArray(value) ? value
9140
: arguments, 0, { readNull: true });
9141
if (current !== value) {
9142
if (current)
9143
current._removeOwner(this);
9144
if (value)
9145
value._addOwner(this);
9146
}
9147
return value;
9148
}
9149
: type === 'gradient'
9150
? function() {
9151
return Point.read(arguments, 0, {
9152
readNull: name === 'highlight',
9153
clone: true
9154
});
9155
}
9156
: function(value) {
9157
return value == null || isNaN(value) ? 0 : value;
9158
};
9159
9160
this['get' + part] = function() {
9161
return this._type === type
9162
|| hasOverlap && /^hs[bl]$/.test(this._type)
9163
? this._components[index]
9164
: this._convert(type)[index];
9165
};
9166
9167
this['set' + part] = function(value) {
9168
if (this._type !== type
9169
&& !(hasOverlap && /^hs[bl]$/.test(this._type))) {
9170
this._components = this._convert(type);
9171
this._properties = types[type];
9172
this._type = type;
9173
}
9174
value = parser.call(this, value);
9175
if (value != null) {
9176
this._components[index] = value;
9177
this._changed();
9178
}
9179
};
9180
}, this);
9181
}, {
9182
_class: 'Color',
9183
_readIndex: true,
9184
9185
initialize: function Color(arg) {
9186
var slice = Array.prototype.slice,
9187
args = arguments,
9188
read = 0,
9189
type,
9190
components,
9191
alpha,
9192
values;
9193
if (Array.isArray(arg)) {
9194
args = arg;
9195
arg = args[0];
9196
}
9197
var argType = arg != null && typeof arg;
9198
if (argType === 'string' && arg in types) {
9199
type = arg;
9200
arg = args[1];
9201
if (Array.isArray(arg)) {
9202
components = arg;
9203
alpha = args[2];
9204
} else {
9205
if (this.__read)
9206
read = 1;
9207
args = slice.call(args, 1);
9208
argType = typeof arg;
9209
}
9210
}
9211
if (!components) {
9212
values = argType === 'number'
9213
? args
9214
: argType === 'object' && arg.length != null
9215
? arg
9216
: null;
9217
if (values) {
9218
if (!type)
9219
type = values.length >= 3
9220
? 'rgb'
9221
: 'gray';
9222
var length = types[type].length;
9223
alpha = values[length];
9224
if (this.__read)
9225
read += values === arguments
9226
? length + (alpha != null ? 1 : 0)
9227
: 1;
9228
if (values.length > length)
9229
values = slice.call(values, 0, length);
9230
} else if (argType === 'string') {
9231
type = 'rgb';
9232
components = fromCSS(arg);
9233
if (components.length === 4) {
9234
alpha = components[3];
9235
components.length--;
9236
}
9237
} else if (argType === 'object') {
9238
if (arg.constructor === Color) {
9239
type = arg._type;
9240
components = arg._components.slice();
9241
alpha = arg._alpha;
9242
if (type === 'gradient') {
9243
for (var i = 1, l = components.length; i < l; i++) {
9244
var point = components[i];
9245
if (point)
9246
components[i] = point.clone();
9247
}
9248
}
9249
} else if (arg.constructor === Gradient) {
9250
type = 'gradient';
9251
values = args;
9252
} else {
9253
type = 'hue' in arg
9254
? 'lightness' in arg
9255
? 'hsl'
9256
: 'hsb'
9257
: 'gradient' in arg || 'stops' in arg
9258
|| 'radial' in arg
9259
? 'gradient'
9260
: 'gray' in arg
9261
? 'gray'
9262
: 'rgb';
9263
var properties = types[type];
9264
parsers = componentParsers[type];
9265
this._components = components = [];
9266
for (var i = 0, l = properties.length; i < l; i++) {
9267
var value = arg[properties[i]];
9268
if (value == null && i === 0 && type === 'gradient'
9269
&& 'stops' in arg) {
9270
value = {
9271
stops: arg.stops,
9272
radial: arg.radial
9273
};
9274
}
9275
value = parsers[i].call(this, value);
9276
if (value != null)
9277
components[i] = value;
9278
}
9279
alpha = arg.alpha;
9280
}
9281
}
9282
if (this.__read && type)
9283
read = 1;
9284
}
9285
this._type = type || 'rgb';
9286
if (type === 'gradient')
9287
this._id = Color._id = (Color._id || 0) + 1;
9288
if (!components) {
9289
this._components = components = [];
9290
var parsers = componentParsers[this._type];
9291
for (var i = 0, l = parsers.length; i < l; i++) {
9292
var value = parsers[i].call(this, values && values[i]);
9293
if (value != null)
9294
components[i] = value;
9295
}
9296
}
9297
this._components = components;
9298
this._properties = types[this._type];
9299
this._alpha = alpha;
9300
if (this.__read)
9301
this.__read = read;
9302
},
9303
9304
_serialize: function(options, dictionary) {
9305
var components = this.getComponents();
9306
return Base.serialize(
9307
/^(gray|rgb)$/.test(this._type)
9308
? components
9309
: [this._type].concat(components),
9310
options, true, dictionary);
9311
},
9312
9313
_changed: function() {
9314
this._canvasStyle = null;
9315
if (this._owner)
9316
this._owner._changed(65);
9317
},
9318
9319
_convert: function(type) {
9320
var converter;
9321
return this._type === type
9322
? this._components.slice()
9323
: (converter = converters[this._type + '-' + type])
9324
? converter.apply(this, this._components)
9325
: converters['rgb-' + type].apply(this,
9326
converters[this._type + '-rgb'].apply(this,
9327
this._components));
9328
},
9329
9330
convert: function(type) {
9331
return new Color(type, this._convert(type), this._alpha);
9332
},
9333
9334
getType: function() {
9335
return this._type;
9336
},
9337
9338
setType: function(type) {
9339
this._components = this._convert(type);
9340
this._properties = types[type];
9341
this._type = type;
9342
},
9343
9344
getComponents: function() {
9345
var components = this._components.slice();
9346
if (this._alpha != null)
9347
components.push(this._alpha);
9348
return components;
9349
},
9350
9351
getAlpha: function() {
9352
return this._alpha != null ? this._alpha : 1;
9353
},
9354
9355
setAlpha: function(alpha) {
9356
this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1);
9357
this._changed();
9358
},
9359
9360
hasAlpha: function() {
9361
return this._alpha != null;
9362
},
9363
9364
equals: function(color) {
9365
var col = Base.isPlainValue(color)
9366
? Color.read(arguments)
9367
: color;
9368
return col === this || col && this._class === col._class
9369
&& this._type === col._type
9370
&& this._alpha === col._alpha
9371
&& Base.equals(this._components, col._components)
9372
|| false;
9373
},
9374
9375
toString: function() {
9376
var properties = this._properties,
9377
parts = [],
9378
isGradient = this._type === 'gradient',
9379
f = Formatter.instance;
9380
for (var i = 0, l = properties.length; i < l; i++) {
9381
var value = this._components[i];
9382
if (value != null)
9383
parts.push(properties[i] + ': '
9384
+ (isGradient ? value : f.number(value)));
9385
}
9386
if (this._alpha != null)
9387
parts.push('alpha: ' + f.number(this._alpha));
9388
return '{ ' + parts.join(', ') + ' }';
9389
},
9390
9391
toCSS: function(hex) {
9392
var components = this._convert('rgb'),
9393
alpha = hex || this._alpha == null ? 1 : this._alpha;
9394
function convert(val) {
9395
return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
9396
}
9397
components = [
9398
convert(components[0]),
9399
convert(components[1]),
9400
convert(components[2])
9401
];
9402
if (alpha < 1)
9403
components.push(alpha < 0 ? 0 : alpha);
9404
return hex
9405
? '#' + ((1 << 24) + (components[0] << 16)
9406
+ (components[1] << 8)
9407
+ components[2]).toString(16).slice(1)
9408
: (components.length == 4 ? 'rgba(' : 'rgb(')
9409
+ components.join(',') + ')';
9410
},
9411
9412
toCanvasStyle: function(ctx) {
9413
if (this._canvasStyle)
9414
return this._canvasStyle;
9415
if (this._type !== 'gradient')
9416
return this._canvasStyle = this.toCSS();
9417
var components = this._components,
9418
gradient = components[0],
9419
stops = gradient._stops,
9420
origin = components[1],
9421
destination = components[2],
9422
canvasGradient;
9423
if (gradient._radial) {
9424
var radius = destination.getDistance(origin),
9425
highlight = components[3];
9426
if (highlight) {
9427
var vector = highlight.subtract(origin);
9428
if (vector.getLength() > radius)
9429
highlight = origin.add(vector.normalize(radius - 0.1));
9430
}
9431
var start = highlight || origin;
9432
canvasGradient = ctx.createRadialGradient(start.x, start.y,
9433
0, origin.x, origin.y, radius);
9434
} else {
9435
canvasGradient = ctx.createLinearGradient(origin.x, origin.y,
9436
destination.x, destination.y);
9437
}
9438
for (var i = 0, l = stops.length; i < l; i++) {
9439
var stop = stops[i];
9440
canvasGradient.addColorStop(stop._rampPoint,
9441
stop._color.toCanvasStyle());
9442
}
9443
return this._canvasStyle = canvasGradient;
9444
},
9445
9446
transform: function(matrix) {
9447
if (this._type === 'gradient') {
9448
var components = this._components;
9449
for (var i = 1, l = components.length; i < l; i++) {
9450
var point = components[i];
9451
matrix._transformPoint(point, point, true);
9452
}
9453
this._changed();
9454
}
9455
},
9456
9457
statics: {
9458
_types: types,
9459
9460
random: function() {
9461
var random = Math.random;
9462
return new Color(random(), random(), random());
9463
}
9464
}
9465
});
9466
}, new function() {
9467
var operators = {
9468
add: function(a, b) {
9469
return a + b;
9470
},
9471
9472
subtract: function(a, b) {
9473
return a - b;
9474
},
9475
9476
multiply: function(a, b) {
9477
return a * b;
9478
},
9479
9480
divide: function(a, b) {
9481
return a / b;
9482
}
9483
};
9484
9485
return Base.each(operators, function(operator, name) {
9486
this[name] = function(color) {
9487
color = Color.read(arguments);
9488
var type = this._type,
9489
components1 = this._components,
9490
components2 = color._convert(type);
9491
for (var i = 0, l = components1.length; i < l; i++)
9492
components2[i] = operator(components1[i], components2[i]);
9493
return new Color(type, components2,
9494
this._alpha != null
9495
? operator(this._alpha, color.getAlpha())
9496
: null);
9497
};
9498
}, {
9499
});
9500
});
9501
9502
Base.each(Color._types, function(properties, type) {
9503
var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) {
9504
var argType = arg != null && typeof arg,
9505
components = argType === 'object' && arg.length != null
9506
? arg
9507
: argType === 'string'
9508
? null
9509
: arguments;
9510
return components
9511
? new Color(type, components)
9512
: new Color(arg);
9513
};
9514
if (type.length == 3) {
9515
var acronym = type.toUpperCase();
9516
Color[acronym] = this[acronym + 'Color'] = ctor;
9517
}
9518
}, Base.exports);
9519
9520
var Gradient = Base.extend({
9521
_class: 'Gradient',
9522
9523
initialize: function Gradient(stops, radial) {
9524
this._id = Gradient._id = (Gradient._id || 0) + 1;
9525
if (stops && this._set(stops))
9526
stops = radial = null;
9527
if (!this._stops)
9528
this.setStops(stops || ['white', 'black']);
9529
if (this._radial == null)
9530
this.setRadial(typeof radial === 'string' && radial === 'radial'
9531
|| radial || false);
9532
},
9533
9534
_serialize: function(options, dictionary) {
9535
return dictionary.add(this, function() {
9536
return Base.serialize([this._stops, this._radial],
9537
options, true, dictionary);
9538
});
9539
},
9540
9541
_changed: function() {
9542
for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
9543
this._owners[i]._changed();
9544
},
9545
9546
_addOwner: function(color) {
9547
if (!this._owners)
9548
this._owners = [];
9549
this._owners.push(color);
9550
},
9551
9552
_removeOwner: function(color) {
9553
var index = this._owners ? this._owners.indexOf(color) : -1;
9554
if (index != -1) {
9555
this._owners.splice(index, 1);
9556
if (this._owners.length === 0)
9557
this._owners = undefined;
9558
}
9559
},
9560
9561
clone: function() {
9562
var stops = [];
9563
for (var i = 0, l = this._stops.length; i < l; i++)
9564
stops[i] = this._stops[i].clone();
9565
return new Gradient(stops);
9566
},
9567
9568
getStops: function() {
9569
return this._stops;
9570
},
9571
9572
setStops: function(stops) {
9573
if (this.stops) {
9574
for (var i = 0, l = this._stops.length; i < l; i++)
9575
this._stops[i]._owner = undefined;
9576
}
9577
if (stops.length < 2)
9578
throw new Error(
9579
'Gradient stop list needs to contain at least two stops.');
9580
this._stops = GradientStop.readAll(stops, 0, { clone: true });
9581
for (var i = 0, l = this._stops.length; i < l; i++) {
9582
var stop = this._stops[i];
9583
stop._owner = this;
9584
if (stop._defaultRamp)
9585
stop.setRampPoint(i / (l - 1));
9586
}
9587
this._changed();
9588
},
9589
9590
getRadial: function() {
9591
return this._radial;
9592
},
9593
9594
setRadial: function(radial) {
9595
this._radial = radial;
9596
this._changed();
9597
},
9598
9599
equals: function(gradient) {
9600
if (gradient === this)
9601
return true;
9602
if (gradient && this._class === gradient._class
9603
&& this._stops.length === gradient._stops.length) {
9604
for (var i = 0, l = this._stops.length; i < l; i++) {
9605
if (!this._stops[i].equals(gradient._stops[i]))
9606
return false;
9607
}
9608
return true;
9609
}
9610
return false;
9611
}
9612
});
9613
9614
var GradientStop = Base.extend({
9615
_class: 'GradientStop',
9616
9617
initialize: function GradientStop(arg0, arg1) {
9618
if (arg0) {
9619
var color, rampPoint;
9620
if (arg1 === undefined && Array.isArray(arg0)) {
9621
color = arg0[0];
9622
rampPoint = arg0[1];
9623
} else if (arg0.color) {
9624
color = arg0.color;
9625
rampPoint = arg0.rampPoint;
9626
} else {
9627
color = arg0;
9628
rampPoint = arg1;
9629
}
9630
this.setColor(color);
9631
this.setRampPoint(rampPoint);
9632
}
9633
},
9634
9635
clone: function() {
9636
return new GradientStop(this._color.clone(), this._rampPoint);
9637
},
9638
9639
_serialize: function(options, dictionary) {
9640
return Base.serialize([this._color, this._rampPoint], options, true,
9641
dictionary);
9642
},
9643
9644
_changed: function() {
9645
if (this._owner)
9646
this._owner._changed(65);
9647
},
9648
9649
getRampPoint: function() {
9650
return this._rampPoint;
9651
},
9652
9653
setRampPoint: function(rampPoint) {
9654
this._defaultRamp = rampPoint == null;
9655
this._rampPoint = rampPoint || 0;
9656
this._changed();
9657
},
9658
9659
getColor: function() {
9660
return this._color;
9661
},
9662
9663
setColor: function(color) {
9664
this._color = Color.read(arguments);
9665
if (this._color === color)
9666
this._color = color.clone();
9667
this._color._owner = this;
9668
this._changed();
9669
},
9670
9671
equals: function(stop) {
9672
return stop === this || stop && this._class === stop._class
9673
&& this._color.equals(stop._color)
9674
&& this._rampPoint == stop._rampPoint
9675
|| false;
9676
}
9677
});
9678
9679
var Style = Base.extend(new function() {
9680
var defaults = {
9681
fillColor: undefined,
9682
strokeColor: undefined,
9683
strokeWidth: 1,
9684
strokeCap: 'butt',
9685
strokeJoin: 'miter',
9686
miterLimit: 10,
9687
dashOffset: 0,
9688
dashArray: [],
9689
windingRule: 'nonzero',
9690
shadowColor: undefined,
9691
shadowBlur: 0,
9692
shadowOffset: new Point(),
9693
selectedColor: undefined,
9694
fontFamily: 'sans-serif',
9695
fontWeight: 'normal',
9696
fontSize: 12,
9697
font: 'sans-serif',
9698
leading: null,
9699
justification: 'left'
9700
};
9701
9702
var flags = {
9703
strokeWidth: 97,
9704
strokeCap: 97,
9705
strokeJoin: 97,
9706
miterLimit: 97,
9707
fontFamily: 9,
9708
fontWeight: 9,
9709
fontSize: 9,
9710
font: 9,
9711
leading: 9,
9712
justification: 9
9713
};
9714
9715
var item = {},
9716
fields = {
9717
_defaults: defaults,
9718
_textDefaults: new Base(defaults, {
9719
fillColor: new Color()
9720
}),
9721
beans: true
9722
};
9723
9724
Base.each(defaults, function(value, key) {
9725
var isColor = /Color$/.test(key),
9726
part = Base.capitalize(key),
9727
flag = flags[key],
9728
set = 'set' + part,
9729
get = 'get' + part;
9730
9731
fields[set] = function(value) {
9732
var owner = this._owner,
9733
children = owner && owner._children;
9734
if (children && children.length > 0
9735
&& !(owner instanceof CompoundPath)) {
9736
for (var i = 0, l = children.length; i < l; i++)
9737
children[i]._style[set](value);
9738
} else {
9739
var old = this._values[key];
9740
if (old != value) {
9741
if (isColor) {
9742
if (old)
9743
old._owner = undefined;
9744
if (value && value.constructor === Color) {
9745
if (value._owner)
9746
value = value.clone();
9747
value._owner = owner;
9748
}
9749
}
9750
this._values[key] = value;
9751
if (owner)
9752
owner._changed(flag || 65);
9753
}
9754
}
9755
};
9756
9757
fields[get] = function(_dontMerge) {
9758
var owner = this._owner,
9759
children = owner && owner._children,
9760
value;
9761
if (!children || children.length === 0 || _dontMerge
9762
|| owner instanceof CompoundPath) {
9763
var value = this._values[key];
9764
if (value === undefined) {
9765
value = this._defaults[key];
9766
if (value && value.clone)
9767
value = value.clone();
9768
this._values[key] = value;
9769
} else if (isColor && !(value && value.constructor === Color)) {
9770
this._values[key] = value = Color.read([value], 0,
9771
{ readNull: true, clone: true });
9772
if (value)
9773
value._owner = owner;
9774
}
9775
return value;
9776
}
9777
for (var i = 0, l = children.length; i < l; i++) {
9778
var childValue = children[i]._style[get]();
9779
if (i === 0) {
9780
value = childValue;
9781
} else if (!Base.equals(value, childValue)) {
9782
return undefined;
9783
}
9784
}
9785
return value;
9786
};
9787
9788
item[get] = function() {
9789
return this._style[get]();
9790
};
9791
9792
item[set] = function(value) {
9793
this._style[set](value);
9794
};
9795
});
9796
9797
Item.inject(item);
9798
return fields;
9799
}, {
9800
_class: 'Style',
9801
9802
initialize: function Style(style, _owner, _project) {
9803
this._values = {};
9804
this._owner = _owner;
9805
this._project = _owner && _owner._project || _project || paper.project;
9806
if (_owner instanceof TextItem)
9807
this._defaults = this._textDefaults;
9808
if (style)
9809
this.set(style);
9810
},
9811
9812
set: function(style) {
9813
var isStyle = style instanceof Style,
9814
values = isStyle ? style._values : style;
9815
if (values) {
9816
for (var key in values) {
9817
if (key in this._defaults) {
9818
var value = values[key];
9819
this[key] = value && isStyle && value.clone
9820
? value.clone() : value;
9821
}
9822
}
9823
}
9824
},
9825
9826
equals: function(style) {
9827
return style === this || style && this._class === style._class
9828
&& Base.equals(this._values, style._values)
9829
|| false;
9830
},
9831
9832
hasFill: function() {
9833
return !!this.getFillColor();
9834
},
9835
9836
hasStroke: function() {
9837
return !!this.getStrokeColor() && this.getStrokeWidth() > 0;
9838
},
9839
9840
hasShadow: function() {
9841
return !!this.getShadowColor() && this.getShadowBlur() > 0;
9842
},
9843
9844
getView: function() {
9845
return this._project.getView();
9846
},
9847
9848
getFontStyle: function() {
9849
var fontSize = this.getFontSize();
9850
return this.getFontWeight()
9851
+ ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')
9852
+ this.getFontFamily();
9853
},
9854
9855
getFont: '#getFontFamily',
9856
setFont: '#setFontFamily',
9857
9858
getLeading: function getLeading() {
9859
var leading = getLeading.base.call(this),
9860
fontSize = this.getFontSize();
9861
if (/pt|em|%|px/.test(fontSize))
9862
fontSize = this.getView().getPixelSize(fontSize);
9863
return leading != null ? leading : fontSize * 1.2;
9864
}
9865
9866
});
9867
9868
var DomElement = new function() {
9869
9870
var special = /^(checked|value|selected|disabled)$/i,
9871
translated = { text: 'textContent', html: 'innerHTML' },
9872
unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1 };
9873
9874
function create(nodes, parent) {
9875
var res = [];
9876
for (var i = 0, l = nodes && nodes.length; i < l;) {
9877
var el = nodes[i++];
9878
if (typeof el === 'string') {
9879
el = document.createElement(el);
9880
} else if (!el || !el.nodeType) {
9881
continue;
9882
}
9883
if (Base.isPlainObject(nodes[i]))
9884
DomElement.set(el, nodes[i++]);
9885
if (Array.isArray(nodes[i]))
9886
create(nodes[i++], el);
9887
if (parent)
9888
parent.appendChild(el);
9889
res.push(el);
9890
}
9891
return res;
9892
}
9893
9894
function handlePrefix(el, name, set, value) {
9895
var prefixes = ['webkit', 'moz', 'Moz', 'ms', 'o', ''],
9896
suffix = name[0].toUpperCase() + name.substring(1);
9897
for (var i = 0; i < 6; i++) {
9898
var prefix = prefixes[i],
9899
key = prefix ? prefix + suffix : name;
9900
if (key in el) {
9901
if (set) {
9902
el[key] = value;
9903
} else {
9904
return el[key];
9905
}
9906
break;
9907
}
9908
}
9909
}
9910
9911
return {
9912
create: function(nodes, parent) {
9913
var isArray = Array.isArray(nodes),
9914
res = create(isArray ? nodes : arguments, isArray ? parent : null);
9915
return res.length == 1 ? res[0] : res;
9916
},
9917
9918
find: function(selector, root) {
9919
return (root || document).querySelector(selector);
9920
},
9921
9922
findAll: function(selector, root) {
9923
return (root || document).querySelectorAll(selector);
9924
},
9925
9926
get: function(el, key) {
9927
return el
9928
? special.test(key)
9929
? key === 'value' || typeof el[key] !== 'string'
9930
? el[key]
9931
: true
9932
: key in translated
9933
? el[translated[key]]
9934
: el.getAttribute(key)
9935
: null;
9936
},
9937
9938
set: function(el, key, value) {
9939
if (typeof key !== 'string') {
9940
for (var name in key)
9941
if (key.hasOwnProperty(name))
9942
this.set(el, name, key[name]);
9943
} else if (!el || value === undefined) {
9944
return el;
9945
} else if (special.test(key)) {
9946
el[key] = value;
9947
} else if (key in translated) {
9948
el[translated[key]] = value;
9949
} else if (key === 'style') {
9950
this.setStyle(el, value);
9951
} else if (key === 'events') {
9952
DomEvent.add(el, value);
9953
} else {
9954
el.setAttribute(key, value);
9955
}
9956
return el;
9957
},
9958
9959
getStyles: function(el) {
9960
var doc = el && el.nodeType !== 9 ? el.ownerDocument : el,
9961
view = doc && doc.defaultView;
9962
return view && view.getComputedStyle(el, '');
9963
},
9964
9965
getStyle: function(el, key) {
9966
return el && el.style[key] || this.getStyles(el)[key] || null;
9967
},
9968
9969
setStyle: function(el, key, value) {
9970
if (typeof key !== 'string') {
9971
for (var name in key)
9972
if (key.hasOwnProperty(name))
9973
this.setStyle(el, name, key[name]);
9974
} else {
9975
if (/^-?[\d\.]+$/.test(value) && !(key in unitless))
9976
value += 'px';
9977
el.style[key] = value;
9978
}
9979
return el;
9980
},
9981
9982
hasClass: function(el, cls) {
9983
return new RegExp('\\s*' + cls + '\\s*').test(el.className);
9984
},
9985
9986
addClass: function(el, cls) {
9987
el.className = (el.className + ' ' + cls).trim();
9988
},
9989
9990
removeClass: function(el, cls) {
9991
el.className = el.className.replace(
9992
new RegExp('\\s*' + cls + '\\s*'), ' ').trim();
9993
},
9994
9995
remove: function(el) {
9996
if (el.parentNode)
9997
el.parentNode.removeChild(el);
9998
},
9999
10000
removeChildren: function(el) {
10001
while (el.firstChild)
10002
el.removeChild(el.firstChild);
10003
},
10004
10005
getBounds: function(el, viewport) {
10006
var doc = el.ownerDocument,
10007
body = doc.body,
10008
html = doc.documentElement,
10009
rect;
10010
try {
10011
rect = el.getBoundingClientRect();
10012
} catch (e) {
10013
rect = { left: 0, top: 0, width: 0, height: 0 };
10014
}
10015
var x = rect.left - (html.clientLeft || body.clientLeft || 0),
10016
y = rect.top - (html.clientTop || body.clientTop || 0);
10017
if (!viewport) {
10018
var view = doc.defaultView;
10019
x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
10020
y += view.pageYOffset || html.scrollTop || body.scrollTop;
10021
}
10022
return new Rectangle(x, y, rect.width, rect.height);
10023
},
10024
10025
getViewportBounds: function(el) {
10026
var doc = el.ownerDocument,
10027
view = doc.defaultView,
10028
html = doc.documentElement;
10029
return new Rectangle(0, 0,
10030
view.innerWidth || html.clientWidth,
10031
view.innerHeight || html.clientHeight
10032
);
10033
},
10034
10035
getOffset: function(el, viewport) {
10036
return this.getBounds(el, viewport).getPoint();
10037
},
10038
10039
getSize: function(el) {
10040
return this.getBounds(el, true).getSize();
10041
},
10042
10043
isInvisible: function(el) {
10044
return this.getSize(el).equals(new Size(0, 0));
10045
},
10046
10047
isInView: function(el) {
10048
return !this.isInvisible(el) && this.getViewportBounds(el).intersects(
10049
this.getBounds(el, true));
10050
},
10051
10052
getPrefixed: function(el, name) {
10053
return handlePrefix(el, name);
10054
},
10055
10056
setPrefixed: function(el, name, value) {
10057
if (typeof name === 'object') {
10058
for (var key in name)
10059
handlePrefix(el, key, true, name[key]);
10060
} else {
10061
handlePrefix(el, name, true, value);
10062
}
10063
}
10064
};
10065
};
10066
10067
var DomEvent = {
10068
add: function(el, events) {
10069
for (var type in events) {
10070
var func = events[type],
10071
parts = type.split(/[\s,]+/g);
10072
for (var i = 0, l = parts.length; i < l; i++)
10073
el.addEventListener(parts[i], func, false);
10074
}
10075
},
10076
10077
remove: function(el, events) {
10078
for (var type in events) {
10079
var func = events[type],
10080
parts = type.split(/[\s,]+/g);
10081
for (var i = 0, l = parts.length; i < l; i++)
10082
el.removeEventListener(parts[i], func, false);
10083
}
10084
},
10085
10086
getPoint: function(event) {
10087
var pos = event.targetTouches
10088
? event.targetTouches.length
10089
? event.targetTouches[0]
10090
: event.changedTouches[0]
10091
: event;
10092
return new Point(
10093
pos.pageX || pos.clientX + document.documentElement.scrollLeft,
10094
pos.pageY || pos.clientY + document.documentElement.scrollTop
10095
);
10096
},
10097
10098
getTarget: function(event) {
10099
return event.target || event.srcElement;
10100
},
10101
10102
getRelatedTarget: function(event) {
10103
return event.relatedTarget || event.toElement;
10104
},
10105
10106
getOffset: function(event, target) {
10107
return DomEvent.getPoint(event).subtract(DomElement.getOffset(
10108
target || DomEvent.getTarget(event)));
10109
},
10110
10111
stop: function(event) {
10112
event.stopPropagation();
10113
event.preventDefault();
10114
}
10115
};
10116
10117
DomEvent.requestAnimationFrame = new function() {
10118
var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
10119
requested = false,
10120
callbacks = [],
10121
focused = true,
10122
timer;
10123
10124
DomEvent.add(window, {
10125
focus: function() {
10126
focused = true;
10127
},
10128
blur: function() {
10129
focused = false;
10130
}
10131
});
10132
10133
function handleCallbacks() {
10134
for (var i = callbacks.length - 1; i >= 0; i--) {
10135
var entry = callbacks[i],
10136
func = entry[0],
10137
el = entry[1];
10138
if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
10139
|| focused) && DomElement.isInView(el)) {
10140
callbacks.splice(i, 1);
10141
func();
10142
}
10143
}
10144
if (nativeRequest) {
10145
if (callbacks.length) {
10146
nativeRequest(handleCallbacks);
10147
} else {
10148
requested = false;
10149
}
10150
}
10151
}
10152
10153
return function(callback, element) {
10154
callbacks.push([callback, element]);
10155
if (nativeRequest) {
10156
if (!requested) {
10157
nativeRequest(handleCallbacks);
10158
requested = true;
10159
}
10160
} else if (!timer) {
10161
timer = setInterval(handleCallbacks, 1000 / 60);
10162
}
10163
};
10164
};
10165
10166
var View = Base.extend(Callback, {
10167
_class: 'View',
10168
10169
initialize: function View(project, element) {
10170
this._project = project;
10171
this._scope = project._scope;
10172
this._element = element;
10173
var size;
10174
if (!this._pixelRatio)
10175
this._pixelRatio = window.devicePixelRatio || 1;
10176
this._id = element.getAttribute('id');
10177
if (this._id == null)
10178
element.setAttribute('id', this._id = 'view-' + View._id++);
10179
DomEvent.add(element, this._viewEvents);
10180
var none = 'none';
10181
DomElement.setPrefixed(element.style, {
10182
userSelect: none,
10183
touchAction: none,
10184
touchCallout: none,
10185
contentZooming: none,
10186
userDrag: none,
10187
tapHighlightColor: 'rgba(0,0,0,0)'
10188
});
10189
if (PaperScope.hasAttribute(element, 'resize')) {
10190
var offset = DomElement.getOffset(element, true),
10191
that = this;
10192
size = DomElement.getViewportBounds(element)
10193
.getSize().subtract(offset);
10194
this._windowEvents = {
10195
resize: function() {
10196
if (!DomElement.isInvisible(element))
10197
offset = DomElement.getOffset(element, true);
10198
that.setViewSize(DomElement.getViewportBounds(element)
10199
.getSize().subtract(offset));
10200
}
10201
};
10202
DomEvent.add(window, this._windowEvents);
10203
} else {
10204
size = DomElement.getSize(element);
10205
if (size.isNaN() || size.isZero()) {
10206
var getSize = function(name) {
10207
return element[name]
10208
|| parseInt(element.getAttribute(name), 10);
10209
};
10210
size = new Size(getSize('width'), getSize('height'));
10211
}
10212
}
10213
this._setViewSize(size);
10214
if (PaperScope.hasAttribute(element, 'stats')
10215
&& typeof Stats !== 'undefined') {
10216
this._stats = new Stats();
10217
var stats = this._stats.domElement,
10218
style = stats.style,
10219
offset = DomElement.getOffset(element);
10220
style.position = 'absolute';
10221
style.left = offset.x + 'px';
10222
style.top = offset.y + 'px';
10223
document.body.appendChild(stats);
10224
}
10225
View._views.push(this);
10226
View._viewsById[this._id] = this;
10227
this._viewSize = size;
10228
(this._matrix = new Matrix())._owner = this;
10229
this._zoom = 1;
10230
if (!View._focused)
10231
View._focused = this;
10232
this._frameItems = {};
10233
this._frameItemCount = 0;
10234
},
10235
10236
remove: function() {
10237
if (!this._project)
10238
return false;
10239
if (View._focused === this)
10240
View._focused = null;
10241
View._views.splice(View._views.indexOf(this), 1);
10242
delete View._viewsById[this._id];
10243
if (this._project._view === this)
10244
this._project._view = null;
10245
DomEvent.remove(this._element, this._viewEvents);
10246
DomEvent.remove(window, this._windowEvents);
10247
this._element = this._project = null;
10248
this.detach('frame');
10249
this._animate = false;
10250
this._frameItems = {};
10251
return true;
10252
},
10253
10254
_events: {
10255
onFrame: {
10256
install: function() {
10257
this.play();
10258
},
10259
10260
uninstall: function() {
10261
this.pause();
10262
}
10263
},
10264
10265
onResize: {}
10266
},
10267
10268
_animate: false,
10269
_time: 0,
10270
_count: 0,
10271
10272
_requestFrame: function() {
10273
var that = this;
10274
DomEvent.requestAnimationFrame(function() {
10275
that._requested = false;
10276
if (!that._animate)
10277
return;
10278
that._requestFrame();
10279
that._handleFrame();
10280
}, this._element);
10281
this._requested = true;
10282
},
10283
10284
_handleFrame: function() {
10285
paper = this._scope;
10286
var now = Date.now() / 1000,
10287
delta = this._before ? now - this._before : 0;
10288
this._before = now;
10289
this._handlingFrame = true;
10290
this.fire('frame', new Base({
10291
delta: delta,
10292
time: this._time += delta,
10293
count: this._count++
10294
}));
10295
if (this._stats)
10296
this._stats.update();
10297
this._handlingFrame = false;
10298
this.update();
10299
},
10300
10301
_animateItem: function(item, animate) {
10302
var items = this._frameItems;
10303
if (animate) {
10304
items[item._id] = {
10305
item: item,
10306
time: 0,
10307
count: 0
10308
};
10309
if (++this._frameItemCount === 1)
10310
this.attach('frame', this._handleFrameItems);
10311
} else {
10312
delete items[item._id];
10313
if (--this._frameItemCount === 0) {
10314
this.detach('frame', this._handleFrameItems);
10315
}
10316
}
10317
},
10318
10319
_handleFrameItems: function(event) {
10320
for (var i in this._frameItems) {
10321
var entry = this._frameItems[i];
10322
entry.item.fire('frame', new Base(event, {
10323
time: entry.time += event.delta,
10324
count: entry.count++
10325
}));
10326
}
10327
},
10328
10329
_update: function() {
10330
this._project._needsUpdate = true;
10331
if (this._handlingFrame)
10332
return;
10333
if (this._animate) {
10334
this._handleFrame();
10335
} else {
10336
this.update();
10337
}
10338
},
10339
10340
_changed: function(flags) {
10341
if (flags & 1)
10342
this._project._needsUpdate = true;
10343
},
10344
10345
_transform: function(matrix) {
10346
this._matrix.concatenate(matrix);
10347
this._bounds = null;
10348
this._update();
10349
},
10350
10351
getElement: function() {
10352
return this._element;
10353
},
10354
10355
getPixelRatio: function() {
10356
return this._pixelRatio;
10357
},
10358
10359
getResolution: function() {
10360
return this._pixelRatio * 72;
10361
},
10362
10363
getViewSize: function() {
10364
var size = this._viewSize;
10365
return new LinkedSize(size.width, size.height, this, 'setViewSize');
10366
},
10367
10368
setViewSize: function() {
10369
var size = Size.read(arguments),
10370
delta = size.subtract(this._viewSize);
10371
if (delta.isZero())
10372
return;
10373
this._viewSize.set(size.width, size.height);
10374
this._setViewSize(size);
10375
this._bounds = null;
10376
this.fire('resize', {
10377
size: size,
10378
delta: delta
10379
});
10380
this._update();
10381
},
10382
10383
_setViewSize: function(size) {
10384
var element = this._element;
10385
element.width = size.width;
10386
element.height = size.height;
10387
},
10388
10389
getBounds: function() {
10390
if (!this._bounds)
10391
this._bounds = this._matrix.inverted()._transformBounds(
10392
new Rectangle(new Point(), this._viewSize));
10393
return this._bounds;
10394
},
10395
10396
getSize: function() {
10397
return this.getBounds().getSize();
10398
},
10399
10400
getCenter: function() {
10401
return this.getBounds().getCenter();
10402
},
10403
10404
setCenter: function(center) {
10405
center = Point.read(arguments);
10406
this.scrollBy(center.subtract(this.getCenter()));
10407
},
10408
10409
getZoom: function() {
10410
return this._zoom;
10411
},
10412
10413
setZoom: function(zoom) {
10414
this._transform(new Matrix().scale(zoom / this._zoom,
10415
this.getCenter()));
10416
this._zoom = zoom;
10417
},
10418
10419
isVisible: function() {
10420
return DomElement.isInView(this._element);
10421
},
10422
10423
scrollBy: function() {
10424
this._transform(new Matrix().translate(Point.read(arguments).negate()));
10425
},
10426
10427
play: function() {
10428
this._animate = true;
10429
if (!this._requested)
10430
this._requestFrame();
10431
},
10432
10433
pause: function() {
10434
this._animate = false;
10435
},
10436
10437
draw: function() {
10438
this.update();
10439
},
10440
10441
projectToView: function() {
10442
return this._matrix._transformPoint(Point.read(arguments));
10443
},
10444
10445
viewToProject: function() {
10446
return this._matrix._inverseTransform(Point.read(arguments));
10447
}
10448
10449
}, {
10450
statics: {
10451
_views: [],
10452
_viewsById: {},
10453
_id: 0,
10454
10455
create: function(project, element) {
10456
if (typeof element === 'string')
10457
element = document.getElementById(element);
10458
return new CanvasView(project, element);
10459
}
10460
}
10461
}, new function() {
10462
var tool,
10463
prevFocus,
10464
tempFocus,
10465
dragging = false;
10466
10467
function getView(event) {
10468
var target = DomEvent.getTarget(event);
10469
return target.getAttribute && View._viewsById[target.getAttribute('id')];
10470
}
10471
10472
function viewToProject(view, event) {
10473
return view.viewToProject(DomEvent.getOffset(event, view._element));
10474
}
10475
10476
function updateFocus() {
10477
if (!View._focused || !View._focused.isVisible()) {
10478
for (var i = 0, l = View._views.length; i < l; i++) {
10479
var view = View._views[i];
10480
if (view && view.isVisible()) {
10481
View._focused = tempFocus = view;
10482
break;
10483
}
10484
}
10485
}
10486
}
10487
10488
function handleMouseMove(view, point, event) {
10489
view._handleEvent('mousemove', point, event);
10490
var tool = view._scope.tool;
10491
if (tool) {
10492
tool._handleEvent(dragging && tool.responds('mousedrag')
10493
? 'mousedrag' : 'mousemove', point, event);
10494
}
10495
view.update();
10496
return tool;
10497
}
10498
10499
var navigator = window.navigator,
10500
mousedown, mousemove, mouseup;
10501
if (navigator.pointerEnabled || navigator.msPointerEnabled) {
10502
mousedown = 'pointerdown MSPointerDown';
10503
mousemove = 'pointermove MSPointerMove';
10504
mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
10505
} else {
10506
mousedown = 'touchstart';
10507
mousemove = 'touchmove';
10508
mouseup = 'touchend touchcancel';
10509
if (!('ontouchstart' in window && navigator.userAgent.match(
10510
/mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
10511
mousedown += ' mousedown';
10512
mousemove += ' mousemove';
10513
mouseup += ' mouseup';
10514
}
10515
}
10516
10517
var viewEvents = {
10518
'selectstart dragstart': function(event) {
10519
if (dragging)
10520
event.preventDefault();
10521
}
10522
};
10523
10524
var docEvents = {
10525
mouseout: function(event) {
10526
var view = View._focused,
10527
target = DomEvent.getRelatedTarget(event);
10528
if (view && (!target || target.nodeName === 'HTML'))
10529
handleMouseMove(view, viewToProject(view, event), event);
10530
},
10531
10532
scroll: updateFocus
10533
};
10534
10535
viewEvents[mousedown] = function(event) {
10536
var view = View._focused = getView(event),
10537
point = viewToProject(view, event);
10538
dragging = true;
10539
view._handleEvent('mousedown', point, event);
10540
if (tool = view._scope.tool)
10541
tool._handleEvent('mousedown', point, event);
10542
view.update();
10543
};
10544
10545
docEvents[mousemove] = function(event) {
10546
var view = View._focused;
10547
if (!dragging) {
10548
var target = getView(event);
10549
if (target) {
10550
if (view !== target)
10551
handleMouseMove(view, viewToProject(view, event), event);
10552
prevFocus = view;
10553
view = View._focused = tempFocus = target;
10554
} else if (tempFocus && tempFocus === view) {
10555
view = View._focused = prevFocus;
10556
updateFocus();
10557
}
10558
}
10559
if (view) {
10560
var point = viewToProject(view, event);
10561
if (dragging || view.getBounds().contains(point))
10562
tool = handleMouseMove(view, point, event);
10563
}
10564
};
10565
10566
docEvents[mouseup] = function(event) {
10567
var view = View._focused;
10568
if (!view || !dragging)
10569
return;
10570
var point = viewToProject(view, event);
10571
dragging = false;
10572
view._handleEvent('mouseup', point, event);
10573
if (tool)
10574
tool._handleEvent('mouseup', point, event);
10575
view.update();
10576
};
10577
10578
DomEvent.add(document, docEvents);
10579
10580
DomEvent.add(window, {
10581
load: updateFocus
10582
});
10583
10584
return {
10585
_viewEvents: viewEvents,
10586
10587
_handleEvent: function() {},
10588
10589
statics: {
10590
updateFocus: updateFocus
10591
}
10592
};
10593
});
10594
10595
var CanvasView = View.extend({
10596
_class: 'CanvasView',
10597
10598
initialize: function CanvasView(project, canvas) {
10599
if (!(canvas instanceof HTMLCanvasElement)) {
10600
var size = Size.read(arguments);
10601
if (size.isZero())
10602
throw new Error(
10603
'Cannot create CanvasView with the provided argument: '
10604
+ canvas);
10605
canvas = CanvasProvider.getCanvas(size);
10606
}
10607
this._context = canvas.getContext('2d');
10608
this._eventCounters = {};
10609
this._pixelRatio = 1;
10610
if (PaperScope.getAttribute(canvas, 'hidpi') !== 'off') {
10611
var deviceRatio = window.devicePixelRatio || 1,
10612
backingStoreRatio = DomElement.getPrefixed(this._context,
10613
'backingStorePixelRatio') || 1;
10614
this._pixelRatio = deviceRatio / backingStoreRatio;
10615
}
10616
View.call(this, project, canvas);
10617
},
10618
10619
_setViewSize: function(size) {
10620
var width = size.width,
10621
height = size.height,
10622
pixelRatio = this._pixelRatio,
10623
element = this._element,
10624
style = element.style;
10625
element.width = width * pixelRatio;
10626
element.height = height * pixelRatio;
10627
if (pixelRatio !== 1) {
10628
style.width = width + 'px';
10629
style.height = height + 'px';
10630
this._context.scale(pixelRatio, pixelRatio);
10631
}
10632
},
10633
10634
getPixelSize: function(size) {
10635
var ctx = this._context,
10636
prevFont = ctx.font;
10637
ctx.font = size + ' serif';
10638
size = parseFloat(ctx.font);
10639
ctx.font = prevFont;
10640
return size;
10641
},
10642
10643
getTextWidth: function(font, lines) {
10644
var ctx = this._context,
10645
prevFont = ctx.font,
10646
width = 0;
10647
ctx.font = font;
10648
for (var i = 0, l = lines.length; i < l; i++)
10649
width = Math.max(width, ctx.measureText(lines[i]).width);
10650
ctx.font = prevFont;
10651
return width;
10652
},
10653
10654
update: function() {
10655
var project = this._project;
10656
if (!project || !project._needsUpdate)
10657
return false;
10658
var ctx = this._context,
10659
size = this._viewSize;
10660
ctx.clearRect(0, 0, size.width + 1, size.height + 1);
10661
project.draw(ctx, this._matrix, this._pixelRatio);
10662
project._needsUpdate = false;
10663
return true;
10664
}
10665
}, new function() {
10666
10667
var downPoint,
10668
lastPoint,
10669
overPoint,
10670
downItem,
10671
lastItem,
10672
overItem,
10673
dragItem,
10674
dblClick,
10675
clickTime;
10676
10677
function callEvent(view, type, event, point, target, lastPoint) {
10678
var item = target,
10679
mouseEvent;
10680
10681
function call(obj) {
10682
if (obj.responds(type)) {
10683
if (!mouseEvent) {
10684
mouseEvent = new MouseEvent(type, event, point, target,
10685
lastPoint ? point.subtract(lastPoint) : null);
10686
}
10687
if (obj.fire(type, mouseEvent) && mouseEvent.isStopped) {
10688
event.preventDefault();
10689
return true;
10690
}
10691
}
10692
}
10693
10694
while (item) {
10695
if (call(item))
10696
return true;
10697
item = item.getParent();
10698
}
10699
if (call(view))
10700
return true;
10701
return false;
10702
}
10703
10704
return {
10705
_handleEvent: function(type, point, event) {
10706
if (!this._eventCounters[type])
10707
return;
10708
var project = this._project,
10709
hit = project.hitTest(point, {
10710
tolerance: this._scope.settings.hitTolerance,
10711
fill: true,
10712
stroke: true
10713
}),
10714
item = hit && hit.item,
10715
stopped = false;
10716
switch (type) {
10717
case 'mousedown':
10718
stopped = callEvent(this, type, event, point, item);
10719
dblClick = lastItem == item && (Date.now() - clickTime < 300);
10720
downItem = lastItem = item;
10721
downPoint = lastPoint = overPoint = point;
10722
dragItem = !stopped && item;
10723
while (dragItem && !dragItem.responds('mousedrag'))
10724
dragItem = dragItem._parent;
10725
break;
10726
case 'mouseup':
10727
stopped = callEvent(this, type, event, point, item, downPoint);
10728
if (dragItem) {
10729
if (lastPoint && !lastPoint.equals(point))
10730
callEvent(this, 'mousedrag', event, point, dragItem,
10731
lastPoint);
10732
if (item !== dragItem) {
10733
overPoint = point;
10734
callEvent(this, 'mousemove', event, point, item,
10735
overPoint);
10736
}
10737
}
10738
if (!stopped && item && item === downItem) {
10739
clickTime = Date.now();
10740
callEvent(this, dblClick && downItem.responds('doubleclick')
10741
? 'doubleclick' : 'click', event, downPoint, item);
10742
dblClick = false;
10743
}
10744
downItem = dragItem = null;
10745
break;
10746
case 'mousemove':
10747
if (dragItem)
10748
stopped = callEvent(this, 'mousedrag', event, point,
10749
dragItem, lastPoint);
10750
if (!stopped) {
10751
if (item !== overItem)
10752
overPoint = point;
10753
stopped = callEvent(this, type, event, point, item,
10754
overPoint);
10755
}
10756
lastPoint = overPoint = point;
10757
if (item !== overItem) {
10758
callEvent(this, 'mouseleave', event, point, overItem);
10759
overItem = item;
10760
callEvent(this, 'mouseenter', event, point, item);
10761
}
10762
break;
10763
}
10764
return stopped;
10765
}
10766
};
10767
});
10768
10769
var Event = Base.extend({
10770
_class: 'Event',
10771
10772
initialize: function Event(event) {
10773
this.event = event;
10774
},
10775
10776
isPrevented: false,
10777
isStopped: false,
10778
10779
preventDefault: function() {
10780
this.isPrevented = true;
10781
this.event.preventDefault();
10782
},
10783
10784
stopPropagation: function() {
10785
this.isStopped = true;
10786
this.event.stopPropagation();
10787
},
10788
10789
stop: function() {
10790
this.stopPropagation();
10791
this.preventDefault();
10792
},
10793
10794
getModifiers: function() {
10795
return Key.modifiers;
10796
}
10797
});
10798
10799
var KeyEvent = Event.extend({
10800
_class: 'KeyEvent',
10801
10802
initialize: function KeyEvent(down, key, character, event) {
10803
Event.call(this, event);
10804
this.type = down ? 'keydown' : 'keyup';
10805
this.key = key;
10806
this.character = character;
10807
},
10808
10809
toString: function() {
10810
return "{ type: '" + this.type
10811
+ "', key: '" + this.key
10812
+ "', character: '" + this.character
10813
+ "', modifiers: " + this.getModifiers()
10814
+ " }";
10815
}
10816
});
10817
10818
var Key = new function() {
10819
10820
var specialKeys = {
10821
8: 'backspace',
10822
9: 'tab',
10823
13: 'enter',
10824
16: 'shift',
10825
17: 'control',
10826
18: 'option',
10827
19: 'pause',
10828
20: 'caps-lock',
10829
27: 'escape',
10830
32: 'space',
10831
35: 'end',
10832
36: 'home',
10833
37: 'left',
10834
38: 'up',
10835
39: 'right',
10836
40: 'down',
10837
46: 'delete',
10838
91: 'command',
10839
93: 'command',
10840
224: 'command'
10841
},
10842
10843
specialChars = {
10844
9: true,
10845
13: true,
10846
32: true
10847
},
10848
10849
modifiers = new Base({
10850
shift: false,
10851
control: false,
10852
option: false,
10853
command: false,
10854
capsLock: false,
10855
space: false
10856
}),
10857
10858
charCodeMap = {},
10859
keyMap = {},
10860
downCode;
10861
10862
function handleKey(down, keyCode, charCode, event) {
10863
var character = charCode ? String.fromCharCode(charCode) : '',
10864
specialKey = specialKeys[keyCode],
10865
key = specialKey || character.toLowerCase(),
10866
type = down ? 'keydown' : 'keyup',
10867
view = View._focused,
10868
scope = view && view.isVisible() && view._scope,
10869
tool = scope && scope.tool,
10870
name;
10871
keyMap[key] = down;
10872
if (specialKey && (name = Base.camelize(specialKey)) in modifiers)
10873
modifiers[name] = down;
10874
if (down) {
10875
charCodeMap[keyCode] = charCode;
10876
} else {
10877
delete charCodeMap[keyCode];
10878
}
10879
if (tool && tool.responds(type)) {
10880
paper = scope;
10881
tool.fire(type, new KeyEvent(down, key, character, event));
10882
if (view)
10883
view.update();
10884
}
10885
}
10886
10887
DomEvent.add(document, {
10888
keydown: function(event) {
10889
var code = event.which || event.keyCode;
10890
if (code in specialKeys || modifiers.command) {
10891
handleKey(true, code,
10892
code in specialChars || modifiers.command ? code : 0,
10893
event);
10894
} else {
10895
downCode = code;
10896
}
10897
},
10898
10899
keypress: function(event) {
10900
if (downCode != null) {
10901
handleKey(true, downCode, event.which || event.keyCode, event);
10902
downCode = null;
10903
}
10904
},
10905
10906
keyup: function(event) {
10907
var code = event.which || event.keyCode;
10908
if (code in charCodeMap)
10909
handleKey(false, code, charCodeMap[code], event);
10910
}
10911
});
10912
10913
DomEvent.add(window, {
10914
blur: function(event) {
10915
for (var code in charCodeMap)
10916
handleKey(false, code, charCodeMap[code], event);
10917
}
10918
});
10919
10920
return {
10921
modifiers: modifiers,
10922
10923
isDown: function(key) {
10924
return !!keyMap[key];
10925
}
10926
};
10927
};
10928
10929
var MouseEvent = Event.extend({
10930
_class: 'MouseEvent',
10931
10932
initialize: function MouseEvent(type, event, point, target, delta) {
10933
Event.call(this, event);
10934
this.type = type;
10935
this.point = point;
10936
this.target = target;
10937
this.delta = delta;
10938
},
10939
10940
toString: function() {
10941
return "{ type: '" + this.type
10942
+ "', point: " + this.point
10943
+ ', target: ' + this.target
10944
+ (this.delta ? ', delta: ' + this.delta : '')
10945
+ ', modifiers: ' + this.getModifiers()
10946
+ ' }';
10947
}
10948
});
10949
10950
Base.extend(Callback, {
10951
_class: 'Palette',
10952
_events: [ 'onChange' ],
10953
10954
initialize: function Palette(title, components, values) {
10955
var parent = DomElement.find('.palettejs-panel')
10956
|| DomElement.find('body').appendChild(
10957
DomElement.create('div', { 'class': 'palettejs-panel' }));
10958
this._element = parent.appendChild(
10959
DomElement.create('table', { 'class': 'palettejs-pane' }));
10960
this._title = title;
10961
if (!values)
10962
values = {};
10963
for (var name in (this.components = components)) {
10964
var component = components[name];
10965
if (!(component instanceof Component)) {
10966
if (component.value == null)
10967
component.value = values[name];
10968
component.name = name;
10969
component = components[name] = new Component(component);
10970
}
10971
this._element.appendChild(component._element);
10972
component._palette = this;
10973
if (values[name] === undefined)
10974
values[name] = component.value;
10975
}
10976
this.values = Base.each(values, function(value, name) {
10977
var component = components[name];
10978
if (component) {
10979
Base.define(values, name, {
10980
enumerable: true,
10981
configurable: true,
10982
get: function() {
10983
return component._value;
10984
},
10985
set: function(val) {
10986
component.setValue(val);
10987
}
10988
});
10989
}
10990
});
10991
if (window.paper)
10992
paper.palettes.push(this);
10993
},
10994
10995
reset: function() {
10996
for (var i in this.components)
10997
this.components[i].reset();
10998
},
10999
11000
remove: function() {
11001
DomElement.remove(this._element);
11002
}
11003
});
11004
11005
var Component = Base.extend(Callback, {
11006
_class: 'Component',
11007
_events: [ 'onChange', 'onClick' ],
11008
11009
_types: {
11010
'boolean': {
11011
type: 'checkbox',
11012
value: 'checked'
11013
},
11014
11015
string: {
11016
type: 'text'
11017
},
11018
11019
number: {
11020
type: 'number',
11021
number: true
11022
},
11023
11024
button: {
11025
type: 'button'
11026
},
11027
11028
text: {
11029
tag: 'div',
11030
value: 'text'
11031
},
11032
11033
slider: {
11034
type: 'range',
11035
number: true
11036
},
11037
11038
list: {
11039
tag: 'select',
11040
11041
setOptions: function() {
11042
DomElement.removeChildren(this._input);
11043
DomElement.create(Base.each(this._options, function(option) {
11044
this.push('option', { value: option, text: option });
11045
}, []), this._input);
11046
}
11047
},
11048
11049
color: {
11050
type: 'color',
11051
11052
getValue: function(value) {
11053
return new Color(value);
11054
},
11055
11056
setValue: function(value) {
11057
return new Color(value).toCSS(
11058
DomElement.get(this._input, 'type') === 'color');
11059
}
11060
}
11061
},
11062
11063
initialize: function Component(obj) {
11064
this._id = Component._id = (Component._id || 0) + 1;
11065
this._type = obj.type in this._types
11066
? obj.type
11067
: 'options' in obj
11068
? 'list'
11069
: 'onClick' in obj
11070
? 'button'
11071
: typeof obj.value;
11072
this._meta = this._types[this._type] || { type: this._type };
11073
var that = this,
11074
id = 'component-' + this._id;
11075
this._dontFire = true;
11076
this._input = DomElement.create(this._meta.tag || 'input', {
11077
id: id,
11078
type: this._meta.type,
11079
events: {
11080
change: function() {
11081
that.setValue(
11082
DomElement.get(this, that._meta.value || 'value'));
11083
},
11084
click: function() {
11085
that.fire('click');
11086
}
11087
}
11088
});
11089
this.attach('change', function(value) {
11090
if (!this._dontFire)
11091
this._palette.fire('change', this, this.name, value);
11092
});
11093
this._element = DomElement.create('tr', [
11094
'td', [this._label = DomElement.create('label', { 'for': id })],
11095
'td', [this._input]
11096
]);
11097
Base.each(obj, function(value, key) {
11098
this[key] = value;
11099
}, this);
11100
this._defaultValue = this._value;
11101
this._dontFire = false;
11102
},
11103
11104
getType: function() {
11105
return this._type;
11106
},
11107
11108
getLabel: function() {
11109
return this.__label;
11110
},
11111
11112
setLabel: function(label) {
11113
this.__label = label;
11114
DomElement.set(this._label, 'text', label + ':');
11115
},
11116
11117
getOptions: function() {
11118
return this._options;
11119
},
11120
11121
setOptions: function(options) {
11122
this._options = options;
11123
var setOptions = this._meta.setOptions;
11124
if (setOptions)
11125
setOptions.call(this);
11126
},
11127
11128
getValue: function() {
11129
var value = this._value,
11130
getValue = this._meta.getValue;
11131
return getValue ? getValue.call(this, value) : value;
11132
},
11133
11134
setValue: function(value) {
11135
var key = this._meta.value || 'value',
11136
setValue = this._meta.setValue;
11137
if (setValue)
11138
value = setValue.call(this, value);
11139
DomElement.set(this._input, key, value);
11140
value = DomElement.get(this._input, key);
11141
if (this._meta.number)
11142
value = parseFloat(value, 10);
11143
if (this._value !== value) {
11144
this._value = value;
11145
if (!this._dontFire)
11146
this.fire('change', this.getValue());
11147
}
11148
},
11149
11150
getRange: function() {
11151
return [parseFloat(DomElement.get(this._input, 'min')),
11152
parseFloat(DomElement.get(this._input, 'max'))];
11153
},
11154
11155
setRange: function(min, max) {
11156
var range = Array.isArray(min) ? min : [min, max];
11157
DomElement.set(this._input, { min: range[0], max: range[1] });
11158
},
11159
11160
getMin: function() {
11161
return this.getRange()[0];
11162
},
11163
11164
setMin: function(min) {
11165
this.setRange(min, this.getMax());
11166
},
11167
11168
getMax: function() {
11169
return this.getRange()[1];
11170
},
11171
11172
setMax: function(max) {
11173
this.setRange(this.getMin(), max);
11174
},
11175
11176
getStep: function() {
11177
return parseFloat(DomElement.get(this._input, 'step'));
11178
},
11179
11180
setStep: function(step) {
11181
DomElement.set(this._input, 'step', step);
11182
},
11183
11184
reset: function() {
11185
this.setValue(this._defaultValue);
11186
}
11187
});
11188
11189
var ToolEvent = Event.extend({
11190
_class: 'ToolEvent',
11191
_item: null,
11192
11193
initialize: function ToolEvent(tool, type, event) {
11194
this.tool = tool;
11195
this.type = type;
11196
this.event = event;
11197
},
11198
11199
_choosePoint: function(point, toolPoint) {
11200
return point ? point : toolPoint ? toolPoint.clone() : null;
11201
},
11202
11203
getPoint: function() {
11204
return this._choosePoint(this._point, this.tool._point);
11205
},
11206
11207
setPoint: function(point) {
11208
this._point = point;
11209
},
11210
11211
getLastPoint: function() {
11212
return this._choosePoint(this._lastPoint, this.tool._lastPoint);
11213
},
11214
11215
setLastPoint: function(lastPoint) {
11216
this._lastPoint = lastPoint;
11217
},
11218
11219
getDownPoint: function() {
11220
return this._choosePoint(this._downPoint, this.tool._downPoint);
11221
},
11222
11223
setDownPoint: function(downPoint) {
11224
this._downPoint = downPoint;
11225
},
11226
11227
getMiddlePoint: function() {
11228
if (!this._middlePoint && this.tool._lastPoint) {
11229
return this.tool._point.add(this.tool._lastPoint).divide(2);
11230
}
11231
return this._middlePoint;
11232
},
11233
11234
setMiddlePoint: function(middlePoint) {
11235
this._middlePoint = middlePoint;
11236
},
11237
11238
getDelta: function() {
11239
return !this._delta && this.tool._lastPoint
11240
? this.tool._point.subtract(this.tool._lastPoint)
11241
: this._delta;
11242
},
11243
11244
setDelta: function(delta) {
11245
this._delta = delta;
11246
},
11247
11248
getCount: function() {
11249
return /^mouse(down|up)$/.test(this.type)
11250
? this.tool._downCount
11251
: this.tool._count;
11252
},
11253
11254
setCount: function(count) {
11255
this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
11256
= count;
11257
},
11258
11259
getItem: function() {
11260
if (!this._item) {
11261
var result = this.tool._scope.project.hitTest(this.getPoint());
11262
if (result) {
11263
var item = result.item,
11264
parent = item._parent;
11265
while (/^(Group|CompoundPath)$/.test(parent._class)) {
11266
item = parent;
11267
parent = parent._parent;
11268
}
11269
this._item = item;
11270
}
11271
}
11272
return this._item;
11273
},
11274
11275
setItem: function(item) {
11276
this._item = item;
11277
},
11278
11279
toString: function() {
11280
return '{ type: ' + this.type
11281
+ ', point: ' + this.getPoint()
11282
+ ', count: ' + this.getCount()
11283
+ ', modifiers: ' + this.getModifiers()
11284
+ ' }';
11285
}
11286
});
11287
11288
var Tool = PaperScopeItem.extend({
11289
_class: 'Tool',
11290
_list: 'tools',
11291
_reference: 'tool',
11292
_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
11293
'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
11294
'onKeyDown', 'onKeyUp' ],
11295
11296
initialize: function Tool(props) {
11297
PaperScopeItem.call(this);
11298
this._firstMove = true;
11299
this._count = 0;
11300
this._downCount = 0;
11301
this._set(props);
11302
},
11303
11304
getMinDistance: function() {
11305
return this._minDistance;
11306
},
11307
11308
setMinDistance: function(minDistance) {
11309
this._minDistance = minDistance;
11310
if (this._minDistance != null && this._maxDistance != null
11311
&& this._minDistance > this._maxDistance) {
11312
this._maxDistance = this._minDistance;
11313
}
11314
},
11315
11316
getMaxDistance: function() {
11317
return this._maxDistance;
11318
},
11319
11320
setMaxDistance: function(maxDistance) {
11321
this._maxDistance = maxDistance;
11322
if (this._minDistance != null && this._maxDistance != null
11323
&& this._maxDistance < this._minDistance) {
11324
this._minDistance = maxDistance;
11325
}
11326
},
11327
11328
getFixedDistance: function() {
11329
return this._minDistance == this._maxDistance
11330
? this._minDistance : null;
11331
},
11332
11333
setFixedDistance: function(distance) {
11334
this._minDistance = distance;
11335
this._maxDistance = distance;
11336
},
11337
11338
_updateEvent: function(type, point, minDistance, maxDistance, start,
11339
needsChange, matchMaxDistance) {
11340
if (!start) {
11341
if (minDistance != null || maxDistance != null) {
11342
var minDist = minDistance != null ? minDistance : 0,
11343
vector = point.subtract(this._point),
11344
distance = vector.getLength();
11345
if (distance < minDist)
11346
return false;
11347
var maxDist = maxDistance != null ? maxDistance : 0;
11348
if (maxDist != 0) {
11349
if (distance > maxDist) {
11350
point = this._point.add(vector.normalize(maxDist));
11351
} else if (matchMaxDistance) {
11352
return false;
11353
}
11354
}
11355
}
11356
if (needsChange && point.equals(this._point))
11357
return false;
11358
}
11359
this._lastPoint = start && type == 'mousemove' ? point : this._point;
11360
this._point = point;
11361
switch (type) {
11362
case 'mousedown':
11363
this._lastPoint = this._downPoint;
11364
this._downPoint = this._point;
11365
this._downCount++;
11366
break;
11367
case 'mouseup':
11368
this._lastPoint = this._downPoint;
11369
break;
11370
}
11371
this._count = start ? 0 : this._count + 1;
11372
return true;
11373
},
11374
11375
_fireEvent: function(type, event) {
11376
var sets = paper.project._removeSets;
11377
if (sets) {
11378
if (type === 'mouseup')
11379
sets.mousedrag = null;
11380
var set = sets[type];
11381
if (set) {
11382
for (var id in set) {
11383
var item = set[id];
11384
for (var key in sets) {
11385
var other = sets[key];
11386
if (other && other != set)
11387
delete other[item._id];
11388
}
11389
item.remove();
11390
}
11391
sets[type] = null;
11392
}
11393
}
11394
return this.responds(type)
11395
&& this.fire(type, new ToolEvent(this, type, event));
11396
},
11397
11398
_handleEvent: function(type, point, event) {
11399
paper = this._scope;
11400
var called = false;
11401
switch (type) {
11402
case 'mousedown':
11403
this._updateEvent(type, point, null, null, true, false, false);
11404
called = this._fireEvent(type, event);
11405
break;
11406
case 'mousedrag':
11407
var needsChange = false,
11408
matchMaxDistance = false;
11409
while (this._updateEvent(type, point, this.minDistance,
11410
this.maxDistance, false, needsChange, matchMaxDistance)) {
11411
called = this._fireEvent(type, event) || called;
11412
needsChange = true;
11413
matchMaxDistance = true;
11414
}
11415
break;
11416
case 'mouseup':
11417
if (!point.equals(this._point)
11418
&& this._updateEvent('mousedrag', point, this.minDistance,
11419
this.maxDistance, false, false, false)) {
11420
called = this._fireEvent('mousedrag', event);
11421
}
11422
this._updateEvent(type, point, null, this.maxDistance, false,
11423
false, false);
11424
called = this._fireEvent(type, event) || called;
11425
this._updateEvent(type, point, null, null, true, false, false);
11426
this._firstMove = true;
11427
break;
11428
case 'mousemove':
11429
while (this._updateEvent(type, point, this.minDistance,
11430
this.maxDistance, this._firstMove, true, false)) {
11431
called = this._fireEvent(type, event) || called;
11432
this._firstMove = false;
11433
}
11434
break;
11435
}
11436
if (called)
11437
event.preventDefault();
11438
return called;
11439
}
11440
11441
});
11442
11443
var Http = {
11444
request: function(method, url, callback) {
11445
var xhr = new (window.ActiveXObject || XMLHttpRequest)(
11446
'Microsoft.XMLHTTP');
11447
xhr.open(method.toUpperCase(), url, true);
11448
if ('overrideMimeType' in xhr)
11449
xhr.overrideMimeType('text/plain');
11450
xhr.onreadystatechange = function() {
11451
if (xhr.readyState === 4) {
11452
var status = xhr.status;
11453
if (status === 0 || status === 200) {
11454
callback.call(xhr, xhr.responseText);
11455
} else {
11456
throw new Error('Could not load ' + url + ' (Error '
11457
+ status + ')');
11458
}
11459
}
11460
};
11461
return xhr.send(null);
11462
}
11463
};
11464
11465
var CanvasProvider = {
11466
canvases: [],
11467
11468
getCanvas: function(width, height, pixelRatio) {
11469
var canvas,
11470
init = true;
11471
if (typeof width === 'object') {
11472
pixelRatio = height;
11473
height = width.height;
11474
width = width.width;
11475
}
11476
if (!pixelRatio) {
11477
pixelRatio = 1;
11478
} else if (pixelRatio !== 1) {
11479
width *= pixelRatio;
11480
height *= pixelRatio;
11481
}
11482
if (this.canvases.length) {
11483
canvas = this.canvases.pop();
11484
} else {
11485
canvas = document.createElement('canvas');
11486
}
11487
var ctx = canvas.getContext('2d');
11488
if (canvas.width === width && canvas.height === height) {
11489
if (init)
11490
ctx.clearRect(0, 0, width + 1, height + 1);
11491
} else {
11492
canvas.width = width;
11493
canvas.height = height;
11494
}
11495
ctx.save();
11496
if (pixelRatio !== 1)
11497
ctx.scale(pixelRatio, pixelRatio);
11498
return canvas;
11499
},
11500
11501
getContext: function(width, height, pixelRatio) {
11502
return this.getCanvas(width, height, pixelRatio).getContext('2d');
11503
},
11504
11505
release: function(obj) {
11506
var canvas = obj.canvas ? obj.canvas : obj;
11507
canvas.getContext('2d').restore();
11508
this.canvases.push(canvas);
11509
}
11510
};
11511
11512
var BlendMode = new function() {
11513
var min = Math.min,
11514
max = Math.max,
11515
abs = Math.abs,
11516
sr, sg, sb, sa,
11517
br, bg, bb, ba,
11518
dr, dg, db;
11519
11520
function getLum(r, g, b) {
11521
return 0.2989 * r + 0.587 * g + 0.114 * b;
11522
}
11523
11524
function setLum(r, g, b, l) {
11525
var d = l - getLum(r, g, b);
11526
dr = r + d;
11527
dg = g + d;
11528
db = b + d;
11529
var l = getLum(dr, dg, db),
11530
mn = min(dr, dg, db),
11531
mx = max(dr, dg, db);
11532
if (mn < 0) {
11533
var lmn = l - mn;
11534
dr = l + (dr - l) * l / lmn;
11535
dg = l + (dg - l) * l / lmn;
11536
db = l + (db - l) * l / lmn;
11537
}
11538
if (mx > 255) {
11539
var ln = 255 - l,
11540
mxl = mx - l;
11541
dr = l + (dr - l) * ln / mxl;
11542
dg = l + (dg - l) * ln / mxl;
11543
db = l + (db - l) * ln / mxl;
11544
}
11545
}
11546
11547
function getSat(r, g, b) {
11548
return max(r, g, b) - min(r, g, b);
11549
}
11550
11551
function setSat(r, g, b, s) {
11552
var col = [r, g, b],
11553
mx = max(r, g, b),
11554
mn = min(r, g, b),
11555
md;
11556
mn = mn === r ? 0 : mn === g ? 1 : 2;
11557
mx = mx === r ? 0 : mx === g ? 1 : 2;
11558
md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;
11559
if (col[mx] > col[mn]) {
11560
col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]);
11561
col[mx] = s;
11562
} else {
11563
col[md] = col[mx] = 0;
11564
}
11565
col[mn] = 0;
11566
dr = col[0];
11567
dg = col[1];
11568
db = col[2];
11569
}
11570
11571
var modes = {
11572
multiply: function() {
11573
dr = br * sr / 255;
11574
dg = bg * sg / 255;
11575
db = bb * sb / 255;
11576
},
11577
11578
screen: function() {
11579
dr = br + sr - (br * sr / 255);
11580
dg = bg + sg - (bg * sg / 255);
11581
db = bb + sb - (bb * sb / 255);
11582
},
11583
11584
overlay: function() {
11585
dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255;
11586
dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255;
11587
db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255;
11588
},
11589
11590
'soft-light': function() {
11591
var t = sr * br / 255;
11592
dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255;
11593
t = sg * bg / 255;
11594
dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255;
11595
t = sb * bb / 255;
11596
db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255;
11597
},
11598
11599
'hard-light': function() {
11600
dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255;
11601
dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255;
11602
db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255;
11603
},
11604
11605
'color-dodge': function() {
11606
dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr));
11607
dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg));
11608
db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb));
11609
},
11610
11611
'color-burn': function() {
11612
dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr);
11613
dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg);
11614
db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb);
11615
},
11616
11617
darken: function() {
11618
dr = br < sr ? br : sr;
11619
dg = bg < sg ? bg : sg;
11620
db = bb < sb ? bb : sb;
11621
},
11622
11623
lighten: function() {
11624
dr = br > sr ? br : sr;
11625
dg = bg > sg ? bg : sg;
11626
db = bb > sb ? bb : sb;
11627
},
11628
11629
difference: function() {
11630
dr = br - sr;
11631
if (dr < 0)
11632
dr = -dr;
11633
dg = bg - sg;
11634
if (dg < 0)
11635
dg = -dg;
11636
db = bb - sb;
11637
if (db < 0)
11638
db = -db;
11639
},
11640
11641
exclusion: function() {
11642
dr = br + sr * (255 - br - br) / 255;
11643
dg = bg + sg * (255 - bg - bg) / 255;
11644
db = bb + sb * (255 - bb - bb) / 255;
11645
},
11646
11647
hue: function() {
11648
setSat(sr, sg, sb, getSat(br, bg, bb));
11649
setLum(dr, dg, db, getLum(br, bg, bb));
11650
},
11651
11652
saturation: function() {
11653
setSat(br, bg, bb, getSat(sr, sg, sb));
11654
setLum(dr, dg, db, getLum(br, bg, bb));
11655
},
11656
11657
luminosity: function() {
11658
setLum(br, bg, bb, getLum(sr, sg, sb));
11659
},
11660
11661
color: function() {
11662
setLum(sr, sg, sb, getLum(br, bg, bb));
11663
},
11664
11665
add: function() {
11666
dr = min(br + sr, 255);
11667
dg = min(bg + sg, 255);
11668
db = min(bb + sb, 255);
11669
},
11670
11671
subtract: function() {
11672
dr = max(br - sr, 0);
11673
dg = max(bg - sg, 0);
11674
db = max(bb - sb, 0);
11675
},
11676
11677
average: function() {
11678
dr = (br + sr) / 2;
11679
dg = (bg + sg) / 2;
11680
db = (bb + sb) / 2;
11681
},
11682
11683
negation: function() {
11684
dr = 255 - abs(255 - sr - br);
11685
dg = 255 - abs(255 - sg - bg);
11686
db = 255 - abs(255 - sb - bb);
11687
}
11688
};
11689
11690
var nativeModes = this.nativeModes = Base.each([
11691
'source-over', 'source-in', 'source-out', 'source-atop',
11692
'destination-over', 'destination-in', 'destination-out',
11693
'destination-atop', 'lighter', 'darker', 'copy', 'xor'
11694
], function(mode) {
11695
this[mode] = true;
11696
}, {});
11697
11698
var ctx = CanvasProvider.getContext(1, 1);
11699
Base.each(modes, function(func, mode) {
11700
var darken = mode === 'darken',
11701
ok = false;
11702
ctx.save();
11703
try {
11704
ctx.fillStyle = darken ? '#300' : '#a00';
11705
ctx.fillRect(0, 0, 1, 1);
11706
ctx.globalCompositeOperation = mode;
11707
if (ctx.globalCompositeOperation === mode) {
11708
ctx.fillStyle = darken ? '#a00' : '#300';
11709
ctx.fillRect(0, 0, 1, 1);
11710
ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51;
11711
}
11712
} catch (e) {}
11713
ctx.restore();
11714
nativeModes[mode] = ok;
11715
});
11716
CanvasProvider.release(ctx);
11717
11718
this.process = function(mode, srcContext, dstContext, alpha, offset) {
11719
var srcCanvas = srcContext.canvas,
11720
normal = mode === 'normal';
11721
if (normal || nativeModes[mode]) {
11722
dstContext.save();
11723
dstContext.setTransform(1, 0, 0, 1, 0, 0);
11724
dstContext.globalAlpha = alpha;
11725
if (!normal)
11726
dstContext.globalCompositeOperation = mode;
11727
dstContext.drawImage(srcCanvas, offset.x, offset.y);
11728
dstContext.restore();
11729
} else {
11730
var process = modes[mode];
11731
if (!process)
11732
return;
11733
var dstData = dstContext.getImageData(offset.x, offset.y,
11734
srcCanvas.width, srcCanvas.height),
11735
dst = dstData.data,
11736
src = srcContext.getImageData(0, 0,
11737
srcCanvas.width, srcCanvas.height).data;
11738
for (var i = 0, l = dst.length; i < l; i += 4) {
11739
sr = src[i];
11740
br = dst[i];
11741
sg = src[i + 1];
11742
bg = dst[i + 1];
11743
sb = src[i + 2];
11744
bb = dst[i + 2];
11745
sa = src[i + 3];
11746
ba = dst[i + 3];
11747
process();
11748
var a1 = sa * alpha / 255,
11749
a2 = 1 - a1;
11750
dst[i] = a1 * dr + a2 * br;
11751
dst[i + 1] = a1 * dg + a2 * bg;
11752
dst[i + 2] = a1 * db + a2 * bb;
11753
dst[i + 3] = sa * alpha + a2 * ba;
11754
}
11755
dstContext.putImageData(dstData, offset.x, offset.y);
11756
}
11757
};
11758
};
11759
11760
var SVGStyles = Base.each({
11761
fillColor: ['fill', 'color'],
11762
strokeColor: ['stroke', 'color'],
11763
strokeWidth: ['stroke-width', 'number'],
11764
strokeCap: ['stroke-linecap', 'string'],
11765
strokeJoin: ['stroke-linejoin', 'string'],
11766
miterLimit: ['stroke-miterlimit', 'number'],
11767
dashArray: ['stroke-dasharray', 'array'],
11768
dashOffset: ['stroke-dashoffset', 'number'],
11769
fontFamily: ['font-family', 'string'],
11770
fontWeight: ['font-weight', 'string'],
11771
fontSize: ['font-size', 'number'],
11772
justification: ['text-anchor', 'lookup', {
11773
left: 'start',
11774
center: 'middle',
11775
right: 'end'
11776
}],
11777
opacity: ['opacity', 'number'],
11778
blendMode: ['mix-blend-mode', 'string']
11779
}, function(entry, key) {
11780
var part = Base.capitalize(key),
11781
lookup = entry[2];
11782
this[key] = {
11783
type: entry[1],
11784
property: key,
11785
attribute: entry[0],
11786
toSVG: lookup,
11787
fromSVG: lookup && Base.each(lookup, function(value, name) {
11788
this[value] = name;
11789
}, {}),
11790
get: 'get' + part,
11791
set: 'set' + part
11792
};
11793
}, {});
11794
11795
var SVGNamespaces = {
11796
href: 'http://www.w3.org/1999/xlink',
11797
xlink: 'http://www.w3.org/2000/xmlns'
11798
};
11799
11800
new function() {
11801
var formatter;
11802
11803
function setAttributes(node, attrs) {
11804
for (var key in attrs) {
11805
var val = attrs[key],
11806
namespace = SVGNamespaces[key];
11807
if (typeof val === 'number')
11808
val = formatter.number(val);
11809
if (namespace) {
11810
node.setAttributeNS(namespace, key, val);
11811
} else {
11812
node.setAttribute(key, val);
11813
}
11814
}
11815
return node;
11816
}
11817
11818
function createElement(tag, attrs) {
11819
return setAttributes(
11820
document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
11821
}
11822
11823
function getTransform(item, coordinates, center) {
11824
var matrix = item._matrix,
11825
trans = matrix.getTranslation(),
11826
attrs = {};
11827
if (coordinates) {
11828
matrix = matrix.shiftless();
11829
var point = matrix._inverseTransform(trans);
11830
attrs[center ? 'cx' : 'x'] = point.x;
11831
attrs[center ? 'cy' : 'y'] = point.y;
11832
trans = null;
11833
}
11834
if (!matrix.isIdentity()) {
11835
var decomposed = matrix.decompose();
11836
if (decomposed && !decomposed.shearing) {
11837
var parts = [],
11838
angle = decomposed.rotation,
11839
scale = decomposed.scaling;
11840
if (trans && !trans.isZero())
11841
parts.push('translate(' + formatter.point(trans) + ')');
11842
if (angle)
11843
parts.push('rotate(' + formatter.number(angle) + ')');
11844
if (!Numerical.isZero(scale.x - 1)
11845
|| !Numerical.isZero(scale.y - 1))
11846
parts.push('scale(' + formatter.point(scale) +')');
11847
attrs.transform = parts.join(' ');
11848
} else {
11849
attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
11850
}
11851
}
11852
return attrs;
11853
}
11854
11855
function exportGroup(item, options) {
11856
var attrs = getTransform(item),
11857
children = item._children;
11858
var node = createElement('g', attrs);
11859
for (var i = 0, l = children.length; i < l; i++) {
11860
var child = children[i];
11861
var childNode = exportSVG(child, options);
11862
if (childNode) {
11863
if (child.isClipMask()) {
11864
var clip = createElement('clipPath');
11865
clip.appendChild(childNode);
11866
setDefinition(child, clip, 'clip');
11867
setAttributes(node, {
11868
'clip-path': 'url(#' + clip.id + ')'
11869
});
11870
} else {
11871
node.appendChild(childNode);
11872
}
11873
}
11874
}
11875
return node;
11876
}
11877
11878
function exportRaster(item) {
11879
var attrs = getTransform(item, true),
11880
size = item.getSize();
11881
attrs.x -= size.width / 2;
11882
attrs.y -= size.height / 2;
11883
attrs.width = size.width;
11884
attrs.height = size.height;
11885
attrs.href = item.toDataURL();
11886
return createElement('image', attrs);
11887
}
11888
11889
function exportPath(item, options) {
11890
if (options.matchShapes) {
11891
var shape = item.toShape(false);
11892
if (shape)
11893
return exportShape(shape, options);
11894
}
11895
var segments = item._segments,
11896
type,
11897
attrs;
11898
if (segments.length === 0)
11899
return null;
11900
if (item.isPolygon()) {
11901
if (segments.length >= 3) {
11902
type = item._closed ? 'polygon' : 'polyline';
11903
var parts = [];
11904
for(i = 0, l = segments.length; i < l; i++)
11905
parts.push(formatter.point(segments[i]._point));
11906
attrs = {
11907
points: parts.join(' ')
11908
};
11909
} else {
11910
type = 'line';
11911
var first = segments[0]._point,
11912
last = segments[segments.length - 1]._point;
11913
attrs = {
11914
x1: first.x,
11915
y1: first.y,
11916
x2: last.x,
11917
y2: last.y
11918
};
11919
}
11920
} else {
11921
type = 'path';
11922
var data = item.getPathData();
11923
attrs = data && { d: data };
11924
}
11925
return createElement(type, attrs);
11926
}
11927
11928
function exportShape(item) {
11929
var type = item._type,
11930
radius = item._radius,
11931
attrs = getTransform(item, true, type !== 'rectangle');
11932
if (type === 'rectangle') {
11933
type = 'rect';
11934
var size = item._size,
11935
width = size.width,
11936
height = size.height;
11937
attrs.x -= width / 2;
11938
attrs.y -= height / 2;
11939
attrs.width = width;
11940
attrs.height = height;
11941
if (radius.isZero())
11942
radius = null;
11943
}
11944
if (radius) {
11945
if (type === 'circle') {
11946
attrs.r = radius;
11947
} else {
11948
attrs.rx = radius.width;
11949
attrs.ry = radius.height;
11950
}
11951
}
11952
return createElement(type, attrs);
11953
}
11954
11955
function exportCompoundPath(item) {
11956
var attrs = getTransform(item, true);
11957
var data = item.getPathData();
11958
if (data)
11959
attrs.d = data;
11960
return createElement('path', attrs);
11961
}
11962
11963
function exportPlacedSymbol(item, options) {
11964
var attrs = getTransform(item, true),
11965
symbol = item.getSymbol(),
11966
symbolNode = getDefinition(symbol, 'symbol'),
11967
definition = symbol.getDefinition(),
11968
bounds = definition.getBounds();
11969
if (!symbolNode) {
11970
symbolNode = createElement('symbol', {
11971
viewBox: formatter.rectangle(bounds)
11972
});
11973
symbolNode.appendChild(exportSVG(definition, options));
11974
setDefinition(symbol, symbolNode, 'symbol');
11975
}
11976
attrs.href = '#' + symbolNode.id;
11977
attrs.x += bounds.x;
11978
attrs.y += bounds.y;
11979
attrs.width = formatter.number(bounds.width);
11980
attrs.height = formatter.number(bounds.height);
11981
return createElement('use', attrs);
11982
}
11983
11984
function exportGradient(color) {
11985
var gradientNode = getDefinition(color, 'color');
11986
if (!gradientNode) {
11987
var gradient = color.getGradient(),
11988
radial = gradient._radial,
11989
origin = color.getOrigin().transform(),
11990
destination = color.getDestination().transform(),
11991
attrs;
11992
if (radial) {
11993
attrs = {
11994
cx: origin.x,
11995
cy: origin.y,
11996
r: origin.getDistance(destination)
11997
};
11998
var highlight = color.getHighlight();
11999
if (highlight) {
12000
highlight = highlight.transform();
12001
attrs.fx = highlight.x;
12002
attrs.fy = highlight.y;
12003
}
12004
} else {
12005
attrs = {
12006
x1: origin.x,
12007
y1: origin.y,
12008
x2: destination.x,
12009
y2: destination.y
12010
};
12011
}
12012
attrs.gradientUnits = 'userSpaceOnUse';
12013
gradientNode = createElement(
12014
(radial ? 'radial' : 'linear') + 'Gradient', attrs);
12015
var stops = gradient._stops;
12016
for (var i = 0, l = stops.length; i < l; i++) {
12017
var stop = stops[i],
12018
stopColor = stop._color,
12019
alpha = stopColor.getAlpha();
12020
attrs = {
12021
offset: stop._rampPoint,
12022
'stop-color': stopColor.toCSS(true)
12023
};
12024
if (alpha < 1)
12025
attrs['stop-opacity'] = alpha;
12026
gradientNode.appendChild(createElement('stop', attrs));
12027
}
12028
setDefinition(color, gradientNode, 'color');
12029
}
12030
return 'url(#' + gradientNode.id + ')';
12031
}
12032
12033
function exportText(item) {
12034
var node = createElement('text', getTransform(item, true));
12035
node.textContent = item._content;
12036
return node;
12037
}
12038
12039
var exporters = {
12040
Group: exportGroup,
12041
Layer: exportGroup,
12042
Raster: exportRaster,
12043
Path: exportPath,
12044
Shape: exportShape,
12045
CompoundPath: exportCompoundPath,
12046
PlacedSymbol: exportPlacedSymbol,
12047
PointText: exportText
12048
};
12049
12050
function applyStyle(item, node) {
12051
var attrs = {},
12052
parent = item.getParent();
12053
12054
if (item._name != null)
12055
attrs.id = item._name;
12056
12057
Base.each(SVGStyles, function(entry) {
12058
var get = entry.get,
12059
type = entry.type,
12060
value = item[get]();
12061
if (!parent || !Base.equals(parent[get](), value)) {
12062
if (type === 'color' && value != null) {
12063
var alpha = value.getAlpha();
12064
if (alpha < 1)
12065
attrs[entry.attribute + '-opacity'] = alpha;
12066
}
12067
attrs[entry.attribute] = value == null
12068
? 'none'
12069
: type === 'number'
12070
? formatter.number(value)
12071
: type === 'color'
12072
? value.gradient
12073
? exportGradient(value, item)
12074
: value.toCSS(true)
12075
: type === 'array'
12076
? value.join(',')
12077
: type === 'lookup'
12078
? entry.toSVG[value]
12079
: value;
12080
}
12081
});
12082
12083
if (attrs.opacity === 1)
12084
delete attrs.opacity;
12085
12086
if (item._visibility != null && !item._visibility)
12087
attrs.visibility = 'hidden';
12088
12089
return setAttributes(node, attrs);
12090
}
12091
12092
var definitions;
12093
function getDefinition(item, type) {
12094
if (!definitions)
12095
definitions = { ids: {}, svgs: {} };
12096
return item && definitions.svgs[type + '-' + item._id];
12097
}
12098
12099
function setDefinition(item, node, type) {
12100
if (!definitions)
12101
getDefinition();
12102
var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
12103
node.id = type + '-' + id;
12104
definitions.svgs[type + '-' + item._id] = node;
12105
}
12106
12107
function exportDefinitions(node, options) {
12108
var svg = node,
12109
defs = null;
12110
if (definitions) {
12111
svg = node.nodeName.toLowerCase() === 'svg' && node;
12112
for (var i in definitions.svgs) {
12113
if (!defs) {
12114
if (!svg) {
12115
svg = createElement('svg');
12116
svg.appendChild(node);
12117
}
12118
defs = svg.insertBefore(createElement('defs'),
12119
svg.firstChild);
12120
}
12121
defs.appendChild(definitions.svgs[i]);
12122
}
12123
definitions = null;
12124
}
12125
return options.asString
12126
? new XMLSerializer().serializeToString(svg)
12127
: svg;
12128
}
12129
12130
function exportSVG(item, options) {
12131
var exporter = exporters[item._class],
12132
node = exporter && exporter(item, options);
12133
if (node && item._data) {
12134
var data = JSON.stringify(item._data);
12135
if (data !== '{}')
12136
node.setAttribute('data-paper-data', data);
12137
}
12138
return node && applyStyle(item, node);
12139
}
12140
12141
function setOptions(options) {
12142
if (!options)
12143
options = {};
12144
formatter = new Formatter(options.precision);
12145
return options;
12146
}
12147
12148
Item.inject({
12149
exportSVG: function(options) {
12150
options = setOptions(options);
12151
return exportDefinitions(exportSVG(this, options), options);
12152
}
12153
});
12154
12155
Project.inject({
12156
exportSVG: function(options) {
12157
options = setOptions(options);
12158
var layers = this.layers,
12159
size = this.getView().getSize(),
12160
node = createElement('svg', {
12161
x: 0,
12162
y: 0,
12163
width: size.width,
12164
height: size.height,
12165
version: '1.1',
12166
xmlns: 'http://www.w3.org/2000/svg',
12167
'xmlns:xlink': 'http://www.w3.org/1999/xlink'
12168
});
12169
for (var i = 0, l = layers.length; i < l; i++)
12170
node.appendChild(exportSVG(layers[i], options));
12171
return exportDefinitions(node, options);
12172
}
12173
});
12174
};
12175
12176
new function() {
12177
12178
function getValue(node, name, isString, allowNull) {
12179
var namespace = SVGNamespaces[name],
12180
value = namespace
12181
? node.getAttributeNS(namespace, name)
12182
: node.getAttribute(name);
12183
if (value === 'null')
12184
value = null;
12185
return value == null
12186
? allowNull
12187
? null
12188
: isString
12189
? ''
12190
: 0
12191
: isString
12192
? value
12193
: parseFloat(value);
12194
}
12195
12196
function getPoint(node, x, y, allowNull) {
12197
x = getValue(node, x, false, allowNull);
12198
y = getValue(node, y, false, allowNull);
12199
return allowNull && (x == null || y == null) ? null
12200
: new Point(x, y);
12201
}
12202
12203
function getSize(node, w, h, allowNull) {
12204
w = getValue(node, w, false, allowNull);
12205
h = getValue(node, h, false, allowNull);
12206
return allowNull && (w == null || h == null) ? null
12207
: new Size(w, h);
12208
}
12209
12210
function convertValue(value, type, lookup) {
12211
return value === 'none'
12212
? null
12213
: type === 'number'
12214
? parseFloat(value)
12215
: type === 'array'
12216
? value ? value.split(/[\s,]+/g).map(parseFloat) : []
12217
: type === 'color'
12218
? getDefinition(value) || value
12219
: type === 'lookup'
12220
? lookup[value]
12221
: value;
12222
}
12223
12224
function importGroup(node, type, isRoot, options) {
12225
var nodes = node.childNodes,
12226
isClip = type === 'clippath',
12227
item = new Group(),
12228
project = item._project,
12229
currentStyle = project._currentStyle,
12230
children = [];
12231
if (!isClip) {
12232
item = applyAttributes(item, node, isRoot);
12233
project._currentStyle = item._style.clone();
12234
}
12235
for (var i = 0, l = nodes.length; i < l; i++) {
12236
var childNode = nodes[i],
12237
child;
12238
if (childNode.nodeType === 1
12239
&& (child = importSVG(childNode, false, options))
12240
&& !(child instanceof Symbol))
12241
children.push(child);
12242
}
12243
item.addChildren(children);
12244
if (isClip)
12245
item = applyAttributes(item.reduce(), node, isRoot);
12246
project._currentStyle = currentStyle;
12247
if (isClip || type === 'defs') {
12248
item.remove();
12249
item = null;
12250
}
12251
return item;
12252
}
12253
12254
function importPoly(node, type) {
12255
var coords = node.getAttribute('points').match(
12256
/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g),
12257
points = [];
12258
for (var i = 0, l = coords.length; i < l; i += 2)
12259
points.push(new Point(
12260
parseFloat(coords[i]),
12261
parseFloat(coords[i + 1])));
12262
var path = new Path(points);
12263
if (type === 'polygon')
12264
path.closePath();
12265
return path;
12266
}
12267
12268
function importPath(node) {
12269
var data = node.getAttribute('d'),
12270
param = { pathData: data };
12271
return data.match(/m/gi).length > 1 || /z\S+/i.test(data)
12272
? new CompoundPath(param)
12273
: new Path(param);
12274
}
12275
12276
function importGradient(node, type) {
12277
var id = (getValue(node, 'href', true) || '').substring(1),
12278
isRadial = type === 'radialgradient',
12279
gradient;
12280
if (id) {
12281
gradient = definitions[id].getGradient();
12282
} else {
12283
var nodes = node.childNodes,
12284
stops = [];
12285
for (var i = 0, l = nodes.length; i < l; i++) {
12286
var child = nodes[i];
12287
if (child.nodeType === 1)
12288
stops.push(applyAttributes(new GradientStop(), child));
12289
}
12290
gradient = new Gradient(stops, isRadial);
12291
}
12292
var origin, destination, highlight;
12293
if (isRadial) {
12294
origin = getPoint(node, 'cx', 'cy');
12295
destination = origin.add(getValue(node, 'r'), 0);
12296
highlight = getPoint(node, 'fx', 'fy', true);
12297
} else {
12298
origin = getPoint(node, 'x1', 'y1');
12299
destination = getPoint(node, 'x2', 'y2');
12300
}
12301
applyAttributes(
12302
new Color(gradient, origin, destination, highlight), node);
12303
return null;
12304
}
12305
12306
var importers = {
12307
'#document': function (node, type, isRoot, options) {
12308
var nodes = node.childNodes;
12309
for (var i = 0, l = nodes.length; i < l; i++) {
12310
var child = nodes[i];
12311
if (child.nodeType === 1) {
12312
var next = child.nextSibling;
12313
document.body.appendChild(child);
12314
var item = importSVG(child, isRoot, options);
12315
if (next) {
12316
node.insertBefore(child, next);
12317
} else {
12318
node.appendChild(child);
12319
}
12320
return item;
12321
}
12322
}
12323
},
12324
g: importGroup,
12325
svg: importGroup,
12326
clippath: importGroup,
12327
polygon: importPoly,
12328
polyline: importPoly,
12329
path: importPath,
12330
lineargradient: importGradient,
12331
radialgradient: importGradient,
12332
12333
image: function (node) {
12334
var raster = new Raster(getValue(node, 'href', true));
12335
raster.attach('load', function() {
12336
var size = getSize(node, 'width', 'height');
12337
this.setSize(size);
12338
var center = this._matrix._transformPoint(
12339
getPoint(node, 'x', 'y').add(size.divide(2)));
12340
this.translate(center);
12341
});
12342
return raster;
12343
},
12344
12345
symbol: function(node, type, isRoot, options) {
12346
return new Symbol(importGroup(node, type, isRoot, options), true);
12347
},
12348
12349
defs: importGroup,
12350
12351
use: function(node) {
12352
var id = (getValue(node, 'href', true) || '').substring(1),
12353
definition = definitions[id],
12354
point = getPoint(node, 'x', 'y');
12355
return definition
12356
? definition instanceof Symbol
12357
? definition.place(point)
12358
: definition.clone().translate(point)
12359
: null;
12360
},
12361
12362
circle: function(node) {
12363
return new Shape.Circle(getPoint(node, 'cx', 'cy'),
12364
getValue(node, 'r'));
12365
},
12366
12367
ellipse: function(node) {
12368
return new Shape.Ellipse({
12369
center: getPoint(node, 'cx', 'cy'),
12370
radius: getSize(node, 'rx', 'ry')
12371
});
12372
},
12373
12374
rect: function(node) {
12375
var point = getPoint(node, 'x', 'y'),
12376
size = getSize(node, 'width', 'height'),
12377
radius = getSize(node, 'rx', 'ry');
12378
return new Shape.Rectangle(new Rectangle(point, size), radius);
12379
},
12380
12381
line: function(node) {
12382
return new Path.Line(getPoint(node, 'x1', 'y1'),
12383
getPoint(node, 'x2', 'y2'));
12384
},
12385
12386
text: function(node) {
12387
var text = new PointText(getPoint(node, 'x', 'y')
12388
.add(getPoint(node, 'dx', 'dy')));
12389
text.setContent(node.textContent.trim() || '');
12390
return text;
12391
}
12392
};
12393
12394
function applyTransform(item, value, name, node) {
12395
var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
12396
matrix = new Matrix();
12397
for (var i = 0, l = transforms.length; i < l; i++) {
12398
var transform = transforms[i];
12399
if (!transform)
12400
break;
12401
var parts = transform.split('('),
12402
command = parts[0],
12403
v = parts[1].split(/[\s,]+/g);
12404
for (var j = 0, m = v.length; j < m; j++)
12405
v[j] = parseFloat(v[j]);
12406
switch (command) {
12407
case 'matrix':
12408
matrix.concatenate(
12409
new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
12410
break;
12411
case 'rotate':
12412
matrix.rotate(v[0], v[1], v[2]);
12413
break;
12414
case 'translate':
12415
matrix.translate(v[0], v[1]);
12416
break;
12417
case 'scale':
12418
matrix.scale(v);
12419
break;
12420
case 'skewX':
12421
matrix.skew(v[0], 0);
12422
break;
12423
case 'skewY':
12424
matrix.skew(0, v[0]);
12425
break;
12426
}
12427
}
12428
item.transform(matrix);
12429
}
12430
12431
function applyOpacity(item, value, name) {
12432
var color = item[name === 'fill-opacity' ? 'getFillColor'
12433
: 'getStrokeColor']();
12434
if (color)
12435
color.setAlpha(parseFloat(value));
12436
}
12437
12438
var attributes = Base.each(SVGStyles, function(entry) {
12439
this[entry.attribute] = function(item, value) {
12440
item[entry.set](convertValue(value, entry.type, entry.fromSVG));
12441
if (entry.type === 'color' && item instanceof Shape) {
12442
var color = item[entry.get]();
12443
if (color)
12444
color.transform(new Matrix().translate(
12445
item.getPosition(true).negate()));
12446
}
12447
};
12448
}, {
12449
id: function(item, value) {
12450
definitions[value] = item;
12451
if (item.setName)
12452
item.setName(value);
12453
},
12454
12455
'clip-path': function(item, value) {
12456
var clip = getDefinition(value);
12457
if (clip) {
12458
clip = clip.clone();
12459
clip.setClipMask(true);
12460
if (item instanceof Group) {
12461
item.insertChild(0, clip);
12462
} else {
12463
return new Group(clip, item);
12464
}
12465
}
12466
},
12467
12468
gradientTransform: applyTransform,
12469
transform: applyTransform,
12470
12471
'fill-opacity': applyOpacity,
12472
'stroke-opacity': applyOpacity,
12473
12474
visibility: function(item, value) {
12475
item.setVisible(value === 'visible');
12476
},
12477
12478
'stop-color': function(item, value) {
12479
if (item.setColor)
12480
item.setColor(value);
12481
},
12482
12483
'stop-opacity': function(item, value) {
12484
if (item._color)
12485
item._color.setAlpha(parseFloat(value));
12486
},
12487
12488
offset: function(item, value) {
12489
var percentage = value.match(/(.*)%$/);
12490
item.setRampPoint(percentage
12491
? percentage[1] / 100
12492
: parseFloat(value));
12493
},
12494
12495
viewBox: function(item, value, name, node, styles) {
12496
var rect = new Rectangle(convertValue(value, 'array')),
12497
size = getSize(node, 'width', 'height', true);
12498
if (item instanceof Group) {
12499
var scale = size ? rect.getSize().divide(size) : 1,
12500
matrix = new Matrix().translate(rect.getPoint()).scale(scale);
12501
item.transform(matrix.inverted());
12502
} else if (item instanceof Symbol) {
12503
if (size)
12504
rect.setSize(size);
12505
var clip = getAttribute(node, 'overflow', styles) != 'visible',
12506
group = item._definition;
12507
if (clip && !rect.contains(group.getBounds())) {
12508
clip = new Shape.Rectangle(rect).transform(group._matrix);
12509
clip.setClipMask(true);
12510
group.addChild(clip);
12511
}
12512
}
12513
}
12514
});
12515
12516
function getAttribute(node, name, styles) {
12517
var attr = node.attributes[name],
12518
value = attr && attr.value;
12519
if (!value) {
12520
var style = Base.camelize(name);
12521
value = node.style[style];
12522
if (!value && styles.node[style] !== styles.parent[style])
12523
value = styles.node[style];
12524
}
12525
return !value
12526
? undefined
12527
: value === 'none'
12528
? null
12529
: value;
12530
}
12531
12532
function applyAttributes(item, node, isRoot) {
12533
var styles = {
12534
node: DomElement.getStyles(node) || {},
12535
parent: !isRoot && DomElement.getStyles(node.parentNode) || {}
12536
};
12537
Base.each(attributes, function(apply, name) {
12538
var value = getAttribute(node, name, styles);
12539
if (value !== undefined)
12540
item = Base.pick(apply(item, value, name, node, styles), item);
12541
});
12542
return item;
12543
}
12544
12545
var definitions = {};
12546
function getDefinition(value) {
12547
var match = value && value.match(/\((?:#|)([^)']+)/);
12548
return match && definitions[match[1]];
12549
}
12550
12551
function importSVG(source, isRoot, options) {
12552
if (!source)
12553
return null;
12554
if (!options) {
12555
options = {};
12556
} else if (typeof options === 'function') {
12557
options = { onLoad: options };
12558
}
12559
12560
var node = source,
12561
scope = paper;
12562
12563
function onLoadCallback(svg) {
12564
paper = scope;
12565
var item = importSVG(svg, isRoot, options),
12566
onLoad = options.onLoad,
12567
view = scope.project && scope.getView();
12568
if (onLoad)
12569
onLoad.call(this, item);
12570
view.update();
12571
}
12572
12573
if (isRoot) {
12574
if (typeof source === 'string' && !/^.*</.test(source)) {
12575
node = document.getElementById(source);
12576
if (node) {
12577
source = null;
12578
} else {
12579
return Http.request('get', source, onLoadCallback);
12580
}
12581
} else if (typeof File !== 'undefined' && source instanceof File) {
12582
var reader = new FileReader();
12583
reader.onload = function() {
12584
onLoadCallback(reader.result);
12585
};
12586
return reader.readAsText(source);
12587
}
12588
}
12589
12590
if (typeof source === 'string')
12591
node = new DOMParser().parseFromString(source, 'image/svg+xml');
12592
if (!node.nodeName)
12593
throw new Error('Unsupported SVG source: ' + source);
12594
var type = node.nodeName.toLowerCase(),
12595
importer = importers[type],
12596
item,
12597
data = node.getAttribute && node.getAttribute('data-paper-data'),
12598
settings = scope.settings,
12599
prevApplyMatrix = settings.applyMatrix;
12600
settings.applyMatrix = false;
12601
item = importer && importer(node, type, isRoot, options) || null;
12602
settings.applyMatrix = prevApplyMatrix;
12603
if (item) {
12604
if (!(item instanceof Group))
12605
item = applyAttributes(item, node, isRoot);
12606
if (options.expandShapes && item instanceof Shape) {
12607
item.remove();
12608
item = item.toPath();
12609
}
12610
if (data)
12611
item._data = JSON.parse(data);
12612
}
12613
if (isRoot)
12614
definitions = {};
12615
return item;
12616
}
12617
12618
Item.inject({
12619
importSVG: function(node, options) {
12620
return this.addChild(importSVG(node, true, options));
12621
}
12622
});
12623
12624
Project.inject({
12625
importSVG: function(node, options) {
12626
this.activate();
12627
return importSVG(node, true, options);
12628
}
12629
});
12630
};
12631
12632
Base.exports.PaperScript = (function() {
12633
var exports, define,
12634
scope = this;
12635
!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 hr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=hr[r]);mr=fr.sourceFile||null}function t(e,r){var t=vr(pr,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=pr.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(pr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,pr.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=pr.charCodeAt(br+=2);dr>br&&10!==t&&13!==t&&8232!==t&&8329!==t;)++br,t=pr.charCodeAt(br);fr.onComment&&fr.onComment(!1,pr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;dr>br;){var e=pr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=pr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e)++br,++Ar,Sr=br;else if(14>e&&e>8)++br;else if(47===e){var r=pr.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=pr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=pr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function p(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Ft,1)}function d(e){var r=pr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Vt,1)}function m(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Tt,1)}function h(e){var r=pr.charCodeAt(br+1);return r===e?x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=pr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===pr.charCodeAt(br+2)?3:2,61===pr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):(61===r&&(t=61===pr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=pr.charCodeAt(br+1);return 61===r?x(qt,61===pr.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(ht);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(pt);case 123:return++br,i(dt);case 125:return++br,i(mt);case 58:return++br,i(gt);case 63:return++br,i(kt);case 48:var r=pr.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 p();case 124:case 38:return d(e);case 94:return m();case 43:case 45:return h(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>=dr)return i(Br);var r=pr.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=pr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=dr&&t(a,"Unterminated regular expression");var o=pr.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=pr.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=pr.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(pr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===pr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===pr.charCodeAt(br)&&(++br,w(10),n=!0);var o=pr.charCodeAt(br);(69===o||101===o)&&(o=pr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=pr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Vr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=dr&&t(yr,"Unterminated string constant");var n=pr.charCodeAt(br);if(n===e)return++br,i(Fr,r);if(92===n){n=pr.charCodeAt(++br);var a=/^[0-7]+/.exec(pr.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)Vr&&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===pr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8329===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=pr.charCodeAt(br);if(Yt(a))Bt&&(e+=pr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=pr.slice(n,br)),Bt=!0,117!=pr.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:pr.slice(n,br)}function L(){var e=I(),r=Dr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Vr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){for(Vr=e,br=Lr;Sr>br;)Sr=pr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function T(){this.type=null,this.start=yr,this.end=null}function V(){this.start=xr,this.end=null,null!==mr&&(this.source=mr)}function q(){var e=new T;return fr.locations&&(e.loc=new V),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new T;return r.start=e.start,fr.locations&&(r.loc=new V,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 F(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function D(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===mt||Gt.test(pr.slice(Lr,yr)))}function M(){D(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Vr&&"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=Vr=null,Tr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&F(n)&&R(!0),t=!1}return j(r,"Program")}function J(){wr===wt&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;D(yt)||B()?r.label=null:wr!==Dr?X():(r.label=lr(),M());for(var a=0;a<Tr.length;++a){var o=Tr[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===Tr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Tr.push(Zt),r.body=J(),Tr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Tr.push(Zt),z(ht),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),1===i.declarations.length&&D(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return D(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=D(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),D(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(dt),Tr.push(en);for(var s,c;wr!=mt;)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(),Tr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(pr.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(ht),l.param=lr(),Vr&&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=D($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),r=G(r),M(),r;case tt:return U(),r.test=P(),Tr.push(Zt),r.body=J(),Tr.pop(),j(r,"WhileStatement");case nt:return Vr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case dt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,p=K();if(e===Dr&&"Identifier"===p.type&&D(gt)){for(var a=0;a<Tr.length;++a)Tr[a].name===f&&t(p.start,"Label '"+f+"' is already declared");var d=wr.isLoop?"loop":wr===Yr?"switch":null;return Tr.push({name:f,kind:d}),r.body=J(),Tr.pop(),r.label=p,j(r,"LabeledStatement")}return r.expression=p,M(),j(r,"ExpressionStatement")}}function P(){z(ht);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(dt);!D(mt);){var o=J();t.body.push(o),n&&e&&F(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(),Tr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Vr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=D(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!D(bt))break}return j(e,"VariableDeclaration")}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];D(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(D(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 a=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(a,r,t)}return e}function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,U(),e.argument=rr(),r?N(e.argument):Vr&&"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(D(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(D(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(pt),nr(j(t,"MemberExpression"),r)}if(!r&&D(ht)){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 Dr:return lr();case Or:case Fr:case jr:var e=q();return e.value=Cr,e.raw=pr.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 ht: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(pt,!0,!0),j(e,"ArrayExpression");case dt: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=D(ht)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!D(mt);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&D(mt))break;var a,o={key:sr()},i=!1;if(D(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!==ht&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Vr||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&&!Vr&&"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===Fr?ar():lr(!0)}function cr(e,r){wr===Dr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(ht);!D(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Tr;if(Rr=!0,Tr=[],e.body=H(!0),Rr=a,Tr=o,Vr||e.body.body.length&&F(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;!D(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&D(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Dr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),U(),j(r,"Identifier")}e.version="0.3.2";var fr,pr,dr,mr;e.parse=function(e,t){return pr=String(e),dr=pr.length,r(t),o(),W(fr.program)};var hr=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}pr=String(e),dr=pr.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(pr))&&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,Tr,Vr,qr=[],Or={type:"num"},jr={type:"regexp"},Fr={type:"string"},Dr={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"},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},pt={type:"]"},dt={type:"{",beforeExpr:!0},mt={type:"}"},ht={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},Tt={binop:4,beforeExpr:!0},Vt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Ft={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:pt,braceL:dt,braceR:mt,parenL:ht,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Dr,eof:Br,num:Or,regexp:jr,string:Fr};for(var Dt in lt)e.tokTypes["_"+Dt]=lt[Dt];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\u2028\u2029\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\u19c1-\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"}});
12636
12637
var binaryOperators = {
12638
'+': '__add',
12639
'-': '__subtract',
12640
'*': '__multiply',
12641
'/': '__divide',
12642
'%': '__modulo',
12643
'==': 'equals',
12644
'!=': 'equals'
12645
};
12646
12647
var unaryOperators = {
12648
'-': '__negate',
12649
'+': null
12650
};
12651
12652
var fields = Base.each(
12653
['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
12654
function(name) {
12655
this['__' + name] = '#' + name;
12656
},
12657
{}
12658
);
12659
Point.inject(fields);
12660
Size.inject(fields);
12661
Color.inject(fields);
12662
12663
function _$_(left, operator, right) {
12664
var handler = binaryOperators[operator];
12665
if (left && left[handler]) {
12666
var res = left[handler](right);
12667
return operator === '!=' ? !res : res;
12668
}
12669
switch (operator) {
12670
case '+': return left + right;
12671
case '-': return left - right;
12672
case '*': return left * right;
12673
case '/': return left / right;
12674
case '%': return left % right;
12675
case '==': return left == right;
12676
case '!=': return left != right;
12677
}
12678
}
12679
12680
function $_(operator, value) {
12681
var handler = unaryOperators[operator];
12682
if (handler && value && value[handler])
12683
return value[handler]();
12684
switch (operator) {
12685
case '+': return +value;
12686
case '-': return -value;
12687
}
12688
}
12689
12690
function compile(code) {
12691
12692
var insertions = [];
12693
12694
function getOffset(offset) {
12695
for (var i = 0, l = insertions.length; i < l; i++) {
12696
var insertion = insertions[i];
12697
if (insertion[0] >= offset)
12698
break;
12699
offset += insertion[1];
12700
}
12701
return offset;
12702
}
12703
12704
function getCode(node) {
12705
return code.substring(getOffset(node.range[0]),
12706
getOffset(node.range[1]));
12707
}
12708
12709
function replaceCode(node, str) {
12710
var start = getOffset(node.range[0]),
12711
end = getOffset(node.range[1]),
12712
insert = 0;
12713
for (var i = insertions.length - 1; i >= 0; i--) {
12714
if (start > insertions[i][0]) {
12715
insert = i + 1;
12716
break;
12717
}
12718
}
12719
insertions.splice(insert, 0, [start, str.length - end + start]);
12720
code = code.substring(0, start) + str + code.substring(end);
12721
}
12722
12723
function walkAST(node, parent) {
12724
if (!node)
12725
return;
12726
for (var key in node) {
12727
if (key === 'range')
12728
continue;
12729
var value = node[key];
12730
if (Array.isArray(value)) {
12731
for (var i = 0, l = value.length; i < l; i++)
12732
walkAST(value[i], node);
12733
} else if (value && typeof value === 'object') {
12734
walkAST(value, node);
12735
}
12736
}
12737
switch (node && node.type) {
12738
case 'UnaryExpression':
12739
if (node.operator in unaryOperators
12740
&& node.argument.type !== 'Literal') {
12741
var arg = getCode(node.argument);
12742
replaceCode(node, '$_("' + node.operator + '", '
12743
+ arg + ')');
12744
}
12745
break;
12746
case 'BinaryExpression':
12747
if (node.operator in binaryOperators
12748
&& node.left.type !== 'Literal') {
12749
var left = getCode(node.left),
12750
right = getCode(node.right);
12751
replaceCode(node, '_$_(' + left + ', "' + node.operator
12752
+ '", ' + right + ')');
12753
}
12754
break;
12755
case 'UpdateExpression':
12756
case 'AssignmentExpression':
12757
if (!(parent && (
12758
parent.type === 'ForStatement'
12759
|| parent.type === 'BinaryExpression'
12760
&& /^[=!<>]/.test(parent.operator)
12761
|| parent.type === 'MemberExpression'
12762
&& parent.computed))) {
12763
if (node.type === 'UpdateExpression') {
12764
if (!node.prefix) {
12765
var arg = getCode(node.argument);
12766
replaceCode(node, arg + ' = _$_(' + arg + ', "'
12767
+ node.operator[0] + '", 1)');
12768
}
12769
} else {
12770
if (/^.=$/.test(node.operator)
12771
&& node.left.type !== 'Literal') {
12772
var left = getCode(node.left),
12773
right = getCode(node.right);
12774
replaceCode(node, left + ' = _$_(' + left + ', "'
12775
+ node.operator[0] + '", ' + right + ')');
12776
}
12777
}
12778
}
12779
break;
12780
}
12781
}
12782
walkAST(scope.acorn.parse(code, { ranges: true }));
12783
return code;
12784
}
12785
12786
function execute(code, scope) {
12787
paper = scope;
12788
var view = scope.getView(),
12789
tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code)
12790
? new Tool()
12791
: null,
12792
toolHandlers = tool ? tool._events : [],
12793
handlers = ['onFrame', 'onResize'].concat(toolHandlers),
12794
params = [],
12795
args = [],
12796
func;
12797
code = compile(code);
12798
function expose(scope, hidden) {
12799
for (var key in scope) {
12800
if ((hidden || !/^_/.test(key)) && new RegExp(
12801
'\\b' + key.replace(/\$/g, '\\$') + '\\b').test(code)) {
12802
params.push(key);
12803
args.push(scope[key]);
12804
}
12805
}
12806
}
12807
expose({ _$_: _$_, $_: $_, view: view, tool: tool }, true);
12808
expose(scope);
12809
handlers = Base.each(handlers, function(key) {
12810
if (new RegExp('\\s+' + key + '\\b').test(code)) {
12811
params.push(key);
12812
this.push(key + ': ' + key);
12813
}
12814
}, []).join(', ');
12815
if (handlers)
12816
code += '\nreturn { ' + handlers + ' };';
12817
var firefox = window.InstallTrigger;
12818
if (firefox || window.chrome) {
12819
var script = document.createElement('script'),
12820
head = document.head;
12821
if (firefox)
12822
code = '\n' + code;
12823
script.appendChild(document.createTextNode(
12824
'paper._execute = function(' + params + ') {' + code + '\n}'
12825
));
12826
head.appendChild(script);
12827
func = paper._execute;
12828
delete paper._execute;
12829
head.removeChild(script);
12830
} else {
12831
func = Function(params, code);
12832
}
12833
var res = func.apply(scope, args) || {};
12834
Base.each(toolHandlers, function(key) {
12835
var value = res[key];
12836
if (value)
12837
tool[key] = value;
12838
});
12839
if (view) {
12840
if (res.onResize)
12841
view.setOnResize(res.onResize);
12842
view.fire('resize', {
12843
size: view.size,
12844
delta: new Point()
12845
});
12846
if (res.onFrame)
12847
view.setOnFrame(res.onFrame);
12848
view.update();
12849
}
12850
}
12851
12852
function load() {
12853
Base.each(document.getElementsByTagName('script'), function(script) {
12854
if (/^text\/(?:x-|)paperscript$/.test(script.type)
12855
&& !script.getAttribute('data-paper-ignore')) {
12856
var canvas = PaperScope.getAttribute(script, 'canvas'),
12857
scope = PaperScope.get(canvas)
12858
|| new PaperScope(script).setup(canvas),
12859
src = script.src;
12860
if (src) {
12861
Http.request('get', src, function(code) {
12862
execute(code, scope);
12863
});
12864
} else {
12865
execute(script.innerHTML, scope);
12866
}
12867
script.setAttribute('data-paper-ignore', true);
12868
}
12869
}, this);
12870
}
12871
12872
if (document.readyState === 'complete') {
12873
setTimeout(load);
12874
} else {
12875
DomEvent.add(window, { load: load });
12876
}
12877
12878
return {
12879
compile: compile,
12880
execute: execute,
12881
load: load,
12882
lineNumberBase: 0
12883
};
12884
12885
}).call(this);
12886
12887
paper = new (PaperScope.inject(Base.exports, {
12888
enumerable: true,
12889
Base: Base,
12890
Numerical: Numerical,
12891
DomElement: DomElement,
12892
DomEvent: DomEvent,
12893
Http: Http,
12894
Key: Key
12895
}))();
12896
12897
if (typeof define === 'function' && define.amd)
12898
define('paper', paper);
12899
12900
return paper;
12901
};
12902
12903