PageSourceSearch

https://strg-n.com/assets/js/paper.js

js strg-n.com collected 2026-09-29 06:05:30 UTC 361,152 bytes, 13,224 lines download raw bytes

1/*!
2 * Paper.js v0.9.24 - The Swiss Army Knife of Vector Graphics Scripting.
3 * http://paperjs.org/
4 *
5 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
6 * http://scratchdisk.com/ & http://jonathanpuckey.com/
7 *
8 * Distributed under the MIT license. See LICENSE file for details.
9 *
10 * All rights reserved.
11 *
12 * Date: Fri Aug 21 16:39:41 2015 +0200
13 *
14 ***
15 *
16 * Straps.js - Class inheritance library with support for bean-style accessors
17 *
18 * Copyright (c) 2006 - 2013 Juerg Lehni
19 * http://scratchdisk.com/
20 *
21 * Distributed under the MIT license.
22 *
23 ***
24 *
25 * Acorn.js
26 * http://marijnhaverbeke.nl/acorn/
27 *
28 * Acorn is a tiny, fast JavaScript parser written in JavaScript,
29 * created by Marijn Haverbeke and released under an MIT license.
30 *
31 */
32
33var paper = new function(undefined) {
34
35var Base = new function() {
36	var hidden = /^(statics|enumerable|beans|preserve)$/,
37
38		forEach = [].forEach || function(iter, bind) {
39			for (var i = 0, l = this.length; i < l; i++)
40				iter.call(bind, this[i], i, this);
41		},
42
43		forIn = function(iter, bind) {
44			for (var i in this)
45				if (this.hasOwnProperty(i))
46					iter.call(bind, this[i], i, this);
47		},
48
49		create = Object.create || function(proto) {
50			return { __proto__: proto };
51		},
52
53		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
54			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
55			return get
56					? { get: get, set: obj.__lookupSetter__(name),
57						enumerable: true, configurable: true }
58					: obj.hasOwnProperty(name)
59						? { value: obj[name], enumerable: true,
60							configurable: true, writable: true }
61						: null;
62		},
63
64		_define = Object.defineProperty || function(obj, name, desc) {
65			if ((desc.get || desc.set) && obj.__defineGetter__) {
66				if (desc.get)
67					obj.__defineGetter__(name, desc.get);
68				if (desc.set)
69					obj.__defineSetter__(name, desc.set);
70			} else {
71				obj[name] = desc.value;
72			}
73			return obj;
74		},
75
76		define = function(obj, name, desc) {
77			delete obj[name];
78			return _define(obj, name, desc);
79		};
80
81	function inject(dest, src, enumerable, beans, preserve) {
82		var beansNames = {};
83
84		function field(name, val) {
85			val = val || (val = describe(src, name))
86					&& (val.get ? val : val.value);
87			if (typeof val === 'string' && val[0] === '#')
88				val = dest[val.substring(1)] || val;
89			var isFunc = typeof val === 'function',
90				res = val,
91				prev = preserve || isFunc && !val.base
92						? (val && val.get ? name in dest : dest[name])
93						: null,
94				bean;
95			if (!preserve || !prev) {
96				if (isFunc && prev)
97					val.base = prev;
98				if (isFunc && beans !== false
99						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
100					beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
101				if (!res || isFunc || !res.get || typeof res.get !== 'function'
102						|| !Base.isPlainObject(res))
103					res = { value: res, writable: true };
104				if ((describe(dest, name)
105						|| { configurable: true }).configurable) {
106					res.configurable = true;
107					res.enumerable = enumerable;
108				}
109				define(dest, name, res);
110			}
111		}
112		if (src) {
113			for (var name in src) {
114				if (src.hasOwnProperty(name) && !hidden.test(name))
115					field(name);
116			}
117			for (var name in beansNames) {
118				var part = beansNames[name],
119					set = dest['set' + part],
120					get = dest['get' + part] || set && dest['is' + part];
121				if (get && (beans === true || get.length === 0))
122					field(name, { get: get, set: set });
123			}
124		}
125		return dest;
126	}
127
128	function each(obj, iter, bind) {
129		if (obj)
130			('length' in obj && !obj.getLength
131					&& typeof obj.length === 'number'
132				? forEach
133				: forIn).call(obj, iter, bind = bind || obj);
134		return bind;
135	}
136
137	function set(obj, props, exclude) {
138		for (var key in props)
139			if (props.hasOwnProperty(key) && !(exclude && exclude[key]))
140				obj[key] = props[key];
141		return obj;
142	}
143
144	return inject(function Base() {
145		for (var i = 0, l = arguments.length; i < l; i++)
146			set(this, arguments[i]);
147	}, {
148		inject: function(src) {
149			if (src) {
150				var statics = src.statics === true ? src : src.statics,
151					beans = src.beans,
152					preserve = src.preserve;
153				if (statics !== src)
154					inject(this.prototype, src, src.enumerable, beans, preserve);
155				inject(this, statics, true, beans, preserve);
156			}
157			for (var i = 1, l = arguments.length; i < l; i++)
158				this.inject(arguments[i]);
159			return this;
160		},
161
162		extend: function() {
163			var base = this,
164				ctor,
165				proto;
166			for (var i = 0, l = arguments.length; i < l; i++)
167				if (ctor = arguments[i].initialize)
168					break;
169			ctor = ctor || function() {
170				base.apply(this, arguments);
171			};
172			proto = ctor.prototype = create(this.prototype);
173			define(proto, 'constructor',
174					{ value: ctor, writable: true, configurable: true });
175			inject(ctor, this, true);
176			if (arguments.length)
177				this.inject.apply(ctor, arguments);
178			ctor.base = base;
179			return ctor;
180		}
181	}, true).inject({
182		inject: function() {
183			for (var i = 0, l = arguments.length; i < l; i++) {
184				var src = arguments[i];
185				if (src)
186					inject(this, src, src.enumerable, src.beans, src.preserve);
187			}
188			return this;
189		},
190
191		extend: function() {
192			var res = create(this);
193			return res.inject.apply(res, arguments);
194		},
195
196		each: function(iter, bind) {
197			return each(this, iter, bind);
198		},
199
200		set: function(props) {
201			return set(this, props);
202		},
203
204		clone: function() {
205			return new this.constructor(this);
206		},
207
208		statics: {
209			each: each,
210			create: create,
211			define: define,
212			describe: describe,
213			set: set,
214
215			clone: function(obj) {
216				return set(new obj.constructor(), obj);
217			},
218
219			isPlainObject: function(obj) {
220				var ctor = obj != null && obj.constructor;
221				return ctor && (ctor === Object || ctor === Base
222						|| ctor.name === 'Object');
223			},
224
225			pick: function(a, b) {
226				return a !== undefined ? a : b;
227			}
228		}
229	});
230};
231
232if (typeof module !== 'undefined')
233	module.exports = Base;
234
235Base.inject({
236	toString: function() {
237		return this._id != null
238			?  (this._class || 'Object') + (this._name
239				? " '" + this._name + "'"
240				: ' @' + this._id)
241			: '{ ' + Base.each(this, function(value, key) {
242				if (!/^_/.test(key)) {
243					var type = typeof value;
244					this.push(key + ': ' + (type === 'number'
245							? Formatter.instance.number(value)
246							: type === 'string' ? "'" + value + "'" : value));
247				}
248			}, []).join(', ') + ' }';
249	},
250
251	getClassName: function() {
252		return this._class || '';
253	},
254
255	exportJSON: function(options) {
256		return Base.exportJSON(this, options);
257	},
258
259	toJSON: function() {
260		return Base.serialize(this);
261	},
262
263	_set: function(props, exclude, dontCheck) {
264		if (props && (dontCheck || Base.isPlainObject(props))) {
265			var keys = Object.keys(props._filtering || props);
266			for (var i = 0, l = keys.length; i < l; i++) {
267				var key = keys[i];
268				if (!(exclude && exclude[key])) {
269					var value = props[key];
270					if (value !== undefined)
271						this[key] = value;
272				}
273			}
274			return true;
275		}
276	},
277
278	statics: {
279
280		exports: {
281			enumerable: true
282		},
283
284		extend: function extend() {
285			var res = extend.base.apply(this, arguments),
286				name = res.prototype._class;
287			if (name && !Base.exports[name])
288				Base.exports[name] = res;
289			return res;
290		},
291
292		equals: function(obj1, obj2) {
293			if (obj1 === obj2)
294				return true;
295			if (obj1 && obj1.equals)
296				return obj1.equals(obj2);
297			if (obj2 && obj2.equals)
298				return obj2.equals(obj1);
299			if (obj1 && obj2
300					&& typeof obj1 === 'object' && typeof obj2 === 'object') {
301				if (Array.isArray(obj1) && Array.isArray(obj2)) {
302					var length = obj1.length;
303					if (length !== obj2.length)
304						return false;
305					while (length--) {
306						if (!Base.equals(obj1[length], obj2[length]))
307							return false;
308					}
309				} else {
310					var keys = Object.keys(obj1),
311						length = keys.length;
312					if (length !== Object.keys(obj2).length)
313						return false;
314					while (length--) {
315						var key = keys[length];
316						if (!(obj2.hasOwnProperty(key)
317								&& Base.equals(obj1[key], obj2[key])))
318							return false;
319					}
320				}
321				return true;
322			}
323			return false;
324		},
325
326		read: function(list, start, options, length) {
327			if (this === Base) {
328				var value = this.peek(list, start);
329				list.__index++;
330				return value;
331			}
332			var proto = this.prototype,
333				readIndex = proto._readIndex,
334				index = start || readIndex && list.__index || 0;
335			if (!length)
336				length = list.length - index;
337			var obj = list[index];
338			if (obj instanceof this
339				|| options && options.readNull && obj == null && length <= 1) {
340				if (readIndex)
341					list.__index = index + 1;
342				return obj && options && options.clone ? obj.clone() : obj;
343			}
344			obj = Base.create(this.prototype);
345			if (readIndex)
346				obj.__read = true;
347			obj = obj.initialize.apply(obj, index > 0 || length < list.length
348				? Array.prototype.slice.call(list, index, index + length)
349				: list) || obj;
350			if (readIndex) {
351				list.__index = index + obj.__read;
352				obj.__read = undefined;
353			}
354			return obj;
355		},
356
357		peek: function(list, start) {
358			return list[list.__index = start || list.__index || 0];
359		},
360
361		remain: function(list) {
362			return list.length - (list.__index || 0);
363		},
364
365		readAll: function(list, start, options) {
366			var res = [],
367				entry;
368			for (var i = start || 0, l = list.length; i < l; i++) {
369				res.push(Array.isArray(entry = list[i])
370						? this.read(entry, 0, options)
371						: this.read(list, i, options, 1));
372			}
373			return res;
374		},
375
376		readNamed: function(list, name, start, options, length) {
377			var value = this.getNamed(list, name),
378				hasObject = value !== undefined;
379			if (hasObject) {
380				var filtered = list._filtered;
381				if (!filtered) {
382					filtered = list._filtered = Base.create(list[0]);
383					filtered._filtering = list[0];
384				}
385				filtered[name] = undefined;
386			}
387			return this.read(hasObject ? [value] : list, start, options, length);
388		},
389
390		getNamed: function(list, name) {
391			var arg = list[0];
392			if (list._hasObject === undefined)
393				list._hasObject = list.length === 1 && Base.isPlainObject(arg);
394			if (list._hasObject)
395				return name ? arg[name] : list._filtered || arg;
396		},
397
398		hasNamed: function(list, name) {
399			return !!this.getNamed(list, name);
400		},
401
402		isPlainValue: function(obj, asString) {
403			return this.isPlainObject(obj) || Array.isArray(obj)
404					|| asString && typeof obj === 'string';
405		},
406
407		serialize: function(obj, options, compact, dictionary) {
408			options = options || {};
409
410			var root = !dictionary,
411				res;
412			if (root) {
413				options.formatter = new Formatter(options.precision);
414				dictionary = {
415					length: 0,
416					definitions: {},
417					references: {},
418					add: function(item, create) {
419						var id = '#' + item._id,
420							ref = this.references[id];
421						if (!ref) {
422							this.length++;
423							var res = create.call(item),
424								name = item._class;
425							if (name && res[0] !== name)
426								res.unshift(name);
427							this.definitions[id] = res;
428							ref = this.references[id] = [id];
429						}
430						return ref;
431					}
432				};
433			}
434			if (obj && obj._serialize) {
435				res = obj._serialize(options, dictionary);
436				var name = obj._class;
437				if (name && !compact && !res._compact && res[0] !== name)
438					res.unshift(name);
439			} else if (Array.isArray(obj)) {
440				res = [];
441				for (var i = 0, l = obj.length; i < l; i++)
442					res[i] = Base.serialize(obj[i], options, compact,
443							dictionary);
444				if (compact)
445					res._compact = true;
446			} else if (Base.isPlainObject(obj)) {
447				res = {};
448				var keys = Object.keys(obj);
449				for (var i = 0, l = keys.length; i < l; i++) {
450					var key = keys[i];
451					res[key] = Base.serialize(obj[key], options, compact,
452							dictionary);
453				}
454			} else if (typeof obj === 'number') {
455				res = options.formatter.number(obj, options.precision);
456			} else {
457				res = obj;
458			}
459			return root && dictionary.length > 0
460					? [['dictionary', dictionary.definitions], res]
461					: res;
462		},
463
464		deserialize: function(json, create, _data, _isDictionary) {
465			var res = json,
466				isRoot = !_data;
467			_data = _data || {};
468			if (Array.isArray(json)) {
469				var type = json[0],
470					isDictionary = type === 'dictionary';
471				if (json.length == 1 && /^#/.test(type))
472					return _data.dictionary[type];
473				type = Base.exports[type];
474				res = [];
475				if (_isDictionary)
476					_data.dictionary = res;
477				for (var i = type ? 1 : 0, l = json.length; i < l; i++)
478					res.push(Base.deserialize(json[i], create, _data,
479							isDictionary));
480				if (type) {
481					var args = res;
482					if (create) {
483						res = create(type, args);
484					} else {
485						res = Base.create(type.prototype);
486						type.apply(res, args);
487					}
488				}
489			} else if (Base.isPlainObject(json)) {
490				res = {};
491				if (_isDictionary)
492					_data.dictionary = res;
493				for (var key in json)
494					res[key] = Base.deserialize(json[key], create, _data);
495			}
496			return isRoot && json && json.length && json[0][0] === 'dictionary'
497					? res[1]
498					: res;
499		},
500
501		exportJSON: function(obj, options) {
502			var json = Base.serialize(obj, options);
503			return options && options.asString === false
504					? json
505					: JSON.stringify(json);
506		},
507
508		importJSON: function(json, target) {
509			return Base.deserialize(
510					typeof json === 'string' ? JSON.parse(json) : json,
511					function(type, args) {
512						var obj = target && target.constructor === type
513								? target
514								: Base.create(type.prototype),
515							isTarget = obj === target;
516						if (args.length === 1 && obj instanceof Item
517								&& (isTarget || !(obj instanceof Layer))) {
518							var arg = args[0];
519							if (Base.isPlainObject(arg))
520								arg.insert = false;
521						}
522						type.apply(obj, args);
523						if (isTarget)
524							target = null;
525						return obj;
526					});
527		},
528
529		splice: function(list, items, index, remove) {
530			var amount = items && items.length,
531				append = index === undefined;
532			index = append ? list.length : index;
533			if (index > list.length)
534				index = list.length;
535			for (var i = 0; i < amount; i++)
536				items[i]._index = index + i;
537			if (append) {
538				list.push.apply(list, items);
539				return [];
540			} else {
541				var args = [index, remove];
542				if (items)
543					args.push.apply(args, items);
544				var removed = list.splice.apply(list, args);
545				for (var i = 0, l = removed.length; i < l; i++)
546					removed[i]._index = undefined;
547				for (var i = index + amount, l = list.length; i < l; i++)
548					list[i]._index = i;
549				return removed;
550			}
551		},
552
553		capitalize: function(str) {
554			return str.replace(/\b[a-z]/g, function(match) {
555				return match.toUpperCase();
556			});
557		},
558
559		camelize: function(str) {
560			return str.replace(/-(.)/g, function(all, chr) {
561				return chr.toUpperCase();
562			});
563		},
564
565		hyphenate: function(str) {
566			return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
567		}
568	}
569});
570
571var Emitter = {
572	on: function(type, func) {
573		if (typeof type !== 'string') {
574			Base.each(type, function(value, key) {
575				this.on(key, value);
576			}, this);
577		} else {
578			var types = this._eventTypes,
579				entry = types && types[type],
580				handlers = this._callbacks = this._callbacks || {};
581			handlers = handlers[type] = handlers[type] || [];
582			if (handlers.indexOf(func) === -1) {
583				handlers.push(func);
584				if (entry && entry.install && handlers.length == 1)
585					entry.install.call(this, type);
586			}
587		}
588		return this;
589	},
590
591	off: function(type, func) {
592		if (typeof type !== 'string') {
593			Base.each(type, function(value, key) {
594				this.off(key, value);
595			}, this);
596			return;
597		}
598		var types = this._eventTypes,
599			entry = types && types[type],
600			handlers = this._callbacks && this._callbacks[type],
601			index;
602		if (handlers) {
603			if (!func || (index = handlers.indexOf(func)) !== -1
604					&& handlers.length === 1) {
605				if (entry && entry.uninstall)
606					entry.uninstall.call(this, type);
607				delete this._callbacks[type];
608			} else if (index !== -1) {
609				handlers.splice(index, 1);
610			}
611		}
612		return this;
613	},
614
615	once: function(type, func) {
616		return this.on(type, function() {
617			func.apply(this, arguments);
618			this.off(type, func);
619		});
620	},
621
622	emit: function(type, event) {
623		var handlers = this._callbacks && this._callbacks[type];
624		if (!handlers)
625			return false;
626		var args = [].slice.call(arguments, 1);
627		handlers = handlers.slice();
628		for (var i = 0, l = handlers.length; i < l; i++) {
629			if (handlers[i].apply(this, args) === false) {
630				if (event && event.stop)
631					event.stop();
632				break;
633			}
634		}
635		return true;
636	},
637
638	responds: function(type) {
639		return !!(this._callbacks && this._callbacks[type]);
640	},
641
642	attach: '#on',
643	detach: '#off',
644	fire: '#emit',
645
646	_installEvents: function(install) {
647		var handlers = this._callbacks,
648			key = install ? 'install' : 'uninstall';
649		for (var type in handlers) {
650			if (handlers[type].length > 0) {
651				var types = this._eventTypes,
652					entry = types && types[type],
653					func = entry && entry[key];
654				if (func)
655					func.call(this, type);
656			}
657		}
658	},
659
660	statics: {
661		inject: function inject(src) {
662			var events = src._events;
663			if (events) {
664				var types = {};
665				Base.each(events, function(entry, key) {
666					var isString = typeof entry === 'string',
667						name = isString ? entry : key,
668						part = Base.capitalize(name),
669						type = name.substring(2).toLowerCase();
670					types[type] = isString ? {} : entry;
671					name = '_' + name;
672					src['get' + part] = function() {
673						return this[name];
674					};
675					src['set' + part] = function(func) {
676						var prev = this[name];
677						if (prev)
678							this.off(type, prev);
679						if (func)
680							this.on(type, func);
681						this[name] = func;
682					};
683				});
684				src._eventTypes = types;
685			}
686			return inject.base.apply(this, arguments);
687		}
688	}
689};
690
691var PaperScope = Base.extend({
692	_class: 'PaperScope',
693
694	initialize: function PaperScope() {
695		paper = this;
696		this.settings = new Base({
697			applyMatrix: true,
698			handleSize: 4,
699			hitTolerance: 0
700		});
701		this.project = null;
702		this.projects = [];
703		this.tools = [];
704		this.palettes = [];
705		this._id = PaperScope._id++;
706		PaperScope._scopes[this._id] = this;
707		var proto = PaperScope.prototype;
708		if (!this.support) {
709			var ctx = CanvasProvider.getContext(1, 1);
710			proto.support = {
711				nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
712				nativeBlendModes: BlendMode.nativeModes
713			};
714			CanvasProvider.release(ctx);
715		}
716
717		if (!this.browser) {
718			var agent = navigator.userAgent.toLowerCase(),
719				platform = (/(win)/.exec(agent)
720						|| /(mac)/.exec(agent)
721						|| /(linux)/.exec(agent)
722						|| [])[0],
723				browser = proto.browser = { platform: platform };
724			if (platform)
725				browser[platform] = true;
726			agent.replace(
727				/(opera|chrome|safari|webkit|firefox|msie|trident|atom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g,
728				function(all, n, v1, v2, rv) {
729					if (!browser.chrome) {
730						var v = n === 'opera' ? v2 : v1;
731						if (n === 'trident') {
732							v = rv;
733							n = 'msie';
734						}
735						browser.version = v;
736						browser.versionNumber = parseFloat(v);
737						browser.name = n;
738						browser[n] = true;
739					}
740				}
741			);
742			if (browser.chrome)
743				delete browser.webkit;
744			if (browser.atom)
745				delete browser.chrome;
746		}
747	},
748
749	version: '0.9.24',
750
751	getView: function() {
752		return this.project && this.project.getView();
753	},
754
755	getPaper: function() {
756		return this;
757	},
758
759	execute: function(code, url, options) {
760		paper.PaperScript.execute(code, this, url, options);
761		View.updateFocus();
762	},
763
764	install: function(scope) {
765		var that = this;
766		Base.each(['project', 'view', 'tool'], function(key) {
767			Base.define(scope, key, {
768				configurable: true,
769				get: function() {
vendor: 4,894 bytes, lines 770-916
770					return that[key];
771				}
772			});
773		});
774		for (var key in this)
775			if (!/^_/.test(key) && this[key])
776				scope[key] = this[key];
777	},
778
779	setup: function(element) {
780		paper = this;
781		this.project = new Project(element);
782		return this;
783	},
784
785	activate: function() {
786		paper = this;
787	},
788
789	clear: function() {
790		for (var i = this.projects.length - 1; i >= 0; i--)
791			this.projects[i].remove();
792		for (var i = this.tools.length - 1; i >= 0; i--)
793			this.tools[i].remove();
794		for (var i = this.palettes.length - 1; i >= 0; i--)
795			this.palettes[i].remove();
796	},
797
798	remove: function() {
799		this.clear();
800		delete PaperScope._scopes[this._id];
801	},
802
803	statics: new function() {
804		function handleAttribute(name) {
805			name += 'Attribute';
806			return function(el, attr) {
807				return el[name](attr) || el[name]('data-paper-' + attr);
808			};
809		}
810
811		return {
812			_scopes: {},
813			_id: 0,
814
815			get: function(id) {
816				return this._scopes[id] || null;
817			},
818
819			getAttribute: handleAttribute('get'),
820			hasAttribute: handleAttribute('has')
821		};
822	}
823});
824
825var PaperScopeItem = Base.extend(Emitter, {
826
827	initialize: function(activate) {
828		this._scope = paper;
829		this._index = this._scope[this._list].push(this) - 1;
830		if (activate || !this._scope[this._reference])
831			this.activate();
832	},
833
834	activate: function() {
835		if (!this._scope)
836			return false;
837		var prev = this._scope[this._reference];
838		if (prev && prev !== this)
839			prev.emit('deactivate');
840		this._scope[this._reference] = this;
841		this.emit('activate', prev);
842		return true;
843	},
844
845	isActive: function() {
846		return this._scope[this._reference] === this;
847	},
848
849	remove: function() {
850		if (this._index == null)
851			return false;
852		Base.splice(this._scope[this._list], null, this._index, 1);
853		if (this._scope[this._reference] == this)
854			this._scope[this._reference] = null;
855		this._scope = null;
856		return true;
857	}
858});
859
860var Formatter = Base.extend({
861	initialize: function(precision) {
862		this.precision = precision || 5;
863		this.multiplier = Math.pow(10, this.precision);
864	},
865
866	number: function(val) {
867		return Math.round(val * this.multiplier) / this.multiplier;
868	},
869
870	pair: function(val1, val2, separator) {
871		return this.number(val1) + (separator || ',') + this.number(val2);
872	},
873
874	point: function(val, separator) {
875		return this.number(val.x) + (separator || ',') + this.number(val.y);
876	},
877
878	size: function(val, separator) {
879		return this.number(val.width) + (separator || ',')
880				+ this.number(val.height);
881	},
882
883	rectangle: function(val, separator) {
884		return this.point(val, separator) + (separator || ',')
885				+ this.size(val, separator);
886	}
887});
888
889Formatter.instance = new Formatter();
890
891var Numerical = new function() {
892
893	var abscissas = [
894		[  0.5773502691896257645091488],
895		[0,0.7745966692414833770358531],
896		[  0.3399810435848562648026658,0.8611363115940525752239465],
897		[0,0.5384693101056830910363144,0.9061798459386639927976269],
898		[  0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016],
899		[0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897],
900		[  0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609],
901		[0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762],
902		[  0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640],
903		[0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380],
904		[  0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491],
905		[0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294],
906		[  0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973],
907		[0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657],
908		[  0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542]
909	];
910
911	var weights = [
912		[1],
913		[0.8888888888888888888888889,0.5555555555555555555555556],
914		[0.6521451548625461426269361,0.3478548451374538573730639],
915		[0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640],
916		[0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],
917		[0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114],
918		[0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314],
919		[0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922],
920		[0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688],
921		[0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537],
922		[0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160],
923		[0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216],
924		[0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329],
925		[0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284],
926		[0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806]
927	];
928
929	var abs = Math.abs,
930		sqrt = Math.sqrt,
931		pow = Math.pow,
932		TOLERANCE = 1e-6,
933		EPSILON = 1e-12,
934		MACHINE_EPSILON = 1.12e-16;
935
936	return {
937		TOLERANCE: TOLERANCE,
938		EPSILON: EPSILON,
939		MACHINE_EPSILON: MACHINE_EPSILON,
940		KAPPA: 4 * (sqrt(2) - 1) / 3,
941
942		isZero: function(val) {
943			return abs(val) <= EPSILON;
944		},
945
946		integrate: function(f, a, b, n) {
947			var x = abscissas[n - 2],
948				w = weights[n - 2],
949				A = (b - a) * 0.5,
950				B = A + a,
951				i = 0,
952				m = (n + 1) >> 1,
953				sum = n & 1 ? w[i++] * f(B) : 0;
954			while (i < m) {
955				var Ax = A * x[i];
956				sum += w[i++] * (f(B + Ax) + f(B - Ax));
957			}
958			return A * sum;
959		},
960
961		findRoot: function(f, df, x, a, b, n, tolerance) {
962			for (var i = 0; i < n; i++) {
963				var fx = f(x),
964					dx = fx / df(x),
965					nx = x - dx;
966				if (abs(dx) < tolerance)
967					return nx;
968				if (fx > 0) {
969					b = x;
970					x = nx <= a ? (a + b) * 0.5 : nx;
971				} else {
972					a = x;
973					x = nx >= b ? (a + b) * 0.5 : nx;
974				}
975			}
976			return x;
977		},
978
979		solveQuadratic: function(a, b, c, roots, min, max) {
980			var count = 0,
981				x1, x2 = Infinity,
982				B = b,
983				D;
984			b /= 2;
985			D = b * b - a * c;
986			if (D !== 0 && abs(D) < MACHINE_EPSILON) {
987				var gmC = pow(abs(a * b * c), 1 / 3);
988				if (gmC < 1e-8) {
989					var mult = pow(10, abs(
990						Math.floor(Math.log(gmC) * Math.LOG10E)));
991					if (!isFinite(mult))
992						mult = 0;
993					a *= mult;
994					b *= mult;
995					c *= mult;
996					D = b * b - a * c;
997				}
998			}
999			if (abs(a) < EPSILON) {
1000				if (abs(B) < EPSILON)
1001					return abs(c) < EPSILON ? -1 : 0;
1002				x1 = -c / B;
1003			} else {
1004				if (D >= -MACHINE_EPSILON) {
1005					D = D < 0 ? 0 : D;
1006					var R = sqrt(D);
1007					if (b >= MACHINE_EPSILON && b <= MACHINE_EPSILON) {
1008						x1 = abs(a) >= abs(c) ? R / a : -c / R;
1009						x2 = -x1;
1010					} else {
1011						var q = -(b + (b < 0 ? -1 : 1) * R);
1012						x1 = q / a;
1013						x2 = c / q;
1014					}
1015				}
1016			}
1017			if (isFinite(x1) && (min == null || x1 >= min && x1 <= max))
1018				roots[count++] = x1;
1019			if (x2 !== x1
1020					&& isFinite(x2) && (min == null || x2 >= min && x2 <= max))
1021				roots[count++] = x2;
1022			return count;
1023		},
1024
1025		solveCubic: function(a, b, c, d, roots, min, max) {
1026			var count = 0,
1027				x, b1, c2;
1028			if (abs(a) < EPSILON) {
1029				a = b;
1030				b1 = c;
1031				c2 = d;
1032				x = Infinity;
1033			} else if (abs(d) < EPSILON) {
1034				b1 = b;
1035				c2 = c;
1036				x = 0;
1037			} else {
1038				var ec = 1 + MACHINE_EPSILON,
1039					x0, q, qd, t, r, s, tmp;
1040				x = -(b / a) / 3;
1041				tmp = a * x,
1042				b1 = tmp + b,
1043				c2 = b1 * x + c,
1044				qd = (tmp + b1) * x + c2,
1045				q = c2 * x + d;
1046				t = q /a;
1047				r = pow(abs(t), 1/3);
1048				s = t < 0 ? -1 : 1;
1049				t = -qd / a;
1050				r = t > 0 ? 1.3247179572 * Math.max(r, sqrt(t)) : r;
1051				x0 = x - s * r;
1052				if (x0 !== x) {
1053					do {
1054						x = x0;
1055						tmp = a * x,
1056						b1 = tmp + b,
1057						c2 = b1 * x + c,
1058						qd = (tmp + b1) * x + c2,
1059						q = c2 * x + d;
1060						x0 = qd === 0 ? x : x - q / qd / ec;
1061						if (x0 === x) {
1062							x = x0;
1063							break;
1064						}
1065					} while (s * x0 > s * x);
1066					if (abs(a) * x * x > abs(d / x)) {
1067						c2 = -d / x;
1068						b1 = (c2 - c) / x;
1069					}
1070				}
1071			}
1072			var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max);
1073			if (isFinite(x) && (count === 0 || x !== roots[count - 1])
1074					&& (min == null || x >= min && x <= max))
1075				roots[count++] = x;
1076			return count;
1077		}
1078	};
1079};
1080
1081var UID = {
1082	_id: 1,
1083	_pools: {},
1084
1085	get: function(ctor) {
1086		if (ctor) {
1087			var name = ctor._class,
1088				pool = this._pools[name];
1089			if (!pool)
1090				pool = this._pools[name] = { _id: 1 };
1091			return pool._id++;
1092		} else {
1093			return this._id++;
1094		}
1095	}
1096};
1097
1098var Point = Base.extend({
1099	_class: 'Point',
1100	_readIndex: true,
1101
1102	initialize: function Point(arg0, arg1) {
1103		var type = typeof arg0;
1104		if (type === 'number') {
1105			var hasY = typeof arg1 === 'number';
1106			this.x = arg0;
1107			this.y = hasY ? arg1 : arg0;
1108			if (this.__read)
1109				this.__read = hasY ? 2 : 1;
1110		} else if (type === 'undefined' || arg0 === null) {
1111			this.x = this.y = 0;
1112			if (this.__read)
1113				this.__read = arg0 === null ? 1 : 0;
1114		} else {
1115			if (Array.isArray(arg0)) {
1116				this.x = arg0[0];
1117				this.y = arg0.length > 1 ? arg0[1] : arg0[0];
1118			} else if (arg0.x != null) {
1119				this.x = arg0.x;
1120				this.y = arg0.y;
1121			} else if (arg0.width != null) {
1122				this.x = arg0.width;
1123				this.y = arg0.height;
1124			} else if (arg0.angle != null) {
1125				this.x = arg0.length;
1126				this.y = 0;
1127				this.setAngle(arg0.angle);
1128			} else {
1129				this.x = this.y = 0;
1130				if (this.__read)
1131					this.__read = 0;
1132			}
1133			if (this.__read)
1134				this.__read = 1;
1135		}
1136	},
1137
1138	set: function(x, y) {
1139		this.x = x;
1140		this.y = y;
1141		return this;
1142	},
1143
1144	equals: function(point) {
1145		return this === point || point
1146				&& (this.x === point.x && this.y === point.y
1147					|| Array.isArray(point)
1148						&& this.x === point[0] && this.y === point[1])
1149				|| false;
1150	},
1151
1152	clone: function() {
1153		return new Point(this.x, this.y);
1154	},
1155
1156	toString: function() {
1157		var f = Formatter.instance;
1158		return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';
1159	},
1160
1161	_serialize: function(options) {
1162		var f = options.formatter;
1163		return [f.number(this.x), f.number(this.y)];
1164	},
1165
1166	getLength: function() {
1167		return Math.sqrt(this.x * this.x + this.y * this.y);
1168	},
1169
1170	setLength: function(length) {
1171		if (this.isZero()) {
1172			var angle = this._angle || 0;
1173			this.set(
1174				Math.cos(angle) * length,
1175				Math.sin(angle) * length
1176			);
1177		} else {
1178			var scale = length / this.getLength();
1179			if (Numerical.isZero(scale))
1180				this.getAngle();
1181			this.set(
1182				this.x * scale,
1183				this.y * scale
1184			);
1185		}
1186	},
1187	getAngle: function() {
1188		return this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI;
1189	},
1190
1191	setAngle: function(angle) {
1192		this.setAngleInRadians.call(this, angle * Math.PI / 180);
1193	},
1194
1195	getAngleInDegrees: '#getAngle',
1196	setAngleInDegrees: '#setAngle',
1197
1198	getAngleInRadians: function() {
1199		if (!arguments.length) {
1200			return this.isZero()
1201					? this._angle || 0
1202					: this._angle = Math.atan2(this.y, this.x);
1203		} else {
1204			var point = Point.read(arguments),
1205				div = this.getLength() * point.getLength();
1206			if (Numerical.isZero(div)) {
1207				return NaN;
1208			} else {
1209				var a = this.dot(point) / div;
1210				return Math.acos(a < -1 ? -1 : a > 1 ? 1 : a);
1211			}
1212		}
1213	},
1214
1215	setAngleInRadians: function(angle) {
1216		this._angle = angle;
1217		if (!this.isZero()) {
1218			var length = this.getLength();
1219			this.set(
1220				Math.cos(angle) * length,
1221				Math.sin(angle) * length
1222			);
1223		}
1224	},
1225
1226	getQuadrant: function() {
1227		return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;
1228	}
1229}, {
1230	beans: false,
1231
1232	getDirectedAngle: function() {
1233		var point = Point.read(arguments);
1234		return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;
1235	},
1236
1237	getDistance: function() {
1238		var point = Point.read(arguments),
1239			x = point.x - this.x,
1240			y = point.y - this.y,
1241			d = x * x + y * y,
1242			squared = Base.read(arguments);
1243		return squared ? d : Math.sqrt(d);
1244	},
1245
1246	normalize: function(length) {
1247		if (length === undefined)
1248			length = 1;
1249		var current = this.getLength(),
1250			scale = current !== 0 ? length / current : 0,
1251			point = new Point(this.x * scale, this.y * scale);
1252		if (scale >= 0)
1253			point._angle = this._angle;
1254		return point;
1255	},
1256
1257	rotate: function(angle, center) {
1258		if (angle === 0)
1259			return this.clone();
1260		angle = angle * Math.PI / 180;
1261		var point = center ? this.subtract(center) : this,
1262			s = Math.sin(angle),
1263			c = Math.cos(angle);
1264		point = new Point(
1265			point.x * c - point.y * s,
1266			point.x * s + point.y * c
1267		);
1268		return center ? point.add(center) : point;
1269	},
1270
1271	transform: function(matrix) {
1272		return matrix ? matrix._transformPoint(this) : this;
1273	},
1274
1275	add: function() {
1276		var point = Point.read(arguments);
1277		return new Point(this.x + point.x, this.y + point.y);
1278	},
1279
1280	subtract: function() {
1281		var point = Point.read(arguments);
1282		return new Point(this.x - point.x, this.y - point.y);
1283	},
1284
1285	multiply: function() {
1286		var point = Point.read(arguments);
1287		return new Point(this.x * point.x, this.y * point.y);
1288	},
1289
1290	divide: function() {
1291		var point = Point.read(arguments);
1292		return new Point(this.x / point.x, this.y / point.y);
1293	},
1294
1295	modulo: function() {
1296		var point = Point.read(arguments);
1297		return new Point(this.x % point.x, this.y % point.y);
1298	},
1299
1300	negate: function() {
1301		return new Point(-this.x, -this.y);
1302	},
1303
1304	isInside: function() {
1305		return Rectangle.read(arguments).contains(this);
1306	},
1307
1308	isClose: function(point, tolerance) {
1309		return this.getDistance(point) < tolerance;
1310	},
1311
1312	isCollinear: function(point) {
1313		return Math.abs(this.cross(point)) < 0.000001;
1314	},
1315
1316	isColinear: '#isCollinear',
1317
1318	isOrthogonal: function(point) {
1319		return Math.abs(this.dot(point)) < 0.000001;
1320	},
1321
1322	isZero: function() {
1323		return Numerical.isZero(this.x) && Numerical.isZero(this.y);
1324	},
1325
1326	isNaN: function() {
1327		return isNaN(this.x) || isNaN(this.y);
1328	},
1329
1330	dot: function() {
1331		var point = Point.read(arguments);
1332		return this.x * point.x + this.y * point.y;
1333	},
1334
1335	cross: function() {
1336		var point = Point.read(arguments);
1337		return this.x * point.y - this.y * point.x;
1338	},
1339
1340	project: function() {
1341		var point = Point.read(arguments);
1342		if (point.isZero()) {
1343			return new Point(0, 0);
1344		} else {
1345			var scale = this.dot(point) / point.dot(point);
1346			return new Point(
1347				point.x * scale,
1348				point.y * scale
1349			);
1350		}
1351	},
1352
1353	statics: {
1354		min: function() {
1355			var point1 = Point.read(arguments),
1356				point2 = Point.read(arguments);
1357			return new Point(
1358				Math.min(point1.x, point2.x),
1359				Math.min(point1.y, point2.y)
1360			);
1361		},
1362
1363		max: function() {
1364			var point1 = Point.read(arguments),
1365				point2 = Point.read(arguments);
1366			return new Point(
1367				Math.max(point1.x, point2.x),
1368				Math.max(point1.y, point2.y)
1369			);
1370		},
1371
1372		random: function() {
1373			return new Point(Math.random(), Math.random());
1374		}
1375	}
1376}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
1377	var op = Math[name];
1378	this[name] = function() {
1379		return new Point(op(this.x), op(this.y));
1380	};
1381}, {}));
1382
1383var LinkedPoint = Point.extend({
1384	initialize: function Point(x, y, owner, setter) {
1385		this._x = x;
1386		this._y = y;
1387		this._owner = owner;
1388		this._setter = setter;
1389	},
1390
1391	set: function(x, y, _dontNotify) {
1392		this._x = x;
1393		this._y = y;
1394		if (!_dontNotify)
1395			this._owner[this._setter](this);
1396		return this;
1397	},
1398
1399	getX: function() {
1400		return this._x;
1401	},
1402
1403	setX: function(x) {
1404		this._x = x;
1405		this._owner[this._setter](this);
1406	},
1407
1408	getY: function() {
1409		return this._y;
1410	},
1411
1412	setY: function(y) {
1413		this._y = y;
1414		this._owner[this._setter](this);
1415	}
1416});
1417
1418var Size = Base.extend({
1419	_class: 'Size',
1420	_readIndex: true,
1421
1422	initialize: function Size(arg0, arg1) {
1423		var type = typeof arg0;
1424		if (type === 'number') {
1425			var hasHeight = typeof arg1 === 'number';
1426			this.width = arg0;
1427			this.height = hasHeight ? arg1 : arg0;
1428			if (this.__read)
1429				this.__read = hasHeight ? 2 : 1;
1430		} else if (type === 'undefined' || arg0 === null) {
1431			this.width = this.height = 0;
1432			if (this.__read)
1433				this.__read = arg0 === null ? 1 : 0;
1434		} else {
1435			if (Array.isArray(arg0)) {
1436				this.width = arg0[0];
1437				this.height = arg0.length > 1 ? arg0[1] : arg0[0];
1438			} else if (arg0.width != null) {
1439				this.width = arg0.width;
1440				this.height = arg0.height;
1441			} else if (arg0.x != null) {
1442				this.width = arg0.x;
1443				this.height = arg0.y;
1444			} else {
1445				this.width = this.height = 0;
1446				if (this.__read)
1447					this.__read = 0;
1448			}
1449			if (this.__read)
1450				this.__read = 1;
1451		}
1452	},
1453
1454	set: function(width, height) {
1455		this.width = width;
1456		this.height = height;
1457		return this;
1458	},
1459
1460	equals: function(size) {
1461		return size === this || size && (this.width === size.width
1462				&& this.height === size.height
1463				|| Array.isArray(size) && this.width === size[0]
1464					&& this.height === size[1]) || false;
1465	},
1466
1467	clone: function() {
1468		return new Size(this.width, this.height);
1469	},
1470
1471	toString: function() {
1472		var f = Formatter.instance;
1473		return '{ width: ' + f.number(this.width)
1474				+ ', height: ' + f.number(this.height) + ' }';
1475	},
1476
1477	_serialize: function(options) {
1478		var f = options.formatter;
1479		return [f.number(this.width),
1480				f.number(this.height)];
1481	},
1482
1483	add: function() {
1484		var size = Size.read(arguments);
1485		return new Size(this.width + size.width, this.height + size.height);
1486	},
1487
1488	subtract: function() {
1489		var size = Size.read(arguments);
1490		return new Size(this.width - size.width, this.height - size.height);
1491	},
1492
1493	multiply: function() {
1494		var size = Size.read(arguments);
1495		return new Size(this.width * size.width, this.height * size.height);
1496	},
1497
1498	divide: function() {
1499		var size = Size.read(arguments);
1500		return new Size(this.width / size.width, this.height / size.height);
1501	},
1502
1503	modulo: function() {
1504		var size = Size.read(arguments);
1505		return new Size(this.width % size.width, this.height % size.height);
1506	},
1507
1508	negate: function() {
1509		return new Size(-this.width, -this.height);
1510	},
1511
1512	isZero: function() {
1513		return Numerical.isZero(this.width) && Numerical.isZero(this.height);
1514	},
1515
1516	isNaN: function() {
1517		return isNaN(this.width) || isNaN(this.height);
1518	},
1519
1520	statics: {
1521		min: function(size1, size2) {
1522			return new Size(
1523				Math.min(size1.width, size2.width),
1524				Math.min(size1.height, size2.height));
1525		},
1526
1527		max: function(size1, size2) {
1528			return new Size(
1529				Math.max(size1.width, size2.width),
1530				Math.max(size1.height, size2.height));
1531		},
1532
1533		random: function() {
1534			return new Size(Math.random(), Math.random());
1535		}
1536	}
1537}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
1538	var op = Math[name];
1539	this[name] = function() {
1540		return new Size(op(this.width), op(this.height));
1541	};
1542}, {}));
1543
1544var LinkedSize = Size.extend({
1545	initialize: function Size(width, height, owner, setter) {
1546		this._width = width;
1547		this._height = height;
1548		this._owner = owner;
1549		this._setter = setter;
1550	},
1551
1552	set: function(width, height, _dontNotify) {
1553		this._width = width;
1554		this._height = height;
1555		if (!_dontNotify)
1556			this._owner[this._setter](this);
1557		return this;
1558	},
1559
1560	getWidth: function() {
1561		return this._width;
1562	},
1563
1564	setWidth: function(width) {
1565		this._width = width;
1566		this._owner[this._setter](this);
1567	},
1568
1569	getHeight: function() {
1570		return this._height;
1571	},
1572
1573	setHeight: function(height) {
1574		this._height = height;
1575		this._owner[this._setter](this);
1576	}
1577});
1578
1579var Rectangle = Base.extend({
1580	_class: 'Rectangle',
1581	_readIndex: true,
1582	beans: true,
1583
1584	initialize: function Rectangle(arg0, arg1, arg2, arg3) {
1585		var type = typeof arg0,
1586			read = 0;
1587		if (type === 'number') {
1588			this.x = arg0;
1589			this.y = arg1;
1590			this.width = arg2;
1591			this.height = arg3;
1592			read = 4;
1593		} else if (type === 'undefined' || arg0 === null) {
1594			this.x = this.y = this.width = this.height = 0;
1595			read = arg0 === null ? 1 : 0;
1596		} else if (arguments.length === 1) {
1597			if (Array.isArray(arg0)) {
1598				this.x = arg0[0];
1599				this.y = arg0[1];
1600				this.width = arg0[2];
1601				this.height = arg0[3];
1602				read = 1;
1603			} else if (arg0.x !== undefined || arg0.width !== undefined) {
1604				this.x = arg0.x || 0;
1605				this.y = arg0.y || 0;
1606				this.width = arg0.width || 0;
1607				this.height = arg0.height || 0;
1608				read = 1;
1609			} else if (arg0.from === undefined && arg0.to === undefined) {
1610				this.x = this.y = this.width = this.height = 0;
1611				this._set(arg0);
1612				read = 1;
1613			}
1614		}
1615		if (!read) {
1616			var point = Point.readNamed(arguments, 'from'),
1617				next = Base.peek(arguments);
1618			this.x = point.x;
1619			this.y = point.y;
1620			if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
1621				var to = Point.readNamed(arguments, 'to');
1622				this.width = to.x - point.x;
1623				this.height = to.y - point.y;
1624				if (this.width < 0) {
1625					this.x = to.x;
1626					this.width = -this.width;
1627				}
1628				if (this.height < 0) {
1629					this.y = to.y;
1630					this.height = -this.height;
1631				}
1632			} else {
1633				var size = Size.read(arguments);
1634				this.width = size.width;
1635				this.height = size.height;
1636			}
1637			read = arguments.__index;
1638		}
1639		if (this.__read)
1640			this.__read = read;
1641	},
1642
1643	set: function(x, y, width, height) {
1644		this.x = x;
1645		this.y = y;
1646		this.width = width;
1647		this.height = height;
1648		return this;
1649	},
1650
1651	clone: function() {
1652		return new Rectangle(this.x, this.y, this.width, this.height);
1653	},
1654
1655	equals: function(rect) {
1656		var rt = Base.isPlainValue(rect)
1657				? Rectangle.read(arguments)
1658				: rect;
1659		return rt === this
1660				|| rt && this.x === rt.x && this.y === rt.y
1661					&& this.width === rt.width && this.height === rt.height
1662				|| false;
1663	},
1664
1665	toString: function() {
1666		var f = Formatter.instance;
1667		return '{ x: ' + f.number(this.x)
1668				+ ', y: ' + f.number(this.y)
1669				+ ', width: ' + f.number(this.width)
1670				+ ', height: ' + f.number(this.height)
1671				+ ' }';
1672	},
1673
1674	_serialize: function(options) {
1675		var f = options.formatter;
1676		return [f.number(this.x),
1677				f.number(this.y),
1678				f.number(this.width),
1679				f.number(this.height)];
1680	},
1681
1682	getPoint: function(_dontLink) {
1683		var ctor = _dontLink ? Point : LinkedPoint;
1684		return new ctor(this.x, this.y, this, 'setPoint');
1685	},
1686
1687	setPoint: function() {
1688		var point = Point.read(arguments);
1689		this.x = point.x;
1690		this.y = point.y;
1691	},
1692
1693	getSize: function(_dontLink) {
1694		var ctor = _dontLink ? Size : LinkedSize;
1695		return new ctor(this.width, this.height, this, 'setSize');
1696	},
1697
1698	setSize: function() {
1699		var size = Size.read(arguments);
1700		if (this._fixX)
1701			this.x += (this.width - size.width) * this._fixX;
1702		if (this._fixY)
1703			this.y += (this.height - size.height) * this._fixY;
1704		this.width = size.width;
1705		this.height = size.height;
1706		this._fixW = 1;
1707		this._fixH = 1;
1708	},
1709
1710	getLeft: function() {
1711		return this.x;
1712	},
1713
1714	setLeft: function(left) {
1715		if (!this._fixW)
1716			this.width -= left - this.x;
1717		this.x = left;
1718		this._fixX = 0;
1719	},
1720
1721	getTop: function() {
1722		return this.y;
1723	},
1724
1725	setTop: function(top) {
1726		if (!this._fixH)
1727			this.height -= top - this.y;
1728		this.y = top;
1729		this._fixY = 0;
1730	},
1731
1732	getRight: function() {
1733		return this.x + this.width;
1734	},
1735
1736	setRight: function(right) {
1737		if (this._fixX !== undefined && this._fixX !== 1)
1738			this._fixW = 0;
1739		if (this._fixW)
1740			this.x = right - this.width;
1741		else
1742			this.width = right - this.x;
1743		this._fixX = 1;
1744	},
1745
1746	getBottom: function() {
1747		return this.y + this.height;
1748	},
1749
1750	setBottom: function(bottom) {
1751		if (this._fixY !== undefined && this._fixY !== 1)
1752			this._fixH = 0;
1753		if (this._fixH)
1754			this.y = bottom - this.height;
1755		else
1756			this.height = bottom - this.y;
1757		this._fixY = 1;
1758	},
1759
1760	getCenterX: function() {
1761		return this.x + this.width * 0.5;
1762	},
1763
1764	setCenterX: function(x) {
1765		this.x = x - this.width * 0.5;
1766		this._fixX = 0.5;
1767	},
1768
1769	getCenterY: function() {
1770		return this.y + this.height * 0.5;
1771	},
1772
1773	setCenterY: function(y) {
1774		this.y = y - this.height * 0.5;
1775		this._fixY = 0.5;
1776	},
1777
1778	getCenter: function(_dontLink) {
1779		var ctor = _dontLink ? Point : LinkedPoint;
1780		return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');
1781	},
1782
1783	setCenter: function() {
1784		var point = Point.read(arguments);
1785		this.setCenterX(point.x);
1786		this.setCenterY(point.y);
1787		return this;
1788	},
1789
1790	getArea: function() {
1791		return this.width * this.height;
1792	},
1793
1794	isEmpty: function() {
1795		return this.width === 0 || this.height === 0;
1796	},
1797
1798	contains: function(arg) {
1799		return arg && arg.width !== undefined
1800				|| (Array.isArray(arg) ? arg : arguments).length == 4
1801				? this._containsRectangle(Rectangle.read(arguments))
1802				: this._containsPoint(Point.read(arguments));
1803	},
1804
1805	_containsPoint: function(point) {
1806		var x = point.x,
1807			y = point.y;
1808		return x >= this.x && y >= this.y
1809				&& x <= this.x + this.width
1810				&& y <= this.y + this.height;
1811	},
1812
1813	_containsRectangle: function(rect) {
1814		var x = rect.x,
1815			y = rect.y;
1816		return x >= this.x && y >= this.y
1817				&& x + rect.width <= this.x + this.width
1818				&& y + rect.height <= this.y + this.height;
1819	},
1820
1821	intersects: function() {
1822		var rect = Rectangle.read(arguments);
1823		return rect.x + rect.width > this.x
1824				&& rect.y + rect.height > this.y
1825				&& rect.x < this.x + this.width
1826				&& rect.y < this.y + this.height;
1827	},
1828
1829	touches: function() {
1830		var rect = Rectangle.read(arguments);
1831		return rect.x + rect.width >= this.x
1832				&& rect.y + rect.height >= this.y
1833				&& rect.x <= this.x + this.width
1834				&& rect.y <= this.y + this.height;
1835	},
1836
1837	intersect: function() {
1838		var rect = Rectangle.read(arguments),
1839			x1 = Math.max(this.x, rect.x),
1840			y1 = Math.max(this.y, rect.y),
1841			x2 = Math.min(this.x + this.width, rect.x + rect.width),
1842			y2 = Math.min(this.y + this.height, rect.y + rect.height);
1843		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
1844	},
1845
1846	unite: function() {
1847		var rect = Rectangle.read(arguments),
1848			x1 = Math.min(this.x, rect.x),
1849			y1 = Math.min(this.y, rect.y),
1850			x2 = Math.max(this.x + this.width, rect.x + rect.width),
1851			y2 = Math.max(this.y + this.height, rect.y + rect.height);
1852		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
1853	},
1854
1855	include: function() {
1856		var point = Point.read(arguments);
1857		var x1 = Math.min(this.x, point.x),
1858			y1 = Math.min(this.y, point.y),
1859			x2 = Math.max(this.x + this.width, point.x),
1860			y2 = Math.max(this.y + this.height, point.y);
1861		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
1862	},
1863
1864	expand: function() {
1865		var amount = Size.read(arguments),
1866			hor = amount.width,
1867			ver = amount.height;
1868		return new Rectangle(this.x - hor / 2, this.y - ver / 2,
1869				this.width + hor, this.height + ver);
1870	},
1871
1872	scale: function(hor, ver) {
1873		return this.expand(this.width * hor - this.width,
1874				this.height * (ver === undefined ? hor : ver) - this.height);
1875	}
1876}, Base.each([
1877		['Top', 'Left'], ['Top', 'Right'],
1878		['Bottom', 'Left'], ['Bottom', 'Right'],
1879		['Left', 'Center'], ['Top', 'Center'],
1880		['Right', 'Center'], ['Bottom', 'Center']
1881	],
1882	function(parts, index) {
1883		var part = parts.join('');
1884		var xFirst = /^[RL]/.test(part);
1885		if (index >= 4)
1886			parts[1] += xFirst ? 'Y' : 'X';
1887		var x = parts[xFirst ? 0 : 1],
1888			y = parts[xFirst ? 1 : 0],
1889			getX = 'get' + x,
1890			getY = 'get' + y,
1891			setX = 'set' + x,
1892			setY = 'set' + y,
1893			get = 'get' + part,
1894			set = 'set' + part;
vendor: 4,541 bytes, lines 1895-2089
1895		this[get] = function(_dontLink) {
1896			var ctor = _dontLink ? Point : LinkedPoint;
1897			return new ctor(this[getX](), this[getY](), this, set);
1898		};
1899		this[set] = function() {
1900			var point = Point.read(arguments);
1901			this[setX](point.x);
1902			this[setY](point.y);
1903		};
1904	}, {
1905		beans: true
1906	}
1907));
1908
1909var LinkedRectangle = Rectangle.extend({
1910	initialize: function Rectangle(x, y, width, height, owner, setter) {
1911		this.set(x, y, width, height, true);
1912		this._owner = owner;
1913		this._setter = setter;
1914	},
1915
1916	set: function(x, y, width, height, _dontNotify) {
1917		this._x = x;
1918		this._y = y;
1919		this._width = width;
1920		this._height = height;
1921		if (!_dontNotify)
1922			this._owner[this._setter](this);
1923		return this;
1924	}
1925}, new function() {
1926	var proto = Rectangle.prototype;
1927
1928	return Base.each(['x', 'y', 'width', 'height'], function(key) {
1929		var part = Base.capitalize(key);
1930		var internal = '_' + key;
1931		this['get' + part] = function() {
1932			return this[internal];
1933		};
1934
1935		this['set' + part] = function(value) {
1936			this[internal] = value;
1937			if (!this._dontNotify)
1938				this._owner[this._setter](this);
1939		};
1940	}, Base.each(['Point', 'Size', 'Center',
1941			'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',
1942			'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
1943			'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],
1944		function(key) {
1945			var name = 'set' + key;
1946			this[name] = function() {
1947				this._dontNotify = true;
1948				proto[name].apply(this, arguments);
1949				this._dontNotify = false;
1950				this._owner[this._setter](this);
1951			};
1952		}, {
1953			isSelected: function() {
1954				return this._owner._boundsSelected;
1955			},
1956
1957			setSelected: function(selected) {
1958				var owner = this._owner;
1959				if (owner.setSelected) {
1960					owner._boundsSelected = selected;
1961					owner.setSelected(selected || owner._selectedSegmentState > 0);
1962				}
1963			}
1964		})
1965	);
1966});
1967
1968var Matrix = Base.extend({
1969	_class: 'Matrix',
1970
1971	initialize: function Matrix(arg) {
1972		var count = arguments.length,
1973			ok = true;
1974		if (count === 6) {
1975			this.set.apply(this, arguments);
1976		} else if (count === 1) {
1977			if (arg instanceof Matrix) {
1978				this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
1979			} else if (Array.isArray(arg)) {
1980				this.set.apply(this, arg);
1981			} else {
1982				ok = false;
1983			}
1984		} else if (count === 0) {
1985			this.reset();
1986		} else {
1987			ok = false;
1988		}
1989		if (!ok)
1990			throw new Error('Unsupported matrix parameters');
1991	},
1992
1993	set: function(a, c, b, d, tx, ty, _dontNotify) {
1994		this._a = a;
1995		this._c = c;
1996		this._b = b;
1997		this._d = d;
1998		this._tx = tx;
1999		this._ty = ty;
2000		if (!_dontNotify)
2001			this._changed();
2002		return this;
2003	},
2004
2005	_serialize: function(options) {
2006		return Base.serialize(this.getValues(), options);
2007	},
2008
2009	_changed: function() {
2010		var owner = this._owner;
2011		if (owner) {
2012			if (owner._applyMatrix) {
2013				owner.transform(null, true);
2014			} else {
2015				owner._changed(9);
2016			}
2017		}
2018	},
2019
2020	clone: function() {
2021		return new Matrix(this._a, this._c, this._b, this._d,
2022				this._tx, this._ty);
2023	},
2024
2025	equals: function(mx) {
2026		return mx === this || mx && this._a === mx._a && this._b === mx._b
2027				&& this._c === mx._c && this._d === mx._d
2028				&& this._tx === mx._tx && this._ty === mx._ty
2029				|| false;
2030	},
2031
2032	toString: function() {
2033		var f = Formatter.instance;
2034		return '[[' + [f.number(this._a), f.number(this._b),
2035					f.number(this._tx)].join(', ') + '], ['
2036				+ [f.number(this._c), f.number(this._d),
2037					f.number(this._ty)].join(', ') + ']]';
2038	},
2039
2040	reset: function(_dontNotify) {
2041		this._a = this._d = 1;
2042		this._c = this._b = this._tx = this._ty = 0;
2043		if (!_dontNotify)
2044			this._changed();
2045		return this;
2046	},
2047
2048	apply: function(recursively, _setApplyMatrix) {
2049		var owner = this._owner;
2050		if (owner) {
2051			owner.transform(null, true, Base.pick(recursively, true),
2052					_setApplyMatrix);
2053			return this.isIdentity();
2054		}
2055		return false;
2056	},
2057
2058	translate: function() {
2059		var point = Point.read(arguments),
2060			x = point.x,
2061			y = point.y;
2062		this._tx += x * this._a + y * this._b;
2063		this._ty += x * this._c + y * this._d;
2064		this._changed();
2065		return this;
2066	},
2067
2068	scale: function() {
2069		var scale = Point.read(arguments),
2070			center = Point.read(arguments, 0, { readNull: true });
2071		if (center)
2072			this.translate(center);
2073		this._a *= scale.x;
2074		this._c *= scale.x;
2075		this._b *= scale.y;
2076		this._d *= scale.y;
2077		if (center)
2078			this.translate(center.negate());
2079		this._changed();
2080		return this;
2081	},
2082
2083	rotate: function(angle ) {
2084		angle *= Math.PI / 180;
2085		var center = Point.read(arguments, 1),
2086			x = center.x,
2087			y = center.y,
2088			cos = Math.cos(angle),
2089			sin = Math.sin(angle),
2090			tx = x - x * cos + y * sin,
2091			ty = y - x * sin - y * cos,
2092			a = this._a,
2093			b = this._b,
2094			c = this._c,
2095			d = this._d;
2096		this._a = cos * a + sin * b;
2097		this._b = -sin * a + cos * b;
2098		this._c = cos * c + sin * d;
2099		this._d = -sin * c + cos * d;
2100		this._tx += tx * a + ty * b;
2101		this._ty += tx * c + ty * d;
2102		this._changed();
2103		return this;
2104	},
2105
2106	shear: function() {
2107		var shear = Point.read(arguments),
2108			center = Point.read(arguments, 0, { readNull: true });
2109		if (center)
2110			this.translate(center);
2111		var a = this._a,
2112			c = this._c;
2113		this._a += shear.y * this._b;
2114		this._c += shear.y * this._d;
2115		this._b += shear.x * a;
2116		this._d += shear.x * c;
2117		if (center)
2118			this.translate(center.negate());
2119		this._changed();
2120		return this;
2121	},
2122
2123	skew: function() {
2124		var skew = Point.read(arguments),
2125			center = Point.read(arguments, 0, { readNull: true }),
2126			toRadians = Math.PI / 180,
2127			shear = new Point(Math.tan(skew.x * toRadians),
2128				Math.tan(skew.y * toRadians));
2129		return this.shear(shear, center);
2130	},
2131
2132	concatenate: function(mx) {
2133		var a1 = this._a,
2134			b1 = this._b,
2135			c1 = this._c,
2136			d1 = this._d,
2137			a2 = mx._a,
2138			b2 = mx._b,
2139			c2 = mx._c,
2140			d2 = mx._d,
2141			tx2 = mx._tx,
2142			ty2 = mx._ty;
2143		this._a = a2 * a1 + c2 * b1;
2144		this._b = b2 * a1 + d2 * b1;
2145		this._c = a2 * c1 + c2 * d1;
2146		this._d = b2 * c1 + d2 * d1;
2147		this._tx += tx2 * a1 + ty2 * b1;
2148		this._ty += tx2 * c1 + ty2 * d1;
2149		this._changed();
2150		return this;
2151	},
2152
2153	preConcatenate: function(mx) {
2154		var a1 = this._a,
2155			b1 = this._b,
2156			c1 = this._c,
2157			d1 = this._d,
2158			tx1 = this._tx,
2159			ty1 = this._ty,
2160			a2 = mx._a,
2161			b2 = mx._b,
2162			c2 = mx._c,
2163			d2 = mx._d,
2164			tx2 = mx._tx,
2165			ty2 = mx._ty;
2166		this._a = a2 * a1 + b2 * c1;
2167		this._b = a2 * b1 + b2 * d1;
2168		this._c = c2 * a1 + d2 * c1;
2169		this._d = c2 * b1 + d2 * d1;
2170		this._tx = a2 * tx1 + b2 * ty1 + tx2;
2171		this._ty = c2 * tx1 + d2 * ty1 + ty2;
2172		this._changed();
2173		return this;
2174	},
2175
2176	chain: function(mx) {
2177		var a1 = this._a,
2178			b1 = this._b,
2179			c1 = this._c,
2180			d1 = this._d,
2181			tx1 = this._tx,
2182			ty1 = this._ty,
2183			a2 = mx._a,
2184			b2 = mx._b,
2185			c2 = mx._c,
2186			d2 = mx._d,
2187			tx2 = mx._tx,
2188			ty2 = mx._ty;
2189		return new Matrix(
2190				a2 * a1 + c2 * b1,
2191				a2 * c1 + c2 * d1,
2192				b2 * a1 + d2 * b1,
2193				b2 * c1 + d2 * d1,
2194				tx1 + tx2 * a1 + ty2 * b1,
2195				ty1 + tx2 * c1 + ty2 * d1);
2196	},
2197
2198	isIdentity: function() {
2199		return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
2200				&& this._tx === 0 && this._ty === 0;
2201	},
2202
2203	orNullIfIdentity: function() {
2204		return this.isIdentity() ? null : this;
2205	},
2206
2207	isInvertible: function() {
2208		return !!this._getDeterminant();
2209	},
2210
2211	isSingular: function() {
2212		return !this._getDeterminant();
2213	},
2214
2215	transform: function( src, dst, count) {
2216		return arguments.length < 3
2217			? this._transformPoint(Point.read(arguments))
2218			: this._transformCoordinates(src, dst, count);
2219	},
2220
2221	_transformPoint: function(point, dest, _dontNotify) {
2222		var x = point.x,
2223			y = point.y;
2224		if (!dest)
2225			dest = new Point();
2226		return dest.set(
2227			x * this._a + y * this._b + this._tx,
2228			x * this._c + y * this._d + this._ty,
2229			_dontNotify
2230		);
2231	},
2232
2233	_transformCoordinates: function(src, dst, count) {
2234		var i = 0,
2235			j = 0,
2236			max = 2 * count;
2237		while (i < max) {
2238			var x = src[i++],
2239				y = src[i++];
2240			dst[j++] = x * this._a + y * this._b + this._tx;
2241			dst[j++] = x * this._c + y * this._d + this._ty;
2242		}
2243		return dst;
2244	},
2245
2246	_transformCorners: function(rect) {
2247		var x1 = rect.x,
2248			y1 = rect.y,
2249			x2 = x1 + rect.width,
2250			y2 = y1 + rect.height,
2251			coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
2252		return this._transformCoordinates(coords, coords, 4);
2253	},
2254
2255	_transformBounds: function(bounds, dest, _dontNotify) {
2256		var coords = this._transformCorners(bounds),
2257			min = coords.slice(0, 2),
2258			max = coords.slice();
2259		for (var i = 2; i < 8; i++) {
2260			var val = coords[i],
2261				j = i & 1;
2262			if (val < min[j])
2263				min[j] = val;
2264			else if (val > max[j])
2265				max[j] = val;
2266		}
2267		if (!dest)
2268			dest = new Rectangle();
2269		return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
2270				_dontNotify);
2271	},
2272
2273	inverseTransform: function() {
2274		return this._inverseTransform(Point.read(arguments));
2275	},
2276
2277	_getDeterminant: function() {
2278		var det = this._a * this._d - this._b * this._c;
2279		return isFinite(det) && !Numerical.isZero(det)
2280				&& isFinite(this._tx) && isFinite(this._ty)
2281				? det : null;
2282	},
2283
2284	_inverseTransform: function(point, dest, _dontNotify) {
2285		var det = this._getDeterminant();
2286		if (!det)
2287			return null;
2288		var x = point.x - this._tx,
2289			y = point.y - this._ty;
2290		if (!dest)
2291			dest = new Point();
2292		return dest.set(
2293			(x * this._d - y * this._b) / det,
2294			(y * this._a - x * this._c) / det,
2295			_dontNotify
2296		);
2297	},
2298
2299	decompose: function() {
2300		var a = this._a, b = this._b, c = this._c, d = this._d;
2301		if (Numerical.isZero(a * d - b * c))
2302			return null;
2303
2304		var scaleX = Math.sqrt(a * a + b * b);
2305		a /= scaleX;
2306		b /= scaleX;
2307
2308		var shear = a * c + b * d;
2309		c -= a * shear;
2310		d -= b * shear;
2311
2312		var scaleY = Math.sqrt(c * c + d * d);
2313		c /= scaleY;
2314		d /= scaleY;
2315		shear /= scaleY;
2316
2317		if (a * d < b * c) {
2318			a = -a;
2319			b = -b;
2320			shear = -shear;
2321			scaleX = -scaleX;
2322		}
2323
2324		return {
2325			scaling: new Point(scaleX, scaleY),
2326			rotation: -Math.atan2(b, a) * 180 / Math.PI,
2327			shearing: shear
2328		};
2329	},
2330
2331	getValues: function() {
2332		return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
2333	},
2334
2335	getTranslation: function() {
2336		return new Point(this._tx, this._ty);
2337	},
2338
2339	getScaling: function() {
2340		return (this.decompose() || {}).scaling;
2341	},
2342
2343	getRotation: function() {
2344		return (this.decompose() || {}).rotation;
2345	},
2346
2347	inverted: function() {
2348		var det = this._getDeterminant();
2349		return det && new Matrix(
2350				this._d / det,
2351				-this._c / det,
2352				-this._b / det,
2353				this._a / det,
2354				(this._b * this._ty - this._d * this._tx) / det,
2355				(this._c * this._tx - this._a * this._ty) / det);
2356	},
2357
2358	shiftless: function() {
2359		return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
2360	},
2361
2362	applyToContext: function(ctx) {
2363		ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
2364	}
2365}, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) {
2366	var part = Base.capitalize(name),
2367		prop = '_' + name;
2368	this['get' + part] = function() {
2369		return this[prop];
2370	};
2371	this['set' + part] = function(value) {
2372		this[prop] = value;
2373		this._changed();
2374	};
2375}, {}));
2376
2377var Line = Base.extend({
2378	_class: 'Line',
2379
2380	initialize: function Line(arg0, arg1, arg2, arg3, arg4) {
2381		var asVector = false;
2382		if (arguments.length >= 4) {
2383			this._px = arg0;
2384			this._py = arg1;
2385			this._vx = arg2;
2386			this._vy = arg3;
2387			asVector = arg4;
2388		} else {
2389			this._px = arg0.x;
2390			this._py = arg0.y;
2391			this._vx = arg1.x;
2392			this._vy = arg1.y;
2393			asVector = arg2;
2394		}
2395		if (!asVector) {
2396			this._vx -= this._px;
2397			this._vy -= this._py;
2398		}
2399	},
2400
2401	getPoint: function() {
2402		return new Point(this._px, this._py);
2403	},
2404
2405	getVector: function() {
2406		return new Point(this._vx, this._vy);
2407	},
2408
2409	getLength: function() {
2410		return this.getVector().getLength();
2411	},
2412
2413	intersect: function(line, isInfinite) {
2414		return Line.intersect(
2415				this._px, this._py, this._vx, this._vy,
2416				line._px, line._py, line._vx, line._vy,
2417				true, isInfinite);
2418	},
2419
2420	getSide: function(point) {
2421		return Line.getSide(
2422				this._px, this._py, this._vx, this._vy,
2423				point.x, point.y, true);
2424	},
2425
2426	getDistance: function(point) {
2427		return Math.abs(Line.getSignedDistance(
2428				this._px, this._py, this._vx, this._vy,
2429				point.x, point.y, true));
2430	},
2431
2432	statics: {
2433		intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector,
2434				isInfinite) {
2435			if (!asVector) {
2436				avx -= apx;
2437				avy -= apy;
2438				bvx -= bpx;
2439				bvy -= bpy;
2440			}
2441			var cross = avx * bvy - avy * bvx;
2442			if (!Numerical.isZero(cross)) {
2443				var dx = apx - bpx,
2444					dy = apy - bpy,
2445					ta = (bvx * dy - bvy * dx) / cross,
2446					tb = (avx * dy - avy * dx) / cross;
2447				if (isInfinite || 0 <= ta && ta <= 1 && 0 <= tb && tb <= 1)
2448					return new Point(
2449								apx + ta * avx,
2450								apy + ta * avy);
2451			}
2452		},
2453
2454		getSide: function(px, py, vx, vy, x, y, asVector) {
2455			if (!asVector) {
2456				vx -= px;
2457				vy -= py;
2458			}
2459			var v2x = x - px,
2460				v2y = y - py,
2461				ccw = v2x * vy - v2y * vx;
2462			if (ccw === 0) {
2463				ccw = v2x * vx + v2y * vy;
2464				if (ccw > 0) {
2465					v2x -= vx;
2466					v2y -= vy;
2467					ccw = v2x * vx + v2y * vy;
2468					if (ccw < 0)
2469						ccw = 0;
2470				}
2471			}
2472			return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
2473		},
2474
2475		getSignedDistance: function(px, py, vx, vy, x, y, asVector) {
2476			if (!asVector) {
2477				vx -= px;
2478				vy -= py;
2479			}
2480			return Numerical.isZero(vx)
2481					? vy >= 0 ? px - x : x - px
2482					: Numerical.isZero(vy)
2483						? vx >= 0 ? y - py : py - y
2484						: (vx * (y - py) - vy * (x - px)) / Math.sqrt(vx * vx + vy * vy);
2485		}
2486	}
2487});
2488
2489var Project = PaperScopeItem.extend({
2490	_class: 'Project',
2491	_list: 'projects',
2492	_reference: 'project',
2493
2494	initialize: function Project(element) {
2495		PaperScopeItem.call(this, true);
2496		this.layers = [];
2497		this._activeLayer = null;
2498		this.symbols = [];
2499		this._currentStyle = new Style(null, null, this);
2500		this._view = View.create(this,
2501				element || CanvasProvider.getCanvas(1, 1));
2502		this._selectedItems = {};
2503		this._selectedItemCount = 0;
2504		this._updateVersion = 0;
2505	},
2506
2507	_serialize: function(options, dictionary) {
2508		return Base.serialize(this.layers, options, true, dictionary);
2509	},
2510
2511	clear: function() {
2512		for (var i = this.layers.length - 1; i >= 0; i--)
2513			this.layers[i].remove();
2514		this.symbols = [];
2515	},
2516
2517	isEmpty: function() {
2518		return this.layers.length === 0;
2519	},
2520
2521	remove: function remove() {
2522		if (!remove.base.call(this))
2523			return false;
2524		if (this._view)
2525			this._view.remove();
2526		return true;
2527	},
2528
2529	getView: function() {
2530		return this._view;
2531	},
2532
2533	getCurrentStyle: function() {
2534		return this._currentStyle;
2535	},
2536
2537	setCurrentStyle: function(style) {
2538		this._currentStyle.initialize(style);
2539	},
2540
2541	getIndex: function() {
2542		return this._index;
2543	},
2544
2545	getOptions: function() {
2546		return this._scope.settings;
2547	},
2548
2549	getActiveLayer: function() {
2550		return this._activeLayer || new Layer({ project: this });
2551	},
2552
2553	getSelectedItems: function() {
2554		var items = [];
2555		for (var id in this._selectedItems) {
2556			var item = this._selectedItems[id];
2557			if (item.isInserted())
2558				items.push(item);
2559		}
2560		return items;
2561	},
2562
2563	insertChild: function(index, item, _preserve) {
2564		if (item instanceof Layer) {
2565			item._remove(false, true);
2566			Base.splice(this.layers, [item], index, 0);
2567			item._setProject(this, true);
2568			if (this._changes)
2569				item._changed(5);
2570			if (!this._activeLayer)
2571				this._activeLayer = item;
2572		} else if (item instanceof Item) {
2573			(this._activeLayer
2574				|| this.insertChild(index, new Layer(Item.NO_INSERT)))
2575					.insertChild(index, item, _preserve);
2576		} else {
2577			item = null;
2578		}
2579		return item;
2580	},
2581
2582	addChild: function(item, _preserve) {
2583		return this.insertChild(undefined, item, _preserve);
2584	},
2585
2586	_updateSelection: function(item) {
2587		var id = item._id,
2588			selectedItems = this._selectedItems;
2589		if (item._selected) {
2590			if (selectedItems[id] !== item) {
2591				this._selectedItemCount++;
2592				selectedItems[id] = item;
2593			}
2594		} else if (selectedItems[id] === item) {
2595			this._selectedItemCount--;
2596			delete selectedItems[id];
2597		}
2598	},
2599
2600	selectAll: function() {
2601		var layers = this.layers;
2602		for (var i = 0, l = layers.length; i < l; i++)
2603			layers[i].setFullySelected(true);
2604	},
2605
2606	deselectAll: function() {
2607		var selectedItems = this._selectedItems;
2608		for (var i in selectedItems)
2609			selectedItems[i].setFullySelected(false);
2610	},
2611
2612	hitTest: function() {
2613		var point = Point.read(arguments),
2614			options = HitResult.getOptions(Base.read(arguments));
2615		for (var i = this.layers.length - 1; i >= 0; i--) {
2616			var res = this.layers[i]._hitTest(point, options);
2617			if (res) return res;
2618		}
2619		return null;
2620	},
2621
2622	getItems: function(match) {
2623		return Item._getItems(this.layers, match);
2624	},
2625
2626	getItem: function(match) {
2627		return Item._getItems(this.layers, match, null, null, true)[0] || null;
2628	},
2629
2630	importJSON: function(json) {
2631		this.activate();
2632		var layer = this._activeLayer;
2633		return Base.importJSON(json, layer && layer.isEmpty() && layer);
2634	},
2635
2636	draw: function(ctx, matrix, pixelRatio) {
2637		this._updateVersion++;
2638		ctx.save();
2639		matrix.applyToContext(ctx);
2640		var param = new Base({
2641			offset: new Point(0, 0),
2642			pixelRatio: pixelRatio,
2643			viewMatrix: matrix.isIdentity() ? null : matrix,
2644			matrices: [new Matrix()],
2645			updateMatrix: true
2646		});
2647		for (var i = 0, layers = this.layers, l = layers.length; i < l; i++)
2648			layers[i].draw(ctx, param);
2649		ctx.restore();
2650
2651		if (this._selectedItemCount > 0) {
2652			ctx.save();
2653			ctx.strokeWidth = 1;
2654			var items = this._selectedItems,
2655				size = this._scope.settings.handleSize,
2656				version = this._updateVersion;
2657			for (var id in items)
2658				items[id]._drawSelection(ctx, matrix, size, items, version);
2659			ctx.restore();
2660		}
2661	}
2662});
2663
2664var Symbol = Base.extend({
2665	_class: 'Symbol',
2666
2667	initialize: function Symbol(item, dontCenter) {
2668		this._id = UID.get();
2669		this.project = paper.project;
2670		this.project.symbols.push(this);
2671		if (item)
2672			this.setDefinition(item, dontCenter);
2673	},
2674
2675	_serialize: function(options, dictionary) {
2676		return dictionary.add(this, function() {
2677			return Base.serialize([this._class, this._definition],
2678					options, false, dictionary);
2679		});
2680	},
2681
2682	_changed: function(flags) {
2683		if (flags & 8) {
2684			Item._clearBoundsCache(this);
2685		}
2686		if (flags & 1) {
2687			this.project._needsUpdate = true;
2688		}
2689	},
2690
2691	getDefinition: function() {
2692		return this._definition;
2693	},
2694
2695	setDefinition: function(item, _dontCenter) {
2696		if (item._parentSymbol)
2697			item = item.clone();
2698		if (this._definition)
2699			this._definition._parentSymbol = null;
2700		this._definition = item;
2701		item.remove();
2702		item.setSelected(false);
2703		if (!_dontCenter)
2704			item.setPosition(new Point());
2705		item._parentSymbol = this;
2706		this._changed(9);
2707	},
2708
2709	place: function(position) {
2710		return new PlacedSymbol(this, position);
2711	},
2712
2713	clone: function() {
2714		return new Symbol(this._definition.clone(false));
2715	},
2716
2717	equals: function(symbol) {
2718		return symbol === this
2719				|| symbol && this.definition.equals(symbol.definition)
2720				|| false;
2721	}
2722});
2723
2724var Item = Base.extend(Emitter, {
2725	statics: {
2726		extend: function extend(src) {
2727			if (src._serializeFields)
2728				src._serializeFields = new Base(
2729						this.prototype._serializeFields, src._serializeFields);
2730			return extend.base.apply(this, arguments);
2731		},
2732
2733		NO_INSERT: { insert: false }
2734	},
2735
2736	_class: 'Item',
2737	_applyMatrix: true,
2738	_canApplyMatrix: true,
2739	_boundsSelected: false,
2740	_selectChildren: false,
2741	_serializeFields: {
2742		name: null,
2743		applyMatrix: null,
2744		matrix: new Matrix(),
2745		pivot: null,
2746		locked: false,
2747		visible: true,
2748		blendMode: 'normal',
2749		opacity: 1,
2750		guide: false,
2751		selected: false,
2752		clipMask: false,
2753		data: {}
2754	},
2755
2756	initialize: function Item() {
2757	},
2758
2759	_initialize: function(props, point) {
2760		var hasProps = props && Base.isPlainObject(props),
2761			internal = hasProps && props.internal === true,
2762			matrix = this._matrix = new Matrix(),
2763			project = hasProps && props.project || paper.project;
2764		if (!internal)
2765			this._id = UID.get();
2766		this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix;
2767		if (point)
2768			matrix.translate(point);
2769		matrix._owner = this;
2770		this._style = new Style(project._currentStyle, this, project);
2771		if (!this._project) {
2772			if (internal || hasProps && props.insert === false) {
2773				this._setProject(project);
2774			} else if (hasProps && props.parent) {
2775				this.setParent(props.parent);
2776			} else {
2777				(project._activeLayer || new Layer()).addChild(this);
2778			}
2779		}
2780		if (hasProps && props !== Item.NO_INSERT)
2781			this._set(props, { insert: true, project: true, parent: true },
2782					true);
2783		return hasProps;
2784	},
2785
2786	_events: new function() {
2787
2788		var mouseFlags = {
2789			mousedown: {
2790				mousedown: 1,
2791				mousedrag: 1,
2792				click: 1,
2793				doubleclick: 1
2794			},
2795			mouseup: {
2796				mouseup: 1,
2797				mousedrag: 1,
2798				click: 1,
2799				doubleclick: 1
2800			},
2801			mousemove: {
2802				mousedrag: 1,
2803				mousemove: 1,
2804				mouseenter: 1,
2805				mouseleave: 1
2806			}
2807		};
2808
2809		var mouseEvent = {
2810			install: function(type) {
2811				var counters = this.getView()._eventCounters;
2812				if (counters) {
2813					for (var key in mouseFlags) {
2814						counters[key] = (counters[key] || 0)
2815								+ (mouseFlags[key][type] || 0);
2816					}
2817				}
2818			},
2819			uninstall: function(type) {
2820				var counters = this.getView()._eventCounters;
2821				if (counters) {
2822					for (var key in mouseFlags)
2823						counters[key] -= mouseFlags[key][type] || 0;
2824				}
2825			}
2826		};
2827
2828		return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
2829			'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
2830			function(name) {
2831				this[name] = mouseEvent;
2832			}, {
2833				onFrame: {
2834					install: function() {
2835						this._animateItem(true);
2836					},
2837					uninstall: function() {
2838						this._animateItem(false);
2839					}
2840				},
2841
2842				onLoad: {}
2843			}
2844		);
2845	},
2846
2847	_animateItem: function(animate) {
2848		this.getView()._animateItem(this, animate);
2849	},
2850
2851	_serialize: function(options, dictionary) {
2852		var props = {},
2853			that = this;
2854
2855		function serialize(fields) {
2856			for (var key in fields) {
2857				var value = that[key];
2858				if (!Base.equals(value, key === 'leading'
2859						? fields.fontSize * 1.2 : fields[key])) {
2860					props[key] = Base.serialize(value, options,
2861							key !== 'data', dictionary);
2862				}
2863			}
2864		}
2865
2866		serialize(this._serializeFields);
2867		if (!(this instanceof Group))
2868			serialize(this._style._defaults);
2869		return [ this._class, props ];
2870	},
2871
2872	_changed: function(flags) {
2873		var symbol = this._parentSymbol,
2874			cacheParent = this._parent || symbol,
2875			project = this._project;
2876		if (flags & 8) {
2877			this._bounds = this._position = this._decomposed =
2878					this._globalMatrix = this._currentPath = undefined;
2879		}
2880		if (cacheParent
2881				&& (flags & 40)) {
2882			Item._clearBoundsCache(cacheParent);
2883		}
2884		if (flags & 2) {
2885			Item._clearBoundsCache(this);
2886		}
2887		if (project) {
2888			if (flags & 1) {
2889				project._needsUpdate = true;
2890			}
2891			if (project._changes) {
2892				var entry = project._changesById[this._id];
2893				if (entry) {
2894					entry.flags |= flags;
2895				} else {
2896					entry = { item: this, flags: flags };
2897					project._changesById[this._id] = entry;
2898					project._changes.push(entry);
2899				}
2900			}
2901		}
2902		if (symbol)
2903			symbol._changed(flags);
2904	},
2905
2906	set: function(props) {
2907		if (props)
2908			this._set(props);
2909		return this;
2910	},
2911
2912	getId: function() {
2913		return this._id;
2914	},
2915
2916	getName: function() {
2917		return this._name;
2918	},
2919
2920	setName: function(name, unique) {
2921
2922		if (this._name)
2923			this._removeNamed();
2924		if (name === (+name) + '')
2925			throw new Error(
2926					'Names consisting only of numbers are not supported.');
2927		var parent = this._parent;
2928		if (name && parent) {
2929			var children = parent._children,
2930				namedChildren = parent._namedChildren,
2931				orig = name,
2932				i = 1;
2933			while (unique && children[name])
2934				name = orig + ' ' + (i++);
2935			(namedChildren[name] = namedChildren[name] || []).push(this);
2936			children[name] = this;
2937		}
2938		this._name = name || undefined;
2939		this._changed(128);
2940	},
2941
2942	getStyle: function() {
2943		return this._style;
2944	},
2945
2946	setStyle: function(style) {
2947		this.getStyle().set(style);
2948	}
2949}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
2950	function(name) {
2951		var part = Base.capitalize(name),
2952			name = '_' + name;
2953		this['get' + part] = function() {
2954			return this[name];
2955		};
2956		this['set' + part] = function(value) {
2957			if (value != this[name]) {
2958				this[name] = value;
2959				this._changed(name === '_locked'
2960						? 128 : 129);
2961			}
2962		};
2963	},
2964{}), {
2965	beans: true,
2966
2967	_locked: false,
2968
2969	_visible: true,
2970
2971	_blendMode: 'normal',
2972
2973	_opacity: 1,
2974
2975	_guide: false,
2976
2977	isSelected: function() {
2978		if (this._selectChildren) {
2979			var children = this._children;
2980			for (var i = 0, l = children.length; i < l; i++)
2981				if (children[i].isSelected())
2982					return true;
2983		}
2984		return this._selected;
2985	},
2986
2987	setSelected: function(selected, noChildren) {
2988		if (!noChildren && this._selectChildren) {
2989			var children = this._children;
2990			for (var i = 0, l = children.length; i < l; i++)
2991				children[i].setSelected(selected);
2992		}
2993		if ((selected = !!selected) ^ this._selected) {
2994			this._selected = selected;
2995			this._project._updateSelection(this);
2996			this._changed(129);
2997		}
2998	},
2999
3000	_selected: false,
3001
3002	isFullySelected: function() {
3003		var children = this._children;
3004		if (children && this._selected) {
3005			for (var i = 0, l = children.length; i < l; i++)
3006				if (!children[i].isFullySelected())
3007					return false;
3008			return true;
3009		}
3010		return this._selected;
3011	},
3012
3013	setFullySelected: function(selected) {
3014		var children = this._children;
3015		if (children) {
3016			for (var i = 0, l = children.length; i < l; i++)
3017				children[i].setFullySelected(selected);
3018		}
3019		this.setSelected(selected, true);
3020	},
3021
3022	isClipMask: function() {
3023		return this._clipMask;
3024	},
3025
3026	setClipMask: function(clipMask) {
3027		if (this._clipMask != (clipMask = !!clipMask)) {
3028			this._clipMask = clipMask;
3029			if (clipMask) {
3030				this.setFillColor(null);
3031				this.setStrokeColor(null);
3032			}
3033			this._changed(129);
3034			if (this._parent)
3035				this._parent._changed(1024);
3036		}
3037	},
3038
3039	_clipMask: false,
3040
3041	getData: function() {
3042		if (!this._data)
3043			this._data = {};
3044		return this._data;
3045	},
3046
3047	setData: function(data) {
3048		this._data = data;
3049	},
3050
3051	getPosition: function(_dontLink) {
3052		var position = this._position,
3053			ctor = _dontLink ? Point : LinkedPoint;
3054		if (!position) {
3055			var pivot = this._pivot;
3056			position = this._position = pivot
3057					? this._matrix._transformPoint(pivot)
3058					: this.getBounds().getCenter(true);
3059		}
3060		return new ctor(position.x, position.y, this, 'setPosition');
3061	},
3062
3063	setPosition: function() {
3064		this.translate(Point.read(arguments).subtract(this.getPosition(true)));
3065	},
3066
3067	getPivot: function(_dontLink) {
3068		var pivot = this._pivot;
3069		if (pivot) {
3070			var ctor = _dontLink ? Point : LinkedPoint;
3071			pivot = new ctor(pivot.x, pivot.y, this, 'setPivot');
3072		}
3073		return pivot;
3074	},
3075
3076	setPivot: function() {
3077		this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
3078		this._position = undefined;
3079	},
3080
3081	_pivot: null,
3082}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds',
3083		'internalBounds', 'internalRoughBounds'],
3084	function(key) {
3085		var getter = 'get' + Base.capitalize(key),
3086			match = key.match(/^internal(.*)$/),
3087			internalGetter = match ? 'get' + match[1] : null;
3088		this[getter] = function(_matrix) {
3089			var boundsGetter = this._boundsGetter,
3090				name = !internalGetter && (typeof boundsGetter === 'string'
3091						? boundsGetter : boundsGetter && boundsGetter[getter])
3092						|| getter,
3093				bounds = this._getCachedBounds(name, _matrix, this,
3094						internalGetter);
3095			return key === 'bounds'
3096					? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
3097							bounds.height, this, 'setBounds')
3098					: bounds;
3099		};
3100	},
3101{
3102	beans: true,
3103
3104	_getBounds: function(getter, matrix, cacheItem) {
3105		var children = this._children;
3106		if (!children || children.length == 0)
3107			return new Rectangle();
3108		Item._updateBoundsCache(this, cacheItem);
3109		var x1 = Infinity,
3110			x2 = -x1,
3111			y1 = x1,
3112			y2 = x2;
3113		for (var i = 0, l = children.length; i < l; i++) {
3114			var child = children[i];
3115			if (child._visible && !child.isEmpty()) {
3116				var rect = child._getCachedBounds(getter,
3117						matrix && matrix.chain(child._matrix), cacheItem);
3118				x1 = Math.min(rect.x, x1);
3119				y1 = Math.min(rect.y, y1);
3120				x2 = Math.max(rect.x + rect.width, x2);
3121				y2 = Math.max(rect.y + rect.height, y2);
3122			}
3123		}
3124		return isFinite(x1)
3125				? new Rectangle(x1, y1, x2 - x1, y2 - y1)
3126				: new Rectangle();
3127	},
3128
3129	setBounds: function() {
3130		var rect = Rectangle.read(arguments),
3131			bounds = this.getBounds(),
3132			matrix = new Matrix(),
3133			center = rect.getCenter();
3134		matrix.translate(center);
3135		if (rect.width != bounds.width || rect.height != bounds.height) {
3136			matrix.scale(
3137					bounds.width != 0 ? rect.width / bounds.width : 1,
3138					bounds.height != 0 ? rect.height / bounds.height : 1);
3139		}
3140		center = bounds.getCenter();
3141		matrix.translate(-center.x, -center.y);
3142		this.transform(matrix);
3143	},
3144
3145	_getCachedBounds: function(getter, matrix, cacheItem, internalGetter) {
3146		matrix = matrix && matrix.orNullIfIdentity();
3147		var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(),
3148			cache = (!matrix || matrix.equals(_matrix)) && getter;
3149		Item._updateBoundsCache(this._parent || this._parentSymbol, cacheItem);
3150		if (cache && this._bounds && this._bounds[cache])
3151			return this._bounds[cache].clone();
3152		var bounds = this._getBounds(internalGetter || getter,
3153				matrix || _matrix, cacheItem);
3154		if (cache) {
3155			if (!this._bounds)
3156				this._bounds = {};
3157			var cached = this._bounds[cache] = bounds.clone();
3158			cached._internal = !!internalGetter;
3159		}
3160		return bounds;
3161	},
3162
3163	statics: {
3164		_updateBoundsCache: function(parent, item) {
3165			if (parent) {
3166				var id = item._id,
3167					ref = parent._boundsCache = parent._boundsCache || {
3168						ids: {},
3169						list: []
3170					};
3171				if (!ref.ids[id]) {
3172					ref.list.push(item);
3173					ref.ids[id] = item;
3174				}
3175			}
3176		},
3177
3178		_clearBoundsCache: function(item) {
3179			var cache = item._boundsCache;
3180			if (cache) {
3181				item._bounds = item._position = item._boundsCache = undefined;
3182				for (var i = 0, list = cache.list, l = list.length; i < l; i++){
3183					var other = list[i];
3184					if (other !== item) {
3185						other._bounds = other._position = undefined;
3186						if (other._boundsCache)
3187							Item._clearBoundsCache(other);
3188					}
3189				}
3190			}
3191		}
3192	}
3193
3194}), {
3195	beans: true,
3196
3197	_decompose: function() {
3198		return this._decomposed = this._matrix.decompose();
3199	},
3200
3201	getRotation: function() {
3202		var decomposed = this._decomposed || this._decompose();
3203		return decomposed && decomposed.rotation;
3204	},
3205
3206	setRotation: function(rotation) {
3207		var current = this.getRotation();
3208		if (current != null && rotation != null) {
3209			var decomposed = this._decomposed;
3210			this.rotate(rotation - current);
3211			decomposed.rotation = rotation;
3212			this._decomposed = decomposed;
3213		}
3214	},
3215
3216	getScaling: function(_dontLink) {
3217		var decomposed = this._decomposed || this._decompose(),
3218			scaling = decomposed && decomposed.scaling,
3219			ctor = _dontLink ? Point : LinkedPoint;
3220		return scaling && new ctor(scaling.x, scaling.y, this, 'setScaling');
3221	},
3222
3223	setScaling: function() {
3224		var current = this.getScaling();
3225		if (current) {
3226			var scaling = Point.read(arguments, 0, { clone: true }),
3227				decomposed = this._decomposed;
3228			this.scale(scaling.x / current.x, scaling.y / current.y);
3229			decomposed.scaling = scaling;
3230			this._decomposed = decomposed;
3231		}
3232	},
3233
3234	getMatrix: function() {
3235		return this._matrix;
3236	},
3237
3238	setMatrix: function() {
3239		var matrix = this._matrix;
3240		matrix.initialize.apply(matrix, arguments);
3241		if (this._applyMatrix) {
3242			this.transform(null, true);
3243		} else {
3244			this._changed(9);
3245		}
3246	},
3247
3248	getGlobalMatrix: function(_dontClone) {
3249		var matrix = this._globalMatrix,
3250			updateVersion = this._project._updateVersion;
3251		if (matrix && matrix._updateVersion !== updateVersion)
3252			matrix = null;
3253		if (!matrix) {
3254			matrix = this._globalMatrix = this._matrix.clone();
3255			var parent = this._parent;
3256			if (parent)
3257				matrix.preConcatenate(parent.getGlobalMatrix(true));
3258			matrix._updateVersion = updateVersion;
3259		}
3260		return _dontClone ? matrix : matrix.clone();
3261	},
3262
3263	getApplyMatrix: function() {
3264		return this._applyMatrix;
3265	},
3266
3267	setApplyMatrix: function(apply) {
3268		if (this._applyMatrix = this._canApplyMatrix && !!apply)
3269			this.transform(null, true);
3270	},
3271
3272	getTransformContent: '#getApplyMatrix',
3273	setTransformContent: '#setApplyMatrix',
3274}, {
3275	getProject: function() {
3276		return this._project;
3277	},
3278
3279	_setProject: function(project, installEvents) {
3280		if (this._project !== project) {
3281			if (this._project)
3282				this._installEvents(false);
3283			this._project = project;
3284			var children = this._children;
3285			for (var i = 0, l = children && children.length; i < l; i++)
3286				children[i]._setProject(project);
3287			installEvents = true;
3288		}
3289		if (installEvents)
3290			this._installEvents(true);
3291	},
3292
3293	getView: function() {
3294		return this._project.getView();
3295	},
3296
3297	_installEvents: function _installEvents(install) {
3298		_installEvents.base.call(this, install);
3299		var children = this._children;
3300		for (var i = 0, l = children && children.length; i < l; i++)
3301			children[i]._installEvents(install);
3302	},
3303
3304	getLayer: function() {
3305		var parent = this;
3306		while (parent = parent._parent) {
3307			if (parent instanceof Layer)
3308				return parent;
3309		}
3310		return null;
3311	},
3312
3313	getParent: function() {
3314		return this._parent;
3315	},
3316
3317	setParent: function(item) {
3318		return item.addChild(this);
3319	},
3320
3321	getChildren: function() {
3322		return this._children;
3323	},
3324
3325	setChildren: function(items) {
3326		this.removeChildren();
3327		this.addChildren(items);
3328	},
3329
3330	getFirstChild: function() {
3331		return this._children && this._children[0] || null;
3332	},
3333
3334	getLastChild: function() {
3335		return this._children && this._children[this._children.length - 1]
3336				|| null;
3337	},
3338
3339	getNextSibling: function() {
3340		return this._parent && this._parent._children[this._index + 1] || null;
3341	},
3342
3343	getPreviousSibling: function() {
3344		return this._parent && this._parent._children[this._index - 1] || null;
3345	},
3346
3347	getIndex: function() {
3348		return this._index;
3349	},
3350
3351	equals: function(item) {
3352		return item === this || item && this._class === item._class
3353				&& this._style.equals(item._style)
3354				&& this._matrix.equals(item._matrix)
3355				&& this._locked === item._locked
3356				&& this._visible === item._visible
3357				&& this._blendMode === item._blendMode
3358				&& this._opacity === item._opacity
3359				&& this._clipMask === item._clipMask
3360				&& this._guide === item._guide
3361				&& this._equals(item)
3362				|| false;
3363	},
3364
3365	_equals: function(item) {
3366		return Base.equals(this._children, item._children);
3367	},
3368
3369	clone: function(insert) {
3370		return this._clone(new this.constructor(Item.NO_INSERT), insert);
3371	},
3372
3373	_clone: function(copy, insert, includeMatrix) {
3374		var keys = ['_locked', '_visible', '_blendMode', '_opacity',
3375				'_clipMask', '_guide'],
3376			children = this._children;
3377		copy.setStyle(this._style);
3378		for (var i = 0, l = children && children.length; i < l; i++) {
3379			copy.addChild(children[i].clone(false), true);
3380		}
3381		for (var i = 0, l = keys.length; i < l; i++) {
3382			var key = keys[i];
3383			if (this.hasOwnProperty(key))
3384				copy[key] = this[key];
3385		}
3386		if (includeMatrix !== false)
3387			copy._matrix.initialize(this._matrix);
3388		copy.setApplyMatrix(this._applyMatrix);
3389		copy.setPivot(this._pivot);
3390		copy.setSelected(this._selected);
3391		copy._data = this._data ? Base.clone(this._data) : null;
3392		if (insert || insert === undefined)
3393			copy.insertAbove(this);
3394		if (this._name)
3395			copy.setName(this._name, true);
3396		return copy;
3397	},
3398
3399	copyTo: function(itemOrProject) {
3400		return itemOrProject.addChild(this.clone(false));
3401	},
3402
3403	rasterize: function(resolution) {
3404		var bounds = this.getStrokeBounds(),
3405			scale = (resolution || this.getView().getResolution()) / 72,
3406			topLeft = bounds.getTopLeft().floor(),
3407			bottomRight = bounds.getBottomRight().ceil(),
3408			size = new Size(bottomRight.subtract(topLeft)),
3409			canvas = CanvasProvider.getCanvas(size.multiply(scale)),
3410			ctx = canvas.getContext('2d'),
3411			matrix = new Matrix().scale(scale).translate(topLeft.negate());
3412		ctx.save();
3413		matrix.applyToContext(ctx);
3414		this.draw(ctx, new Base({ matrices: [matrix] }));
3415		ctx.restore();
3416		var raster = new Raster(Item.NO_INSERT);
3417		raster.setCanvas(canvas);
3418		raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
3419				.scale(1 / scale));
3420		raster.insertAbove(this);
3421		return raster;
3422	},
3423
3424	contains: function() {
3425		return !!this._contains(
3426				this._matrix._inverseTransform(Point.read(arguments)));
3427	},
3428
3429	_contains: function(point) {
3430		if (this._children) {
3431			for (var i = this._children.length - 1; i >= 0; i--) {
3432				if (this._children[i].contains(point))
3433					return true;
3434			}
3435			return false;
3436		}
3437		return point.isInside(this.getInternalBounds());
3438	},
3439
3440	isInside: function() {
3441		return Rectangle.read(arguments).contains(this.getBounds());
3442	},
3443
3444	_asPathItem: function() {
3445		return new Path.Rectangle({
3446			rectangle: this.getInternalBounds(),
3447			matrix: this._matrix,
3448			insert: false,
3449		});
3450	},
3451
3452	intersects: function(item, _matrix) {
3453		if (!(item instanceof Item))
3454			return false;
3455		return this._asPathItem().getIntersections(item._asPathItem(),
3456				_matrix || item._matrix).length > 0;
3457	},
3458
3459	hitTest: function() {
3460		return this._hitTest(
3461				Point.read(arguments),
3462				HitResult.getOptions(Base.read(arguments)));
3463	},
3464
3465	_hitTest: function(point, options) {
3466		if (this._locked || !this._visible || this._guide && !options.guides
3467				|| this.isEmpty())
3468			return null;
3469
3470		var matrix = this._matrix,
3471			parentTotalMatrix = options._totalMatrix,
3472			view = this.getView(),
3473			totalMatrix = options._totalMatrix = parentTotalMatrix
3474					? parentTotalMatrix.chain(matrix)
3475					: this.getGlobalMatrix().preConcatenate(view._matrix),
3476			tolerancePadding = options._tolerancePadding = new Size(
3477						Path._getPenPadding(1, totalMatrix.inverted())
3478					).multiply(
3479						Math.max(options.tolerance, 0.000001)
3480					);
3481		point = matrix._inverseTransform(point);
3482
3483		if (!this._children && !this.getInternalRoughBounds()
3484				.expand(tolerancePadding.multiply(2))._containsPoint(point))
3485			return null;
3486		var checkSelf = !(options.guides && !this._guide
3487				|| options.selected && !this._selected
3488				|| options.type && options.type !== Base.hyphenate(this._class)
3489				|| options.class && !(this instanceof options.class)),
3490			that = this,
3491			res;
3492
3493		function checkBounds(type, part) {
3494			var pt = bounds['get' + part]();
3495			if (point.subtract(pt).divide(tolerancePadding).length <= 1)
3496				return new HitResult(type, that,
3497						{ name: Base.hyphenate(part), point: pt });
3498		}
3499
3500		if (checkSelf && (options.center || options.bounds) && this._parent) {
3501			var bounds = this.getInternalBounds();
3502			if (options.center)
3503				res = checkBounds('center', 'Center');
3504			if (!res && options.bounds) {
3505				var points = [
3506					'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
3507					'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
3508				];
3509				for (var i = 0; i < 8 && !res; i++)
3510					res = checkBounds('bounds', points[i]);
3511			}
3512		}
3513
3514		var children = !res && this._children;
3515		if (children) {
3516			var opts = this._getChildHitTestOptions(options);
3517			for (var i = children.length - 1; i >= 0 && !res; i--)
3518				res = children[i]._hitTest(point, opts);
3519		}
3520		if (!res && checkSelf)
3521			res = this._hitTestSelf(point, options);
3522		if (res && res.point)
3523			res.point = matrix.transform(res.point);
3524		options._totalMatrix = parentTotalMatrix;
3525		return res;
3526	},
3527
3528	_getChildHitTestOptions: function(options) {
3529		return options;
3530	},
3531
3532	_hitTestSelf: function(point, options) {
3533		if (options.fill && this.hasFill() && this._contains(point))
3534			return new HitResult('fill', this);
3535	},
3536
3537	matches: function(name, compare) {
3538		function matchObject(obj1, obj2) {
3539			for (var i in obj1) {
3540				if (obj1.hasOwnProperty(i)) {
3541					var val1 = obj1[i],
3542						val2 = obj2[i];
3543					if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {
3544						if (!matchObject(val1, val2))
3545							return false;
3546					} else if (!Base.equals(val1, val2)) {
3547						return false;
3548					}
3549				}
3550			}
3551			return true;
3552		}
3553		var type = typeof name;
3554		if (type === 'object') {
3555			for (var key in name) {
3556				if (name.hasOwnProperty(key) && !this.matches(key, name[key]))
3557					return false;
3558			}
3559		} else if (type === 'function') {
3560			return name(this);
3561		} else {
3562			var value = /^(empty|editable)$/.test(name)
3563					? this['is' + Base.capitalize(name)]()
3564					: name === 'type'
3565						? Base.hyphenate(this._class)
3566						: this[name];
3567			if (/^(constructor|class)$/.test(name)) {
3568				if (!(this instanceof compare))
3569					return false;
3570			} else if (compare instanceof RegExp) {
3571				if (!compare.test(value))
3572					return false;
3573			} else if (typeof compare === 'function') {
3574				if (!compare(value))
3575					return false;
3576			} else if (Base.isPlainObject(compare)) {
3577				if (!matchObject(compare, value))
3578					return false;
3579			} else if (!Base.equals(value, compare)) {
3580				return false;
3581			}
3582		}
3583		return true;
3584	},
3585
3586	getItems: function(match) {
3587		return Item._getItems(this._children, match, this._matrix);
3588	},
3589
3590	getItem: function(match) {
3591		return Item._getItems(this._children, match, this._matrix, null, true)
3592				[0] || null;
3593	},
3594
3595	statics: {
3596		_getItems: function _getItems(children, match, matrix, param,
3597				firstOnly) {
3598			if (!param && typeof match === 'object') {
3599				var overlapping = match.overlapping,
3600					inside = match.inside,
3601					bounds = overlapping || inside,
3602					rect =	bounds && Rectangle.read([bounds]);
3603				param = {
3604					items: [],
3605					inside: !!inside,
3606					overlapping: !!overlapping,
3607					rect: rect,
3608					path: overlapping && new Path.Rectangle({
3609						rectangle: rect,
3610						insert: false
3611					})
3612				};
3613				if (bounds)
3614					match = Base.set({}, match,
3615							{ inside: true, overlapping: true });
3616			}
3617			var items = param && param.items,
3618				rect = param && param.rect;
3619			matrix = rect && (matrix || new Matrix());
3620			for (var i = 0, l = children && children.length; i < l; i++) {
3621				var child = children[i],
3622					childMatrix = matrix && matrix.chain(child._matrix),
3623					add = true;
3624				if (rect) {
3625					var bounds = child.getBounds(childMatrix);
3626					if (!rect.intersects(bounds))
3627						continue;
3628					if (!(param.inside && rect.contains(bounds))
3629							&& !(param.overlapping && (bounds.contains(rect)
3630								|| param.path.intersects(child, childMatrix))))
3631						add = false;
3632				}
3633				if (add && child.matches(match)) {
3634					items.push(child);
3635					if (firstOnly)
3636						break;
3637				}
3638				_getItems(child._children, match,
3639						childMatrix, param,
3640						firstOnly);
3641				if (firstOnly && items.length > 0)
3642					break;
3643			}
3644			return items;
3645		}
3646	}
3647}, {
3648
3649	importJSON: function(json) {
3650		var res = Base.importJSON(json, this);
3651		return res !== this
3652				? this.addChild(res)
3653				: res;
3654	},
3655
3656	addChild: function(item, _preserve) {
3657		return this.insertChild(undefined, item, _preserve);
3658	},
3659
3660	insertChild: function(index, item, _preserve) {
3661		var res = item ? this.insertChildren(index, [item], _preserve) : null;
3662		return res && res[0];
3663	},
3664
3665	addChildren: function(items, _preserve) {
3666		return this.insertChildren(this._children.length, items, _preserve);
3667	},
3668
3669	insertChildren: function(index, items, _preserve, _proto) {
3670		var children = this._children;
3671		if (children && items && items.length > 0) {
3672			items = Array.prototype.slice.apply(items);
3673			for (var i = items.length - 1; i >= 0; i--) {
3674				var item = items[i];
3675				if (_proto && !(item instanceof _proto)) {
3676					items.splice(i, 1);
3677				} else {
3678					var shift = item._parent === this && item._index < index;
3679					if (item._remove(false, true) && shift)
3680						index--;
3681				}
3682			}
3683			Base.splice(children, items, index, 0);
3684			var project = this._project,
3685				notifySelf = project && project._changes;
3686			for (var i = 0, l = items.length; i < l; i++) {
3687				var item = items[i];
3688				item._parent = this;
3689				item._setProject(this._project, true);
3690				if (item._name)
3691					item.setName(item._name);
3692				if (notifySelf)
3693					this._changed(5);
3694			}
3695			this._changed(11);
3696		} else {
3697			items = null;
3698		}
3699		return items;
3700	},
3701
3702	_insertSibling: function(index, item, _preserve) {
3703		return this._parent
3704				? this._parent.insertChild(index, item, _preserve)
3705				: null;
3706	},
3707
3708	insertAbove: function(item, _preserve) {
3709		return item._insertSibling(item._index + 1, this, _preserve);
3710	},
3711
3712	insertBelow: function(item, _preserve) {
3713		return item._insertSibling(item._index, this, _preserve);
3714	},
3715
3716	sendToBack: function() {
3717		return (this._parent || this instanceof Layer && this._project)
3718				.insertChild(0, this);
3719	},
3720
3721	bringToFront: function() {
3722		return (this._parent || this instanceof Layer && this._project)
3723				.addChild(this);
3724	},
3725
3726	appendTop: '#addChild',
3727
3728	appendBottom: function(item) {
3729		return this.insertChild(0, item);
3730	},
3731
3732	moveAbove: '#insertAbove',
3733
3734	moveBelow: '#insertBelow',
3735
3736	reduce: function() {
3737		if (this._children && this._children.length === 1) {
3738			var child = this._children[0].reduce();
3739			child.insertAbove(this);
3740			child.setStyle(this._style);
3741			this.remove();
3742			return child;
3743		}
3744		return this;
3745	},
3746
3747	_removeNamed: function() {
3748		var parent = this._parent;
3749		if (parent) {
3750			var children = parent._children,
3751				namedChildren = parent._namedChildren,
3752				name = this._name,
3753				namedArray = namedChildren[name],
3754				index = namedArray ? namedArray.indexOf(this) : -1;
3755			if (index !== -1) {
3756				if (children[name] == this)
3757					delete children[name];
3758				namedArray.splice(index, 1);
3759				if (namedArray.length) {
3760					children[name] = namedArray[namedArray.length - 1];
3761				} else {
3762					delete namedChildren[name];
3763				}
3764			}
3765		}
3766	},
3767
3768	_remove: function(notifySelf, notifyParent) {
3769		var parent = this._parent;
3770		if (parent) {
3771			if (this._name)
3772				this._removeNamed();
3773			if (this._index != null)
3774				Base.splice(parent._children, null, this._index, 1);
3775			this._installEvents(false);
3776			if (notifySelf) {
3777				var project = this._project;
3778				if (project && project._changes)
3779					this._changed(5);
3780			}
3781			if (notifyParent)
3782				parent._changed(11);
3783			this._parent = null;
3784			return true;
3785		}
3786		return false;
3787	},
3788
3789	remove: function() {
3790		return this._remove(true, true);
3791	},
3792
3793	replaceWith: function(item) {
3794		var ok = item && item.insertBelow(this);
3795		if (ok)
3796			this.remove();
3797		return ok;
3798	},
3799
3800	removeChildren: function(from, to) {
3801		if (!this._children)
3802			return null;
3803		from = from || 0;
3804		to = Base.pick(to, this._children.length);
3805		var removed = Base.splice(this._children, null, from, to - from);
3806		for (var i = removed.length - 1; i >= 0; i--) {
3807			removed[i]._remove(true, false);
3808		}
3809		if (removed.length > 0)
3810			this._changed(11);
3811		return removed;
3812	},
3813
3814	clear: '#removeChildren',
3815
3816	reverseChildren: function() {
3817		if (this._children) {
3818			this._children.reverse();
3819			for (var i = 0, l = this._children.length; i < l; i++)
3820				this._children[i]._index = i;
3821			this._changed(11);
3822		}
3823	},
3824
3825	isEmpty: function() {
3826		return !this._children || this._children.length === 0;
3827	},
3828
3829	isEditable: function() {
3830		var item = this;
3831		while (item) {
3832			if (!item._visible || item._locked)
3833				return false;
3834			item = item._parent;
3835		}
3836		return true;
3837	},
3838
3839	hasFill: function() {
3840		return this.getStyle().hasFill();
3841	},
3842
3843	hasStroke: function() {
3844		return this.getStyle().hasStroke();
3845	},
3846
3847	hasShadow: function() {
3848		return this.getStyle().hasShadow();
3849	},
3850
3851	_getOrder: function(item) {
3852		function getList(item) {
3853			var list = [];
3854			do {
3855				list.unshift(item);
3856			} while (item = item._parent);
3857			return list;
3858		}
3859		var list1 = getList(this),
3860			list2 = getList(item);
3861		for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {
3862			if (list1[i] != list2[i]) {
3863				return list1[i]._index < list2[i]._index ? 1 : -1;
3864			}
3865		}
3866		return 0;
3867	},
3868
3869	hasChildren: function() {
3870		return this._children && this._children.length > 0;
3871	},
3872
3873	isInserted: function() {
3874		return this._parent ? this._parent.isInserted() : false;
3875	},
3876
3877	isAbove: function(item) {
3878		return this._getOrder(item) === -1;
3879	},
3880
3881	isBelow: function(item) {
3882		return this._getOrder(item) === 1;
3883	},
3884
3885	isParent: function(item) {
3886		return this._parent === item;
3887	},
3888
3889	isChild: function(item) {
3890		return item && item._parent === this;
3891	},
3892
3893	isDescendant: function(item) {
3894		var parent = this;
3895		while (parent = parent._parent) {
3896			if (parent == item)
3897				return true;
3898		}
3899		return false;
3900	},
3901
3902	isAncestor: function(item) {
3903		return item ? item.isDescendant(this) : false;
3904	},
3905
3906	isGroupedWith: function(item) {
3907		var parent = this._parent;
3908		while (parent) {
3909			if (parent._parent
3910				&& /^(Group|Layer|CompoundPath)$/.test(parent._class)
3911				&& item.isDescendant(parent))
3912					return true;
3913			parent = parent._parent;
3914		}
3915		return false;
3916	},
3917
3918	translate: function() {
3919		var mx = new Matrix();
3920		return this.transform(mx.translate.apply(mx, arguments));
3921	},
3922
3923	rotate: function(angle ) {
3924		return this.transform(new Matrix().rotate(angle,
3925				Point.read(arguments, 1, { readNull: true })
3926					|| this.getPosition(true)));
3927	}
3928}, Base.each(['scale', 'shear', 'skew'], function(name) {
3929	this[name] = function() {
3930		var point = Point.read(arguments),
3931			center = Point.read(arguments, 0, { readNull: true });
3932		return this.transform(new Matrix()[name](point,
3933				center || this.getPosition(true)));
3934	};
3935}, {
3936
3937}), {
3938	transform: function(matrix, _applyMatrix, _applyRecursively,
3939			_setApplyMatrix) {
3940		if (matrix && matrix.isIdentity())
3941			matrix = null;
3942		var _matrix = this._matrix,
3943			applyMatrix = (_applyMatrix || this._applyMatrix)
3944					&& ((!_matrix.isIdentity() || matrix)
3945						|| _applyMatrix && _applyRecursively && this._children);
3946		if (!matrix && !applyMatrix)
3947			return this;
3948		if (matrix)
3949			_matrix.preConcatenate(matrix);
3950		if (applyMatrix = applyMatrix && this._transformContent(_matrix,
3951					_applyRecursively, _setApplyMatrix)) {
3952			var pivot = this._pivot,
3953				style = this._style,
3954				fillColor = style.getFillColor(true),
3955				strokeColor = style.getStrokeColor(true);
3956			if (pivot)
3957				_matrix._transformPoint(pivot, pivot, true);
3958			if (fillColor)
3959				fillColor.transform(_matrix);
3960			if (strokeColor)
3961				strokeColor.transform(_matrix);
3962			_matrix.reset(true);
3963			if (_setApplyMatrix && this._canApplyMatrix)
3964				this._applyMatrix = true;
3965		}
3966		var bounds = this._bounds,
3967			position = this._position;
3968		this._changed(9);
3969		var decomp = bounds && matrix && matrix.decompose();
3970		if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
3971			for (var key in bounds) {
3972				var rect = bounds[key];
3973				if (applyMatrix || !rect._internal)
3974					matrix._transformBounds(rect, rect);
3975			}
3976			var getter = this._boundsGetter,
3977				rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
3978			if (rect)
3979				this._position = rect.getCenter(true);
3980			this._bounds = bounds;
3981		} else if (matrix && position) {
3982			this._position = matrix._transformPoint(position, position);
3983		}
3984		return this;
3985	},
3986
3987	_transformContent: function(matrix, applyRecursively, setApplyMatrix) {
3988		var children = this._children;
3989		if (children) {
3990			for (var i = 0, l = children.length; i < l; i++)
3991				children[i].transform(matrix, true, applyRecursively,
3992						setApplyMatrix);
3993			return true;
3994		}
3995	},
3996
3997	globalToLocal: function() {
3998		return this.getGlobalMatrix(true)._inverseTransform(
3999				Point.read(arguments));
4000	},
4001
4002	localToGlobal: function() {
4003		return this.getGlobalMatrix(true)._transformPoint(
4004				Point.read(arguments));
4005	},
4006
4007	parentToLocal: function() {
4008		return this._matrix._inverseTransform(Point.read(arguments));
4009	},
4010
4011	localToParent: function() {
4012		return this._matrix._transformPoint(Point.read(arguments));
4013	},
4014
4015	fitBounds: function(rectangle, fill) {
4016		rectangle = Rectangle.read(arguments);
4017		var bounds = this.getBounds(),
4018			itemRatio = bounds.height / bounds.width,
4019			rectRatio = rectangle.height / rectangle.width,
4020			scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
4021					? rectangle.width / bounds.width
4022					: rectangle.height / bounds.height,
4023			newBounds = new Rectangle(new Point(),
4024					new Size(bounds.width * scale, bounds.height * scale));
4025		newBounds.setCenter(rectangle.getCenter());
4026		this.setBounds(newBounds);
4027	},
4028
4029	_setStyles: function(ctx) {
4030		var style = this._style,
4031			fillColor = style.getFillColor(),
4032			strokeColor = style.getStrokeColor(),
4033			shadowColor = style.getShadowColor();
4034		if (fillColor)
4035			ctx.fillStyle = fillColor.toCanvasStyle(ctx);
4036		if (strokeColor) {
4037			var strokeWidth = style.getStrokeWidth();
4038			if (strokeWidth > 0) {
4039				ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
4040				ctx.lineWidth = strokeWidth;
4041				var strokeJoin = style.getStrokeJoin(),
4042					strokeCap = style.getStrokeCap(),
4043					miterLimit = style.getMiterLimit();
4044				if (strokeJoin)
4045					ctx.lineJoin = strokeJoin;
4046				if (strokeCap)
4047					ctx.lineCap = strokeCap;
4048				if (miterLimit)
4049					ctx.miterLimit = miterLimit;
4050				if (paper.support.nativeDash) {
4051					var dashArray = style.getDashArray(),
4052						dashOffset = style.getDashOffset();
4053					if (dashArray && dashArray.length) {
4054						if ('setLineDash' in ctx) {
4055							ctx.setLineDash(dashArray);
4056							ctx.lineDashOffset = dashOffset;
4057						} else {
4058							ctx.mozDash = dashArray;
4059							ctx.mozDashOffset = dashOffset;
4060						}
4061					}
4062				}
4063			}
4064		}
4065		if (shadowColor) {
4066			var shadowBlur = style.getShadowBlur();
4067			if (shadowBlur > 0) {
4068				ctx.shadowColor = shadowColor.toCanvasStyle(ctx);
4069				ctx.shadowBlur = shadowBlur;
4070				var offset = this.getShadowOffset();
4071				ctx.shadowOffsetX = offset.x;
4072				ctx.shadowOffsetY = offset.y;
4073			}
4074		}
4075	},
4076
4077	draw: function(ctx, param, parentStrokeMatrix) {
4078		var updateVersion = this._updateVersion = this._project._updateVersion;
4079		if (!this._visible || this._opacity === 0)
4080			return;
4081		var matrices = param.matrices,
4082			viewMatrix = param.viewMatrix,
4083			matrix = this._matrix,
4084			globalMatrix = matrices[matrices.length - 1].chain(matrix);
4085		if (!globalMatrix.isInvertible())
4086			return;
4087
4088		function getViewMatrix(matrix) {
4089			return viewMatrix ? viewMatrix.chain(matrix) : matrix;
4090		}
4091
4092		matrices.push(globalMatrix);
4093		if (param.updateMatrix) {
4094			globalMatrix._updateVersion = updateVersion;
4095			this._globalMatrix = globalMatrix;
4096		}
4097
4098		var blendMode = this._blendMode,
4099			opacity = this._opacity,
4100			normalBlend = blendMode === 'normal',
4101			nativeBlend = BlendMode.nativeModes[blendMode],
4102			direct = normalBlend && opacity === 1
4103					|| param.dontStart
4104					|| param.clip
4105					|| (nativeBlend || normalBlend && opacity < 1)
4106						&& this._canComposite(),
4107			pixelRatio = param.pixelRatio || 1,
4108			mainCtx, itemOffset, prevOffset;
4109		if (!direct) {
4110			var bounds = this.getStrokeBounds(getViewMatrix(globalMatrix));
4111			if (!bounds.width || !bounds.height)
4112				return;
4113			prevOffset = param.offset;
4114			itemOffset = param.offset = bounds.getTopLeft().floor();
4115			mainCtx = ctx;
4116			ctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1)
4117					.multiply(pixelRatio));
4118			if (pixelRatio !== 1)
4119				ctx.scale(pixelRatio, pixelRatio);
4120		}
4121		ctx.save();
4122		var strokeMatrix = parentStrokeMatrix
4123				? parentStrokeMatrix.chain(matrix)
4124				: !this.getStrokeScaling(true) && getViewMatrix(globalMatrix),
4125			clip = !direct && param.clipItem,
4126			transform = !strokeMatrix || clip;
4127		if (direct) {
4128			ctx.globalAlpha = opacity;
4129			if (nativeBlend)
4130				ctx.globalCompositeOperation = blendMode;
4131		} else if (transform) {
4132			ctx.translate(-itemOffset.x, -itemOffset.y);
4133		}
4134		if (transform)
4135			(direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
4136		if (clip)
4137			param.clipItem.draw(ctx, param.extend({ clip: true }));
4138		if (strokeMatrix) {
4139			ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
4140			var offset = param.offset;
4141			if (offset)
4142				ctx.translate(-offset.x, -offset.y);
4143		}
4144		this._draw(ctx, param, strokeMatrix);
4145		ctx.restore();
4146		matrices.pop();
4147		if (param.clip && !param.dontFinish)
4148			ctx.clip();
4149		if (!direct) {
4150			BlendMode.process(blendMode, ctx, mainCtx, opacity,
4151					itemOffset.subtract(prevOffset).multiply(pixelRatio));
4152			CanvasProvider.release(ctx);
4153			param.offset = prevOffset;
4154		}
4155	},
4156
4157	_isUpdated: function(updateVersion) {
4158		var parent = this._parent;
4159		if (parent instanceof CompoundPath)
4160			return parent._isUpdated(updateVersion);
4161		var updated = this._updateVersion === updateVersion;
4162		if (!updated && parent && parent._visible
4163				&& parent._isUpdated(updateVersion)) {
4164			this._updateVersion = updateVersion;
4165			updated = true;
4166		}
4167		return updated;
4168	},
4169
4170	_drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
4171		if ((this._drawSelected || this._boundsSelected)
4172				&& this._isUpdated(updateVersion)) {
4173			var color = this.getSelectedColor(true)
4174					|| this.getLayer().getSelectedColor(true),
4175				mx = matrix.chain(this.getGlobalMatrix(true));
4176			ctx.strokeStyle = ctx.fillStyle = color
4177					? color.toCanvasStyle(ctx) : '#009dec';
4178			if (this._drawSelected)
4179				this._drawSelected(ctx, mx, selectedItems);
4180			if (this._boundsSelected) {
4181				var half = size / 2;
4182					coords = mx._transformCorners(this.getInternalBounds());
4183				ctx.beginPath();
4184				for (var i = 0; i < 8; i++)
4185					ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
4186				ctx.closePath();
4187				ctx.stroke();
4188				for (var i = 0; i < 8; i++)
4189					ctx.fillRect(coords[i] - half, coords[++i] - half,
4190							size, size);
4191			}
4192		}
4193	},
4194
4195	_canComposite: function() {
4196		return false;
4197	}
4198}, Base.each(['down', 'drag', 'up', 'move'], function(name) {
4199	this['removeOn' + Base.capitalize(name)] = function() {
4200		var hash = {};
4201		hash[name] = true;
4202		return this.removeOn(hash);
4203	};
4204}, {
4205
4206	removeOn: function(obj) {
4207		for (var name in obj) {
4208			if (obj[name]) {
4209				var key = 'mouse' + name,
4210					project = this._project,
4211					sets = project._removeSets = project._removeSets || {};
4212				sets[key] = sets[key] || {};
4213				sets[key][this._id] = this;
4214			}
4215		}
4216		return this;
4217	}
4218}));
4219
4220var Group = Item.extend({
4221	_class: 'Group',
4222	_selectChildren: true,
4223	_serializeFields: {
4224		children: []
4225	},
4226
4227	initialize: function Group(arg) {
4228		this._children = [];
4229		this._namedChildren = {};
4230		if (!this._initialize(arg))
4231			this.addChildren(Array.isArray(arg) ? arg : arguments);
4232	},
4233
4234	_changed: function _changed(flags) {
4235		_changed.base.call(this, flags);
4236		if (flags & 1026) {
4237			this._clipItem = undefined;
4238		}
4239	},
4240
4241	_getClipItem: function() {
4242		var clipItem = this._clipItem;
4243		if (clipItem === undefined) {
4244			clipItem = null;
4245			for (var i = 0, l = this._children.length; i < l; i++) {
4246				var child = this._children[i];
4247				if (child._clipMask) {
4248					clipItem = child;
4249					break;
4250				}
4251			}
4252			this._clipItem = clipItem;
4253		}
4254		return clipItem;
4255	},
4256
4257	isClipped: function() {
4258		return !!this._getClipItem();
4259	},
4260
4261	setClipped: function(clipped) {
4262		var child = this.getFirstChild();
4263		if (child)
4264			child.setClipMask(clipped);
4265	},
4266
4267	_draw: function(ctx, param) {
4268		var clip = param.clip,
4269			clipItem = !clip && this._getClipItem(),
4270			draw = true;
4271		param = param.extend({ clipItem: clipItem, clip: false });
4272		if (clip) {
4273			if (this._currentPath) {
4274				ctx.currentPath = this._currentPath;
4275				draw = false;
4276			} else {
4277				ctx.beginPath();
4278				param.dontStart = param.dontFinish = true;
4279			}
4280		} else if (clipItem) {
4281			clipItem.draw(ctx, param.extend({ clip: true }));
4282		}
4283		if (draw) {
4284			for (var i = 0, l = this._children.length; i < l; i++) {
4285				var item = this._children[i];
4286				if (item !== clipItem)
4287					item.draw(ctx, param);
4288			}
4289		}
4290		if (clip) {
4291			this._currentPath = ctx.currentPath;
4292		}
4293	}
4294});
4295
4296var Layer = Group.extend({
4297	_class: 'Layer',
4298
4299	initialize: function Layer(arg) {
4300		var props = Base.isPlainObject(arg)
4301				? new Base(arg)
4302				: { children: Array.isArray(arg) ? arg : arguments },
4303			insert = props.insert;
4304		props.insert = false;
4305		Group.call(this, props);
4306		if (insert || insert === undefined) {
4307			this._project.addChild(this);
4308			this.activate();
4309		}
4310	},
4311
4312	_remove: function _remove(notifySelf, notifyParent) {
4313		if (this._parent)
4314			return _remove.base.call(this, notifySelf, notifyParent);
4315		if (this._index != null) {
4316			var project = this._project;
4317			if (project._activeLayer === this)
4318				project._activeLayer = this.getNextSibling()
4319						|| this.getPreviousSibling();
4320			Base.splice(project.layers, null, this._index, 1);
4321			this._installEvents(false);
4322			if (notifySelf && project._changes)
4323				this._changed(5);
4324			if (notifyParent) {
4325				project._needsUpdate = true;
4326			}
4327			return true;
4328		}
4329		return false;
4330	},
4331
4332	getNextSibling: function getNextSibling() {
4333		return this._parent ? getNextSibling.base.call(this)
4334				: this._project.layers[this._index + 1] || null;
4335	},
4336
4337	getPreviousSibling: function getPreviousSibling() {
4338		return this._parent ? getPreviousSibling.base.call(this)
4339				: this._project.layers[this._index - 1] || null;
4340	},
4341
4342	isInserted: function isInserted() {
4343		return this._parent ? isInserted.base.call(this) : this._index != null;
4344	},
4345
4346	activate: function() {
4347		this._project._activeLayer = this;
4348	},
4349
4350	_insertSibling: function _insertSibling(index, item, _preserve) {
4351		return !this._parent
4352				? this._project.insertChild(index, item, _preserve)
4353				: _insertSibling.base.call(this, index, item, _preserve);
4354	}
4355});
4356
4357var Shape = Item.extend({
4358	_class: 'Shape',
4359	_applyMatrix: false,
4360	_canApplyMatrix: false,
4361	_boundsSelected: true,
4362	_serializeFields: {
4363		type: null,
4364		size: null,
4365		radius: null
4366	},
4367
4368	initialize: function Shape(props) {
4369		this._initialize(props);
4370	},
4371
4372	_equals: function(item) {
4373		return this._type === item._type
4374			&& this._size.equals(item._size)
4375			&& Base.equals(this._radius, item._radius);
4376	},
4377
4378	clone: function(insert) {
4379		var copy = new Shape(Item.NO_INSERT);
4380		copy.setType(this._type);
4381		copy.setSize(this._size);
4382		copy.setRadius(this._radius);
4383		return this._clone(copy, insert);
4384	},
4385
4386	getType: function() {
4387		return this._type;
4388	},
4389
4390	setType: function(type) {
4391		this._type = type;
4392	},
4393
4394	getShape: '#getType',
4395	setShape: '#setType',
4396
4397	getSize: function() {
4398		var size = this._size;
4399		return new LinkedSize(size.width, size.height, this, 'setSize');
4400	},
4401
4402	setSize: function() {
4403		var size = Size.read(arguments);
4404		if (!this._size) {
4405			this._size = size.clone();
4406		} else if (!this._size.equals(size)) {
4407			var type = this._type,
4408				width = size.width,
4409				height = size.height;
4410			if (type === 'rectangle') {
4411				var radius = Size.min(this._radius, size.divide(2));
4412				this._radius.set(radius.width, radius.height);
4413			} else if (type === 'circle') {
4414				width = height = (width + height) / 2;
4415				this._radius = width / 2;
4416			} else if (type === 'ellipse') {
4417				this._radius.set(width / 2, height / 2);
4418			}
4419			this._size.set(width, height);
4420			this._changed(9);
4421		}
4422	},
4423
4424	getRadius: function() {
4425		var rad = this._radius;
4426		return this._type === 'circle'
4427				? rad
4428				: new LinkedSize(rad.width, rad.height, this, 'setRadius');
4429	},
4430
4431	setRadius: function(radius) {
4432		var type = this._type;
4433		if (type === 'circle') {
4434			if (radius === this._radius)
4435				return;
4436			var size = radius * 2;
4437			this._radius = radius;
4438			this._size.set(size, size);
4439		} else {
4440			radius = Size.read(arguments);
4441			if (!this._radius) {
4442				this._radius = radius.clone();
4443			} else {
4444				if (this._radius.equals(radius))
4445					return;
4446				this._radius.set(radius.width, radius.height);
4447				if (type === 'rectangle') {
4448					var size = Size.max(this._size, radius.multiply(2));
4449					this._size.set(size.width, size.height);
4450				} else if (type === 'ellipse') {
4451					this._size.set(radius.width * 2, radius.height * 2);
4452				}
4453			}
4454		}
4455		this._changed(9);
4456	},
4457
4458	isEmpty: function() {
4459		return false;
4460	},
4461
4462	toPath: function(insert) {
4463		var path = this._clone(new Path[Base.capitalize(this._type)]({
4464			center: new Point(),
4465			size: this._size,
4466			radius: this._radius,
4467			insert: false
4468		}), insert);
4469		if (paper.settings.applyMatrix)
4470			path.setApplyMatrix(true);
4471		return path;
4472	},
4473
4474	_draw: function(ctx, param, strokeMatrix) {
4475		var style = this._style,
4476			hasFill = style.hasFill(),
4477			hasStroke = style.hasStroke(),
4478			dontPaint = param.dontFinish || param.clip,
4479			untransformed = !strokeMatrix;
4480		if (hasFill || hasStroke || dontPaint) {
4481			var type = this._type,
4482				radius = this._radius,
4483				isCircle = type === 'circle';
4484			if (!param.dontStart)
4485				ctx.beginPath();
4486			if (untransformed && isCircle) {
4487				ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
4488			} else {
4489				var rx = isCircle ? radius : radius.width,
4490					ry = isCircle ? radius : radius.height,
4491					size = this._size,
4492					width = size.width,
4493					height = size.height;
4494				if (untransformed && type === 'rectangle' && rx === 0 && ry === 0) {
4495					ctx.rect(-width / 2, -height / 2, width, height);
4496				} else {
4497					var x = width / 2,
4498						y = height / 2,
4499						kappa = 1 - 0.5522847498307936,
4500						cx = rx * kappa,
4501						cy = ry * kappa,
4502						c = [
4503							-x, -y + ry,
4504							-x, -y + cy,
4505							-x + cx, -y,
4506							-x + rx, -y,
4507							x - rx, -y,
4508							x - cx, -y,
4509							x, -y + cy,
4510							x, -y + ry,
4511							x, y - ry,
4512							x, y - cy,
4513							x - cx, y,
4514							x - rx, y,
4515							-x + rx, y,
4516							-x + cx, y,
4517							-x, y - cy,
4518							-x, y - ry
4519						];
4520					if (strokeMatrix)
4521						strokeMatrix.transform(c, c, 32);
4522					ctx.moveTo(c[0], c[1]);
4523					ctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]);
4524					if (x !== rx)
4525						ctx.lineTo(c[8], c[9]);
4526					ctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]);
4527					if (y !== ry)
4528						ctx.lineTo(c[16], c[17]);
4529					ctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]);
4530					if (x !== rx)
4531						ctx.lineTo(c[24], c[25]);
4532					ctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]);
4533				}
4534			}
4535			ctx.closePath();
4536		}
4537		if (!dontPaint && (hasFill || hasStroke)) {
4538			this._setStyles(ctx);
4539			if (hasFill) {
4540				ctx.fill(style.getWindingRule());
4541				ctx.shadowColor = 'rgba(0,0,0,0)';
4542			}
4543			if (hasStroke)
4544				ctx.stroke();
4545		}
4546	},
4547
4548	_canComposite: function() {
4549		return !(this.hasFill() && this.hasStroke());
4550	},
4551
4552	_getBounds: function(getter, matrix) {
4553		var rect = new Rectangle(this._size).setCenter(0, 0);
4554		if (getter !== 'getBounds' && this.hasStroke())
4555			rect = rect.expand(this.getStrokeWidth());
4556		return matrix ? matrix._transformBounds(rect) : rect;
4557	}
4558},
4559new function() {
4560
4561	function getCornerCenter(that, point, expand) {
4562		var radius = that._radius;
4563		if (!radius.isZero()) {
4564			var halfSize = that._size.divide(2);
4565			for (var i = 0; i < 4; i++) {
4566				var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1),
4567					corner = dir.multiply(halfSize),
4568					center = corner.subtract(dir.multiply(radius)),
4569					rect = new Rectangle(corner, center);
4570				if ((expand ? rect.expand(expand) : rect).contains(point))
4571					return center;
4572			}
4573		}
4574	}
4575
4576	function getEllipseRadius(point, radius) {
4577		var angle = point.getAngleInRadians(),
4578			width = radius.width * 2,
4579			height = radius.height * 2,
4580			x = width * Math.sin(angle),
4581			y = height * Math.cos(angle);
4582		return width * height / (2 * Math.sqrt(x * x + y * y));
4583	}
4584
4585	return {
4586		_contains: function _contains(point) {
4587			if (this._type === 'rectangle') {
4588				var center = getCornerCenter(this, point);
4589				return center
4590						? point.subtract(center).divide(this._radius)
4591							.getLength() <= 1
4592						: _contains.base.call(this, point);
4593			} else {
4594				return point.divide(this.size).getLength() <= 0.5;
4595			}
4596		},
4597
4598		_hitTestSelf: function _hitTestSelf(point, options) {
4599			var hit = false;
4600			if (this.hasStroke()) {
4601				var type = this._type,
4602					radius = this._radius,
4603					strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
4604				if (type === 'rectangle') {
4605					var center = getCornerCenter(this, point, strokeWidth);
4606					if (center) {
4607						var pt = point.subtract(center);
4608						hit = 2 * Math.abs(pt.getLength()
4609								- getEllipseRadius(pt, radius)) <= strokeWidth;
4610					} else {
4611						var rect = new Rectangle(this._size).setCenter(0, 0),
4612							outer = rect.expand(strokeWidth),
4613							inner = rect.expand(-strokeWidth);
4614						hit = outer._containsPoint(point)
4615								&& !inner._containsPoint(point);
4616					}
4617				} else {
4618					if (type === 'ellipse')
4619						radius = getEllipseRadius(point, radius);
4620					hit = 2 * Math.abs(point.getLength() - radius)
4621							<= strokeWidth;
4622				}
4623			}
4624			return hit
4625					? new HitResult('stroke', this)
4626					: _hitTestSelf.base.apply(this, arguments);
4627		}
4628	};
4629}, {
4630
4631statics: new function() {
4632	function createShape(type, point, size, radius, args) {
4633		var item = new Shape(Base.getNamed(args));
4634		item._type = type;
4635		item._size = size;
4636		item._radius = radius;
4637		return item.translate(point);
4638	}
4639
4640	return {
4641		Circle: function() {
4642			var center = Point.readNamed(arguments, 'center'),
4643				radius = Base.readNamed(arguments, 'radius');
4644			return createShape('circle', center, new Size(radius * 2), radius,
4645					arguments);
4646		},
4647
4648		Rectangle: function() {
4649			var rect = Rectangle.readNamed(arguments, 'rectangle'),
4650				radius = Size.min(Size.readNamed(arguments, 'radius'),
4651						rect.getSize(true).divide(2));
4652			return createShape('rectangle', rect.getCenter(true),
4653					rect.getSize(true), radius, arguments);
4654		},
4655
4656		Ellipse: function() {
4657			var ellipse = Shape._readEllipse(arguments),
4658				radius = ellipse.radius;
4659			return createShape('ellipse', ellipse.center, radius.multiply(2),
4660					radius, arguments);
4661		},
4662
4663		_readEllipse: function(args) {
4664			var center,
4665				radius;
4666			if (Base.hasNamed(args, 'radius')) {
4667				center = Point.readNamed(args, 'center');
4668				radius = Size.readNamed(args, 'radius');
4669			} else {
4670				var rect = Rectangle.readNamed(args, 'rectangle');
4671				center = rect.getCenter(true);
4672				radius = rect.getSize(true).divide(2);
4673			}
4674			return { center: center, radius: radius };
4675		}
4676	};
4677}});
4678
4679var Raster = Item.extend({
4680	_class: 'Raster',
4681	_applyMatrix: false,
4682	_canApplyMatrix: false,
4683	_boundsGetter: 'getBounds',
4684	_boundsSelected: true,
4685	_serializeFields: {
4686		crossOrigin: null,
4687		source: null
4688	},
4689
4690	initialize: function Raster(object, position) {
4691		if (!this._initialize(object,
4692				position !== undefined && Point.read(arguments, 1))) {
4693			if (typeof object === 'string') {
4694				this.setSource(object);
4695			} else {
4696				this.setImage(object);
4697			}
4698		}
4699		if (!this._size) {
4700			this._size = new Size();
4701			this._loaded = false;
4702		}
4703	},
4704
4705	_equals: function(item) {
4706		return this.getSource() === item.getSource();
4707	},
4708
4709	clone: function(insert) {
4710		var copy = new Raster(Item.NO_INSERT),
4711			image = this._image,
4712			canvas = this._canvas;
4713		if (image) {
4714			copy.setImage(image);
4715		} else if (canvas) {
4716			var copyCanvas = CanvasProvider.getCanvas(this._size);
4717			copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
4718			copy.setImage(copyCanvas);
4719		}
4720		copy._crossOrigin = this._crossOrigin;
4721		return this._clone(copy, insert);
4722	},
4723
4724	getSize: function() {
4725		var size = this._size;
4726		return new LinkedSize(size ? size.width : 0, size ? size.height : 0,
4727				this, 'setSize');
4728	},
4729
4730	setSize: function() {
4731		var size = Size.read(arguments);
4732		if (!size.equals(this._size)) {
4733			if (size.width > 0 && size.height > 0) {
4734				var element = this.getElement();
4735				this.setImage(CanvasProvider.getCanvas(size));
4736				if (element)
4737					this.getContext(true).drawImage(element, 0, 0,
4738							size.width, size.height);
4739			} else {
4740				if (this._canvas)
4741					CanvasProvider.release(this._canvas);
4742				this._size = size.clone();
4743			}
4744		}
4745	},
4746
vendor: 4,874 bytes, lines 4747-4946
4747	getWidth: function() {
4748		return this._size ? this._size.width : 0;
4749	},
4750
4751	setWidth: function(width) {
4752		this.setSize(width, this.getHeight());
4753	},
4754
4755	getHeight: function() {
4756		return this._size ? this._size.height : 0;
4757	},
4758
4759	setHeight: function(height) {
4760		this.setSize(this.getWidth(), height);
4761	},
4762
4763	isEmpty: function() {
4764		var size = this._size;
4765		return !size || size.width === 0 && size.height === 0;
4766	},
4767
4768	getResolution: function() {
4769		var matrix = this._matrix,
4770			orig = new Point(0, 0).transform(matrix),
4771			u = new Point(1, 0).transform(matrix).subtract(orig),
4772			v = new Point(0, 1).transform(matrix).subtract(orig);
4773		return new Size(
4774			72 / u.getLength(),
4775			72 / v.getLength()
4776		);
4777	},
4778
4779	getPpi: '#getResolution',
4780
4781	getImage: function() {
4782		return this._image;
4783	},
4784
4785	setImage: function(image) {
4786		if (this._canvas)
4787			CanvasProvider.release(this._canvas);
4788		if (image && image.getContext) {
4789			this._image = null;
4790			this._canvas = image;
4791			this._loaded = true;
4792		} else {
4793			this._image = image;
4794			this._canvas = null;
4795			this._loaded = image && image.complete;
4796		}
4797		this._size = new Size(
4798				image ? image.naturalWidth || image.width : 0,
4799				image ? image.naturalHeight || image.height : 0);
4800		this._context = null;
4801		this._changed(521);
4802	},
4803
4804	getCanvas: function() {
4805		if (!this._canvas) {
4806			var ctx = CanvasProvider.getContext(this._size);
4807			try {
4808				if (this._image)
4809					ctx.drawImage(this._image, 0, 0);
4810				this._canvas = ctx.canvas;
4811			} catch (e) {
4812				CanvasProvider.release(ctx);
4813			}
4814		}
4815		return this._canvas;
4816	},
4817
4818	setCanvas: '#setImage',
4819
4820	getContext: function(modify) {
4821		if (!this._context)
4822			this._context = this.getCanvas().getContext('2d');
4823		if (modify) {
4824			this._image = null;
4825			this._changed(513);
4826		}
4827		return this._context;
4828	},
4829
4830	setContext: function(context) {
4831		this._context = context;
4832	},
4833
4834	getSource: function() {
4835		return this._image && this._image.src || this.toDataURL();
4836	},
4837
4838	setSource: function(src) {
4839		var that = this,
4840			crossOrigin = this._crossOrigin,
4841			image;
4842
4843		function loaded() {
4844			var view = that.getView();
4845			if (view) {
4846				paper = view._scope;
4847				that.setImage(image);
4848				that.emit('load');
4849				view.update();
4850			}
4851		}
4852
4853		image = document.getElementById(src) || new Image();
4854		if (crossOrigin)
4855			image.crossOrigin = crossOrigin;
4856		if (image.naturalWidth && image.naturalHeight) {
4857			setTimeout(loaded, 0);
4858		} else {
4859			DomEvent.add(image, { load: loaded });
4860			if (!image.src)
4861				image.src = src;
4862		}
4863		this.setImage(image);
4864	},
4865
4866	getCrossOrigin: function() {
4867		return this._image && this._image.crossOrigin || this._crossOrigin || '';
4868	},
4869
4870	setCrossOrigin: function(crossOrigin) {
4871		this._crossOrigin = crossOrigin;
4872		if (this._image)
4873			this._image.crossOrigin = crossOrigin;
4874	},
4875
4876	getElement: function() {
4877		return this._canvas || this._loaded && this._image;
4878	}
4879}, {
4880	beans: false,
4881
4882	getSubCanvas: function() {
4883		var rect = Rectangle.read(arguments),
4884			ctx = CanvasProvider.getContext(rect.getSize());
4885		ctx.drawImage(this.getCanvas(), rect.x, rect.y,
4886				rect.width, rect.height, 0, 0, rect.width, rect.height);
4887		return ctx.canvas;
4888	},
4889
4890	getSubRaster: function() {
4891		var rect = Rectangle.read(arguments),
4892			raster = new Raster(Item.NO_INSERT);
4893		raster.setImage(this.getSubCanvas(rect));
4894		raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
4895		raster._matrix.preConcatenate(this._matrix);
4896		raster.insertAbove(this);
4897		return raster;
4898	},
4899
4900	toDataURL: function() {
4901		var src = this._image && this._image.src;
4902		if (/^data:/.test(src))
4903			return src;
4904		var canvas = this.getCanvas();
4905		return canvas ? canvas.toDataURL() : null;
4906	},
4907
4908	drawImage: function(image ) {
4909		var point = Point.read(arguments, 1);
4910		this.getContext(true).drawImage(image, point.x, point.y);
4911	},
4912
4913	getAverageColor: function(object) {
4914		var bounds, path;
4915		if (!object) {
4916			bounds = this.getBounds();
4917		} else if (object instanceof PathItem) {
4918			path = object;
4919			bounds = object.getBounds();
4920		} else if (object.width) {
4921			bounds = new Rectangle(object);
4922		} else if (object.x) {
4923			bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
4924		}
4925		var sampleSize = 32,
4926			width = Math.min(bounds.width, sampleSize),
4927			height = Math.min(bounds.height, sampleSize);
4928		var ctx = Raster._sampleContext;
4929		if (!ctx) {
4930			ctx = Raster._sampleContext = CanvasProvider.getContext(
4931					new Size(sampleSize));
4932		} else {
4933			ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);
4934		}
4935		ctx.save();
4936		var matrix = new Matrix()
4937				.scale(width / bounds.width, height / bounds.height)
4938				.translate(-bounds.x, -bounds.y);
4939		matrix.applyToContext(ctx);
4940		if (path)
4941			path.draw(ctx, new Base({ clip: true, matrices: [matrix] }));
4942		this._matrix.applyToContext(ctx);
4943		var element = this.getElement(),
4944			size = this._size;
4945		if (element)
4946			ctx.drawImage(element, -size.width / 2, -size.height / 2);
4947		ctx.restore();
4948		var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
4949				Math.ceil(height)).data,
4950			channels = [0, 0, 0],
4951			total = 0;
4952		for (var i = 0, l = pixels.length; i < l; i += 4) {
4953			var alpha = pixels[i + 3];
4954			total += alpha;
4955			alpha /= 255;
4956			channels[0] += pixels[i] * alpha;
4957			channels[1] += pixels[i + 1] * alpha;
4958			channels[2] += pixels[i + 2] * alpha;
4959		}
4960		for (var i = 0; i < 3; i++)
4961			channels[i] /= total;
4962		return total ? Color.read(channels) : null;
4963	},
4964
4965	getPixel: function() {
4966		var point = Point.read(arguments);
4967		var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
4968		return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
4969				data[3] / 255);
4970	},
4971
4972	setPixel: function() {
4973		var point = Point.read(arguments),
4974			color = Color.read(arguments),
4975			components = color._convert('rgb'),
4976			alpha = color._alpha,
4977			ctx = this.getContext(true),
4978			imageData = ctx.createImageData(1, 1),
4979			data = imageData.data;
4980		data[0] = components[0] * 255;
4981		data[1] = components[1] * 255;
4982		data[2] = components[2] * 255;
4983		data[3] = alpha != null ? alpha * 255 : 255;
4984		ctx.putImageData(imageData, point.x, point.y);
4985	},
4986
4987	createImageData: function() {
4988		var size = Size.read(arguments);
4989		return this.getContext().createImageData(size.width, size.height);
4990	},
4991
4992	getImageData: function() {
4993		var rect = Rectangle.read(arguments);
4994		if (rect.isEmpty())
4995			rect = new Rectangle(this._size);
4996		return this.getContext().getImageData(rect.x, rect.y,
4997				rect.width, rect.height);
4998	},
4999
5000	setImageData: function(data ) {
5001		var point = Point.read(arguments, 1);
5002		this.getContext(true).putImageData(data, point.x, point.y);
5003	},
5004
5005	_getBounds: function(getter, matrix) {
5006		var rect = new Rectangle(this._size).setCenter(0, 0);
5007		return matrix ? matrix._transformBounds(rect) : rect;
5008	},
5009
5010	_hitTestSelf: function(point) {
5011		if (this._contains(point)) {
5012			var that = this;
5013			return new HitResult('pixel', that, {
5014				offset: point.add(that._size.divide(2)).round(),
5015				color: {
5016					get: function() {
5017						return that.getPixel(this.offset);
5018					}
5019				}
5020			});
5021		}
5022	},
5023
5024	_draw: function(ctx) {
5025		var element = this.getElement();
5026		if (element) {
5027			ctx.globalAlpha = this._opacity;
5028			ctx.drawImage(element,
5029					-this._size.width / 2, -this._size.height / 2);
5030		}
5031	},
5032
5033	_canComposite: function() {
5034		return true;
5035	}
5036});
5037
5038var PlacedSymbol = Item.extend({
5039	_class: 'PlacedSymbol',
5040	_applyMatrix: false,
5041	_canApplyMatrix: false,
5042	_boundsGetter: { getBounds: 'getStrokeBounds' },
5043	_boundsSelected: true,
5044	_serializeFields: {
5045		symbol: null
5046	},
5047
5048	initialize: function PlacedSymbol(arg0, arg1) {
5049		if (!this._initialize(arg0,
5050				arg1 !== undefined && Point.read(arguments, 1)))
5051			this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
5052	},
5053
5054	_equals: function(item) {
5055		return this._symbol === item._symbol;
5056	},
5057
5058	getSymbol: function() {
5059		return this._symbol;
5060	},
5061
5062	setSymbol: function(symbol) {
5063		this._symbol = symbol;
5064		this._changed(9);
5065	},
5066
5067	clone: function(insert) {
5068		var copy = new PlacedSymbol(Item.NO_INSERT);
5069		copy.setSymbol(this._symbol);
5070		return this._clone(copy, insert);
5071	},
5072
5073	isEmpty: function() {
5074		return this._symbol._definition.isEmpty();
5075	},
5076
5077	_getBounds: function(getter, matrix, cacheItem) {
5078		var definition = this.symbol._definition;
5079		return definition._getCachedBounds(getter,
5080				matrix && matrix.chain(definition._matrix), cacheItem);
5081	},
5082
5083	_hitTestSelf: function(point, options) {
5084		var res = this._symbol._definition._hitTest(point, options);
5085		if (res)
5086			res.item = this;
5087		return res;
5088	},
5089
5090	_draw: function(ctx, param) {
5091		this.symbol._definition.draw(ctx, param);
5092	}
5093
5094});
5095
5096var HitResult = Base.extend({
5097	_class: 'HitResult',
5098
5099	initialize: function HitResult(type, item, values) {
5100		this.type = type;
5101		this.item = item;
5102		if (values) {
5103			values.enumerable = true;
5104			this.inject(values);
5105		}
5106	},
5107
5108	statics: {
5109		getOptions: function(options) {
5110			return new Base({
5111				type: null,
5112				tolerance: paper.settings.hitTolerance,
5113				fill: !options,
5114				stroke: !options,
5115				segments: !options,
5116				handles: false,
5117				ends: false,
5118				center: false,
5119				bounds: false,
5120				guides: false,
5121				selected: false
5122			}, options);
5123		}
5124	}
5125});
5126
5127var Segment = Base.extend({
5128	_class: 'Segment',
5129	beans: true,
5130
5131	initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
5132		var count = arguments.length,
5133			point, handleIn, handleOut;
5134		if (count === 0) {
5135		} else if (count === 1) {
5136			if (arg0.point) {
5137				point = arg0.point;
5138				handleIn = arg0.handleIn;
5139				handleOut = arg0.handleOut;
5140			} else {
5141				point = arg0;
5142			}
5143		} else if (count === 2 && typeof arg0 === 'number') {
5144			point = arguments;
5145		} else if (count <= 3) {
5146			point = arg0;
5147			handleIn = arg1;
5148			handleOut = arg2;
5149		} else {
5150			point = arg0 !== undefined ? [ arg0, arg1 ] : null;
5151			handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
5152			handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
5153		}
5154		new SegmentPoint(point, this, '_point');
5155		new SegmentPoint(handleIn, this, '_handleIn');
5156		new SegmentPoint(handleOut, this, '_handleOut');
5157	},
5158
5159	_serialize: function(options) {
5160		return Base.serialize(this.isStraight() ? this._point
5161				: [this._point, this._handleIn, this._handleOut],
5162				options, true);
5163	},
5164
5165	_changed: function(point) {
5166		var path = this._path;
5167		if (!path)
5168			return;
5169		var curves = path._curves,
5170			index = this._index,
5171			curve;
5172		if (curves) {
5173			if ((!point || point === this._point || point === this._handleIn)
5174					&& (curve = index > 0 ? curves[index - 1] : path._closed
5175						? curves[curves.length - 1] : null))
5176				curve._changed();
5177			if ((!point || point === this._point || point === this._handleOut)
5178					&& (curve = curves[index]))
5179				curve._changed();
5180		}
5181		path._changed(25);
5182	},
5183
5184	getPoint: function() {
5185		return this._point;
5186	},
5187
5188	setPoint: function() {
5189		var point = Point.read(arguments);
5190		this._point.set(point.x, point.y);
5191	},
5192
5193	getHandleIn: function() {
5194		return this._handleIn;
5195	},
5196
5197	setHandleIn: function() {
5198		var point = Point.read(arguments);
5199		this._handleIn.set(point.x, point.y);
5200	},
5201
5202	getHandleOut: function() {
5203		return this._handleOut;
5204	},
5205
5206	setHandleOut: function() {
5207		var point = Point.read(arguments);
5208		this._handleOut.set(point.x, point.y);
5209	},
5210
5211	hasHandles: function() {
5212		return !this.isStraight();
5213	},
5214
5215	isStraight: function() {
5216		return this._handleIn.isZero() && this._handleOut.isZero();
5217	},
5218
5219	isLinear: function() {
5220		return Segment.isLinear(this, this.getNext());
5221	},
5222
5223	isCollinear: function(segment) {
5224		return Segment.isCollinear(this, this.getNext(),
5225				segment, segment.getNext());
5226	},
5227
5228	isColinear: '#isCollinear',
5229
5230	isOrthogonal: function() {
5231		return Segment.isOrthogonal(this.getPrevious(), this, this.getNext());
5232	},
5233
5234	isOrthogonalArc: function() {
5235		return Segment.isOrthogonalArc(this, this.getNext());
5236	},
5237
5238	isArc: '#isOrthogonalArc',
5239
5240	_selectionState: 0,
5241
5242	isSelected: function(_point) {
5243		var state = this._selectionState;
5244		return !_point ? !!(state & 7)
5245			: _point === this._point ? !!(state & 4)
5246			: _point === this._handleIn ? !!(state & 1)
5247			: _point === this._handleOut ? !!(state & 2)
5248			: false;
5249	},
5250
5251	setSelected: function(selected, _point) {
5252		var path = this._path,
5253			selected = !!selected,
5254			state = this._selectionState,
5255			oldState = state,
5256			flag = !_point ? 7
5257					: _point === this._point ? 4
5258					: _point === this._handleIn ? 1
5259					: _point === this._handleOut ? 2
5260					: 0;
5261		if (selected) {
5262			state |= flag;
5263		} else {
5264			state &= ~flag;
5265		}
5266		this._selectionState = state;
5267		if (path && state !== oldState) {
5268			path._updateSelection(this, oldState, state);
5269			path._changed(129);
5270		}
5271	},
5272
5273	getIndex: function() {
5274		return this._index !== undefined ? this._index : null;
5275	},
5276
5277	getPath: function() {
5278		return this._path || null;
5279	},
5280
5281	getCurve: function() {
5282		var path = this._path,
5283			index = this._index;
5284		if (path) {
5285			if (index > 0 && !path._closed
5286					&& index === path._segments.length - 1)
5287				index--;
5288			return path.getCurves()[index] || null;
5289		}
5290		return null;
5291	},
5292
5293	getLocation: function() {
5294		var curve = this.getCurve();
5295		return curve
5296				? new CurveLocation(curve, this === curve._segment1 ? 0 : 1)
5297				: null;
5298	},
5299
5300	getNext: function() {
5301		var segments = this._path && this._path._segments;
5302		return segments && (segments[this._index + 1]
5303				|| this._path._closed && segments[0]) || null;
5304	},
5305
5306	getPrevious: function() {
5307		var segments = this._path && this._path._segments;
5308		return segments && (segments[this._index - 1]
5309				|| this._path._closed && segments[segments.length - 1]) || null;
5310	},
5311
5312	reverse: function() {
5313		return new Segment(this._point, this._handleOut, this._handleIn);
5314	},
5315
5316	remove: function() {
5317		return this._path ? !!this._path.removeSegment(this._index) : false;
5318	},
5319
5320	clone: function() {
5321		return new Segment(this._point, this._handleIn, this._handleOut);
5322	},
5323
5324	equals: function(segment) {
5325		return segment === this || segment && this._class === segment._class
5326				&& this._point.equals(segment._point)
5327				&& this._handleIn.equals(segment._handleIn)
5328				&& this._handleOut.equals(segment._handleOut)
5329				|| false;
5330	},
5331
5332	toString: function() {
5333		var parts = [ 'point: ' + this._point ];
5334		if (!this._handleIn.isZero())
5335			parts.push('handleIn: ' + this._handleIn);
5336		if (!this._handleOut.isZero())
5337			parts.push('handleOut: ' + this._handleOut);
5338		return '{ ' + parts.join(', ') + ' }';
5339	},
5340
5341	transform: function(matrix) {
5342		this._transformCoordinates(matrix, new Array(6), true);
5343		this._changed();
5344	},
5345
5346	_transformCoordinates: function(matrix, coords, change) {
5347		var point = this._point,
5348			handleIn = !change || !this._handleIn.isZero()
5349					? this._handleIn : null,
5350			handleOut = !change || !this._handleOut.isZero()
5351					? this._handleOut : null,
5352			x = point._x,
5353			y = point._y,
5354			i = 2;
5355		coords[0] = x;
5356		coords[1] = y;
5357		if (handleIn) {
5358			coords[i++] = handleIn._x + x;
5359			coords[i++] = handleIn._y + y;
5360		}
5361		if (handleOut) {
5362			coords[i++] = handleOut._x + x;
5363			coords[i++] = handleOut._y + y;
5364		}
5365		if (matrix) {
5366			matrix._transformCoordinates(coords, coords, i / 2);
5367			x = coords[0];
5368			y = coords[1];
5369			if (change) {
5370				point._x = x;
5371				point._y = y;
5372				i  = 2;
5373				if (handleIn) {
5374					handleIn._x = coords[i++] - x;
5375					handleIn._y = coords[i++] - y;
5376				}
5377				if (handleOut) {
5378					handleOut._x = coords[i++] - x;
5379					handleOut._y = coords[i++] - y;
5380				}
5381			} else {
5382				if (!handleIn) {
5383					coords[i++] = x;
5384					coords[i++] = y;
5385				}
5386				if (!handleOut) {
5387					coords[i++] = x;
5388					coords[i++] = y;
5389				}
5390			}
5391		}
5392		return coords;
5393	},
5394
5395   statics: {
5396
5397		isLinear: function(seg1, seg2) {
5398			var l = seg2._point.subtract(seg1._point);
5399			return l.isCollinear(seg1._handleOut)
5400					&& l.isCollinear(seg2._handleIn);
5401		},
5402
5403		isCollinear: function(seg1, seg2, seg3, seg4) {
5404			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
5405					&& seg3._handleOut.isZero() && seg4._handleIn.isZero()
5406					&& seg2._point.subtract(seg1._point).isCollinear(
5407						seg4._point.subtract(seg3._point));
5408		},
5409
5410		isOrthogonal: function(seg1, seg2, seg3) {
5411			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
5412				&& seg2._handleOut.isZero() && seg3._handleIn.isZero()
5413				&& seg2._point.subtract(seg1._point).isOrthogonal(
5414						seg3._point.subtract(seg2._point));
5415		},
5416
5417		isOrthogonalArc: function(seg1, seg2) {
5418			var handle1 = seg1._handleOut,
5419				handle2 = seg2._handleIn,
5420				kappa = 0.5522847498307936;
5421			if (handle1.isOrthogonal(handle2)) {
5422				var pt1 = seg1._point,
5423					pt2 = seg2._point,
5424					corner = new Line(pt1, handle1, true).intersect(
5425							new Line(pt2, handle2, true), true);
5426				return corner && Numerical.isZero(handle1.getLength() /
5427						corner.subtract(pt1).getLength() - kappa)
5428					&& Numerical.isZero(handle2.getLength() /
5429						corner.subtract(pt2).getLength() - kappa);
5430			}
5431			return false;
5432		},
5433	}
5434});
5435
5436var SegmentPoint = Point.extend({
5437	initialize: function SegmentPoint(point, owner, key) {
5438		var x, y, selected;
5439		if (!point) {
5440			x = y = 0;
5441		} else if ((x = point[0]) !== undefined) {
5442			y = point[1];
5443		} else {
5444			var pt = point;
5445			if ((x = pt.x) === undefined) {
5446				pt = Point.read(arguments);
5447				x = pt.x;
5448			}
5449			y = pt.y;
5450			selected = pt.selected;
5451		}
5452		this._x = x;
5453		this._y = y;
5454		this._owner = owner;
5455		owner[key] = this;
5456		if (selected)
5457			this.setSelected(true);
5458	},
5459
5460	set: function(x, y) {
5461		this._x = x;
5462		this._y = y;
5463		this._owner._changed(this);
5464		return this;
5465	},
5466
5467	_serialize: function(options) {
5468		var f = options.formatter,
5469			x = f.number(this._x),
5470			y = f.number(this._y);
5471		return this.isSelected()
5472				? { x: x, y: y, selected: true }
5473				: [x, y];
5474	},
5475
5476	getX: function() {
5477		return this._x;
5478	},
5479
5480	setX: function(x) {
5481		this._x = x;
5482		this._owner._changed(this);
5483	},
5484
5485	getY: function() {
5486		return this._y;
5487	},
5488
5489	setY: function(y) {
5490		this._y = y;
5491		this._owner._changed(this);
5492	},
5493
5494	isZero: function() {
5495		return Numerical.isZero(this._x) && Numerical.isZero(this._y);
5496	},
5497
5498	setSelected: function(selected) {
5499		this._owner.setSelected(selected, this);
5500	},
5501
5502	isSelected: function() {
5503		return this._owner.isSelected(this);
5504	}
5505});
5506
5507var Curve = Base.extend({
5508	_class: 'Curve',
5509
5510	initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
5511		var count = arguments.length;
5512		if (count === 3) {
5513			this._path = arg0;
5514			this._segment1 = arg1;
5515			this._segment2 = arg2;
5516		} else if (count === 0) {
5517			this._segment1 = new Segment();
5518			this._segment2 = new Segment();
5519		} else if (count === 1) {
5520			this._segment1 = new Segment(arg0.segment1);
5521			this._segment2 = new Segment(arg0.segment2);
5522		} else if (count === 2) {
5523			this._segment1 = new Segment(arg0);
5524			this._segment2 = new Segment(arg1);
5525		} else {
5526			var point1, handle1, handle2, point2;
5527			if (count === 4) {
5528				point1 = arg0;
5529				handle1 = arg1;
5530				handle2 = arg2;
5531				point2 = arg3;
5532			} else if (count === 8) {
5533				point1 = [arg0, arg1];
5534				point2 = [arg6, arg7];
5535				handle1 = [arg2 - arg0, arg3 - arg1];
5536				handle2 = [arg4 - arg6, arg5 - arg7];
5537			}
5538			this._segment1 = new Segment(point1, null, handle1);
5539			this._segment2 = new Segment(point2, handle2, null);
5540		}
5541	},
5542
5543	_changed: function() {
5544		this._length = this._bounds = undefined;
5545	},
5546
5547	getPoint1: function() {
5548		return this._segment1._point;
5549	},
5550
5551	setPoint1: function() {
5552		var point = Point.read(arguments);
5553		this._segment1._point.set(point.x, point.y);
5554	},
5555
5556	getPoint2: function() {
5557		return this._segment2._point;
5558	},
5559
5560	setPoint2: function() {
5561		var point = Point.read(arguments);
5562		this._segment2._point.set(point.x, point.y);
5563	},
5564
5565	getHandle1: function() {
5566		return this._segment1._handleOut;
5567	},
5568
5569	setHandle1: function() {
5570		var point = Point.read(arguments);
5571		this._segment1._handleOut.set(point.x, point.y);
5572	},
5573
5574	getHandle2: function() {
5575		return this._segment2._handleIn;
5576	},
5577
5578	setHandle2: function() {
5579		var point = Point.read(arguments);
5580		this._segment2._handleIn.set(point.x, point.y);
5581	},
5582
5583	getSegment1: function() {
5584		return this._segment1;
5585	},
5586
5587	getSegment2: function() {
5588		return this._segment2;
5589	},
5590
5591	getPath: function() {
5592		return this._path;
5593	},
5594
5595	getIndex: function() {
5596		return this._segment1._index;
5597	},
5598
5599	getNext: function() {
5600		var curves = this._path && this._path._curves;
5601		return curves && (curves[this._segment1._index + 1]
5602				|| this._path._closed && curves[0]) || null;
5603	},
5604
5605	getPrevious: function() {
5606		var curves = this._path && this._path._curves;
5607		return curves && (curves[this._segment1._index - 1]
5608				|| this._path._closed && curves[curves.length - 1]) || null;
5609	},
5610
5611	isSelected: function() {
5612		return this.getPoint1().isSelected()
5613				&& this.getHandle2().isSelected()
5614				&& this.getHandle2().isSelected()
5615				&& this.getPoint2().isSelected();
5616	},
5617
5618	setSelected: function(selected) {
5619		this.getPoint1().setSelected(selected);
5620		this.getHandle1().setSelected(selected);
5621		this.getHandle2().setSelected(selected);
5622		this.getPoint2().setSelected(selected);
5623	},
5624
5625	getValues: function(matrix) {
5626		return Curve.getValues(this._segment1, this._segment2, matrix);
5627	},
5628
5629	getPoints: function() {
5630		var coords = this.getValues(),
5631			points = [];
5632		for (var i = 0; i < 8; i += 2)
5633			points.push(new Point(coords[i], coords[i + 1]));
5634		return points;
5635	},
5636
5637	getLength: function() {
5638		if (this._length == null) {
5639			this._length = this.isLinear()
5640				? this._segment2._point.getDistance(this._segment1._point)
5641				: Curve.getLength(this.getValues(), 0, 1);
5642		}
5643		return this._length;
5644	},
5645
5646	getArea: function() {
5647		return Curve.getArea(this.getValues());
5648	},
5649
5650	getPart: function(from, to) {
5651		return new Curve(Curve.getPart(this.getValues(), from, to));
5652	},
5653
5654	getPartLength: function(from, to) {
5655		return Curve.getLength(this.getValues(), from, to);
5656	},
5657
5658	hasHandles: function() {
5659		return !this._segment1._handleOut.isZero()
5660				|| !this._segment2._handleIn.isZero();
5661	},
5662
5663	isLinear: function() {
5664		return Segment.isLinear(this._segment1, this._segment2);
5665	},
5666
5667	isCollinear: function(curve) {
5668		return Ssegment.isCollinear(this._segment1, this._segment2,
5669				curve._segment1, curve._segment2);
5670	},
5671
5672	isOrthogonalArc: function() {
5673		return Segment.isOrthogonalArc(this._segment1, this._segment2);
5674	},
5675
5676	getIntersections: function(curve) {
5677		return Curve.filterIntersections(Curve.getIntersections(
5678				this.getValues(), curve.getValues(), this, curve, []));
5679	},
5680
5681	_getParameter: function(offset, isParameter) {
5682		return isParameter
5683				? offset
5684				: offset && offset.curve === this
5685					? offset.parameter
5686					: offset === undefined && isParameter === undefined
5687						? 0.5
5688						: this.getParameterAt(offset, 0);
5689	},
5690
5691	divide: function(offset, isParameter, ignoreLinear) {
5692		var parameter = this._getParameter(offset, isParameter),
5693			tolerance = 0.000001,
5694			res = null;
5695		if (parameter > tolerance && parameter < 1 - tolerance) {
5696			var parts = Curve.subdivide(this.getValues(), parameter),
5697				isLinear = ignoreLinear ? false : this.isLinear(),
5698				left = parts[0],
5699				right = parts[1];
5700
5701			if (!isLinear) {
5702				this._segment1._handleOut.set(left[2] - left[0],
5703						left[3] - left[1]);
5704				this._segment2._handleIn.set(right[4] - right[6],
5705						right[5] - right[7]);
5706			}
5707
5708			var x = left[6], y = left[7],
5709				segment = new Segment(new Point(x, y),
5710						!isLinear && new Point(left[4] - x, left[5] - y),
5711						!isLinear && new Point(right[2] - x, right[3] - y));
5712
5713			if (this._path) {
5714				if (this._segment1._index > 0 && this._segment2._index === 0) {
5715					this._path.add(segment);
5716				} else {
5717					this._path.insert(this._segment2._index, segment);
5718				}
5719				res = this;
5720			} else {
5721				var end = this._segment2;
5722				this._segment2 = segment;
5723				res = new Curve(segment, end);
5724			}
5725		}
5726		return res;
5727	},
5728
5729	split: function(offset, isParameter) {
5730		return this._path
5731			? this._path.split(this._segment1._index,
5732					this._getParameter(offset, isParameter))
5733			: null;
5734	},
5735
5736	reverse: function() {
5737		return new Curve(this._segment2.reverse(), this._segment1.reverse());
5738	},
5739
5740	remove: function() {
5741		var removed = false;
5742		if (this._path) {
5743			var segment2 = this._segment2,
5744				handleOut = segment2._handleOut;
5745			removed = segment2.remove();
5746			if (removed)
5747				this._segment1._handleOut.set(handleOut.x, handleOut.y);
5748		}
5749		return removed;
5750	},
5751
5752	clone: function() {
5753		return new Curve(this._segment1, this._segment2);
5754	},
5755
5756	toString: function() {
5757		var parts = [ 'point1: ' + this._segment1._point ];
5758		if (!this._segment1._handleOut.isZero())
5759			parts.push('handle1: ' + this._segment1._handleOut);
5760		if (!this._segment2._handleIn.isZero())
5761			parts.push('handle2: ' + this._segment2._handleIn);
5762		parts.push('point2: ' + this._segment2._point);
5763		return '{ ' + parts.join(', ') + ' }';
5764	},
5765
5766statics: {
5767	getValues: function(segment1, segment2, matrix) {
5768		var p1 = segment1._point,
5769			h1 = segment1._handleOut,
5770			h2 = segment2._handleIn,
5771			p2 = segment2._point,
5772			values = [
5773				p1._x, p1._y,
5774				p1._x + h1._x, p1._y + h1._y,
5775				p2._x + h2._x, p2._y + h2._y,
5776				p2._x, p2._y
5777			];
5778		if (matrix)
5779			matrix._transformCoordinates(values, values, 4);
5780		return values;
5781	},
5782
5783	subdivide: function(v, t) {
5784		var p1x = v[0], p1y = v[1],
5785			c1x = v[2], c1y = v[3],
5786			c2x = v[4], c2y = v[5],
5787			p2x = v[6], p2y = v[7];
5788		if (t === undefined)
5789			t = 0.5;
5790		var u = 1 - t,
5791			p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y,
5792			p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y,
5793			p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y,
5794			p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y,
5795			p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
5796			p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
5797		return [
5798			[p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
5799			[p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
5800		];
5801	},
5802
5803	solveCubic: function (v, coord, val, roots, min, max) {
5804		var p1 = v[coord],
5805			c1 = v[coord + 2],
5806			c2 = v[coord + 4],
5807			p2 = v[coord + 6],
5808			c = 3 * (c1 - p1),
5809			b = 3 * (c2 - c1) - c,
5810			a = p2 - p1 - c - b;
5811		return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
5812	},
5813
5814	getParameterOf: function(v, x, y) {
5815		var tolerance = 0.000001;
5816		if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance)
5817			return 0;
5818		if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance)
5819			return 1;
5820		var txs = [],
5821			tys = [],
5822			sx = Curve.solveCubic(v, 0, x, txs, 0, 1),
5823			sy = Curve.solveCubic(v, 1, y, tys, 0, 1),
5824			tx, ty;
5825		for (var cx = 0;  sx === -1 || cx < sx;) {
5826			if (sx === -1 || (tx = txs[cx++]) > 0 && tx < 1) {
5827				for (var cy = 0; sy === -1 || cy < sy;) {
5828					if (sy === -1 || (ty = tys[cy++]) > 0 && ty < 1) {
5829						if (sx === -1) {
5830							tx = ty;
5831						} else if (sy === -1) {
5832							ty = tx;
5833						}
5834						if (Math.abs(tx - ty) < tolerance)
5835							return (tx + ty) * 0.5;
5836					}
5837				}
5838				if (sx === -1)
5839					break;
5840			}
5841		}
5842		return null;
5843	},
5844
5845	getPart: function(v, from, to) {
5846		if (from > 0)
5847			v = Curve.subdivide(v, from)[1];
5848		if (to < 1)
5849			v = Curve.subdivide(v, (to - from) / (1 - from))[0];
5850		return v;
5851	},
5852
5853	hasHandles: function(v) {
5854		var isZero = Numerical.isZero;
5855		return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3])
5856				&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]));
5857	},
5858
5859	isLinear: function(v) {
5860		var p1x = v[0], p1y = v[1],
5861			p2x = v[6], p2y = v[7],
5862			l = new Point(p2x - p1x, p2y - p1y);
5863		return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y))
5864				&& l.isCollinear(new Point(v[4] - p2x, v[5] - p2y));
5865	},
5866
5867	isFlatEnough: function(v, tolerance) {
5868		var p1x = v[0], p1y = v[1],
5869			c1x = v[2], c1y = v[3],
5870			c2x = v[4], c2y = v[5],
5871			p2x = v[6], p2y = v[7],
5872			ux = 3 * c1x - 2 * p1x - p2x,
5873			uy = 3 * c1y - 2 * p1y - p2y,
5874			vx = 3 * c2x - 2 * p2x - p1x,
5875			vy = 3 * c2y - 2 * p2y - p1y;
5876		return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
5877				< 10 * tolerance * tolerance;
5878	},
5879
5880	getArea: function(v) {
5881		var p1x = v[0], p1y = v[1],
5882			c1x = v[2], c1y = v[3],
5883			c2x = v[4], c2y = v[5],
5884			p2x = v[6], p2y = v[7];
5885		return (  3.0 * c1y * p1x - 1.5 * c1y * c2x
5886				- 1.5 * c1y * p2x - 3.0 * p1y * c1x
5887				- 1.5 * p1y * c2x - 0.5 * p1y * p2x
5888				+ 1.5 * c2y * p1x + 1.5 * c2y * c1x
5889				- 3.0 * c2y * p2x + 0.5 * p2y * p1x
5890				+ 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10;
5891	},
5892
5893	getEdgeSum: function(v) {
5894		return	  (v[0] - v[2]) * (v[3] + v[1])
5895				+ (v[2] - v[4]) * (v[5] + v[3])
5896				+ (v[4] - v[6]) * (v[7] + v[5]);
5897	},
5898
5899	getBounds: function(v) {
5900		var min = v.slice(0, 2),
5901			max = min.slice(),
5902			roots = [0, 0];
5903		for (var i = 0; i < 2; i++)
5904			Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],
5905					i, 0, min, max, roots);
5906		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
5907	},
5908
5909	_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {
5910		function add(value, padding) {
5911			var left = value - padding,
5912				right = value + padding;
5913			if (left < min[coord])
5914				min[coord] = left;
5915			if (right > max[coord])
5916				max[coord] = right;
5917		}
5918		var a = 3 * (v1 - v2) - v0 + v3,
5919			b = 2 * (v0 + v2) - 4 * v1,
5920			c = v1 - v0,
5921			count = Numerical.solveQuadratic(a, b, c, roots),
5922			tMin = 0.000001,
5923			tMax = 1 - tMin;
5924		add(v3, 0);
5925		for (var i = 0; i < count; i++) {
5926			var t = roots[i],
5927				u = 1 - t;
5928			if (tMin < t && t < tMax)
5929				add(u * u * u * v0
5930					+ 3 * u * u * t * v1
5931					+ 3 * u * t * t * v2
5932					+ t * t * t * v3,
5933					padding);
5934		}
5935	}
5936}}, Base.each(
5937	['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
5938	function(name) {
5939		this[name] = function() {
5940			if (!this._bounds)
5941				this._bounds = {};
5942			var bounds = this._bounds[name];
5943			if (!bounds) {
5944				bounds = this._bounds[name] = Path[name]([this._segment1,
5945						this._segment2], false, this._path.getStyle());
5946			}
5947			return bounds.clone();
5948		};
5949	},
5950{
5951
5952}), {
5953	beans: false,
5954
5955	getParameterAt: function(offset, start) {
5956		return Curve.getParameterAt(this.getValues(), offset, start);
5957	},
5958
5959	getParameterOf: function() {
5960		var point = Point.read(arguments);
5961		return Curve.getParameterOf(this.getValues(), point.x, point.y);
5962	},
5963
5964	getLocationAt: function(offset, isParameter) {
5965		var t = isParameter ? offset : this.getParameterAt(offset);
5966		return t != null && t >= 0 && t <= 1
5967				? new CurveLocation(this, t)
5968				: null;
5969	},
5970
5971	getLocationOf: function() {
5972		return this.getLocationAt(this.getParameterOf(Point.read(arguments)),
5973				true);
5974	},
5975
5976	getOffsetOf: function() {
5977		var loc = this.getLocationOf.apply(this, arguments);
5978		return loc ? loc.getOffset() : null;
5979	},
5980
5981	getNearestLocation: function() {
5982		var point = Point.read(arguments),
5983			values = this.getValues(),
5984			count = 100,
5985			minDist = Infinity,
5986			minT = 0;
5987
5988		function refine(t) {
5989			if (t >= 0 && t <= 1) {
5990				var dist = point.getDistance(Curve.getPoint(values, t), true);
5991				if (dist < minDist) {
5992					minDist = dist;
5993					minT = t;
5994					return true;
5995				}
5996			}
5997		}
5998
5999		for (var i = 0; i <= count; i++)
6000			refine(i / count);
6001
6002		var step = 1 / (count * 2);
6003		while (step > 0.000001) {
6004			if (!refine(minT - step) && !refine(minT + step))
6005				step /= 2;
6006		}
6007		var pt = Curve.getPoint(values, minT);
6008		return new CurveLocation(this, minT, pt, null, null, null,
6009				point.getDistance(pt));
6010	},
6011
6012	getNearestPoint: function() {
6013		return this.getNearestLocation.apply(this, arguments).getPoint();
6014	}
6015
6016},
6017new function() {
6018	var methods = ['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',
6019		'getWeightedNormal', 'getCurvature'];
6020	return Base.each(methods,
6021	function(name) {
6022		this[name + 'At'] = function(offset, isParameter) {
6023			var values = this.getValues();
6024			return Curve[name](values, isParameter ? offset
6025					: Curve.getParameterAt(values, offset, 0));
6026		};
6027	}, {
6028		statics: {
6029			evaluateMethods: methods
6030		}
6031	})
6032},
6033new function() {
6034
6035	function getLengthIntegrand(v) {
6036		var p1x = v[0], p1y = v[1],
6037			c1x = v[2], c1y = v[3],
6038			c2x = v[4], c2y = v[5],
6039			p2x = v[6], p2y = v[7],
6040
6041			ax = 9 * (c1x - c2x) + 3 * (p2x - p1x),
6042			bx = 6 * (p1x + c2x) - 12 * c1x,
6043			cx = 3 * (c1x - p1x),
6044
6045			ay = 9 * (c1y - c2y) + 3 * (p2y - p1y),
6046			by = 6 * (p1y + c2y) - 12 * c1y,
6047			cy = 3 * (c1y - p1y);
6048
6049		return function(t) {
6050			var dx = (ax * t + bx) * t + cx,
6051				dy = (ay * t + by) * t + cy;
6052			return Math.sqrt(dx * dx + dy * dy);
6053		};
6054	}
6055
6056	function getIterations(a, b) {
6057		return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));
6058	}
6059
6060	function evaluate(v, t, type, normalized) {
6061		if (t == null || t < 0 || t > 1)
6062			return null;
6063		var p1x = v[0], p1y = v[1],
6064			c1x = v[2], c1y = v[3],
6065			c2x = v[4], c2y = v[5],
6066			p2x = v[6], p2y = v[7],
6067			tolerance = 0.000001,
6068			x, y;
6069
6070		if (type === 0 && (t < tolerance || t > 1 - tolerance)) {
6071			var isZero = t < tolerance;
6072			x = isZero ? p1x : p2x;
6073			y = isZero ? p1y : p2y;
6074		} else {
6075			var cx = 3 * (c1x - p1x),
6076				bx = 3 * (c2x - c1x) - cx,
6077				ax = p2x - p1x - cx - bx,
6078
6079				cy = 3 * (c1y - p1y),
6080				by = 3 * (c2y - c1y) - cy,
6081				ay = p2y - p1y - cy - by;
6082			if (type === 0) {
6083				x = ((ax * t + bx) * t + cx) * t + p1x;
6084				y = ((ay * t + by) * t + cy) * t + p1y;
6085			} else {
6086				if (t < tolerance) {
6087					x = cx;
6088					y = cy;
6089				} else if (t > 1 - tolerance) {
6090					x = 3 * (p2x - c2x);
6091					y = 3 * (p2y - c2y);
6092				} else {
6093					x = (3 * ax * t + 2 * bx) * t + cx;
6094					y = (3 * ay * t + 2 * by) * t + cy;
6095				}
6096				if (normalized) {
6097					if (x === 0 && y === 0
6098							&& (t < tolerance || t > 1 - tolerance)) {
6099						x = c2x - c1x;
6100						y = c2y - c1y;
6101					}
6102					var len = Math.sqrt(x * x + y * y);
6103					x /= len;
6104					y /= len;
6105				}
6106				if (type === 3) {
6107					var x2 = 6 * ax * t + 2 * bx,
6108						y2 = 6 * ay * t + 2 * by,
6109						d = Math.pow(x * x + y * y, 3 / 2);
6110					x = d !== 0 ? (x * y2 - y * x2) / d : 0;
6111					y = 0;
6112				}
6113			}
6114		}
6115		return type === 2 ? new Point(y, -x) : new Point(x, y);
6116	}
6117
6118	return {
6119		statics: true,
6120
6121		getLength: function(v, a, b) {
6122			if (a === undefined)
6123				a = 0;
6124			if (b === undefined)
6125				b = 1;
6126			var isZero = Numerical.isZero;
6127			if (a === 0 && b === 1
6128					&& isZero(v[0] - v[2]) && isZero(v[1] - v[3])
6129					&& isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
6130				var dx = v[6] - v[0],
6131					dy = v[7] - v[1];
6132				return Math.sqrt(dx * dx + dy * dy);
6133			}
6134			var ds = getLengthIntegrand(v);
6135			return Numerical.integrate(ds, a, b, getIterations(a, b));
6136		},
6137
6138		getParameterAt: function(v, offset, start) {
6139			if (start === undefined)
6140				start = offset < 0 ? 1 : 0
6141			if (offset === 0)
6142				return start;
6143			var tolerance = 0.000001,
6144				abs = Math.abs,
6145				forward = offset > 0,
6146				a = forward ? start : 0,
6147				b = forward ? 1 : start,
6148				ds = getLengthIntegrand(v),
6149				rangeLength = Numerical.integrate(ds, a, b,
6150						getIterations(a, b));
6151			if (abs(offset - rangeLength) < tolerance) {
6152				return forward ? b : a;
6153			} else if (abs(offset) > rangeLength) {
6154				return null;
6155			}
6156			var guess = offset / rangeLength,
6157				length = 0;
6158			function f(t) {
6159				length += Numerical.integrate(ds, start, t,
6160						getIterations(start, t));
6161				start = t;
6162				return length - offset;
6163			}
6164			return Numerical.findRoot(f, ds, start + guess, a, b, 16,
6165					tolerance);
6166		},
6167
6168		getPoint: function(v, t) {
6169			return evaluate(v, t, 0, false);
6170		},
6171
6172		getTangent: function(v, t) {
6173			return evaluate(v, t, 1, true);
6174		},
6175
6176		getWeightedTangent: function(v, t) {
6177			return evaluate(v, t, 1, false);
6178		},
6179
6180		getNormal: function(v, t) {
6181			return evaluate(v, t, 2, true);
6182		},
6183
6184		getWeightedNormal: function(v, t) {
6185			return evaluate(v, t, 2, false);
6186		},
6187
6188		getCurvature: function(v, t) {
6189			return evaluate(v, t, 3, false).x;
6190		}
6191	};
6192}, new function() {
6193	function addLocation(locations, include, curve1, t1, point1, curve2, t2,
6194			point2) {
6195		var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2);
6196		if (!include || include(loc))
6197			locations.push(loc);
6198	}
6199
6200	function addCurveIntersections(v1, v2, curve1, curve2, locations, include,
6201			tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) {
6202		if (recursion > 32)
6203			return;
6204		var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
6205			tolerance = 0.000001,
6206			getSignedDistance = Line.getSignedDistance,
6207			d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0,
6208			d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0,
6209			factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
6210			dMin = factor * Math.min(0, d1, d2),
6211			dMax = factor * Math.max(0, d1, d2),
6212			dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]),
6213			dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),
6214			dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),
6215			dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),
6216			tMinNew, tMaxNew, tDiff;
6217		if (q0x === q3x && uMax - uMin < tolerance && recursion > 3) {
6218			tMaxNew = tMinNew = (tMax + tMin) / 2;
6219			tDiff = 0;
6220		} else {
6221			var hull = getConvexHull(dp0, dp1, dp2, dp3),
6222				top = hull[0],
6223				bottom = hull[1],
6224				tMinClip, tMaxClip;
6225			tMinClip = clipConvexHull(top, bottom, dMin, dMax);
6226			top.reverse();
6227			bottom.reverse();
6228			tMaxClip = clipConvexHull(top, bottom, dMin, dMax);
6229			if (tMinClip == null || tMaxClip == null)
6230				return;
6231			v1 = Curve.getPart(v1, tMinClip, tMaxClip);
6232			tDiff = tMaxClip - tMinClip;
6233			tMinNew = tMax * tMinClip + tMin * (1 - tMinClip);
6234			tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip);
6235		}
6236		if (oldTDiff > 0.5 && tDiff > 0.5) {
6237			if (tMaxNew - tMinNew > uMax - uMin) {
6238				var parts = Curve.subdivide(v1, 0.5),
6239					t = tMinNew + (tMaxNew - tMinNew) / 2;
6240				addCurveIntersections(
6241					v2, parts[0], curve2, curve1, locations, include,
6242					uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion);
6243				addCurveIntersections(
6244					v2, parts[1], curve2, curve1, locations, include,
6245					uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion);
6246			} else {
6247				var parts = Curve.subdivide(v2, 0.5),
6248					t = uMin + (uMax - uMin) / 2;
6249				addCurveIntersections(
6250					parts[0], v1, curve2, curve1, locations, include,
6251					uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
6252				addCurveIntersections(
6253					parts[1], v1, curve2, curve1, locations, include,
6254					t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion);
6255			}
6256		} else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) {
6257			var t1 = tMinNew + (tMaxNew - tMinNew) / 2,
6258				t2 = uMin + (uMax - uMin) / 2;
6259			if (reverse) {
6260				addLocation(locations, include,
6261						curve2, t2, Curve.getPoint(v2, t2),
6262						curve1, t1, Curve.getPoint(v1, t1));
6263			} else {
6264				addLocation(locations, include,
6265						curve1, t1, Curve.getPoint(v1, t1),
6266						curve2, t2, Curve.getPoint(v2, t2));
6267			}
6268		} else if (tDiff > 0) {
6269			addCurveIntersections(v2, v1, curve2, curve1, locations, include,
6270					uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
6271		}
6272	}
6273
6274	function getConvexHull(dq0, dq1, dq2, dq3) {
6275		var p0 = [ 0, dq0 ],
6276			p1 = [ 1 / 3, dq1 ],
6277			p2 = [ 2 / 3, dq2 ],
6278			p3 = [ 1, dq3 ],
6279			getSignedDistance = Line.getSignedDistance,
6280			dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
6281			dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2),
6282			flip = false,
6283			hull;
6284		if (dist1 * dist2 < 0) {
6285			hull = [[p0, p1, p3], [p0, p2, p3]];
6286			flip = dist1 < 0;
6287		} else {
6288			var pmax, cross = 0,
6289				distZero = dist1 === 0 || dist2 === 0;
6290			if (Math.abs(dist1) > Math.abs(dist2)) {
6291				pmax = p1;
6292				cross = (dq3 - dq2 - (dq3 - dq0) / 3)
6293						* (2 * (dq3 - dq2) - dq3 + dq1) / 3;
6294			} else {
6295				pmax = p2;
6296				cross = (dq1 - dq0 + (dq0 - dq3) / 3)
6297						* (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
6298			}
6299			hull = cross < 0 || distZero
6300					? [[p0, pmax, p3], [p0, p3]]
6301					: [[p0, p1, p2, p3], [p0, p3]];
6302			flip = dist1 ? dist1 < 0 : dist2 < 0;
6303		}
6304		return flip ? hull.reverse() : hull;
6305	}
6306
6307	function clipConvexHull(hullTop, hullBottom, dMin, dMax) {
6308		if (hullTop[0][1] < dMin) {
6309			return clipConvexHullPart(hullTop, true, dMin);
6310		} else if (hullBottom[0][1] > dMax) {
6311			return clipConvexHullPart(hullBottom, false, dMax);
6312		} else {
6313			return hullTop[0][0];
6314		}
6315	}
6316
6317	function clipConvexHullPart(part, top, threshold) {
6318		var px = part[0][0],
6319			py = part[0][1];
6320		for (var i = 1, l = part.length; i < l; i++) {
6321			var qx = part[i][0],
6322				qy = part[i][1];
6323			if (top ? qy >= threshold : qy <= threshold)
6324				return px + (threshold - py) * (qx - px) / (qy - py);
6325			px = qx;
6326			py = qy;
6327		}
6328		return null;
6329	}
6330
6331	function addCurveLineIntersections(v1, v2, curve1, curve2, locations,
6332			include) {
6333		var flip = Curve.isLinear(v1),
6334			vc = flip ? v2 : v1,
6335			vl = flip ? v1 : v2,
6336			lx1 = vl[0], ly1 = vl[1],
6337			lx2 = vl[6], ly2 = vl[7],
6338			ldx = lx2 - lx1,
6339			ldy = ly2 - ly1,
6340			angle = Math.atan2(-ldy, ldx),
6341			sin = Math.sin(angle),
6342			cos = Math.cos(angle),
6343			rlx2 = ldx * cos - ldy * sin,
6344			rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0],
6345			rvc = [];
6346		for(var i = 0; i < 8; i += 2) {
6347			var x = vc[i] - lx1,
6348				y = vc[i + 1] - ly1;
6349			rvc.push(
6350				x * cos - y * sin,
6351				y * cos + x * sin);
6352		}
6353		var roots = [],
6354			count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1);
6355		for (var i = 0; i < count; i++) {
6356			var tc = roots[i],
6357				x = Curve.getPoint(rvc, tc).x;
6358			if (x >= 0 && x <= rlx2) {
6359				var tl = Curve.getParameterOf(rvl, x, 0),
6360					t1 = flip ? tl : tc,
6361					t2 = flip ? tc : tl;
6362				addLocation(locations, include,
6363						curve1, t1, Curve.getPoint(v1, t1),
6364						curve2, t2, Curve.getPoint(v2, t2));
6365			}
6366		}
6367	}
6368
6369	function addLineIntersection(v1, v2, curve1, curve2, locations, include) {
6370		var point = Line.intersect(
6371				v1[0], v1[1], v1[6], v1[7],
6372				v2[0], v2[1], v2[6], v2[7]);
6373		if (point) {
6374			var x = point.x,
6375				y = point.y;
6376			addLocation(locations, include,
6377					curve1, Curve.getParameterOf(v1, x, y), point,
6378					curve2, Curve.getParameterOf(v2, x, y), point);
6379		}
6380	}
6381
6382	return { statics: {
6383		getIntersections: function(v1, v2, c1, c2, locations, include) {
6384			var linear1 = Curve.isLinear(v1),
6385				linear2 = Curve.isLinear(v2),
6386				c1p1 = c1.getPoint1(),
6387				c1p2 = c1.getPoint2(),
6388				c2p1 = c2.getPoint1(),
6389				c2p2 = c2.getPoint2(),
6390				tolerance = 0.000001;
6391			if (c1p1.isClose(c2p1, tolerance))
6392				addLocation(locations, include, c1, 0, c1p1, c2, 0, c1p1);
6393			if (c1p1.isClose(c2p2, tolerance))
6394				addLocation(locations, include, c1, 0, c1p1, c2, 1, c1p1);
6395			(linear1 && linear2
6396				? addLineIntersection
6397				: linear1 || linear2
6398					? addCurveLineIntersections
6399					: addCurveIntersections)(
6400						v1, v2, c1, c2, locations, include,
6401						0, 1, 0, 1, 0, false, 0);
6402			if (c1p2.isClose(c2p1, tolerance))
6403				addLocation(locations, include, c1, 1, c1p2, c2, 0, c1p2);
6404			if (c1p2.isClose(c2p2, tolerance))
6405				addLocation(locations, include, c1, 1, c1p2, c2, 1, c1p2);
6406			return locations;
6407		},
6408
6409		filterIntersections: function(locations, _expand) {
6410			var last = locations.length - 1,
6411				tMax = 1 - 0.000001;
6412			for (var i = last; i >= 0; i--) {
6413				var loc = locations[i],
6414					next = loc._curve.getNext(),
6415					next2 = loc._curve2.getNext();
6416				if (next && loc._parameter >= tMax) {
6417					loc._parameter = 0;
6418					loc._curve = next;
6419				}
6420				if (next2 && loc._parameter2 >= tMax) {
6421					loc._parameter2 = 0;
6422					loc._curve2 = next2;
6423				}
6424			}
6425
6426			function compare(loc1, loc2) {
6427				var path1 = loc1.getPath(),
6428					path2 = loc2.getPath();
6429				return path1 === path2
6430						? (loc1.getIndex() + loc1.getParameter())
6431								- (loc2.getIndex() + loc2.getParameter())
6432						: path1._id - path2._id;
6433			}
6434
6435			if (last > 0) {
6436				locations.sort(compare);
6437				for (var i = last; i > 0; i--) {
6438					if (locations[i].equals(locations[i - 1])) {
6439						locations.splice(i, 1);
6440						last--;
6441					}
6442				}
6443			}
6444			if (_expand) {
6445				for (var i = last; i >= 0; i--)
6446					locations.push(locations[i].getIntersection());
6447				locations.sort(compare);
6448			}
6449			return locations;
6450		}
6451	}};
6452});
6453
6454var CurveLocation = Base.extend({
6455	_class: 'CurveLocation',
6456	beans: true,
6457
6458	initialize: function CurveLocation(curve, parameter, point, _curve2,
6459			_parameter2, _point2, _distance) {
6460		this._id = UID.get(CurveLocation);
6461		var path = curve._path;
6462		this._version = path ? path._version : 0;
6463		this._curve = curve;
6464		this._parameter = parameter;
6465		this._point = point || curve.getPointAt(parameter, true);
6466		this._curve2 = _curve2;
6467		this._parameter2 = _parameter2;
6468		this._point2 = _point2;
6469		this._distance = _distance;
6470		this._segment1 = curve._segment1;
6471		this._segment2 = curve._segment2;
6472	},
6473
6474	getSegment: function(_preferFirst) {
6475		if (!this._segment) {
6476			var curve = this.getCurve(),
6477				parameter = this.getParameter();
6478			if (parameter === 1) {
6479				this._segment = curve._segment2;
6480			} else if (parameter === 0 || _preferFirst) {
6481				this._segment = curve._segment1;
6482			} else if (parameter == null) {
6483				return null;
6484			} else {
6485				this._segment = curve.getPartLength(0, parameter)
6486					< curve.getPartLength(parameter, 1)
6487						? curve._segment1
6488						: curve._segment2;
6489			}
6490		}
6491		return this._segment;
6492	},
6493
6494	getCurve: function() {
6495		var curve = this._curve,
6496			path = curve && curve._path;
6497		if (path && path._version !== this._version) {
6498			curve = null;
6499			this._parameter = null;
6500		}
6501		if (!curve) {
6502			curve = this._segment1.getCurve();
6503			if (curve.getParameterOf(this._point) == null)
6504				curve = this._segment2.getPrevious().getCurve();
6505			this._curve = curve;
6506			path = curve._path;
6507			this._version = path ? path._version : 0;
6508		}
6509		return curve;
6510	},
6511
6512	getPath: function() {
6513		var curve = this.getCurve();
6514		return curve && curve._path;
6515	},
6516
6517	getIndex: function() {
6518		var curve = this.getCurve();
6519		return curve && curve.getIndex();
6520	},
6521
6522	getParameter: function() {
6523		var curve = this.getCurve(),
6524			parameter = this._parameter;
6525		return curve && parameter == null
6526			? this._parameter = curve.getParameterOf(this._point)
6527			: parameter;
6528	},
6529
6530	getPoint: function() {
6531		return this._point;
6532	},
6533
6534	getOffset: function() {
6535		var path = this.getPath();
6536		return path ? path._getOffset(this) : this.getCurveOffset();
6537	},
6538
6539	getCurveOffset: function() {
6540		var curve = this.getCurve(),
6541			parameter = this.getParameter();
6542		return parameter != null && curve && curve.getPartLength(0, parameter);
6543	},
6544
6545	getIntersection: function() {
6546		var intersection = this._intersection;
6547		if (!intersection && this._curve2) {
6548			this._intersection = intersection = new CurveLocation(this._curve2,
6549					this._parameter2, this._point2 || this._point, this);
6550			intersection._intersection = this;
6551		}
6552		return intersection;
6553	},
6554
6555	getDistance: function() {
6556		return this._distance;
6557	},
6558
6559	divide: function() {
6560		var curve = this.getCurve();
6561		return curve && curve.divide(this.getParameter(), true);
6562	},
6563
6564	split: function() {
6565		var curve = this.getCurve();
6566		return curve && curve.split(this.getParameter(), true);
6567	},
6568
6569	equals: function(loc) {
6570		var abs = Math.abs,
6571			tolerance = 0.000001;
6572		return this === loc
6573				|| loc instanceof CurveLocation
6574					&& this.getCurve() === loc.getCurve()
6575					&& abs(this.getParameter() - loc.getParameter()) < tolerance
6576					&& this._curve2 === loc._curve2
6577					&& abs(this._parameter2 - loc._parameter2) < tolerance
6578				|| false;
6579	},
6580
6581	toString: function() {
6582		var parts = [],
6583			point = this.getPoint(),
6584			f = Formatter.instance;
6585		if (point)
6586			parts.push('point: ' + point);
6587		var index = this.getIndex();
6588		if (index != null)
6589			parts.push('index: ' + index);
6590		var parameter = this.getParameter();
6591		if (parameter != null)
6592			parts.push('parameter: ' + f.number(parameter));
6593		if (this._distance != null)
6594			parts.push('distance: ' + f.number(this._distance));
6595		return '{ ' + parts.join(', ') + ' }';
6596	}
6597}, Base.each(Curve.evaluateMethods, function(name) {
6598	if (name !== 'getPoint') {
6599		var get = name + 'At';
6600		this[name] = function() {
6601			var parameter = this.getParameter(),
6602				curve = this.getCurve();
6603			return parameter != null && curve && curve[get](parameter, true);
6604		};
6605	}
6606}, {}));
6607
6608var PathItem = Item.extend({
6609	_class: 'PathItem',
6610
6611	initialize: function PathItem() {
6612	},
6613
6614	getIntersections: function(path, _matrix, _expand) {
6615		if (this === path)
6616			path = null;
6617		var locations = [],
6618			curves1 = this.getCurves(),
6619			curves2 = path ? path.getCurves() : curves1,
6620			matrix1 = this._matrix.orNullIfIdentity(),
6621			matrix2 = path ? (_matrix || path._matrix).orNullIfIdentity()
6622				: matrix1,
6623			length1 = curves1.length,
6624			length2 = path ? curves2.length : length1,
6625			values2 = [],
6626			tMin = 0.000001,
6627			tMax = 1 - tMin;
6628		if (path && !this.getBounds(matrix1).touches(path.getBounds(matrix2)))
6629			return [];
6630		for (var i = 0; i < length2; i++)
6631			values2[i] = curves2[i].getValues(matrix2);
6632		for (var i = 0; i < length1; i++) {
6633			var curve1 = curves1[i],
6634				values1 = path ? curve1.getValues(matrix1) : values2[i];
6635			if (!path) {
6636				var seg1 = curve1.getSegment1(),
6637					seg2 = curve1.getSegment2(),
6638					h1 = seg1._handleOut,
6639					h2 = seg2._handleIn;
6640				if (new Line(seg1._point.subtract(h1), h1.multiply(2), true)
6641						.intersect(new Line(seg2._point.subtract(h2),
6642						h2.multiply(2), true), false)) {
6643					var parts = Curve.subdivide(values1);
6644					Curve.getIntersections(
6645						parts[0], parts[1], curve1, curve1, locations,
6646						function(loc) {
6647							if (loc._parameter <= tMax) {
6648								loc._parameter /= 2;
6649								loc._parameter2 = 0.5 + loc._parameter2 / 2;
6650								return true;
6651							}
6652						}
6653					);
6654				}
6655			}
6656			for (var j = path ? 0 : i + 1; j < length2; j++) {
6657				Curve.getIntersections(
6658					values1, values2[j], curve1, curves2[j], locations,
6659					!path && (j === i + 1 || j === length2 - 1 && i === 0)
6660						&& function(loc) {
6661							var t = loc._parameter;
6662							return t >= tMin && t <= tMax;
6663						}
6664				);
6665			}
6666		}
6667		return Curve.filterIntersections(locations, _expand);
6668	},
6669
6670	_asPathItem: function() {
6671		return this;
6672	},
6673
6674	setPathData: function(data) {
6675
6676		var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
6677			coords,
6678			relative = false,
6679			previous,
6680			control,
6681			current = new Point(),
6682			start = new Point();
6683
6684		function getCoord(index, coord) {
6685			var val = +coords[index];
6686			if (relative)
6687				val += current[coord];
6688			return val;
6689		}
6690
6691		function getPoint(index) {
6692			return new Point(
6693				getCoord(index, 'x'),
6694				getCoord(index + 1, 'y')
6695			);
6696		}
6697
6698		this.clear();
6699
6700		for (var i = 0, l = parts && parts.length; i < l; i++) {
6701			var part = parts[i],
6702				command = part[0],
6703				lower = command.toLowerCase();
6704			coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
6705			var length = coords && coords.length;
6706			relative = command === lower;
6707			if (previous === 'z' && !/[mz]/.test(lower))
6708				this.moveTo(current = start);
6709			switch (lower) {
6710			case 'm':
6711			case 'l':
6712				var move = lower === 'm';
6713				for (var j = 0; j < length; j += 2)
6714					this[j === 0 && move ? 'moveTo' : 'lineTo'](
6715							current = getPoint(j));
6716				control = current;
6717				if (move)
6718					start = current;
6719				break;
6720			case 'h':
6721			case 'v':
6722				var coord = lower === 'h' ? 'x' : 'y';
6723				for (var j = 0; j < length; j++) {
6724					current[coord] = getCoord(j, coord);
6725					this.lineTo(current);
6726				}
6727				control = current;
6728				break;
6729			case 'c':
6730				for (var j = 0; j < length; j += 6) {
6731					this.cubicCurveTo(
6732							getPoint(j),
6733							control = getPoint(j + 2),
6734							current = getPoint(j + 4));
6735				}
6736				break;
6737			case 's':
6738				for (var j = 0; j < length; j += 4) {
6739					this.cubicCurveTo(
6740							/[cs]/.test(previous)
6741									? current.multiply(2).subtract(control)
6742									: current,
6743							control = getPoint(j),
6744							current = getPoint(j + 2));
6745					previous = lower;
6746				}
6747				break;
6748			case 'q':
6749				for (var j = 0; j < length; j += 4) {
6750					this.quadraticCurveTo(
6751							control = getPoint(j),
6752							current = getPoint(j + 2));
6753				}
6754				break;
6755			case 't':
6756				for (var j = 0; j < length; j += 2) {
6757					this.quadraticCurveTo(
6758							control = (/[qt]/.test(previous)
6759									? current.multiply(2).subtract(control)
6760									: current),
6761							current = getPoint(j));
6762					previous = lower;
6763				}
6764				break;
6765			case 'a':
6766				for (var j = 0; j < length; j += 7) {
6767					this.arcTo(current = getPoint(j + 5),
6768							new Size(+coords[j], +coords[j + 1]),
6769							+coords[j + 2], +coords[j + 4], +coords[j + 3]);
6770				}
6771				break;
6772			case 'z':
6773				this.closePath(true);
6774				break;
6775			}
6776			previous = lower;
6777		}
6778	},
6779
6780	_canComposite: function() {
6781		return !(this.hasFill() && this.hasStroke());
6782	},
6783
6784	_contains: function(point) {
6785		var winding = this._getWinding(point, false, true);
6786		return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
6787	}
6788
6789});
6790
6791var Path = PathItem.extend({
6792	_class: 'Path',
6793	_serializeFields: {
6794		segments: [],
6795		closed: false
6796	},
6797
6798	initialize: function Path(arg) {
6799		this._closed = false;
6800		this._segments = [];
6801		this._version = 0;
6802		var segments = Array.isArray(arg)
6803			? typeof arg[0] === 'object'
6804				? arg
6805				: arguments
6806			: arg && (arg.size === undefined && (arg.x !== undefined
6807					|| arg.point !== undefined))
6808				? arguments
6809				: null;
6810		if (segments && segments.length > 0) {
6811			this.setSegments(segments);
6812		} else {
6813			this._curves = undefined;
6814			this._selectedSegmentState = 0;
6815			if (!segments && typeof arg === 'string') {
6816				this.setPathData(arg);
6817				arg = null;
6818			}
6819		}
6820		this._initialize(!segments && arg);
6821	},
6822
6823	_equals: function(item) {
6824		return this._closed === item._closed
6825				&& Base.equals(this._segments, item._segments);
6826	},
6827
6828	clone: function(insert) {
6829		var copy = new Path(Item.NO_INSERT);
6830		copy.setSegments(this._segments);
6831		copy._closed = this._closed;
6832		if (this._clockwise !== undefined)
6833			copy._clockwise = this._clockwise;
6834		return this._clone(copy, insert);
6835	},
6836
6837	_changed: function _changed(flags) {
6838		_changed.base.call(this, flags);
6839		if (flags & 8) {
6840			var parent = this._parent;
6841			if (parent)
6842				parent._currentPath = undefined;
6843			this._length = this._clockwise = undefined;
6844			if (flags & 16) {
6845				this._version++;
6846			} else if (this._curves) {
6847			   for (var i = 0, l = this._curves.length; i < l; i++)
6848					this._curves[i]._changed();
6849			}
6850			this._monoCurves = undefined;
6851		} else if (flags & 32) {
6852			this._bounds = undefined;
6853		}
6854	},
6855
6856	getStyle: function() {
6857		var parent = this._parent;
6858		return (parent instanceof CompoundPath ? parent : this)._style;
6859	},
6860
6861	getSegments: function() {
6862		return this._segments;
6863	},
6864
6865	setSegments: function(segments) {
6866		var fullySelected = this.isFullySelected();
6867		this._segments.length = 0;
6868		this._selectedSegmentState = 0;
6869		this._curves = undefined;
6870		if (segments && segments.length > 0)
6871			this._add(Segment.readAll(segments));
6872		if (fullySelected)
6873			this.setFullySelected(true);
6874	},
6875
6876	getFirstSegment: function() {
6877		return this._segments[0];
6878	},
6879
6880	getLastSegment: function() {
6881		return this._segments[this._segments.length - 1];
6882	},
6883
6884	getCurves: function() {
6885		var curves = this._curves,
6886			segments = this._segments;
6887		if (!curves) {
6888			var length = this._countCurves();
6889			curves = this._curves = new Array(length);
6890			for (var i = 0; i < length; i++)
6891				curves[i] = new Curve(this, segments[i],
6892					segments[i + 1] || segments[0]);
6893		}
6894		return curves;
6895	},
6896
6897	getFirstCurve: function() {
6898		return this.getCurves()[0];
6899	},
6900
6901	getLastCurve: function() {
6902		var curves = this.getCurves();
6903		return curves[curves.length - 1];
6904	},
6905
6906	isClosed: function() {
6907		return this._closed;
6908	},
6909
6910	setClosed: function(closed) {
6911		if (this._closed != (closed = !!closed)) {
6912			this._closed = closed;
6913			if (this._curves) {
6914				var length = this._curves.length = this._countCurves();
6915				if (closed)
6916					this._curves[length - 1] = new Curve(this,
6917						this._segments[length - 1], this._segments[0]);
6918			}
6919			this._changed(25);
6920		}
6921	}
6922}, {
6923	beans: true,
6924
6925	getPathData: function(_matrix, _precision) {
6926		var segments = this._segments,
6927			length = segments.length,
6928			f = new Formatter(_precision),
6929			coords = new Array(6),
6930			first = true,
6931			curX, curY,
6932			prevX, prevY,
6933			inX, inY,
6934			outX, outY,
6935			parts = [];
6936
6937		function addSegment(segment, skipLine) {
6938			segment._transformCoordinates(_matrix, coords, false);
6939			curX = coords[0];
6940			curY = coords[1];
6941			if (first) {
6942				parts.push('M' + f.pair(curX, curY));
6943				first = false;
6944			} else {
6945				inX = coords[2];
6946				inY = coords[3];
6947				if (inX === curX && inY === curY
6948						&& outX === prevX && outY === prevY) {
6949					if (!skipLine)
6950						parts.push('l' + f.pair(curX - prevX, curY - prevY));
6951				} else {
6952					parts.push('c' + f.pair(outX - prevX, outY - prevY)
6953							+ ' ' + f.pair(inX - prevX, inY - prevY)
6954							+ ' ' + f.pair(curX - prevX, curY - prevY));
6955				}
6956			}
6957			prevX = curX;
6958			prevY = curY;
6959			outX = coords[4];
6960			outY = coords[5];
6961		}
6962
6963		if (length === 0)
6964			return '';
6965
6966		for (var i = 0; i < length; i++)
6967			addSegment(segments[i]);
6968		if (this._closed && length > 0) {
6969			addSegment(segments[0], true);
6970			parts.push('z');
6971		}
6972		return parts.join('');
6973	}
6974}, {
6975
6976	isEmpty: function() {
6977		return this._segments.length === 0;
6978	},
6979
6980	isLinear: function() {
6981		var segments = this._segments;
6982		for (var i = 0, l = segments.length; i < l; i++) {
6983			if (!segments[i].isLinear())
6984				return false;
6985		}
6986		return true;
6987	},
6988
6989	hasHandles: function() {
6990		var segments = this._segments;
6991		for (var i = 0, l = segments.length; i < l; i++) {
6992			if (segments[i].hasHandles())
6993				return true;
6994		}
6995		return false;
6996	},
6997
6998	_transformContent: function(matrix) {
6999		var coords = new Array(6);
7000		for (var i = 0, l = this._segments.length; i < l; i++)
7001			this._segments[i]._transformCoordinates(matrix, coords, true);
7002		return true;
7003	},
7004
7005	_add: function(segs, index) {
7006		var segments = this._segments,
7007			curves = this._curves,
7008			amount = segs.length,
7009			append = index == null,
7010			index = append ? segments.length : index;
7011		for (var i = 0; i < amount; i++) {
7012			var segment = segs[i];
7013			if (segment._path)
7014				segment = segs[i] = segment.clone();
7015			segment._path = this;
7016			segment._index = index + i;
7017			if (segment._selectionState)
7018				this._updateSelection(segment, 0, segment._selectionState);
7019		}
7020		if (append) {
7021			segments.push.apply(segments, segs);
7022		} else {
7023			segments.splice.apply(segments, [index, 0].concat(segs));
7024			for (var i = index + amount, l = segments.length; i < l; i++)
7025				segments[i]._index = i;
7026		}
7027		if (curves || segs._curves) {
7028			if (!curves)
7029				curves = this._curves = [];
7030			var from = index > 0 ? index - 1 : index,
7031				start = from,
7032				to = Math.min(from + amount, this._countCurves());
7033			if (segs._curves) {
7034				curves.splice.apply(curves, [from, 0].concat(segs._curves));
7035				start += segs._curves.length;
7036			}
7037			for (var i = start; i < to; i++)
7038				curves.splice(i, 0, new Curve(this, null, null));
7039			this._adjustCurves(from, to);
7040		}
7041		this._changed(25);
7042		return segs;
7043	},
7044
7045	_adjustCurves: function(from, to) {
7046		var segments = this._segments,
7047			curves = this._curves,
7048			curve;
7049		for (var i = from; i < to; i++) {
7050			curve = curves[i];
7051			curve._path = this;
7052			curve._segment1 = segments[i];
7053			curve._segment2 = segments[i + 1] || segments[0];
7054			curve._changed();
7055		}
7056		if (curve = curves[this._closed && from === 0 ? segments.length - 1
7057				: from - 1]) {
7058			curve._segment2 = segments[from] || segments[0];
7059			curve._changed();
7060		}
7061		if (curve = curves[to]) {
7062			curve._segment1 = segments[to];
7063			curve._changed();
7064		}
7065	},
7066
7067	_countCurves: function() {
7068		var length = this._segments.length;
7069		return !this._closed && length > 0 ? length - 1 : length;
7070	},
7071
7072	add: function(segment1 ) {
7073		return arguments.length > 1 && typeof segment1 !== 'number'
7074			? this._add(Segment.readAll(arguments))
7075			: this._add([ Segment.read(arguments) ])[0];
7076	},
7077
7078	insert: function(index, segment1 ) {
7079		return arguments.length > 2 && typeof segment1 !== 'number'
7080			? this._add(Segment.readAll(arguments, 1), index)
7081			: this._add([ Segment.read(arguments, 1) ], index)[0];
7082	},
7083
7084	addSegment: function() {
7085		return this._add([ Segment.read(arguments) ])[0];
7086	},
7087
7088	insertSegment: function(index ) {
7089		return this._add([ Segment.read(arguments, 1) ], index)[0];
7090	},
7091
7092	addSegments: function(segments) {
7093		return this._add(Segment.readAll(segments));
7094	},
7095
7096	insertSegments: function(index, segments) {
7097		return this._add(Segment.readAll(segments), index);
7098	},
7099
7100	removeSegment: function(index) {
7101		return this.removeSegments(index, index + 1)[0] || null;
7102	},
7103
7104	removeSegments: function(from, to, _includeCurves) {
7105		from = from || 0;
7106		to = Base.pick(to, this._segments.length);
7107		var segments = this._segments,
7108			curves = this._curves,
7109			count = segments.length,
7110			removed = segments.splice(from, to - from),
7111			amount = removed.length;
7112		if (!amount)
7113			return removed;
7114		for (var i = 0; i < amount; i++) {
7115			var segment = removed[i];
7116			if (segment._selectionState)
7117				this._updateSelection(segment, segment._selectionState, 0);
7118			segment._index = segment._path = null;
7119		}
7120		for (var i = from, l = segments.length; i < l; i++)
7121			segments[i]._index = i;
7122		if (curves) {
7123			var index = from > 0 && to === count + (this._closed ? 1 : 0)
7124					? from - 1
7125					: from,
7126				curves = curves.splice(index, amount);
7127			if (_includeCurves)
7128				removed._curves = curves.slice(1);
7129			this._adjustCurves(index, index);
7130		}
7131		this._changed(25);
7132		return removed;
7133	},
7134
7135	clear: '#removeSegments',
7136
7137	getLength: function() {
7138		if (this._length == null) {
7139			var curves = this.getCurves();
7140			this._length = 0;
7141			for (var i = 0, l = curves.length; i < l; i++)
7142				this._length += curves[i].getLength();
7143		}
7144		return this._length;
7145	},
7146
7147	getArea: function() {
7148		var curves = this.getCurves();
7149		var area = 0;
7150		for (var i = 0, l = curves.length; i < l; i++)
7151			area += curves[i].getArea();
7152		return area;
7153	},
7154
7155	isFullySelected: function() {
7156		var length = this._segments.length;
7157		return this._selected && length > 0 && this._selectedSegmentState
7158				=== length * 7;
7159	},
7160
7161	setFullySelected: function(selected) {
7162		if (selected)
7163			this._selectSegments(true);
7164		this.setSelected(selected);
7165	},
7166
7167	setSelected: function setSelected(selected) {
7168		if (!selected)
7169			this._selectSegments(false);
7170		setSelected.base.call(this, selected);
7171	},
7172
7173	_selectSegments: function(selected) {
7174		var length = this._segments.length;
7175		this._selectedSegmentState = selected
7176				? length * 7 : 0;
7177		for (var i = 0; i < length; i++)
7178			this._segments[i]._selectionState = selected
7179					? 7 : 0;
7180	},
7181
7182	_updateSelection: function(segment, oldState, newState) {
7183		segment._selectionState = newState;
7184		var total = this._selectedSegmentState += newState - oldState;
7185		if (total > 0)
7186			this.setSelected(true);
7187	},
7188
7189	flatten: function(maxDistance) {
7190		var iterator = new PathIterator(this, 64, 0.1),
7191			pos = 0,
7192			step = iterator.length / Math.ceil(iterator.length / maxDistance),
7193			end = iterator.length + (this._closed ? -step : step) / 2;
7194		var segments = [];
7195		while (pos <= end) {
7196			segments.push(new Segment(iterator.getPointAt(pos)));
7197			pos += step;
7198		}
7199		this.setSegments(segments);
7200	},
7201
7202	reduce: function() {
7203		var curves = this.getCurves();
7204		for (var i = curves.length - 1; i >= 0; i--) {
7205			var curve = curves[i];
7206			if (curve.isLinear() && curve.getLength() === 0)
7207				curve.remove();
7208		}
7209		return this;
7210	},
7211
7212	simplify: function(tolerance) {
7213		if (this._segments.length > 2) {
7214			var fitter = new PathFitter(this, tolerance || 2.5);
7215			this.setSegments(fitter.fit());
7216		}
7217	},
7218
7219	split: function(index, parameter) {
7220		if (parameter === null)
7221			return null;
7222		if (arguments.length === 1) {
7223			var arg = index;
7224			if (typeof arg === 'number')
7225				arg = this.getLocationAt(arg);
7226			if (!arg)
7227				return null
7228			index = arg.index;
7229			parameter = arg.parameter;
7230		}
7231		var tolerance = 0.000001;
7232		if (parameter >= 1 - tolerance) {
7233			index++;
7234			parameter--;
7235		}
7236		var curves = this.getCurves();
7237		if (index >= 0 && index < curves.length) {
7238			if (parameter > tolerance) {
7239				curves[index++].divide(parameter, true);
7240			}
7241			var segs = this.removeSegments(index, this._segments.length, true),
7242				path;
7243			if (this._closed) {
7244				this.setClosed(false);
7245				path = this;
7246			} else {
7247				path = this._clone(new Path().insertAbove(this, true));
7248			}
7249			path._add(segs, 0);
7250			this.addSegment(segs[0]);
7251			return path;
7252		}
7253		return null;
7254	},
7255
7256	isClockwise: function() {
7257		if (this._clockwise !== undefined)
7258			return this._clockwise;
7259		return Path.isClockwise(this._segments);
7260	},
7261
7262	setClockwise: function(clockwise) {
7263		if (this.isClockwise() != (clockwise = !!clockwise))
7264			this.reverse();
7265		this._clockwise = clockwise;
7266	},
7267
7268	reverse: function() {
7269		this._segments.reverse();
7270		for (var i = 0, l = this._segments.length; i < l; i++) {
7271			var segment = this._segments[i];
7272			var handleIn = segment._handleIn;
7273			segment._handleIn = segment._handleOut;
7274			segment._handleOut = handleIn;
7275			segment._index = i;
7276		}
7277		this._curves = null;
7278		if (this._clockwise !== undefined)
7279			this._clockwise = !this._clockwise;
7280		this._changed(9);
7281	},
7282
7283	join: function(path) {
7284		if (path) {
7285			var segments = path._segments,
7286				last1 = this.getLastSegment(),
7287				last2 = path.getLastSegment();
7288			if (!last2)
7289				return this;
7290			if (last1 && last1._point.equals(last2._point))
7291				path.reverse();
7292			var first2 = path.getFirstSegment();
7293			if (last1 && last1._point.equals(first2._point)) {
7294				last1.setHandleOut(first2._handleOut);
7295				this._add(segments.slice(1));
7296			} else {
7297				var first1 = this.getFirstSegment();
7298				if (first1 && first1._point.equals(first2._point))
7299					path.reverse();
7300				last2 = path.getLastSegment();
7301				if (first1 && first1._point.equals(last2._point)) {
7302					first1.setHandleIn(last2._handleIn);
7303					this._add(segments.slice(0, segments.length - 1), 0);
7304				} else {
7305					this._add(segments.slice());
7306				}
7307			}
7308			if (path.closed)
7309				this._add([segments[0]]);
7310			path.remove();
7311		}
7312		var first = this.getFirstSegment(),
7313			last = this.getLastSegment();
7314		if (first !== last && first._point.equals(last._point)) {
7315			first.setHandleIn(last._handleIn);
7316			last.remove();
7317			this.setClosed(true);
7318		}
7319		return this;
7320	},
7321
7322	toShape: function(insert) {
7323		if (!this._closed)
7324			return null;
7325
7326		var segments = this._segments,
7327			type,
7328			size,
7329			radius,
7330			topCenter;
7331
7332		function isCollinear(i, j) {
7333			return segments[i].isCollinear(segments[j]);
7334		}
7335
7336		function isOrthogonal(i) {
7337			return segments[i].isOrthogonal();
7338		}
7339
7340		function isArc(i) {
7341			return segments[i].isOrthogonalArc();
7342		}
7343
7344		function getDistance(i, j) {
7345			return segments[i]._point.getDistance(segments[j]._point);
7346		}
7347
7348		if (!this.hasHandles() && segments.length === 4
7349				&& isCollinear(0, 2) && isCollinear(1, 3) && isOrthogonal(1)) {
7350			type = Shape.Rectangle;
7351			size = new Size(getDistance(0, 3), getDistance(0, 1));
7352			topCenter = segments[1]._point.add(segments[2]._point).divide(2);
7353		} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)
7354				&& isArc(6) && isCollinear(1, 5) && isCollinear(3, 7)) {
7355			type = Shape.Rectangle;
7356			size = new Size(getDistance(1, 6), getDistance(0, 3));
7357			radius = size.subtract(new Size(getDistance(0, 7),
7358					getDistance(1, 2))).divide(2);
7359			topCenter = segments[3]._point.add(segments[4]._point).divide(2);
7360		} else if (segments.length === 4
7361				&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
7362			if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {
7363				type = Shape.Circle;
7364				radius = getDistance(0, 2) / 2;
7365			} else {
7366				type = Shape.Ellipse;
7367				radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);
7368			}
7369			topCenter = segments[1]._point;
7370		}
7371
7372		if (type) {
7373			var center = this.getPosition(true),
7374				shape = this._clone(new type({
7375					center: center,
7376					size: size,
7377					radius: radius,
7378					insert: false
7379				}), insert, false);
7380			shape.rotate(topCenter.subtract(center).getAngle() + 90);
7381			return shape;
7382		}
7383		return null;
7384	},
7385
7386	_hitTestSelf: function(point, options) {
7387		var that = this,
7388			style = this.getStyle(),
7389			segments = this._segments,
7390			numSegments = segments.length,
7391			closed = this._closed,
7392			tolerancePadding = options._tolerancePadding,
7393			strokePadding = tolerancePadding,
7394			join, cap, miterLimit,
7395			area, loc, res,
7396			hitStroke = options.stroke && style.hasStroke(),
7397			hitFill = options.fill && style.hasFill(),
7398			hitCurves = options.curves,
7399			radius = hitStroke
7400					? style.getStrokeWidth() / 2
7401					: hitFill && options.tolerance > 0 || hitCurves
7402						? 0 : null;
7403		if (radius !== null) {
7404			if (radius > 0) {
7405				join = style.getStrokeJoin();
7406				cap = style.getStrokeCap();
7407				miterLimit = radius * style.getMiterLimit();
7408				strokePadding = tolerancePadding.add(new Point(radius, radius));
7409			} else {
7410				join = cap = 'round';
7411			}
7412		}
7413
7414		function isCloseEnough(pt, padding) {
7415			return point.subtract(pt).divide(padding).length <= 1;
7416		}
7417
7418		function checkSegmentPoint(seg, pt, name) {
7419			if (!options.selected || pt.isSelected()) {
7420				var anchor = seg._point;
7421				if (pt !== anchor)
7422					pt = pt.add(anchor);
7423				if (isCloseEnough(pt, strokePadding)) {
7424					return new HitResult(name, that, {
7425						segment: seg,
7426						point: pt
7427					});
7428				}
7429			}
7430		}
7431
7432		function checkSegmentPoints(seg, ends) {
7433			return (ends || options.segments)
7434				&& checkSegmentPoint(seg, seg._point, 'segment')
7435				|| (!ends && options.handles) && (
7436					checkSegmentPoint(seg, seg._handleIn, 'handle-in') ||
7437					checkSegmentPoint(seg, seg._handleOut, 'handle-out'));
7438		}
7439
7440		function addToArea(point) {
7441			area.add(point);
7442		}
7443
7444		function checkSegmentStroke(segment) {
7445			if (join !== 'round' || cap !== 'round') {
7446				area = new Path({ internal: true, closed: true });
7447				if (closed || segment._index > 0
7448						&& segment._index < numSegments - 1) {
7449					if (join !== 'round' && (segment._handleIn.isZero()
7450							|| segment._handleOut.isZero()))
7451						Path._addBevelJoin(segment, join, radius, miterLimit,
7452								addToArea, true);
7453				} else if (cap !== 'round') {
7454					Path._addSquareCap(segment, cap, radius, addToArea, true);
7455				}
7456				if (!area.isEmpty()) {
7457					var loc;
7458					return area.contains(point)
7459						|| (loc = area.getNearestLocation(point))
7460							&& isCloseEnough(loc.getPoint(), tolerancePadding);
7461				}
7462			}
7463			return isCloseEnough(segment._point, strokePadding);
7464		}
7465
7466		if (options.ends && !options.segments && !closed) {
7467			if (res = checkSegmentPoints(segments[0], true)
7468					|| checkSegmentPoints(segments[numSegments - 1], true))
7469				return res;
7470		} else if (options.segments || options.handles) {
7471			for (var i = 0; i < numSegments; i++)
7472				if (res = checkSegmentPoints(segments[i]))
7473					return res;
7474		}
7475		if (radius !== null) {
7476			loc = this.getNearestLocation(point);
7477			if (loc) {
7478				var parameter = loc.getParameter();
7479				if (parameter === 0 || parameter === 1 && numSegments > 1) {
7480					if (!checkSegmentStroke(loc.getSegment()))
7481						loc = null;
7482				} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
7483					loc = null;
7484				}
7485			}
7486			if (!loc && join === 'miter' && numSegments > 1) {
7487				for (var i = 0; i < numSegments; i++) {
7488					var segment = segments[i];
7489					if (point.getDistance(segment._point) <= miterLimit
7490							&& checkSegmentStroke(segment)) {
7491						loc = segment.getLocation();
7492						break;
7493					}
7494				}
7495			}
7496		}
7497		return !loc && hitFill && this._contains(point)
7498				|| loc && !hitStroke && !hitCurves
7499					? new HitResult('fill', this)
7500					: loc
7501						? new HitResult(hitStroke ? 'stroke' : 'curve', this, {
7502							location: loc,
7503							point: loc.getPoint()
7504						})
7505						: null;
7506	}
7507
7508}, Base.each(Curve.evaluateMethods,
7509	function(name) {
7510		this[name + 'At'] = function(offset, isParameter) {
7511			var loc = this.getLocationAt(offset, isParameter);
7512			return loc && loc[name]();
7513		};
7514	},
7515{
7516	beans: false,
7517
7518	_getOffset: function(location) {
7519		var index = location && location.getIndex();
7520		if (index != null) {
7521			var curves = this.getCurves(),
7522				offset = 0;
7523			for (var i = 0; i < index; i++)
7524				offset += curves[i].getLength();
7525			var curve = curves[index],
7526				parameter = location.getParameter();
7527			if (parameter > 0)
7528				offset += curve.getPartLength(0, parameter);
7529			return offset;
7530		}
7531		return null;
7532	},
7533
7534	getLocationOf: function() {
7535		var point = Point.read(arguments),
7536			curves = this.getCurves();
7537		for (var i = 0, l = curves.length; i < l; i++) {
7538			var loc = curves[i].getLocationOf(point);
7539			if (loc)
7540				return loc;
7541		}
7542		return null;
7543	},
7544
7545	getOffsetOf: function() {
7546		var loc = this.getLocationOf.apply(this, arguments);
7547		return loc ? loc.getOffset() : null;
7548	},
7549
7550	getLocationAt: function(offset, isParameter) {
7551		var curves = this.getCurves(),
7552			length = 0;
7553		if (isParameter) {
7554			var index = ~~offset,
7555				curve = curves[index];
7556			return curve ? curve.getLocationAt(offset - index, true) : null;
7557		}
7558		for (var i = 0, l = curves.length; i < l; i++) {
7559			var start = length,
7560				curve = curves[i];
7561			length += curve.getLength();
7562			if (length > offset) {
7563				return curve.getLocationAt(offset - start);
7564			}
7565		}
7566		if (offset <= this.getLength())
7567			return new CurveLocation(curves[curves.length - 1], 1);
7568		return null;
7569	},
7570
7571	getNearestLocation: function() {
7572		var point = Point.read(arguments),
7573			curves = this.getCurves(),
7574			minDist = Infinity,
7575			minLoc = null;
7576		for (var i = 0, l = curves.length; i < l; i++) {
7577			var loc = curves[i].getNearestLocation(point);
7578			if (loc._distance < minDist) {
7579				minDist = loc._distance;
7580				minLoc = loc;
7581			}
7582		}
7583		return minLoc;
7584	},
7585
7586	getNearestPoint: function() {
7587		return this.getNearestLocation.apply(this, arguments).getPoint();
7588	}
7589}), new function() {
7590
7591	function drawHandles(ctx, segments, matrix, size) {
7592		var half = size / 2;
7593
7594		function drawHandle(index) {
7595			var hX = coords[index],
7596				hY = coords[index + 1];
7597			if (pX != hX || pY != hY) {
7598				ctx.beginPath();
7599				ctx.moveTo(pX, pY);
7600				ctx.lineTo(hX, hY);
7601				ctx.stroke();
7602				ctx.beginPath();
7603				ctx.arc(hX, hY, half, 0, Math.PI * 2, true);
7604				ctx.fill();
7605			}
7606		}
7607
7608		var coords = new Array(6);
7609		for (var i = 0, l = segments.length; i < l; i++) {
7610			var segment = segments[i];
7611			segment._transformCoordinates(matrix, coords, false);
7612			var state = segment._selectionState,
7613				pX = coords[0],
7614				pY = coords[1];
7615			if (state & 1)
7616				drawHandle(2);
7617			if (state & 2)
7618				drawHandle(4);
7619			ctx.fillRect(pX - half, pY - half, size, size);
7620			if (!(state & 4)) {
7621				var fillStyle = ctx.fillStyle;
7622				ctx.fillStyle = '#ffffff';
7623				ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2);
7624				ctx.fillStyle = fillStyle;
7625			}
7626		}
7627	}
7628
7629	function drawSegments(ctx, path, matrix) {
7630		var segments = path._segments,
7631			length = segments.length,
7632			coords = new Array(6),
7633			first = true,
7634			curX, curY,
7635			prevX, prevY,
7636			inX, inY,
7637			outX, outY;
7638
7639		function drawSegment(segment) {
7640			if (matrix) {
7641				segment._transformCoordinates(matrix, coords, false);
7642				curX = coords[0];
7643				curY = coords[1];
7644			} else {
7645				var point = segment._point;
7646				curX = point._x;
7647				curY = point._y;
7648			}
7649			if (first) {
7650				ctx.moveTo(curX, curY);
7651				first = false;
7652			} else {
7653				if (matrix) {
7654					inX = coords[2];
7655					inY = coords[3];
7656				} else {
7657					var handle = segment._handleIn;
7658					inX = curX + handle._x;
7659					inY = curY + handle._y;
7660				}
7661				if (inX === curX && inY === curY
7662						&& outX === prevX && outY === prevY) {
7663					ctx.lineTo(curX, curY);
7664				} else {
7665					ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
7666				}
7667			}
7668			prevX = curX;
7669			prevY = curY;
7670			if (matrix) {
7671				outX = coords[4];
7672				outY = coords[5];
7673			} else {
7674				var handle = segment._handleOut;
7675				outX = prevX + handle._x;
7676				outY = prevY + handle._y;
7677			}
7678		}
7679
7680		for (var i = 0; i < length; i++)
7681			drawSegment(segments[i]);
7682		if (path._closed && length > 0)
7683			drawSegment(segments[0]);
7684	}
7685
7686	return {
7687		_draw: function(ctx, param, strokeMatrix) {
7688			var dontStart = param.dontStart,
7689				dontPaint = param.dontFinish || param.clip,
7690				style = this.getStyle(),
7691				hasFill = style.hasFill(),
7692				hasStroke = style.hasStroke(),
7693				dashArray = style.getDashArray(),
7694				dashLength = !paper.support.nativeDash && hasStroke
7695						&& dashArray && dashArray.length;
7696
7697			if (!dontStart)
7698				ctx.beginPath();
7699
7700			if (!dontStart && this._currentPath) {
7701				ctx.currentPath = this._currentPath;
7702			} else if (hasFill || hasStroke && !dashLength || dontPaint) {
7703				drawSegments(ctx, this, strokeMatrix);
7704				if (this._closed)
7705					ctx.closePath();
7706				if (!dontStart)
7707					this._currentPath = ctx.currentPath;
7708			}
7709
7710			function getOffset(i) {
7711				return dashArray[((i % dashLength) + dashLength) % dashLength];
7712			}
7713
7714			if (!dontPaint && (hasFill || hasStroke)) {
7715				this._setStyles(ctx);
7716				if (hasFill) {
7717					ctx.fill(style.getWindingRule());
7718					ctx.shadowColor = 'rgba(0,0,0,0)';
7719				}
7720				if (hasStroke) {
7721					if (dashLength) {
7722						if (!dontStart)
7723							ctx.beginPath();
7724						var iterator = new PathIterator(this, 32, 0.25,
7725								strokeMatrix),
7726							length = iterator.length,
7727							from = -style.getDashOffset(), to,
7728							i = 0;
7729						from = from % length;
7730						while (from > 0) {
7731							from -= getOffset(i--) + getOffset(i--);
7732						}
7733						while (from < length) {
7734							to = from + getOffset(i++);
7735							if (from > 0 || to > 0)
7736								iterator.drawPart(ctx,
7737										Math.max(from, 0), Math.max(to, 0));
7738							from = to + getOffset(i++);
7739						}
7740					}
7741					ctx.stroke();
7742				}
7743			}
7744		},
7745
7746		_drawSelected: function(ctx, matrix) {
7747			ctx.beginPath();
7748			drawSegments(ctx, this, matrix);
7749			ctx.stroke();
7750			drawHandles(ctx, this._segments, matrix, paper.settings.handleSize);
7751		}
7752	};
7753}, new function() {
7754
7755	function getFirstControlPoints(rhs) {
7756		var n = rhs.length,
7757			x = [],
7758			tmp = [],
7759			b = 2;
7760		x[0] = rhs[0] / b;
7761		for (var i = 1; i < n; i++) {
7762			tmp[i] = 1 / b;
7763			b = (i < n - 1 ? 4 : 2) - tmp[i];
7764			x[i] = (rhs[i] - x[i - 1]) / b;
7765		}
7766		for (var i = 1; i < n; i++) {
7767			x[n - i - 1] -= tmp[n - i] * x[n - i];
7768		}
7769		return x;
7770	}
7771
7772	return {
7773		smooth: function() {
7774			var segments = this._segments,
7775				size = segments.length,
7776				closed = this._closed,
7777				n = size,
7778				overlap = 0;
7779			if (size <= 2)
7780				return;
7781			if (closed) {
7782				overlap = Math.min(size, 4);
7783				n += Math.min(size, overlap) * 2;
7784			}
7785			var knots = [];
7786			for (var i = 0; i < size; i++)
7787				knots[i + overlap] = segments[i]._point;
7788			if (closed) {
7789				for (var i = 0; i < overlap; i++) {
7790					knots[i] = segments[i + size - overlap]._point;
7791					knots[i + size + overlap] = segments[i]._point;
7792				}
7793			} else {
7794				n--;
7795			}
7796			var rhs = [];
7797
7798			for (var i = 1; i < n - 1; i++)
7799				rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
7800			rhs[0] = knots[0]._x + 2 * knots[1]._x;
7801			rhs[n - 1] = 3 * knots[n - 1]._x;
7802			var x = getFirstControlPoints(rhs);
7803
7804			for (var i = 1; i < n - 1; i++)
7805				rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
7806			rhs[0] = knots[0]._y + 2 * knots[1]._y;
7807			rhs[n - 1] = 3 * knots[n - 1]._y;
7808			var y = getFirstControlPoints(rhs);
7809
7810			if (closed) {
7811				for (var i = 0, j = size; i < overlap; i++, j++) {
7812					var f1 = i / overlap,
7813						f2 = 1 - f1,
7814						ie = i + overlap,
7815						je = j + overlap;
7816					x[j] = x[i] * f1 + x[j] * f2;
7817					y[j] = y[i] * f1 + y[j] * f2;
7818					x[je] = x[ie] * f2 + x[je] * f1;
7819					y[je] = y[ie] * f2 + y[je] * f1;
7820				}
7821				n--;
7822			}
7823			var handleIn = null;
7824			for (var i = overlap; i <= n - overlap; i++) {
7825				var segment = segments[i - overlap];
7826				if (handleIn)
7827					segment.setHandleIn(handleIn.subtract(segment._point));
7828				if (i < n) {
7829					segment.setHandleOut(
7830							new Point(x[i], y[i]).subtract(segment._point));
7831					handleIn = i < n - 1
7832							? new Point(
7833								2 * knots[i + 1]._x - x[i + 1],
7834								2 * knots[i + 1]._y - y[i + 1])
7835							: new Point(
7836								(knots[n]._x + x[n - 1]) / 2,
7837								(knots[n]._y + y[n - 1]) / 2);
7838				}
7839			}
7840			if (closed && handleIn) {
7841				var segment = this._segments[0];
7842				segment.setHandleIn(handleIn.subtract(segment._point));
7843			}
7844		}
7845	};
7846}, new function() {
7847	function getCurrentSegment(that) {
7848		var segments = that._segments;
7849		if (segments.length === 0)
7850			throw new Error('Use a moveTo() command first');
7851		return segments[segments.length - 1];
7852	}
7853
7854	return {
7855		moveTo: function() {
7856			var segments = this._segments;
7857			if (segments.length === 1)
7858				this.removeSegment(0);
7859			if (!segments.length)
7860				this._add([ new Segment(Point.read(arguments)) ]);
7861		},
7862
7863		moveBy: function() {
7864			throw new Error('moveBy() is unsupported on Path items.');
7865		},
7866
7867		lineTo: function() {
7868			this._add([ new Segment(Point.read(arguments)) ]);
7869		},
7870
7871		cubicCurveTo: function() {
7872			var handle1 = Point.read(arguments),
7873				handle2 = Point.read(arguments),
7874				to = Point.read(arguments),
7875				current = getCurrentSegment(this);
7876			current.setHandleOut(handle1.subtract(current._point));
7877			this._add([ new Segment(to, handle2.subtract(to)) ]);
7878		},
7879
7880		quadraticCurveTo: function() {
7881			var handle = Point.read(arguments),
7882				to = Point.read(arguments),
7883				current = getCurrentSegment(this)._point;
7884			this.cubicCurveTo(
7885				handle.add(current.subtract(handle).multiply(1 / 3)),
7886				handle.add(to.subtract(handle).multiply(1 / 3)),
7887				to
7888			);
7889		},
7890
7891		curveTo: function() {
7892			var through = Point.read(arguments),
7893				to = Point.read(arguments),
7894				t = Base.pick(Base.read(arguments), 0.5),
7895				t1 = 1 - t,
7896				current = getCurrentSegment(this)._point,
7897				handle = through.subtract(current.multiply(t1 * t1))
7898					.subtract(to.multiply(t * t)).divide(2 * t * t1);
7899			if (handle.isNaN())
7900				throw new Error(
7901					'Cannot put a curve through points with parameter = ' + t);
vendor: 4,933 bytes, lines 7902-8071
7902			this.quadraticCurveTo(handle, to);
7903		},
7904
7905		arcTo: function() {
7906			var current = getCurrentSegment(this),
7907				from = current._point,
7908				to = Point.read(arguments),
7909				through,
7910				peek = Base.peek(arguments),
7911				clockwise = Base.pick(peek, true),
7912				center, extent, vector, matrix;
7913			if (typeof clockwise === 'boolean') {
7914				var middle = from.add(to).divide(2),
7915				through = middle.add(middle.subtract(from).rotate(
7916						clockwise ? -90 : 90));
7917			} else if (Base.remain(arguments) <= 2) {
7918				through = to;
7919				to = Point.read(arguments);
7920			} else {
7921				var radius = Size.read(arguments);
7922				if (radius.isZero())
7923					return this.lineTo(to);
7924				var rotation = Base.read(arguments),
7925					clockwise = !!Base.read(arguments),
7926					large = !!Base.read(arguments),
7927					middle = from.add(to).divide(2),
7928					pt = from.subtract(middle).rotate(-rotation),
7929					x = pt.x,
7930					y = pt.y,
7931					abs = Math.abs,
7932					epsilon = 1e-12,
7933					rx = abs(radius.width),
7934					ry = abs(radius.height),
7935					rxSq = rx * rx,
7936					rySq = ry * ry,
7937					xSq =  x * x,
7938					ySq =  y * y;
7939				var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
7940				if (factor > 1) {
7941					rx *= factor;
7942					ry *= factor;
7943					rxSq = rx * rx;
7944					rySq = ry * ry;
7945				}
7946				factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
7947						(rxSq * ySq + rySq * xSq);
7948				if (abs(factor) < epsilon)
7949					factor = 0;
7950				if (factor < 0)
7951					throw new Error(
7952							'Cannot create an arc with the given arguments');
7953				center = new Point(rx * y / ry, -ry * x / rx)
7954						.multiply((large === clockwise ? -1 : 1)
7955							* Math.sqrt(factor))
7956						.rotate(rotation).add(middle);
7957				matrix = new Matrix().translate(center).rotate(rotation)
7958						.scale(rx, ry);
7959				vector = matrix._inverseTransform(from);
7960				extent = vector.getDirectedAngle(matrix._inverseTransform(to));
7961				if (!clockwise && extent > 0)
7962					extent -= 360;
7963				else if (clockwise && extent < 0)
7964					extent += 360;
7965			}
7966			if (through) {
7967				var l1 = new Line(from.add(through).divide(2),
7968							through.subtract(from).rotate(90), true),
7969					l2 = new Line(through.add(to).divide(2),
7970							to.subtract(through).rotate(90), true),
7971					line = new Line(from, to),
7972					throughSide = line.getSide(through);
7973				center = l1.intersect(l2, true);
7974				if (!center) {
7975					if (!throughSide)
7976						return this.lineTo(to);
7977					throw new Error(
7978							'Cannot create an arc with the given arguments');
7979				}
7980				vector = from.subtract(center);
7981				extent = vector.getDirectedAngle(to.subtract(center));
7982				var centerSide = line.getSide(center);
7983				if (centerSide === 0) {
7984					extent = throughSide * Math.abs(extent);
7985				} else if (throughSide === centerSide) {
7986					extent += extent < 0 ? 360 : -360;
7987				}
7988			}
7989			var ext = Math.abs(extent),
7990				count = ext >= 360 ? 4 : Math.ceil(ext / 90),
7991				inc = extent / count,
7992				half = inc * Math.PI / 360,
7993				z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
7994				segments = [];
7995			for (var i = 0; i <= count; i++) {
7996				var pt = to,
7997					out = null;
7998				if (i < count) {
7999					out = vector.rotate(90).multiply(z);
8000					if (matrix) {
8001						pt = matrix._transformPoint(vector);
8002						out = matrix._transformPoint(vector.add(out))
8003								.subtract(pt);
8004					} else {
8005						pt = center.add(vector);
8006					}
8007				}
8008				if (i === 0) {
8009					current.setHandleOut(out);
8010				} else {
8011					var _in = vector.rotate(-90).multiply(z);
8012					if (matrix) {
8013						_in = matrix._transformPoint(vector.add(_in))
8014								.subtract(pt);
8015					}
8016					segments.push(new Segment(pt, _in, out));
8017				}
8018				vector = vector.rotate(inc);
8019			}
8020			this._add(segments);
8021		},
8022
8023		lineBy: function() {
8024			var to = Point.read(arguments),
8025				current = getCurrentSegment(this)._point;
8026			this.lineTo(current.add(to));
8027		},
8028
8029		curveBy: function() {
8030			var through = Point.read(arguments),
8031				to = Point.read(arguments),
8032				parameter = Base.read(arguments),
8033				current = getCurrentSegment(this)._point;
8034			this.curveTo(current.add(through), current.add(to), parameter);
8035		},
8036
8037		cubicCurveBy: function() {
8038			var handle1 = Point.read(arguments),
8039				handle2 = Point.read(arguments),
8040				to = Point.read(arguments),
8041				current = getCurrentSegment(this)._point;
8042			this.cubicCurveTo(current.add(handle1), current.add(handle2),
8043					current.add(to));
8044		},
8045
8046		quadraticCurveBy: function() {
8047			var handle = Point.read(arguments),
8048				to = Point.read(arguments),
8049				current = getCurrentSegment(this)._point;
8050			this.quadraticCurveTo(current.add(handle), current.add(to));
8051		},
8052
8053		arcBy: function() {
8054			var current = getCurrentSegment(this)._point,
8055				point = current.add(Point.read(arguments)),
8056				clockwise = Base.pick(Base.peek(arguments), true);
8057			if (typeof clockwise === 'boolean') {
8058				this.arcTo(point, clockwise);
8059			} else {
8060				this.arcTo(point, current.add(Point.read(arguments)));
8061			}
8062		},
8063
8064		closePath: function(join) {
8065			this.setClosed(true);
8066			if (join)
8067				this.join();
8068		}
8069	};
8070}, {
8071
8072	_getBounds: function(getter, matrix) {
8073		return Path[getter](this._segments, this._closed, this.getStyle(),
8074				matrix);
8075	},
8076
8077statics: {
8078	isClockwise: function(segments) {
8079		var sum = 0;
8080		for (var i = 0, l = segments.length; i < l; i++)
8081			sum += Curve.getEdgeSum(Curve.getValues(
8082					segments[i], segments[i + 1 < l ? i + 1 : 0]));
8083		return sum > 0;
8084	},
8085
8086	getBounds: function(segments, closed, style, matrix, strokePadding) {
8087		var first = segments[0];
8088		if (!first)
8089			return new Rectangle();
8090		var coords = new Array(6),
8091			prevCoords = first._transformCoordinates(matrix, new Array(6), false),
8092			min = prevCoords.slice(0, 2),
8093			max = min.slice(),
8094			roots = new Array(2);
8095
8096		function processSegment(segment) {
8097			segment._transformCoordinates(matrix, coords, false);
8098			for (var i = 0; i < 2; i++) {
8099				Curve._addBounds(
8100					prevCoords[i],
8101					prevCoords[i + 4],
8102					coords[i + 2],
8103					coords[i],
8104					i, strokePadding ? strokePadding[i] : 0, min, max, roots);
8105			}
8106			var tmp = prevCoords;
8107			prevCoords = coords;
8108			coords = tmp;
8109		}
8110
8111		for (var i = 1, l = segments.length; i < l; i++)
8112			processSegment(segments[i]);
8113		if (closed)
8114			processSegment(first);
8115		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
8116	},
8117
8118	getStrokeBounds: function(segments, closed, style, matrix) {
8119		if (!style.hasStroke())
8120			return Path.getBounds(segments, closed, style, matrix);
8121		var length = segments.length - (closed ? 0 : 1),
8122			radius = style.getStrokeWidth() / 2,
8123			padding = Path._getPenPadding(radius, matrix),
8124			bounds = Path.getBounds(segments, closed, style, matrix, padding),
8125			join = style.getStrokeJoin(),
8126			cap = style.getStrokeCap(),
8127			miterLimit = radius * style.getMiterLimit();
8128		var joinBounds = new Rectangle(new Size(padding).multiply(2));
8129
8130		function add(point) {
8131			bounds = bounds.include(matrix
8132				? matrix._transformPoint(point, point) : point);
8133		}
8134
8135		function addRound(segment) {
8136			bounds = bounds.unite(joinBounds.setCenter(matrix
8137				? matrix._transformPoint(segment._point) : segment._point));
8138		}
8139
8140		function addJoin(segment, join) {
8141			var handleIn = segment._handleIn,
8142				handleOut = segment._handleOut;
8143			if (join === 'round' || !handleIn.isZero() && !handleOut.isZero()
8144					&& handleIn.isCollinear(handleOut)) {
8145				addRound(segment);
8146			} else {
8147				Path._addBevelJoin(segment, join, radius, miterLimit, add);
8148			}
8149		}
8150
8151		function addCap(segment, cap) {
8152			if (cap === 'round') {
8153				addRound(segment);
8154			} else {
8155				Path._addSquareCap(segment, cap, radius, add);
8156			}
8157		}
8158
8159		for (var i = 1; i < length; i++)
8160			addJoin(segments[i], join);
8161		if (closed) {
8162			addJoin(segments[0], join);
8163		} else if (length > 0) {
8164			addCap(segments[0], cap);
8165			addCap(segments[segments.length - 1], cap);
8166		}
8167		return bounds;
8168	},
8169
8170	_getPenPadding: function(radius, matrix) {
8171		if (!matrix)
8172			return [radius, radius];
8173		var mx = matrix.shiftless(),
8174			hor = mx.transform(new Point(radius, 0)),
8175			ver = mx.transform(new Point(0, radius)),
8176			phi = hor.getAngleInRadians(),
8177			a = hor.getLength(),
8178			b = ver.getLength();
8179		var sin = Math.sin(phi),
8180			cos = Math.cos(phi),
8181			tan = Math.tan(phi),
8182			tx = -Math.atan(b * tan / a),
8183			ty = Math.atan(b / (tan * a));
8184		return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
8185				Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
8186	},
8187
8188	_addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) {
8189		var curve2 = segment.getCurve(),
8190			curve1 = curve2.getPrevious(),
8191			point = curve2.getPointAt(0, true),
8192			normal1 = curve1.getNormalAt(1, true),
8193			normal2 = curve2.getNormalAt(0, true),
8194			step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
8195		normal1.setLength(step);
8196		normal2.setLength(step);
8197		if (area) {
8198			addPoint(point);
8199			addPoint(point.add(normal1));
8200		}
8201		if (join === 'miter') {
8202			var corner = new Line(
8203					point.add(normal1),
8204					new Point(-normal1.y, normal1.x), true
8205				).intersect(new Line(
8206					point.add(normal2),
8207					new Point(-normal2.y, normal2.x), true
8208				), true);
8209			if (corner && point.getDistance(corner) <= miterLimit) {
8210				addPoint(corner);
8211				if (!area)
8212					return;
8213			}
8214		}
8215		if (!area)
8216			addPoint(point.add(normal1));
8217		addPoint(point.add(normal2));
8218	},
8219
8220	_addSquareCap: function(segment, cap, radius, addPoint, area) {
8221		var point = segment._point,
8222			loc = segment.getLocation(),
8223			normal = loc.getNormal().multiply(radius);
8224		if (area) {
8225			addPoint(point.subtract(normal));
8226			addPoint(point.add(normal));
8227		}
8228		if (cap === 'square')
8229			point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90));
8230		addPoint(point.add(normal));
8231		addPoint(point.subtract(normal));
8232	},
8233
8234	getHandleBounds: function(segments, closed, style, matrix, strokePadding,
8235			joinPadding) {
8236		var coords = new Array(6),
8237			x1 = Infinity,
8238			x2 = -x1,
8239			y1 = x1,
8240			y2 = x2;
8241		for (var i = 0, l = segments.length; i < l; i++) {
8242			var segment = segments[i];
8243			segment._transformCoordinates(matrix, coords, false);
8244			for (var j = 0; j < 6; j += 2) {
8245				var padding = j === 0 ? joinPadding : strokePadding,
8246					paddingX = padding ? padding[0] : 0,
8247					paddingY = padding ? padding[1] : 0,
8248					x = coords[j],
8249					y = coords[j + 1],
8250					xn = x - paddingX,
8251					xx = x + paddingX,
8252					yn = y - paddingY,
8253					yx = y + paddingY;
8254				if (xn < x1) x1 = xn;
8255				if (xx > x2) x2 = xx;
vendor: 4,292 bytes, lines 8256-8397
8256				if (yn < y1) y1 = yn;
8257				if (yx > y2) y2 = yx;
8258			}
8259		}
8260		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
8261	},
8262
8263	getRoughBounds: function(segments, closed, style, matrix) {
8264		var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
8265			joinRadius = strokeRadius;
8266		if (strokeRadius > 0) {
8267			if (style.getStrokeJoin() === 'miter')
8268				joinRadius = strokeRadius * style.getMiterLimit();
8269			if (style.getStrokeCap() === 'square')
8270				joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
8271		}
8272		return Path.getHandleBounds(segments, closed, style, matrix,
8273				Path._getPenPadding(strokeRadius, matrix),
8274				Path._getPenPadding(joinRadius, matrix));
8275	}
8276}});
8277
8278Path.inject({ statics: new function() {
8279
8280	var kappa = 0.5522847498307936,
8281		ellipseSegments = [
8282			new Segment([-1, 0], [0, kappa ], [0, -kappa]),
8283			new Segment([0, -1], [-kappa, 0], [kappa, 0 ]),
8284			new Segment([1, 0], [0, -kappa], [0, kappa ]),
8285			new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
8286		];
8287
8288	function createPath(segments, closed, args) {
8289		var props = Base.getNamed(args),
8290			path = new Path(props && props.insert === false && Item.NO_INSERT);
8291		path._add(segments);
8292		path._closed = closed;
8293		return path.set(props);
8294	}
8295
8296	function createEllipse(center, radius, args) {
8297		var segments = new Array(4);
8298		for (var i = 0; i < 4; i++) {
8299			var segment = ellipseSegments[i];
8300			segments[i] = new Segment(
8301				segment._point.multiply(radius).add(center),
8302				segment._handleIn.multiply(radius),
8303				segment._handleOut.multiply(radius)
8304			);
8305		}
8306		return createPath(segments, true, args);
8307	}
8308
8309	return {
8310		Line: function() {
8311			return createPath([
8312				new Segment(Point.readNamed(arguments, 'from')),
8313				new Segment(Point.readNamed(arguments, 'to'))
8314			], false, arguments);
8315		},
8316
8317		Circle: function() {
8318			var center = Point.readNamed(arguments, 'center'),
8319				radius = Base.readNamed(arguments, 'radius');
8320			return createEllipse(center, new Size(radius), arguments);
8321		},
8322
8323		Rectangle: function() {
8324			var rect = Rectangle.readNamed(arguments, 'rectangle'),
8325				radius = Size.readNamed(arguments, 'radius', 0,
8326						{ readNull: true }),
8327				bl = rect.getBottomLeft(true),
8328				tl = rect.getTopLeft(true),
8329				tr = rect.getTopRight(true),
8330				br = rect.getBottomRight(true),
8331				segments;
8332			if (!radius || radius.isZero()) {
8333				segments = [
8334					new Segment(bl),
8335					new Segment(tl),
8336					new Segment(tr),
8337					new Segment(br)
8338				];
8339			} else {
8340				radius = Size.min(radius, rect.getSize(true).divide(2));
8341				var rx = radius.width,
8342					ry = radius.height,
8343					hx = rx * kappa,
8344					hy = ry * kappa;
8345				segments = [
8346					new Segment(bl.add(rx, 0), null, [-hx, 0]),
8347					new Segment(bl.subtract(0, ry), [0, hy]),
8348					new Segment(tl.add(0, ry), null, [0, -hy]),
8349					new Segment(tl.add(rx, 0), [-hx, 0], null),
8350					new Segment(tr.subtract(rx, 0), null, [hx, 0]),
8351					new Segment(tr.add(0, ry), [0, -hy], null),
8352					new Segment(br.subtract(0, ry), null, [0, hy]),
8353					new Segment(br.subtract(rx, 0), [hx, 0])
8354				];
8355			}
8356			return createPath(segments, true, arguments);
8357		},
8358
8359		RoundRectangle: '#Rectangle',
8360
8361		Ellipse: function() {
8362			var ellipse = Shape._readEllipse(arguments);
8363			return createEllipse(ellipse.center, ellipse.radius, arguments);
8364		},
8365
8366		Oval: '#Ellipse',
8367
8368		Arc: function() {
8369			var from = Point.readNamed(arguments, 'from'),
8370				through = Point.readNamed(arguments, 'through'),
8371				to = Point.readNamed(arguments, 'to'),
8372				props = Base.getNamed(arguments),
8373				path = new Path(props && props.insert === false
8374						&& Item.NO_INSERT);
8375			path.moveTo(from);
8376			path.arcTo(through, to);
8377			return path.set(props);
8378		},
8379
8380		RegularPolygon: function() {
8381			var center = Point.readNamed(arguments, 'center'),
8382				sides = Base.readNamed(arguments, 'sides'),
8383				radius = Base.readNamed(arguments, 'radius'),
8384				step = 360 / sides,
8385				three = !(sides % 3),
8386				vector = new Point(0, three ? -radius : radius),
8387				offset = three ? -1 : 0.5,
8388				segments = new Array(sides);
8389			for (var i = 0; i < sides; i++)
8390				segments[i] = new Segment(center.add(
8391					vector.rotate((i + offset) * step)));
8392			return createPath(segments, true, arguments);
8393		},
8394
8395		Star: function() {
8396			var center = Point.readNamed(arguments, 'center'),
8397				points = Base.readNamed(arguments, 'points') * 2,
8398				radius1 = Base.readNamed(arguments, 'radius1'),
8399				radius2 = Base.readNamed(arguments, 'radius2'),
8400				step = 360 / points,
8401				vector = new Point(0, -1),
8402				segments = new Array(points);
8403			for (var i = 0; i < points; i++)
8404				segments[i] = new Segment(center.add(vector.rotate(step * i)
8405						.multiply(i % 2 ? radius2 : radius1)));
8406			return createPath(segments, true, arguments);
8407		}
8408	};
8409}});
8410
8411var CompoundPath = PathItem.extend({
8412	_class: 'CompoundPath',
8413	_serializeFields: {
8414		children: []
8415	},
8416
8417	initialize: function CompoundPath(arg) {
8418		this._children = [];
8419		this._namedChildren = {};
8420		if (!this._initialize(arg)) {
8421			if (typeof arg === 'string') {
8422				this.setPathData(arg);
8423			} else {
8424				this.addChildren(Array.isArray(arg) ? arg : arguments);
8425			}
8426		}
8427	},
8428
8429	insertChildren: function insertChildren(index, items, _preserve) {
8430		items = insertChildren.base.call(this, index, items, _preserve, Path);
8431		for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
8432			var item = items[i];
8433			if (item._clockwise === undefined)
8434				item.setClockwise(item._index === 0);
8435		}
8436		return items;
8437	},
8438
8439	reverse: function() {
8440		var children = this._children;
8441		for (var i = 0, l = children.length; i < l; i++)
8442			children[i].reverse();
8443	},
8444
8445	smooth: function() {
8446		for (var i = 0, l = this._children.length; i < l; i++)
8447			this._children[i].smooth();
8448	},
8449
8450	reduce: function reduce() {
8451		if (this._children.length === 0) {
8452			var path = new Path(Item.NO_INSERT);
8453			path.insertAbove(this);
8454			path.setStyle(this._style);
8455			this.remove();
8456			return path;
8457		} else {
8458			return reduce.base.call(this);
8459		}
8460	},
8461
8462	isClockwise: function() {
8463		var child = this.getFirstChild();
8464		return child && child.isClockwise();
8465	},
8466
8467	setClockwise: function(clockwise) {
8468		if (this.isClockwise() !== !!clockwise)
8469			this.reverse();
8470	},
8471
8472	getFirstSegment: function() {
8473		var first = this.getFirstChild();
8474		return first && first.getFirstSegment();
8475	},
8476
8477	getLastSegment: function() {
8478		var last = this.getLastChild();
8479		return last && last.getLastSegment();
8480	},
8481
8482	getCurves: function() {
8483		var children = this._children,
8484			curves = [];
8485		for (var i = 0, l = children.length; i < l; i++)
8486			curves.push.apply(curves, children[i].getCurves());
8487		return curves;
8488	},
8489
8490	getFirstCurve: function() {
8491		var first = this.getFirstChild();
8492		return first && first.getFirstCurve();
8493	},
8494
8495	getLastCurve: function() {
8496		var last = this.getLastChild();
8497		return last && last.getFirstCurve();
8498	},
8499
8500	getArea: function() {
8501		var children = this._children,
8502			area = 0;
8503		for (var i = 0, l = children.length; i < l; i++)
8504			area += children[i].getArea();
8505		return area;
8506	}
8507}, {
8508	beans: true,
8509
8510	getPathData: function(_matrix, _precision) {
8511		var children = this._children,
8512			paths = [];
8513		for (var i = 0, l = children.length; i < l; i++) {
8514			var child = children[i],
8515				mx = child._matrix;
8516			paths.push(child.getPathData(_matrix && !mx.isIdentity()
8517					? _matrix.chain(mx) : mx, _precision));
8518		}
8519		return paths.join(' ');
8520	}
8521}, {
8522	_getChildHitTestOptions: function(options) {
8523		return options.class === Path || options.type === 'path'
8524				? options
8525				: new Base(options, { fill: false });
8526	},
8527
8528	_draw: function(ctx, param, strokeMatrix) {
8529		var children = this._children;
8530		if (children.length === 0)
8531			return;
8532
8533		if (this._currentPath) {
8534			ctx.currentPath = this._currentPath;
8535		} else {
8536			param = param.extend({ dontStart: true, dontFinish: true });
8537			ctx.beginPath();
8538			for (var i = 0, l = children.length; i < l; i++)
8539				children[i].draw(ctx, param, strokeMatrix);
8540			this._currentPath = ctx.currentPath;
8541		}
8542
8543		if (!param.clip) {
8544			this._setStyles(ctx);
8545			var style = this._style;
8546			if (style.hasFill()) {
8547				ctx.fill(style.getWindingRule());
8548				ctx.shadowColor = 'rgba(0,0,0,0)';
8549			}
8550			if (style.hasStroke())
8551				ctx.stroke();
8552		}
8553	},
8554
8555	_drawSelected: function(ctx, matrix, selectedItems) {
8556		var children = this._children;
8557		for (var i = 0, l = children.length; i < l; i++) {
8558			var child = children[i],
8559				mx = child._matrix;
8560			if (!selectedItems[child._id])
8561				child._drawSelected(ctx, mx.isIdentity() ? matrix
8562						: matrix.chain(mx));
8563		}
8564	}
8565}, new function() {
8566	function getCurrentPath(that, check) {
8567		var children = that._children;
8568		if (check && children.length === 0)
8569			throw new Error('Use a moveTo() command first');
8570		return children[children.length - 1];
8571	}
8572
8573	var fields = {
8574		moveTo: function() {
8575			var current = getCurrentPath(this),
8576				path = current && current.isEmpty() ? current : new Path();
8577			if (path !== current)
8578				this.addChild(path);
8579			path.moveTo.apply(path, arguments);
8580		},
8581
8582		moveBy: function() {
8583			var current = getCurrentPath(this, true),
8584				last = current && current.getLastSegment(),
8585				point = Point.read(arguments);
8586			this.moveTo(last ? point.add(last._point) : point);
8587		},
8588
8589		closePath: function(join) {
8590			getCurrentPath(this, true).closePath(join);
8591		}
8592	};
8593
8594	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
8595			'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
8596			function(key) {
8597				fields[key] = function() {
8598					var path = getCurrentPath(this, true);
8599					path[key].apply(path, arguments);
8600				};
8601			}
8602	);
8603
8604	return fields;
8605});
8606
8607PathItem.inject(new function() {
8608	var operators = {
8609		unite: function(w) {
8610			return w === 1 || w === 0;
8611		},
8612
8613		intersect: function(w) {
8614			return w === 2;
8615		},
8616
8617		subtract: function(w) {
8618			return w === 1;
8619		},
8620
8621		exclude: function(w) {
8622			return w === 1;
8623		}
8624	};
8625
8626	function computeBoolean(path1, path2, operation) {
8627		var operator = operators[operation];
8628		function preparePath(path) {
8629			return path.clone(false).reduce().reorient().transform(null, true,
8630					true);
8631		}
8632
8633		var _path1 = preparePath(path1),
8634			_path2 = path2 && path1 !== path2 && preparePath(path2);
8635		if (_path2 && /^(subtract|exclude)$/.test(operation)
8636				^ (_path2.isClockwise() !== _path1.isClockwise()))
8637			_path2.reverse();
8638		splitPath(_path1.getIntersections(_path2, null, true));
8639
8640		var chain = [],
8641			segments = [],
8642			monoCurves = [],
8643			tolerance = 0.000001;
8644
8645		function collect(paths) {
8646			for (var i = 0, l = paths.length; i < l; i++) {
8647				var path = paths[i];
8648				segments.push.apply(segments, path._segments);
8649				monoCurves.push.apply(monoCurves, path._getMonoCurves());
8650			}
8651		}
8652
8653		collect(_path1._children || [_path1]);
8654		if (_path2)
8655			collect(_path2._children || [_path2]);
8656		segments.sort(function(a, b) {
8657			var _a = a._intersection,
8658				_b = b._intersection;
8659			return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1;
8660		});
8661		for (var i = 0, l = segments.length; i < l; i++) {
8662			var segment = segments[i];
8663			if (segment._winding != null)
8664				continue;
8665			chain.length = 0;
8666			var startSeg = segment,
8667				totalLength = 0,
8668				windingSum = 0;
8669			do {
8670				var length = segment.getCurve().getLength();
8671				chain.push({ segment: segment, length: length });
8672				totalLength += length;
8673				segment = segment.getNext();
8674			} while (segment && !segment._intersection && segment !== startSeg);
8675			for (var j = 0; j < 3; j++) {
8676				var length = totalLength * (j + 1) / 4;
8677				for (var k = 0, m = chain.length; k < m; k++) {
8678					var node = chain[k],
8679						curveLength = node.length;
8680					if (length <= curveLength) {
8681						if (length < tolerance
8682								|| curveLength - length < tolerance)
8683							length = curveLength / 2;
8684						var curve = node.segment.getCurve(),
8685							pt = curve.getPointAt(length),
8686							hor = curve.isLinear() && Math.abs(curve
8687									.getTangentAt(0.5, true).y) < tolerance,
8688							path = curve._path;
8689						if (path._parent instanceof CompoundPath)
8690							path = path._parent;
8691						windingSum += operation === 'subtract' && _path2
8692							&& (path === _path1 && _path2._getWinding(pt, hor)
8693							|| path === _path2 && !_path1._getWinding(pt, hor))
8694							? 0
8695							: getWinding(pt, monoCurves, hor);
8696						break;
8697					}
8698					length -= curveLength;
8699				}
8700			}
8701			var winding = Math.round(windingSum / 3);
8702			for (var j = chain.length - 1; j >= 0; j--)
8703				chain[j].segment._winding = winding;
8704		}
8705		var result = new CompoundPath(Item.NO_INSERT);
8706		result.insertAbove(path1);
8707		result.addChildren(tracePaths(segments, operator), true);
8708		result = result.reduce();
8709		result.setStyle(path1._style);
8710		return result;
8711	}
8712
8713	function splitPath(intersections) {
8714		var tMin = 0.000001,
8715			tMax = 1 - tMin,
8716			linearHandles;
8717
8718		function resetLinear() {
8719			for (var i = 0, l = linearHandles.length; i < l; i++)
8720				linearHandles[i].set(0, 0);
8721		}
8722
8723		for (var i = intersections.length - 1, curve, prev; i >= 0; i--) {
8724			var loc = intersections[i],
8725				t = loc._parameter;
8726			if (prev && prev._curve === loc._curve && prev._parameter > 0) {
8727				t /= prev._parameter;
8728			} else {
8729				curve = loc._curve;
8730				if (linearHandles)
8731					resetLinear();
8732				linearHandles = curve.isLinear() ? [
8733						curve._segment1._handleOut,
8734						curve._segment2._handleIn
8735					] : null;
8736			}
8737			var newCurve,
8738				segment;
8739			if (newCurve = curve.divide(t, true, true)) {
8740				segment = newCurve._segment1;
8741				curve = newCurve.getPrevious();
8742				if (linearHandles)
8743					linearHandles.push(segment._handleOut, segment._handleIn);
8744			} else {
8745				segment = t < tMin
8746					? curve._segment1
8747					: t > tMax
8748						? curve._segment2
8749						: curve.getPartLength(0, t) < curve.getPartLength(t, 1)
8750							? curve._segment1
8751							: curve._segment2;
8752			}
8753			segment._intersection = loc.getIntersection();
8754			loc._segment = segment;
8755			prev = loc;
8756		}
8757		if (linearHandles)
8758			resetLinear();
8759	}
8760
8761	function getWinding(point, curves, horizontal, testContains) {
8762		var tolerance = 0.000001,
8763			tMin = tolerance,
8764			tMax = 1 - tMin,
8765			px = point.x,
8766			py = point.y,
8767			windLeft = 0,
8768			windRight = 0,
8769			roots = [],
8770			abs = Math.abs;
8771		if (horizontal) {
8772			var yTop = -Infinity,
8773				yBottom = Infinity,
8774				yBefore = py - tolerance,
8775				yAfter = py + tolerance;
8776			for (var i = 0, l = curves.length; i < l; i++) {
8777				var values = curves[i].values;
8778				if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) {
8779					for (var j = roots.length - 1; j >= 0; j--) {
8780						var y = Curve.getPoint(values, roots[j]).y;
8781						if (y < yBefore && y > yTop) {
8782							yTop = y;
8783						} else if (y > yAfter && y < yBottom) {
8784							yBottom = y;
8785						}
8786					}
8787				}
8788			}
8789			yTop = (yTop + py) / 2;
8790			yBottom = (yBottom + py) / 2;
8791			if (yTop > -Infinity)
8792				windLeft = getWinding(new Point(px, yTop), curves);
8793			if (yBottom < Infinity)
8794				windRight = getWinding(new Point(px, yBottom), curves);
8795		} else {
8796			var xBefore = px - tolerance,
8797				xAfter = px + tolerance;
8798			var startCounted = false,
8799				prevCurve,
8800				prevT;
8801			for (var i = 0, l = curves.length; i < l; i++) {
8802				var curve = curves[i],
8803					values = curve.values,
8804					winding = curve.winding;
8805				if (winding && (winding === 1
8806						&& py >= values[1] && py <= values[7]
8807						|| py >= values[7] && py <= values[1])
8808					&& Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) {
8809					var t = roots[0];
8810					if (!(
8811						t > tMax && startCounted && curve.next !== curves[i + 1]
8812						|| t < tMin && prevT > tMax
8813							&& curve.previous === prevCurve)) {
8814						var x = Curve.getPoint(values, t).x,
8815							slope = Curve.getTangent(values, t).y,
8816							counted = false;
8817						if (Numerical.isZero(slope) && !Curve.isLinear(values)
8818								|| t < tMin && slope * Curve.getTangent(
8819									curve.previous.values, 1).y < 0
8820								|| t > tMax && slope * Curve.getTangent(
8821									curve.next.values, 0).y < 0) {
8822							if (testContains && x >= xBefore && x <= xAfter) {
8823								++windLeft;
8824								++windRight;
8825								counted = true;
8826							}
8827						} else if (x <= xBefore) {
8828							windLeft += winding;
8829							counted = true;
8830						} else if (x >= xAfter) {
8831							windRight += winding;
8832							counted = true;
8833						}
8834						if (curve.previous !== curves[i - 1])
8835							startCounted = t < tMin && counted;
8836					}
8837					prevCurve = curve;
8838					prevT = t;
8839				}
8840			}
8841		}
8842		return Math.max(abs(windLeft), abs(windRight));
8843	}
8844
8845	function tracePaths(segments, operator, selfOp) {
8846		var paths = [],
8847			tMin = 0.000001,
8848			tMax = 1 - tMin;
8849		for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) {
8850			seg = startSeg = segments[i];
8851			if (seg._visited || !operator(seg._winding))
8852				continue;
8853			var path = new Path(Item.NO_INSERT),
8854				inter = seg._intersection,
8855				startInterSeg = inter && inter._segment,
8856				added = false,
8857				dir = 1;
8858			do {
8859				var handleIn = dir > 0 ? seg._handleIn : seg._handleOut,
8860					handleOut = dir > 0 ? seg._handleOut : seg._handleIn,
8861					interSeg;
8862				if (added && (!operator(seg._winding) || selfOp)
8863						&& (inter = seg._intersection)
8864						&& (interSeg = inter._segment)
8865						&& interSeg !== startSeg) {
8866					if (selfOp) {
8867						seg._visited = interSeg._visited;
8868						seg = interSeg;
8869						dir = 1;
8870					} else {
8871						var c1 = seg.getCurve();
8872						if (dir > 0)
8873							c1 = c1.getPrevious();
8874						var t1 = c1.getTangentAt(dir < 1 ? tMin : tMax, true),
8875							c4 = interSeg.getCurve(),
8876							c3 = c4.getPrevious(),
8877							t3 = c3.getTangentAt(tMax, true),
8878							t4 = c4.getTangentAt(tMin, true),
8879							w3 = t1.cross(t3),
8880							w4 = t1.cross(t4);
8881						if (w3 * w4 !== 0) {
8882							var curve = w3 < w4 ? c3 : c4,
8883								nextCurve = operator(curve._segment1._winding)
8884									? curve
8885									: w3 < w4 ? c4 : c3,
8886								nextSeg = nextCurve._segment1;
8887							dir = nextCurve === c3 ? -1 : 1;
8888							if (nextSeg._visited && seg._path !== nextSeg._path
8889										|| !operator(nextSeg._winding)) {
8890								dir = 1;
8891							} else {
8892								seg._visited = interSeg._visited;
8893								seg = interSeg;
8894								if (nextSeg._visited)
8895									dir = 1;
8896							}
8897						} else {
8898							dir = 1;
8899						}
8900					}
8901					handleOut = dir > 0 ? seg._handleOut : seg._handleIn;
8902				}
8903				path.add(new Segment(seg._point, added && handleIn, handleOut));
8904				added = true;
8905				seg._visited = true;
8906				seg = dir > 0 ? seg.getNext() : seg. getPrevious();
8907			} while (seg && !seg._visited
8908					&& seg !== startSeg && seg !== startInterSeg
8909					&& (seg._intersection || operator(seg._winding)));
8910			if (seg && (seg === startSeg || seg === startInterSeg)) {
8911				path.firstSegment.setHandleIn((seg === startInterSeg
8912						? startInterSeg : seg)._handleIn);
8913				path.setClosed(true);
8914			} else {
8915				path.lastSegment._handleOut.set(0, 0);
8916			}
8917			if (path._segments.length >
8918					(path._closed ? path.isLinear() ? 2 : 0 : 1))
8919				paths.push(path);
8920		}
8921		return paths;
8922	}
8923
8924	return {
8925		_getWinding: function(point, horizontal, testContains) {
8926			return getWinding(point, this._getMonoCurves(),
8927					horizontal, testContains);
8928		},
8929
8930		unite: function(path) {
8931			return computeBoolean(this, path, 'unite');
8932		},
8933
8934		intersect: function(path) {
8935			return computeBoolean(this, path, 'intersect');
8936		},
8937
8938		subtract: function(path) {
8939			return computeBoolean(this, path, 'subtract');
8940		},
8941
8942		exclude: function(path) {
8943			return computeBoolean(this, path, 'exclude');
8944		},
8945
8946		divide: function(path) {
8947			return new Group([this.subtract(path), this.intersect(path)]);
8948		}
8949	};
8950});
8951
8952Path.inject({
8953	_getMonoCurves: function() {
8954		var monoCurves = this._monoCurves,
8955			prevCurve;
8956
8957		function insertCurve(v) {
8958			var y0 = v[1],
8959				y1 = v[7],
8960				curve = {
8961					values: v,
8962					winding: y0 === y1
8963						? 0
8964						: y0 > y1
8965							? -1
8966							: 1,
8967					previous: prevCurve,
8968					next: null
8969				};
8970			if (prevCurve)
8971				prevCurve.next = curve;
8972			monoCurves.push(curve);
8973			prevCurve = curve;
8974		}
8975
8976		function handleCurve(v) {
8977			if (Curve.getLength(v) === 0)
8978				return;
8979			var y0 = v[1],
8980				y1 = v[3],
8981				y2 = v[5],
8982				y3 = v[7];
8983			if (Curve.isLinear(v)) {
8984				insertCurve(v);
8985			} else {
8986				var a = 3 * (y1 - y2) - y0 + y3,
8987					b = 2 * (y0 + y2) - 4 * y1,
8988					c = y1 - y0,
8989					tolerance = 0.000001,
8990					roots = [];
8991				var count = Numerical.solveQuadratic(a, b, c, roots, tolerance,
8992						1 - tolerance);
8993				if (count === 0) {
8994					insertCurve(v);
8995				} else {
8996					roots.sort();
8997					var t = roots[0],
8998						parts = Curve.subdivide(v, t);
8999					insertCurve(parts[0]);
9000					if (count > 1) {
9001						t = (roots[1] - t) / (1 - t);
9002						parts = Curve.subdivide(parts[1], t);
9003						insertCurve(parts[0]);
9004					}
9005					insertCurve(parts[1]);
9006				}
9007			}
9008		}
9009
9010		if (!monoCurves) {
9011			monoCurves = this._monoCurves = [];
9012			var curves = this.getCurves(),
9013				segments = this._segments;
9014			for (var i = 0, l = curves.length; i < l; i++)
9015				handleCurve(curves[i].getValues());
9016			if (!this._closed && segments.length > 1) {
9017				var p1 = segments[segments.length - 1]._point,
9018					p2 = segments[0]._point,
9019					p1x = p1._x, p1y = p1._y,
9020					p2x = p2._x, p2y = p2._y;
9021				handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
9022			}
9023			if (monoCurves.length > 0) {
9024				var first = monoCurves[0],
9025					last = monoCurves[monoCurves.length - 1];
9026				first.previous = last;
9027				last.next = first;
9028			}
9029		}
9030		return monoCurves;
9031	},
9032
9033	getInteriorPoint: function() {
9034		var bounds = this.getBounds(),
9035			point = bounds.getCenter(true);
9036		if (!this.contains(point)) {
9037			var curves = this._getMonoCurves(),
9038				roots = [],
9039				y = point.y,
9040				xIntercepts = [];
9041			for (var i = 0, l = curves.length; i < l; i++) {
9042				var values = curves[i].values;
9043				if ((curves[i].winding === 1
9044						&& y >= values[1] && y <= values[7]
9045						|| y >= values[7] && y <= values[1])
9046						&& Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
9047					for (var j = roots.length - 1; j >= 0; j--)
9048						xIntercepts.push(Curve.getPoint(values, roots[j]).x);
9049				}
9050				if (xIntercepts.length > 1)
9051					break;
9052			}
9053			point.x = (xIntercepts[0] + xIntercepts[1]) / 2;
9054		}
9055		return point;
9056	},
9057
9058	reorient: function() {
9059		this.setClockwise(true);
9060		return this;
9061	}
9062});
9063
9064CompoundPath.inject({
9065	_getMonoCurves: function() {
9066		var children = this._children,
9067			monoCurves = [];
9068		for (var i = 0, l = children.length; i < l; i++)
9069			monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
9070		return monoCurves;
9071	},
9072
9073	reorient: function() {
9074		var children = this.removeChildren().sort(function(a, b) {
9075			return b.getBounds().getArea() - a.getBounds().getArea();
9076		});
9077		if (children.length > 0) {
9078			this.addChildren(children);
9079			var clockwise = children[0].isClockwise();
9080			for (var i = 1, l = children.length; i < l; i++) {
9081				var point = children[i].getInteriorPoint(),
9082					counters = 0;
9083				for (var j = i - 1; j >= 0; j--) {
9084					if (children[j].contains(point))
9085						counters++;
9086				}
9087				children[i].setClockwise(counters % 2 === 0 && clockwise);
9088			}
9089		}
9090		return this;
9091	}
9092});
9093
9094var PathIterator = Base.extend({
9095	_class: 'PathIterator',
9096
9097	initialize: function(path, maxRecursion, tolerance, matrix) {
9098		var curves = [],
9099			parts = [],
9100			length = 0,
9101			minDifference = 1 / (maxRecursion || 32),
9102			segments = path._segments,
9103			segment1 = segments[0],
9104			segment2;
9105
9106		function addCurve(segment1, segment2) {
9107			var curve = Curve.getValues(segment1, segment2, matrix);
9108			curves.push(curve);
9109			computeParts(curve, segment1._index, 0, 1);
9110		}
9111
9112		function computeParts(curve, index, minT, maxT) {
9113			if ((maxT - minT) > minDifference
9114					&& !Curve.isFlatEnough(curve, tolerance || 0.25)) {
9115				var split = Curve.subdivide(curve),
9116					halfT = (minT + maxT) / 2;
9117				computeParts(split[0], index, minT, halfT);
9118				computeParts(split[1], index, halfT, maxT);
9119			} else {
9120				var x = curve[6] - curve[0],
9121					y = curve[7] - curve[1],
9122					dist = Math.sqrt(x * x + y * y);
9123				if (dist > 0.000001) {
9124					length += dist;
9125					parts.push({
9126						offset: length,
9127						value: maxT,
9128						index: index
9129					});
9130				}
9131			}
9132		}
9133
9134		for (var i = 1, l = segments.length; i < l; i++) {
9135			segment2 = segments[i];
9136			addCurve(segment1, segment2);
9137			segment1 = segment2;
9138		}
9139		if (path._closed)
9140			addCurve(segment2, segments[0]);
9141
9142		this.curves = curves;
9143		this.parts = parts;
9144		this.length = length;
9145		this.index = 0;
9146	},
9147
9148	getParameterAt: function(offset) {
9149		var i, j = this.index;
9150		for (;;) {
9151			i = j;
9152			if (j == 0 || this.parts[--j].offset < offset)
9153				break;
9154		}
9155		for (var l = this.parts.length; i < l; i++) {
9156			var part = this.parts[i];
9157			if (part.offset >= offset) {
9158				this.index = i;
9159				var prev = this.parts[i - 1];
9160				var prevVal = prev && prev.index == part.index ? prev.value : 0,
9161					prevLen = prev ? prev.offset : 0;
9162				return {
9163					value: prevVal + (part.value - prevVal)
9164						* (offset - prevLen) / (part.offset - prevLen),
9165					index: part.index
9166				};
9167			}
9168		}
9169		var part = this.parts[this.parts.length - 1];
9170		return {
9171			value: 1,
9172			index: part.index
9173		};
9174	},
9175
9176	drawPart: function(ctx, from, to) {
9177		from = this.getParameterAt(from);
9178		to = this.getParameterAt(to);
9179		for (var i = from.index; i <= to.index; i++) {
9180			var curve = Curve.getPart(this.curves[i],
9181					i == from.index ? from.value : 0,
9182					i == to.index ? to.value : 1);
9183			if (i == from.index)
9184				ctx.moveTo(curve[0], curve[1]);
9185			ctx.bezierCurveTo.apply(ctx, curve.slice(2));
9186		}
9187	}
9188}, Base.each(Curve.evaluateMethods,
9189	function(name) {
9190		this[name + 'At'] = function(offset, weighted) {
9191			var param = this.getParameterAt(offset);
9192			return Curve[name](this.curves[param.index], param.value, weighted);
9193		};
9194	}, {})
9195);
9196
9197var PathFitter = Base.extend({
9198	initialize: function(path, error) {
9199		var points = this.points = [],
9200			segments = path._segments,
9201			prev;
9202		for (var i = 0, l = segments.length; i < l; i++) {
9203			var point = segments[i].point.clone();
9204			if (!prev || !prev.equals(point)) {
9205				points.push(point);
9206				prev = point;
9207			}
9208		}
9209
9210		if (path._closed) {
9211			this.closed = true;
9212			points.unshift(points[points.length - 1]);
9213			points.push(points[1]);
9214		}
9215
9216		this.error = error;
9217	},
9218
9219	fit: function() {
9220		var points = this.points,
9221			length = points.length,
9222			segments = this.segments = length > 0
9223					? [new Segment(points[0])] : [];
9224		if (length > 1)
9225			this.fitCubic(0, length - 1,
9226				points[1].subtract(points[0]).normalize(),
9227				points[length - 2].subtract(points[length - 1]).normalize());
9228
9229		if (this.closed) {
9230			segments.shift();
9231			segments.pop();
9232		}
9233
9234		return segments;
9235	},
9236
9237	fitCubic: function(first, last, tan1, tan2) {
9238		if (last - first == 1) {
9239			var pt1 = this.points[first],
9240				pt2 = this.points[last],
9241				dist = pt1.getDistance(pt2) / 3;
9242			this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
9243					pt2.add(tan2.normalize(dist)), pt2]);
9244			return;
9245		}
9246		var uPrime = this.chordLengthParameterize(first, last),
9247			maxError = Math.max(this.error, this.error * this.error),
9248			split,
9249			parametersInOrder = true;
9250		for (var i = 0; i <= 4; i++) {
9251			var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
9252			var max = this.findMaxError(first, last, curve, uPrime);
9253			if (max.error < this.error && parametersInOrder) {
9254				this.addCurve(curve);
9255				return;
9256			}
9257			split = max.index;
9258			if (max.error >= maxError)
9259				break;
9260			parametersInOrder = this.reparameterize(first, last, uPrime, curve);
9261			maxError = max.error;
9262		}
9263		var V1 = this.points[split - 1].subtract(this.points[split]),
9264			V2 = this.points[split].subtract(this.points[split + 1]),
9265			tanCenter = V1.add(V2).divide(2).normalize();
9266		this.fitCubic(first, split, tan1, tanCenter);
9267		this.fitCubic(split, last, tanCenter.negate(), tan2);
9268	},
9269
9270	addCurve: function(curve) {
9271		var prev = this.segments[this.segments.length - 1];
9272		prev.setHandleOut(curve[1].subtract(curve[0]));
9273		this.segments.push(
9274				new Segment(curve[3], curve[2].subtract(curve[3])));
9275	},
9276
9277	generateBezier: function(first, last, uPrime, tan1, tan2) {
9278		var epsilon = 1e-12,
9279			pt1 = this.points[first],
9280			pt2 = this.points[last],
9281			C = [[0, 0], [0, 0]],
9282			X = [0, 0];
9283
9284		for (var i = 0, l = last - first + 1; i < l; i++) {
9285			var u = uPrime[i],
9286				t = 1 - u,
9287				b = 3 * u * t,
9288				b0 = t * t * t,
9289				b1 = b * t,
9290				b2 = b * u,
9291				b3 = u * u * u,
9292				a1 = tan1.normalize(b1),
9293				a2 = tan2.normalize(b2),
9294				tmp = this.points[first + i]
9295					.subtract(pt1.multiply(b0 + b1))
9296					.subtract(pt2.multiply(b2 + b3));
9297			C[0][0] += a1.dot(a1);
9298			C[0][1] += a1.dot(a2);
9299			C[1][0] = C[0][1];
9300			C[1][1] += a2.dot(a2);
9301			X[0] += a1.dot(tmp);
9302			X[1] += a2.dot(tmp);
9303		}
9304
9305		var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
9306			alpha1, alpha2;
9307		if (Math.abs(detC0C1) > epsilon) {
9308			var detC0X	= C[0][0] * X[1]	- C[1][0] * X[0],
9309				detXC1	= X[0]	  * C[1][1] - X[1]	  * C[0][1];
9310			alpha1 = detXC1 / detC0C1;
9311			alpha2 = detC0X / detC0C1;
9312		} else {
9313			var c0 = C[0][0] + C[0][1],
9314				c1 = C[1][0] + C[1][1];
9315			if (Math.abs(c0) > epsilon) {
9316				alpha1 = alpha2 = X[0] / c0;
9317			} else if (Math.abs(c1) > epsilon) {
9318				alpha1 = alpha2 = X[1] / c1;
9319			} else {
9320				alpha1 = alpha2 = 0;
9321			}
9322		}
9323
9324		var segLength = pt2.getDistance(pt1),
9325			eps = epsilon * segLength,
9326			handle1,
9327			handle2;
9328		if (alpha1 < eps || alpha2 < eps) {
9329			alpha1 = alpha2 = segLength / 3;
9330		} else {
9331			var line = pt2.subtract(pt1);
9332			handle1 = tan1.normalize(alpha1);
9333			handle2 = tan2.normalize(alpha2);
9334			if (handle1.dot(line) - handle2.dot(line) > segLength * segLength) {
9335				alpha1 = alpha2 = segLength / 3;
9336				handle1 = handle2 = null;
9337			}
9338		}
9339
9340		return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)),
9341				pt2.add(handle2 || tan2.normalize(alpha2)), pt2];
9342	},
9343
9344	reparameterize: function(first, last, u, curve) {
9345		for (var i = first; i <= last; i++) {
9346			u[i - first] = this.findRoot(curve, this.points[i], u[i - first]);
9347		}
9348		for (var i = 1, l = u.length; i < l; i++) {
9349			if (u[i] <= u[i - 1])
9350				return false;
vendor: 18,151 bytes, lines 9351-10073
9351		}
9352		return true;
9353	},
9354
9355	findRoot: function(curve, point, u) {
9356		var curve1 = [],
9357			curve2 = [];
9358		for (var i = 0; i <= 2; i++) {
9359			curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3);
9360		}
9361		for (var i = 0; i <= 1; i++) {
9362			curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2);
9363		}
9364		var pt = this.evaluate(3, curve, u),
9365			pt1 = this.evaluate(2, curve1, u),
9366			pt2 = this.evaluate(1, curve2, u),
9367			diff = pt.subtract(point),
9368			df = pt1.dot(pt1) + diff.dot(pt2);
9369		if (Math.abs(df) < 0.000001)
9370			return u;
9371		return u - diff.dot(pt1) / df;
9372	},
9373
9374	evaluate: function(degree, curve, t) {
9375		var tmp = curve.slice();
9376		for (var i = 1; i <= degree; i++) {
9377			for (var j = 0; j <= degree - i; j++) {
9378				tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t));
9379			}
9380		}
9381		return tmp[0];
9382	},
9383
9384	chordLengthParameterize: function(first, last) {
9385		var u = [0];
9386		for (var i = first + 1; i <= last; i++) {
9387			u[i - first] = u[i - first - 1]
9388					+ this.points[i].getDistance(this.points[i - 1]);
9389		}
9390		for (var i = 1, m = last - first; i <= m; i++) {
9391			u[i] /= u[m];
9392		}
9393		return u;
9394	},
9395
9396	findMaxError: function(first, last, curve, u) {
9397		var index = Math.floor((last - first + 1) / 2),
9398			maxDist = 0;
9399		for (var i = first + 1; i < last; i++) {
9400			var P = this.evaluate(3, curve, u[i - first]);
9401			var v = P.subtract(this.points[i]);
9402			var dist = v.x * v.x + v.y * v.y;
9403			if (dist >= maxDist) {
9404				maxDist = dist;
9405				index = i;
9406			}
9407		}
9408		return {
9409			error: maxDist,
9410			index: index
9411		};
9412	}
9413});
9414
9415var TextItem = Item.extend({
9416	_class: 'TextItem',
9417	_boundsSelected: true,
9418	_applyMatrix: false,
9419	_canApplyMatrix: false,
9420	_serializeFields: {
9421		content: null
9422	},
9423	_boundsGetter: 'getBounds',
9424
9425	initialize: function TextItem(arg) {
9426		this._content = '';
9427		this._lines = [];
9428		var hasProps = arg && Base.isPlainObject(arg)
9429				&& arg.x === undefined && arg.y === undefined;
9430		this._initialize(hasProps && arg, !hasProps && Point.read(arguments));
9431	},
9432
9433	_equals: function(item) {
9434		return this._content === item._content;
9435	},
9436
9437	_clone: function _clone(copy, insert, includeMatrix) {
9438		copy.setContent(this._content);
9439		return _clone.base.call(this, copy, insert, includeMatrix);
9440	},
9441
9442	getContent: function() {
9443		return this._content;
9444	},
9445
9446	setContent: function(content) {
9447		this._content = '' + content;
9448		this._lines = this._content.split(/\r\n|\n|\r/mg);
9449		this._changed(265);
9450	},
9451
9452	isEmpty: function() {
9453		return !this._content;
9454	},
9455
9456	getCharacterStyle: '#getStyle',
9457	setCharacterStyle: '#setStyle',
9458
9459	getParagraphStyle: '#getStyle',
9460	setParagraphStyle: '#setStyle'
9461});
9462
9463var PointText = TextItem.extend({
9464	_class: 'PointText',
9465
9466	initialize: function PointText() {
9467		TextItem.apply(this, arguments);
9468	},
9469
9470	clone: function(insert) {
9471		return this._clone(new PointText(Item.NO_INSERT), insert);
9472	},
9473
9474	getPoint: function() {
9475		var point = this._matrix.getTranslation();
9476		return new LinkedPoint(point.x, point.y, this, 'setPoint');
9477	},
9478
9479	setPoint: function() {
9480		var point = Point.read(arguments);
9481		this.translate(point.subtract(this._matrix.getTranslation()));
9482	},
9483
9484	_draw: function(ctx) {
9485		if (!this._content)
9486			return;
9487		this._setStyles(ctx);
9488		var style = this._style,
9489			lines = this._lines,
9490			leading = style.getLeading(),
9491			shadowColor = ctx.shadowColor;
9492		ctx.font = style.getFontStyle();
9493		ctx.textAlign = style.getJustification();
9494		for (var i = 0, l = lines.length; i < l; i++) {
9495			ctx.shadowColor = shadowColor;
9496			var line = lines[i];
9497			if (style.hasFill()) {
9498				ctx.fillText(line, 0, 0);
9499				ctx.shadowColor = 'rgba(0,0,0,0)';
9500			}
9501			if (style.hasStroke())
9502				ctx.strokeText(line, 0, 0);
9503			ctx.translate(0, leading);
9504		}
9505	},
9506
9507	_getBounds: function(getter, matrix) {
9508		var style = this._style,
9509			lines = this._lines,
9510			numLines = lines.length,
9511			justification = style.getJustification(),
9512			leading = style.getLeading(),
9513			width = this.getView().getTextWidth(style.getFontStyle(), lines),
9514			x = 0;
9515		if (justification !== 'left')
9516			x -= width / (justification === 'center' ? 2: 1);
9517		var bounds = new Rectangle(x,
9518					numLines ? - 0.75 * leading : 0,
9519					width, numLines * leading);
9520		return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
9521	}
9522});
9523
9524var Color = Base.extend(new function() {
9525	var types = {
9526		gray: ['gray'],
9527		rgb: ['red', 'green', 'blue'],
9528		hsb: ['hue', 'saturation', 'brightness'],
9529		hsl: ['hue', 'saturation', 'lightness'],
9530		gradient: ['gradient', 'origin', 'destination', 'highlight']
9531	};
9532
9533	var componentParsers = {},
9534		colorCache = {},
9535		colorCtx;
9536
9537	function fromCSS(string) {
9538		var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
9539			components;
9540		if (match) {
9541			components = [0, 0, 0];
9542			for (var i = 0; i < 3; i++) {
9543				var value = match[i + 1];
9544				components[i] = parseInt(value.length == 1
9545						? value + value : value, 16) / 255;
9546			}
9547		} else if (match = string.match(/^rgba?\((.*)\)$/)) {
9548			components = match[1].split(',');
9549			for (var i = 0, l = components.length; i < l; i++) {
9550				var value = +components[i];
9551				components[i] = i < 3 ? value / 255 : value;
9552			}
9553		} else {
9554			var cached = colorCache[string];
9555			if (!cached) {
9556				if (!colorCtx) {
9557					colorCtx = CanvasProvider.getContext(1, 1);
9558					colorCtx.globalCompositeOperation = 'copy';
9559				}
9560				colorCtx.fillStyle = 'rgba(0,0,0,0)';
9561				colorCtx.fillStyle = string;
9562				colorCtx.fillRect(0, 0, 1, 1);
9563				var data = colorCtx.getImageData(0, 0, 1, 1).data;
9564				cached = colorCache[string] = [
9565					data[0] / 255,
9566					data[1] / 255,
9567					data[2] / 255
9568				];
9569			}
9570			components = cached.slice();
9571		}
9572		return components;
9573	}
9574
9575	var hsbIndices = [
9576		[0, 3, 1],
9577		[2, 0, 1],
9578		[1, 0, 3],
9579		[1, 2, 0],
9580		[3, 1, 0],
9581		[0, 1, 2]
9582	];
9583
9584	var converters = {
9585		'rgb-hsb': function(r, g, b) {
9586			var max = Math.max(r, g, b),
9587				min = Math.min(r, g, b),
9588				delta = max - min,
9589				h = delta === 0 ? 0
9590					:	( max == r ? (g - b) / delta + (g < b ? 6 : 0)
9591						: max == g ? (b - r) / delta + 2
9592						:			 (r - g) / delta + 4) * 60;
9593			return [h, max === 0 ? 0 : delta / max, max];
9594		},
9595
9596		'hsb-rgb': function(h, s, b) {
9597			h = (((h / 60) % 6) + 6) % 6;
9598			var i = Math.floor(h),
9599				f = h - i,
9600				i = hsbIndices[i],
9601				v = [
9602					b,
9603					b * (1 - s),
9604					b * (1 - s * f),
9605					b * (1 - s * (1 - f))
9606				];
9607			return [v[i[0]], v[i[1]], v[i[2]]];
9608		},
9609
9610		'rgb-hsl': function(r, g, b) {
9611			var max = Math.max(r, g, b),
9612				min = Math.min(r, g, b),
9613				delta = max - min,
9614				achromatic = delta === 0,
9615				h = achromatic ? 0
9616					:	( max == r ? (g - b) / delta + (g < b ? 6 : 0)
9617						: max == g ? (b - r) / delta + 2
9618						:			 (r - g) / delta + 4) * 60,
9619				l = (max + min) / 2,
9620				s = achromatic ? 0 : l < 0.5
9621						? delta / (max + min)
9622						: delta / (2 - max - min);
9623			return [h, s, l];
9624		},
9625
9626		'hsl-rgb': function(h, s, l) {
9627			h = (((h / 360) % 1) + 1) % 1;
9628			if (s === 0)
9629				return [l, l, l];
9630			var t3s = [ h + 1 / 3, h, h - 1 / 3 ],
9631				t2 = l < 0.5 ? l * (1 + s) : l + s - l * s,
9632				t1 = 2 * l - t2,
9633				c = [];
9634			for (var i = 0; i < 3; i++) {
9635				var t3 = t3s[i];
9636				if (t3 < 0) t3 += 1;
9637				if (t3 > 1) t3 -= 1;
9638				c[i] = 6 * t3 < 1
9639					? t1 + (t2 - t1) * 6 * t3
9640					: 2 * t3 < 1
9641						? t2
9642						: 3 * t3 < 2
9643							? t1 + (t2 - t1) * ((2 / 3) - t3) * 6
9644							: t1;
9645			}
9646			return c;
9647		},
9648
9649		'rgb-gray': function(r, g, b) {
9650			return [r * 0.2989 + g * 0.587 + b * 0.114];
9651		},
9652
9653		'gray-rgb': function(g) {
9654			return [g, g, g];
9655		},
9656
9657		'gray-hsb': function(g) {
9658			return [0, 0, g];
9659		},
9660
9661		'gray-hsl': function(g) {
9662			return [0, 0, g];
9663		},
9664
9665		'gradient-rgb': function() {
9666			return [];
9667		},
9668
9669		'rgb-gradient': function() {
9670			return [];
9671		}
9672
9673	};
9674
9675	return Base.each(types, function(properties, type) {
9676		componentParsers[type] = [];
9677		Base.each(properties, function(name, index) {
9678			var part = Base.capitalize(name),
9679				hasOverlap = /^(hue|saturation)$/.test(name),
9680				parser = componentParsers[type][index] = name === 'gradient'
9681					? function(value) {
9682						var current = this._components[0];
9683						value = Gradient.read(Array.isArray(value) ? value
9684								: arguments, 0, { readNull: true });
9685						if (current !== value) {
9686							if (current)
9687								current._removeOwner(this);
9688							if (value)
9689								value._addOwner(this);
9690						}
9691						return value;
9692					}
9693					: type === 'gradient'
9694						? function() {
9695							return Point.read(arguments, 0, {
9696									readNull: name === 'highlight',
9697									clone: true
9698							});
9699						}
9700						: function(value) {
9701							return value == null || isNaN(value) ? 0 : value;
9702						};
9703
9704			this['get' + part] = function() {
9705				return this._type === type
9706					|| hasOverlap && /^hs[bl]$/.test(this._type)
9707						? this._components[index]
9708						: this._convert(type)[index];
9709			};
9710
9711			this['set' + part] = function(value) {
9712				if (this._type !== type
9713						&& !(hasOverlap && /^hs[bl]$/.test(this._type))) {
9714					this._components = this._convert(type);
9715					this._properties = types[type];
9716					this._type = type;
9717				}
9718				this._components[index] = parser.call(this, value);
9719				this._changed();
9720			};
9721		}, this);
9722	}, {
9723		_class: 'Color',
9724		_readIndex: true,
9725
9726		initialize: function Color(arg) {
9727			var slice = Array.prototype.slice,
9728				args = arguments,
9729				read = 0,
9730				type,
9731				components,
9732				alpha,
9733				values;
9734			if (Array.isArray(arg)) {
9735				args = arg;
9736				arg = args[0];
9737			}
9738			var argType = arg != null && typeof arg;
9739			if (argType === 'string' && arg in types) {
9740				type = arg;
9741				arg = args[1];
9742				if (Array.isArray(arg)) {
9743					components = arg;
9744					alpha = args[2];
9745				} else {
9746					if (this.__read)
9747						read = 1;
9748					args = slice.call(args, 1);
9749					argType = typeof arg;
9750				}
9751			}
9752			if (!components) {
9753				values = argType === 'number'
9754						? args
9755						: argType === 'object' && arg.length != null
9756							? arg
9757							: null;
9758				if (values) {
9759					if (!type)
9760						type = values.length >= 3
9761								? 'rgb'
9762								: 'gray';
9763					var length = types[type].length;
9764					alpha = values[length];
9765					if (this.__read)
9766						read += values === arguments
9767							? length + (alpha != null ? 1 : 0)
9768							: 1;
9769					if (values.length > length)
9770						values = slice.call(values, 0, length);
9771				} else if (argType === 'string') {
9772					type = 'rgb';
9773					components = fromCSS(arg);
9774					if (components.length === 4) {
9775						alpha = components[3];
9776						components.length--;
9777					}
9778				} else if (argType === 'object') {
9779					if (arg.constructor === Color) {
9780						type = arg._type;
9781						components = arg._components.slice();
9782						alpha = arg._alpha;
9783						if (type === 'gradient') {
9784							for (var i = 1, l = components.length; i < l; i++) {
9785								var point = components[i];
9786								if (point)
9787									components[i] = point.clone();
9788							}
9789						}
9790					} else if (arg.constructor === Gradient) {
9791						type = 'gradient';
9792						values = args;
9793					} else {
9794						type = 'hue' in arg
9795							? 'lightness' in arg
9796								? 'hsl'
9797								: 'hsb'
9798							: 'gradient' in arg || 'stops' in arg
9799									|| 'radial' in arg
9800								? 'gradient'
9801								: 'gray' in arg
9802									? 'gray'
9803									: 'rgb';
9804						var properties = types[type];
9805							parsers = componentParsers[type];
9806						this._components = components = [];
9807						for (var i = 0, l = properties.length; i < l; i++) {
9808							var value = arg[properties[i]];
9809							if (value == null && i === 0 && type === 'gradient'
9810									&& 'stops' in arg) {
9811								value = {
9812									stops: arg.stops,
9813									radial: arg.radial
9814								};
9815							}
9816							value = parsers[i].call(this, value);
9817							if (value != null)
9818								components[i] = value;
9819						}
9820						alpha = arg.alpha;
9821					}
9822				}
9823				if (this.__read && type)
9824					read = 1;
9825			}
9826			this._type = type || 'rgb';
9827			this._id = UID.get(Color);
9828			if (!components) {
9829				this._components = components = [];
9830				var parsers = componentParsers[this._type];
9831				for (var i = 0, l = parsers.length; i < l; i++) {
9832					var value = parsers[i].call(this, values && values[i]);
9833					if (value != null)
9834						components[i] = value;
9835				}
9836			}
9837			this._components = components;
9838			this._properties = types[this._type];
9839			this._alpha = alpha;
9840			if (this.__read)
9841				this.__read = read;
9842		},
9843
9844		_serialize: function(options, dictionary) {
9845			var components = this.getComponents();
9846			return Base.serialize(
9847					/^(gray|rgb)$/.test(this._type)
9848						? components
9849						: [this._type].concat(components),
9850					options, true, dictionary);
9851		},
9852
9853		_changed: function() {
9854			this._canvasStyle = null;
9855			if (this._owner)
9856				this._owner._changed(65);
9857		},
9858
9859		_convert: function(type) {
9860			var converter;
9861			return this._type === type
9862					? this._components.slice()
9863					: (converter = converters[this._type + '-' + type])
9864						? converter.apply(this, this._components)
9865						: converters['rgb-' + type].apply(this,
9866							converters[this._type + '-rgb'].apply(this,
9867								this._components));
9868		},
9869
9870		convert: function(type) {
9871			return new Color(type, this._convert(type), this._alpha);
9872		},
9873
9874		getType: function() {
9875			return this._type;
9876		},
9877
9878		setType: function(type) {
9879			this._components = this._convert(type);
9880			this._properties = types[type];
9881			this._type = type;
9882		},
9883
9884		getComponents: function() {
9885			var components = this._components.slice();
9886			if (this._alpha != null)
9887				components.push(this._alpha);
9888			return components;
9889		},
9890
9891		getAlpha: function() {
9892			return this._alpha != null ? this._alpha : 1;
9893		},
9894
9895		setAlpha: function(alpha) {
9896			this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1);
9897			this._changed();
9898		},
9899
9900		hasAlpha: function() {
9901			return this._alpha != null;
9902		},
9903
9904		equals: function(color) {
9905			var col = Base.isPlainValue(color, true)
9906					? Color.read(arguments)
9907					: color;
9908			return col === this || col && this._class === col._class
9909					&& this._type === col._type
9910					&& this._alpha === col._alpha
9911					&& Base.equals(this._components, col._components)
9912					|| false;
9913		},
9914
9915		toString: function() {
9916			var properties = this._properties,
9917				parts = [],
9918				isGradient = this._type === 'gradient',
9919				f = Formatter.instance;
9920			for (var i = 0, l = properties.length; i < l; i++) {
9921				var value = this._components[i];
9922				if (value != null)
9923					parts.push(properties[i] + ': '
9924							+ (isGradient ? value : f.number(value)));
9925			}
9926			if (this._alpha != null)
9927				parts.push('alpha: ' + f.number(this._alpha));
9928			return '{ ' + parts.join(', ') + ' }';
9929		},
9930
9931		toCSS: function(hex) {
9932			var components = this._convert('rgb'),
9933				alpha = hex || this._alpha == null ? 1 : this._alpha;
9934			function convert(val) {
9935				return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
9936			}
9937			components = [
9938				convert(components[0]),
9939				convert(components[1]),
9940				convert(components[2])
9941			];
9942			if (alpha < 1)
9943				components.push(alpha < 0 ? 0 : alpha);
9944			return hex
9945					? '#' + ((1 << 24) + (components[0] << 16)
9946						+ (components[1] << 8)
9947						+ components[2]).toString(16).slice(1)
9948					: (components.length == 4 ? 'rgba(' : 'rgb(')
9949						+ components.join(',') + ')';
9950		},
9951
9952		toCanvasStyle: function(ctx) {
9953			if (this._canvasStyle)
9954				return this._canvasStyle;
9955			if (this._type !== 'gradient')
9956				return this._canvasStyle = this.toCSS();
9957			var components = this._components,
9958				gradient = components[0],
9959				stops = gradient._stops,
9960				origin = components[1],
9961				destination = components[2],
9962				canvasGradient;
9963			if (gradient._radial) {
9964				var radius = destination.getDistance(origin),
9965					highlight = components[3];
9966				if (highlight) {
9967					var vector = highlight.subtract(origin);
9968					if (vector.getLength() > radius)
9969						highlight = origin.add(vector.normalize(radius - 0.1));
9970				}
9971				var start = highlight || origin;
9972				canvasGradient = ctx.createRadialGradient(start.x, start.y,
9973						0, origin.x, origin.y, radius);
9974			} else {
9975				canvasGradient = ctx.createLinearGradient(origin.x, origin.y,
9976						destination.x, destination.y);
9977			}
9978			for (var i = 0, l = stops.length; i < l; i++) {
9979				var stop = stops[i];
9980				canvasGradient.addColorStop(stop._rampPoint,
9981						stop._color.toCanvasStyle());
9982			}
9983			return this._canvasStyle = canvasGradient;
9984		},
9985
9986		transform: function(matrix) {
9987			if (this._type === 'gradient') {
9988				var components = this._components;
9989				for (var i = 1, l = components.length; i < l; i++) {
9990					var point = components[i];
9991					matrix._transformPoint(point, point, true);
9992				}
9993				this._changed();
9994			}
9995		},
9996
9997		statics: {
9998			_types: types,
9999
10000			random: function() {
10001				var random = Math.random;
10002				return new Color(random(), random(), random());
10003			}
10004		}
10005	});
10006}, new function() {
10007	var operators = {
10008		add: function(a, b) {
10009			return a + b;
10010		},
10011
10012		subtract: function(a, b) {
10013			return a - b;
10014		},
10015
10016		multiply: function(a, b) {
10017			return a * b;
10018		},
10019
10020		divide: function(a, b) {
10021			return a / b;
10022		}
10023	};
10024
10025	return Base.each(operators, function(operator, name) {
10026		this[name] = function(color) {
10027			color = Color.read(arguments);
10028			var type = this._type,
10029				components1 = this._components,
10030				components2 = color._convert(type);
10031			for (var i = 0, l = components1.length; i < l; i++)
10032				components2[i] = operator(components1[i], components2[i]);
10033			return new Color(type, components2,
10034					this._alpha != null
10035							? operator(this._alpha, color.getAlpha())
10036							: null);
10037		};
10038	}, {
10039	});
10040});
10041
10042Base.each(Color._types, function(properties, type) {
10043	var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) {
10044			var argType = arg != null && typeof arg,
10045				components = argType === 'object' && arg.length != null
10046					? arg
10047					: argType === 'string'
10048						? null
10049						: arguments;
10050			return components
10051					? new Color(type, components)
10052					: new Color(arg);
10053		};
10054	if (type.length == 3) {
10055		var acronym = type.toUpperCase();
10056		Color[acronym] = this[acronym + 'Color'] = ctor;
10057	}
10058}, Base.exports);
10059
10060var Gradient = Base.extend({
10061	_class: 'Gradient',
10062
10063	initialize: function Gradient(stops, radial) {
10064		this._id = UID.get();
10065		if (stops && this._set(stops))
10066			stops = radial = null;
10067		if (!this._stops)
10068			this.setStops(stops || ['white', 'black']);
10069		if (this._radial == null)
10070			this.setRadial(typeof radial === 'string' && radial === 'radial'
10071					|| radial || false);
10072	},
10073
10074	_serialize: function(options, dictionary) {
10075		return dictionary.add(this, function() {
10076			return Base.serialize([this._stops, this._radial],
10077					options, true, dictionary);
10078		});
10079	},
10080
10081	_changed: function() {
10082		for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
10083			this._owners[i]._changed();
10084	},
10085
10086	_addOwner: function(color) {
10087		if (!this._owners)
10088			this._owners = [];
10089		this._owners.push(color);
10090	},
10091
10092	_removeOwner: function(color) {
10093		var index = this._owners ? this._owners.indexOf(color) : -1;
10094		if (index != -1) {
10095			this._owners.splice(index, 1);
10096			if (this._owners.length === 0)
10097				this._owners = undefined;
10098		}
10099	},
10100
10101	clone: function() {
10102		var stops = [];
10103		for (var i = 0, l = this._stops.length; i < l; i++)
10104			stops[i] = this._stops[i].clone();
10105		return new Gradient(stops, this._radial);
10106	},
10107
10108	getStops: function() {
10109		return this._stops;
10110	},
10111
10112	setStops: function(stops) {
10113		if (this.stops) {
10114			for (var i = 0, l = this._stops.length; i < l; i++)
10115				this._stops[i]._owner = undefined;
10116		}
10117		if (stops.length < 2)
10118			throw new Error(
10119					'Gradient stop list needs to contain at least two stops.');
10120		this._stops = GradientStop.readAll(stops, 0, { clone: true });
10121		for (var i = 0, l = this._stops.length; i < l; i++) {
10122			var stop = this._stops[i];
10123			stop._owner = this;
10124			if (stop._defaultRamp)
10125				stop.setRampPoint(i / (l - 1));
10126		}
10127		this._changed();
10128	},
10129
10130	getRadial: function() {
10131		return this._radial;
10132	},
10133
10134	setRadial: function(radial) {
10135		this._radial = radial;
10136		this._changed();
10137	},
10138
10139	equals: function(gradient) {
10140		if (gradient === this)
10141			return true;
10142		if (gradient && this._class === gradient._class
10143				&& this._stops.length === gradient._stops.length) {
10144			for (var i = 0, l = this._stops.length; i < l; i++) {
10145				if (!this._stops[i].equals(gradient._stops[i]))
10146					return false;
10147			}
10148			return true;
10149		}
10150		return false;
10151	}
10152});
10153
10154var GradientStop = Base.extend({
10155	_class: 'GradientStop',
10156
10157	initialize: function GradientStop(arg0, arg1) {
10158		if (arg0) {
10159			var color, rampPoint;
10160			if (arg1 === undefined && Array.isArray(arg0)) {
10161				color = arg0[0];
10162				rampPoint = arg0[1];
10163			} else if (arg0.color) {
10164				color = arg0.color;
10165				rampPoint = arg0.rampPoint;
10166			} else {
10167				color = arg0;
10168				rampPoint = arg1;
10169			}
10170			this.setColor(color);
10171			this.setRampPoint(rampPoint);
10172		}
10173	},
10174
10175	clone: function() {
10176		return new GradientStop(this._color.clone(), this._rampPoint);
10177	},
10178
10179	_serialize: function(options, dictionary) {
10180		return Base.serialize([this._color, this._rampPoint], options, true,
10181				dictionary);
10182	},
10183
10184	_changed: function() {
10185		if (this._owner)
10186			this._owner._changed(65);
10187	},
10188
10189	getRampPoint: function() {
10190		return this._rampPoint;
10191	},
10192
10193	setRampPoint: function(rampPoint) {
10194		this._defaultRamp = rampPoint == null;
10195		this._rampPoint = rampPoint || 0;
10196		this._changed();
10197	},
10198
10199	getColor: function() {
10200		return this._color;
10201	},
10202
10203	setColor: function(color) {
10204		this._color = Color.read(arguments);
10205		if (this._color === color)
10206			this._color = color.clone();
10207		this._color._owner = this;
10208		this._changed();
10209	},
10210
10211	equals: function(stop) {
10212		return stop === this || stop && this._class === stop._class
10213				&& this._color.equals(stop._color)
10214				&& this._rampPoint == stop._rampPoint
10215				|| false;
10216	}
10217});
10218
10219var Style = Base.extend(new function() {
10220	var defaults = {
10221		fillColor: undefined,
10222		strokeColor: undefined,
10223		strokeWidth: 1,
10224		strokeCap: 'butt',
10225		strokeJoin: 'miter',
10226		strokeScaling: true,
10227		miterLimit: 10,
10228		dashOffset: 0,
10229		dashArray: [],
10230		windingRule: 'nonzero',
10231		shadowColor: undefined,
10232		shadowBlur: 0,
10233		shadowOffset: new Point(),
10234		selectedColor: undefined,
10235		fontFamily: 'sans-serif',
10236		fontWeight: 'normal',
10237		fontSize: 12,
10238		font: 'sans-serif',
10239		leading: null,
10240		justification: 'left'
10241	};
10242
10243	var flags = {
10244		strokeWidth: 97,
10245		strokeCap: 97,
10246		strokeJoin: 97,
10247		strokeScaling: 105,
10248		miterLimit: 97,
10249		fontFamily: 9,
10250		fontWeight: 9,
10251		fontSize: 9,
10252		font: 9,
10253		leading: 9,
10254		justification: 9
10255	};
10256
10257	var item = { beans: true },
10258		fields = {
10259			_defaults: defaults,
10260			_textDefaults: new Base(defaults, {
10261				fillColor: new Color()
10262			}),
10263			beans: true
10264		};
10265
10266	Base.each(defaults, function(value, key) {
10267		var isColor = /Color$/.test(key),
10268			isPoint = key === 'shadowOffset',
10269			part = Base.capitalize(key),
10270			flag = flags[key],
10271			set = 'set' + part,
10272			get = 'get' + part;
10273
10274		fields[set] = function(value) {
10275			var owner = this._owner,
10276				children = owner && owner._children;
10277			if (children && children.length > 0
10278					&& !(owner instanceof CompoundPath)) {
10279				for (var i = 0, l = children.length; i < l; i++)
10280					children[i]._style[set](value);
10281			} else {
10282				var old = this._values[key];
10283				if (old !== value) {
10284					if (isColor) {
10285						if (old)
10286							old._owner = undefined;
10287						if (value && value.constructor === Color) {
10288							if (value._owner)
10289								value = value.clone();
10290							value._owner = owner;
10291						}
10292					}
10293					this._values[key] = value;
10294					if (owner)
10295						owner._changed(flag || 65);
10296				}
10297			}
10298		};
10299
10300		fields[get] = function(_dontMerge) {
10301			var owner = this._owner,
10302				children = owner && owner._children,
10303				value;
10304			if (!children || children.length === 0 || _dontMerge
10305					|| owner instanceof CompoundPath) {
10306				var value = this._values[key];
10307				if (value === undefined) {
10308					value = this._defaults[key];
10309					if (value && value.clone)
10310						value = value.clone();
10311				} else {
10312					var ctor = isColor ? Color : isPoint ? Point : null;
10313					if (ctor && !(value && value.constructor === ctor)) {
10314						this._values[key] = value = ctor.read([value], 0,
10315								{ readNull: true, clone: true });
10316						if (value && isColor)
10317							value._owner = owner;
10318					}
10319				}
10320				return value;
10321			}
10322			for (var i = 0, l = children.length; i < l; i++) {
10323				var childValue = children[i]._style[get]();
10324				if (i === 0) {
10325					value = childValue;
10326				} else if (!Base.equals(value, childValue)) {
10327					return undefined;
10328				}
10329			}
10330			return value;
10331		};
10332
10333		item[get] = function(_dontMerge) {
10334			return this._style[get](_dontMerge);
10335		};
10336
10337		item[set] = function(value) {
10338			this._style[set](value);
10339		};
10340	});
10341
10342	Item.inject(item);
10343	return fields;
10344}, {
10345	_class: 'Style',
10346
10347	initialize: function Style(style, _owner, _project) {
10348		this._values = {};
10349		this._owner = _owner;
10350		this._project = _owner && _owner._project || _project || paper.project;
10351		if (_owner instanceof TextItem)
10352			this._defaults = this._textDefaults;
10353		if (style)
10354			this.set(style);
10355	},
10356
10357	set: function(style) {
10358		var isStyle = style instanceof Style,
10359			values = isStyle ? style._values : style;
10360		if (values) {
10361			for (var key in values) {
10362				if (key in this._defaults) {
10363					var value = values[key];
10364					this[key] = value && isStyle && value.clone
10365							? value.clone() : value;
10366				}
10367			}
10368		}
10369	},
10370
10371	equals: function(style) {
10372		return style === this || style && this._class === style._class
10373				&& Base.equals(this._values, style._values)
10374				|| false;
10375	},
10376
10377	hasFill: function() {
10378		return !!this.getFillColor();
10379	},
10380
10381	hasStroke: function() {
vendor: 4,483 bytes, lines 10382-10563
10382		return !!this.getStrokeColor() && this.getStrokeWidth() > 0;
10383	},
10384
10385	hasShadow: function() {
10386		return !!this.getShadowColor() && this.getShadowBlur() > 0;
10387	},
10388
10389	getView: function() {
10390		return this._project.getView();
10391	},
10392
10393	getFontStyle: function() {
10394		var fontSize = this.getFontSize();
10395		return this.getFontWeight()
10396				+ ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')
10397				+ this.getFontFamily();
10398	},
10399
10400	getFont: '#getFontFamily',
10401	setFont: '#setFontFamily',
10402
10403	getLeading: function getLeading() {
10404		var leading = getLeading.base.call(this),
10405			fontSize = this.getFontSize();
10406		if (/pt|em|%|px/.test(fontSize))
10407			fontSize = this.getView().getPixelSize(fontSize);
10408		return leading != null ? leading : fontSize * 1.2;
10409	}
10410
10411});
10412
10413var DomElement = new function() {
10414	function handlePrefix(el, name, set, value) {
10415		var prefixes = ['', 'webkit', 'moz', 'Moz', 'ms', 'o'],
10416			suffix = name[0].toUpperCase() + name.substring(1);
10417		for (var i = 0; i < 6; i++) {
10418			var prefix = prefixes[i],
10419				key = prefix ? prefix + suffix : name;
10420			if (key in el) {
10421				if (set) {
10422					el[key] = value;
10423				} else {
10424					return el[key];
10425				}
10426				break;
10427			}
10428		}
10429	}
10430
10431	return {
10432		getStyles: function(el) {
10433			var doc = el && el.nodeType !== 9 ? el.ownerDocument : el,
10434				view = doc && doc.defaultView;
10435			return view && view.getComputedStyle(el, '');
10436		},
10437
10438		getBounds: function(el, viewport) {
10439			var doc = el.ownerDocument,
10440				body = doc.body,
10441				html = doc.documentElement,
10442				rect;
10443			try {
10444				rect = el.getBoundingClientRect();
10445			} catch (e) {
10446				rect = { left: 0, top: 0, width: 0, height: 0 };
10447			}
10448			var x = rect.left - (html.clientLeft || body.clientLeft || 0),
10449				y = rect.top - (html.clientTop || body.clientTop || 0);
10450			if (!viewport) {
10451				var view = doc.defaultView;
10452				x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
10453				y += view.pageYOffset || html.scrollTop || body.scrollTop;
10454			}
10455			return new Rectangle(x, y, rect.width, rect.height);
10456		},
10457
10458		getViewportBounds: function(el) {
10459			var doc = el.ownerDocument,
10460				view = doc.defaultView,
10461				html = doc.documentElement;
10462			return new Rectangle(0, 0,
10463				view.innerWidth || html.clientWidth,
10464				view.innerHeight || html.clientHeight
10465			);
10466		},
10467
10468		getOffset: function(el, viewport) {
10469			return DomElement.getBounds(el, viewport).getPoint();
10470		},
10471
10472		getSize: function(el) {
10473			return DomElement.getBounds(el, true).getSize();
10474		},
10475
10476		isInvisible: function(el) {
10477			return DomElement.getSize(el).equals(new Size(0, 0));
10478		},
10479
10480		isInView: function(el) {
10481			return !DomElement.isInvisible(el)
10482					&& DomElement.getViewportBounds(el).intersects(
10483						DomElement.getBounds(el, true));
10484		},
10485
10486		getPrefixed: function(el, name) {
10487			return handlePrefix(el, name);
10488		},
10489
10490		setPrefixed: function(el, name, value) {
10491			if (typeof name === 'object') {
10492				for (var key in name)
10493					handlePrefix(el, key, true, name[key]);
10494			} else {
10495				handlePrefix(el, name, true, value);
10496			}
10497		}
10498	};
10499};
10500
10501var DomEvent = {
10502	add: function(el, events) {
10503		for (var type in events) {
10504			var func = events[type],
10505				parts = type.split(/[\s,]+/g);
10506			for (var i = 0, l = parts.length; i < l; i++)
10507				el.addEventListener(parts[i], func, false);
10508		}
10509	},
10510
10511	remove: function(el, events) {
10512		for (var type in events) {
10513			var func = events[type],
10514				parts = type.split(/[\s,]+/g);
10515			for (var i = 0, l = parts.length; i < l; i++)
10516				el.removeEventListener(parts[i], func, false);
10517		}
10518	},
10519
10520	getPoint: function(event) {
10521		var pos = event.targetTouches
10522				? event.targetTouches.length
10523					? event.targetTouches[0]
10524					: event.changedTouches[0]
10525				: event;
10526		return new Point(
10527			pos.pageX || pos.clientX + document.documentElement.scrollLeft,
10528			pos.pageY || pos.clientY + document.documentElement.scrollTop
10529		);
10530	},
10531
10532	getTarget: function(event) {
10533		return event.target || event.srcElement;
10534	},
10535
10536	getRelatedTarget: function(event) {
10537		return event.relatedTarget || event.toElement;
10538	},
10539
10540	getOffset: function(event, target) {
10541		return DomEvent.getPoint(event).subtract(DomElement.getOffset(
10542				target || DomEvent.getTarget(event)));
10543	},
10544
10545	stop: function(event) {
10546		event.stopPropagation();
10547		event.preventDefault();
10548	}
10549};
10550
10551DomEvent.requestAnimationFrame = new function() {
10552	var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
10553		requested = false,
10554		callbacks = [],
10555		focused = true,
10556		timer;
10557
10558	DomEvent.add(window, {
10559		focus: function() {
10560			focused = true;
10561		},
10562		blur: function() {
10563			focused = false;
10564		}
10565	});
10566
10567	function handleCallbacks() {
10568		for (var i = callbacks.length - 1; i >= 0; i--) {
10569			var entry = callbacks[i],
10570				func = entry[0],
10571				el = entry[1];
10572			if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
10573					|| focused) && DomElement.isInView(el)) {
10574				callbacks.splice(i, 1);
10575				func();
10576			}
10577		}
10578		if (nativeRequest) {
10579			if (callbacks.length) {
10580				nativeRequest(handleCallbacks);
10581			} else {
10582				requested = false;
10583			}
10584		}
10585	}
10586
10587	return function(callback, element) {
10588		callbacks.push([callback, element]);
10589		if (nativeRequest) {
10590			if (!requested) {
10591				nativeRequest(handleCallbacks);
10592				requested = true;
10593			}
10594		} else if (!timer) {
10595			timer = setInterval(handleCallbacks, 1000 / 60);
10596		}
10597	};
10598};
10599
10600var View = Base.extend(Emitter, {
10601	_class: 'View',
10602
10603	initialize: function View(project, element) {
10604		this._project = project;
10605		this._scope = project._scope;
10606		this._element = element;
10607		var size;
10608		if (!this._pixelRatio)
10609			this._pixelRatio = window.devicePixelRatio || 1;
10610		this._id = element.getAttribute('id');
10611		if (this._id == null)
10612			element.setAttribute('id', this._id = 'view-' + View._id++);
10613		DomEvent.add(element, this._viewEvents);
10614		var none = 'none';
10615		DomElement.setPrefixed(element.style, {
10616			userSelect: none,
10617			touchAction: none,
10618			touchCallout: none,
10619			contentZooming: none,
10620			userDrag: none,
10621			tapHighlightColor: 'rgba(0,0,0,0)'
10622		});
10623
10624		function getSize(name) {
10625			return element[name] || parseInt(element.getAttribute(name), 10);
10626		};
10627
10628		function getCanvasSize() {
10629			var size = DomElement.getSize(element);
10630			return size.isNaN() || size.isZero()
10631					? new Size(getSize('width'), getSize('height'))
10632					: size;
10633		};
10634
10635		if (PaperScope.hasAttribute(element, 'resize')) {
10636			var that = this;
10637			DomEvent.add(window, this._windowEvents = {
10638				resize: function() {
10639					that.setViewSize(getCanvasSize());
10640				}
10641			});
10642		}
10643		this._setViewSize(size = getCanvasSize());
10644		if (PaperScope.hasAttribute(element, 'stats')
10645				&& typeof Stats !== 'undefined') {
10646			this._stats = new Stats();
10647			var stats = this._stats.domElement,
10648				style = stats.style,
10649				offset = DomElement.getOffset(element);
10650			style.position = 'absolute';
10651			style.left = offset.x + 'px';
10652			style.top = offset.y + 'px';
10653			document.body.appendChild(stats);
10654		}
10655		View._views.push(this);
10656		View._viewsById[this._id] = this;
10657		this._viewSize = size;
10658		(this._matrix = new Matrix())._owner = this;
10659		this._zoom = 1;
10660		if (!View._focused)
10661			View._focused = this;
10662		this._frameItems = {};
10663		this._frameItemCount = 0;
10664	},
10665
10666	remove: function() {
10667		if (!this._project)
10668			return false;
10669		if (View._focused === this)
10670			View._focused = null;
10671		View._views.splice(View._views.indexOf(this), 1);
10672		delete View._viewsById[this._id];
10673		if (this._project._view === this)
10674			this._project._view = null;
10675		DomEvent.remove(this._element, this._viewEvents);
10676		DomEvent.remove(window, this._windowEvents);
10677		this._element = this._project = null;
10678		this.off('frame');
10679		this._animate = false;
10680		this._frameItems = {};
10681		return true;
10682	},
10683
10684	_events: {
10685		onFrame: {
10686			install: function() {
10687				this.play();
10688			},
10689
10690			uninstall: function() {
10691				this.pause();
10692			}
10693		},
10694
10695		onResize: {}
10696	},
10697
10698	_animate: false,
10699	_time: 0,
10700	_count: 0,
10701
10702	_requestFrame: function() {
10703		var that = this;
10704		DomEvent.requestAnimationFrame(function() {
10705			that._requested = false;
10706			if (!that._animate)
10707				return;
10708			that._requestFrame();
10709			that._handleFrame();
10710		}, this._element);
10711		this._requested = true;
10712	},
10713
10714	_handleFrame: function() {
10715		paper = this._scope;
10716		var now = Date.now() / 1000,
10717			delta = this._before ? now - this._before : 0;
10718		this._before = now;
10719		this._handlingFrame = true;
10720		this.emit('frame', new Base({
10721			delta: delta,
10722			time: this._time += delta,
10723			count: this._count++
10724		}));
10725		if (this._stats)
10726			this._stats.update();
10727		this._handlingFrame = false;
10728		this.update();
10729	},
10730
10731	_animateItem: function(item, animate) {
10732		var items = this._frameItems;
10733		if (animate) {
10734			items[item._id] = {
10735				item: item,
10736				time: 0,
10737				count: 0
10738			};
10739			if (++this._frameItemCount === 1)
10740				this.on('frame', this._handleFrameItems);
10741		} else {
10742			delete items[item._id];
10743			if (--this._frameItemCount === 0) {
10744				this.off('frame', this._handleFrameItems);
10745			}
10746		}
10747	},
10748
10749	_handleFrameItems: function(event) {
10750		for (var i in this._frameItems) {
10751			var entry = this._frameItems[i];
10752			entry.item.emit('frame', new Base(event, {
10753				time: entry.time += event.delta,
10754				count: entry.count++
10755			}));
10756		}
10757	},
10758
10759	_update: function() {
10760		this._project._needsUpdate = true;
10761		if (this._handlingFrame)
10762			return;
10763		if (this._animate) {
10764			this._handleFrame();
10765		} else {
10766			this.update();
10767		}
10768	},
10769
10770	_changed: function(flags) {
10771		if (flags & 1)
10772			this._project._needsUpdate = true;
10773	},
10774
10775	_transform: function(matrix) {
10776		this._matrix.concatenate(matrix);
10777		this._bounds = null;
10778		this._update();
10779	},
10780
10781	getElement: function() {
10782		return this._element;
10783	},
10784
10785	getPixelRatio: function() {
10786		return this._pixelRatio;
10787	},
10788
10789	getResolution: function() {
10790		return this._pixelRatio * 72;
10791	},
10792
10793	getViewSize: function() {
10794		var size = this._viewSize;
10795		return new LinkedSize(size.width, size.height, this, 'setViewSize');
10796	},
10797
10798	setViewSize: function() {
10799		var size = Size.read(arguments),
10800			delta = size.subtract(this._viewSize);
10801		if (delta.isZero())
10802			return;
10803		this._viewSize.set(size.width, size.height);
10804		this._setViewSize(size);
10805		this._bounds = null;
10806		this.emit('resize', {
10807			size: size,
10808			delta: delta
10809		});
10810		this._update();
10811	},
10812
10813	_setViewSize: function(size) {
10814		var element = this._element;
10815		element.width = size.width;
10816		element.height = size.height;
10817	},
10818
10819	getBounds: function() {
10820		if (!this._bounds)
10821			this._bounds = this._matrix.inverted()._transformBounds(
10822					new Rectangle(new Point(), this._viewSize));
10823		return this._bounds;
10824	},
10825
10826	getSize: function() {
10827		return this.getBounds().getSize();
10828	},
10829
10830	getCenter: function() {
10831		return this.getBounds().getCenter();
10832	},
10833
10834	setCenter: function() {
10835		var center = Point.read(arguments);
10836		this.scrollBy(center.subtract(this.getCenter()));
10837	},
10838
10839	getZoom: function() {
10840		return this._zoom;
10841	},
10842
10843	setZoom: function(zoom) {
10844		this._transform(new Matrix().scale(zoom / this._zoom,
10845			this.getCenter()));
10846		this._zoom = zoom;
10847	},
10848
10849	isVisible: function() {
10850		return DomElement.isInView(this._element);
10851	},
10852
10853	scrollBy: function() {
10854		this._transform(new Matrix().translate(Point.read(arguments).negate()));
10855	},
10856
10857	play: function() {
10858		this._animate = true;
10859		if (!this._requested)
10860			this._requestFrame();
10861	},
10862
10863	pause: function() {
10864		this._animate = false;
10865	},
10866
10867	draw: function() {
10868		this.update();
10869	},
10870
10871	projectToView: function() {
10872		return this._matrix._transformPoint(Point.read(arguments));
10873	},
10874
10875	viewToProject: function() {
10876		return this._matrix._inverseTransform(Point.read(arguments));
10877	}
10878
10879}, {
10880	statics: {
10881		_views: [],
10882		_viewsById: {},
10883		_id: 0,
10884
10885		create: function(project, element) {
10886			if (typeof element === 'string')
10887				element = document.getElementById(element);
10888			return new CanvasView(project, element);
10889		}
10890	}
10891}, new function() {
10892	var tool,
10893		prevFocus,
10894		tempFocus,
10895		dragging = false;
10896
10897	function getView(event) {
10898		var target = DomEvent.getTarget(event);
10899		return target.getAttribute && View._viewsById[target.getAttribute('id')];
10900	}
10901
10902	function viewToProject(view, event) {
10903		return view.viewToProject(DomEvent.getOffset(event, view._element));
10904	}
10905
10906	function updateFocus() {
10907		if (!View._focused || !View._focused.isVisible()) {
10908			for (var i = 0, l = View._views.length; i < l; i++) {
10909				var view = View._views[i];
10910				if (view && view.isVisible()) {
10911					View._focused = tempFocus = view;
10912					break;
10913				}
10914			}
10915		}
10916	}
10917
10918	function handleMouseMove(view, point, event) {
10919		view._handleEvent('mousemove', point, event);
10920		var tool = view._scope.tool;
10921		if (tool) {
10922			tool._handleEvent(dragging && tool.responds('mousedrag')
10923					? 'mousedrag' : 'mousemove', point, event);
10924		}
10925		view.update();
10926		return tool;
10927	}
10928
10929	var navigator = window.navigator,
10930		mousedown, mousemove, mouseup;
10931	if (navigator.pointerEnabled || navigator.msPointerEnabled) {
10932		mousedown = 'pointerdown MSPointerDown';
10933		mousemove = 'pointermove MSPointerMove';
10934		mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
10935	} else {
10936		mousedown = 'touchstart';
10937		mousemove = 'touchmove';
10938		mouseup = 'touchend touchcancel';
10939		if (!('ontouchstart' in window && navigator.userAgent.match(
10940				/mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
10941			mousedown += ' mousedown';
10942			mousemove += ' mousemove';
10943			mouseup += ' mouseup';
10944		}
10945	}
10946
10947	var viewEvents = {
10948		'selectstart dragstart': function(event) {
10949			if (dragging)
10950				event.preventDefault();
10951		}
10952	};
10953
10954	var docEvents = {
10955		mouseout: function(event) {
10956			var view = View._focused,
10957				target = DomEvent.getRelatedTarget(event);
10958			if (view && (!target || target.nodeName === 'HTML'))
10959				handleMouseMove(view, viewToProject(view, event), event);
10960		},
10961
10962		scroll: updateFocus
10963	};
10964
10965	viewEvents[mousedown] = function(event) {
10966		var view = View._focused = getView(event),
10967			point = viewToProject(view, event);
10968		dragging = true;
10969		view._handleEvent('mousedown', point, event);
10970		if (tool = view._scope.tool)
10971			tool._handleEvent('mousedown', point, event);
10972		view.update();
10973	};
10974
10975	docEvents[mousemove] = function(event) {
10976		var view = View._focused;
10977		if (!dragging) {
10978			var target = getView(event);
10979			if (target) {
10980				if (view !== target)
10981					handleMouseMove(view, viewToProject(view, event), event);
10982				prevFocus = view;
10983				view = View._focused = tempFocus = target;
10984			} else if (tempFocus && tempFocus === view) {
10985				view = View._focused = prevFocus;
10986				updateFocus();
10987			}
10988		}
10989		if (view) {
10990			var point = viewToProject(view, event);
10991			if (dragging || view.getBounds().contains(point))
10992				tool = handleMouseMove(view, point, event);
10993		}
10994	};
10995
10996	docEvents[mouseup] = function(event) {
10997		var view = View._focused;
10998		if (!view || !dragging)
10999			return;
11000		var point = viewToProject(view, event);
11001		dragging = false;
11002		view._handleEvent('mouseup', point, event);
11003		if (tool)
11004			tool._handleEvent('mouseup', point, event);
11005		view.update();
11006	};
11007
11008	DomEvent.add(document, docEvents);
11009
11010	DomEvent.add(window, {
11011		load: updateFocus
11012	});
11013
11014	return {
11015		_viewEvents: viewEvents,
11016
11017		_handleEvent: function() {},
11018
11019		statics: {
11020			updateFocus: updateFocus
11021		}
11022	};
11023});
11024
11025var CanvasView = View.extend({
11026	_class: 'CanvasView',
11027
11028	initialize: function CanvasView(project, canvas) {
11029		if (!(canvas instanceof HTMLCanvasElement)) {
11030			var size = Size.read(arguments, 1);
11031			if (size.isZero())
11032				throw new Error(
11033						'Cannot create CanvasView with the provided argument: '
11034						+ [].slice.call(arguments, 1));
11035			canvas = CanvasProvider.getCanvas(size);
11036		}
11037		this._context = canvas.getContext('2d');
11038		this._eventCounters = {};
11039		this._pixelRatio = 1;
11040		if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) {
11041			var deviceRatio = window.devicePixelRatio || 1,
11042				backingStoreRatio = DomElement.getPrefixed(this._context,
11043						'backingStorePixelRatio') || 1;
11044			this._pixelRatio = deviceRatio / backingStoreRatio;
11045		}
11046		View.call(this, project, canvas);
11047	},
11048
11049	_setViewSize: function(size) {
11050		var element = this._element,
11051			pixelRatio = this._pixelRatio,
11052			width = size.width,
11053			height = size.height;
11054		element.width = width * pixelRatio;
11055		element.height = height * pixelRatio;
11056		if (pixelRatio !== 1) {
11057			if (!PaperScope.hasAttribute(element, 'resize')) {
11058				var style = element.style;
11059				style.width = width + 'px';
11060				style.height = height + 'px';
11061			}
11062			this._context.scale(pixelRatio, pixelRatio);
11063		}
11064	},
11065
11066	getPixelSize: function(size) {
11067		var browser = paper.browser,
11068			pixels;
11069		if (browser && browser.firefox) {
11070			var parent = this._element.parentNode,
11071				temp = document.createElement('div');
11072			temp.style.fontSize = size;
11073			parent.appendChild(temp);
11074			pixels = parseFloat(DomElement.getStyles(temp).fontSize);
11075			parent.removeChild(temp);
11076		} else {
11077			var ctx = this._context,
11078				prevFont = ctx.font;
11079			ctx.font = size + ' serif';
11080			pixels = parseFloat(ctx.font);
11081			ctx.font = prevFont;
11082		}
11083		return pixels;
11084	},
11085
11086	getTextWidth: function(font, lines) {
11087		var ctx = this._context,
11088			prevFont = ctx.font,
11089			width = 0;
11090		ctx.font = font;
11091		for (var i = 0, l = lines.length; i < l; i++)
11092			width = Math.max(width, ctx.measureText(lines[i]).width);
11093		ctx.font = prevFont;
11094		return width;
11095	},
11096
11097	update: function(force) {
11098		var project = this._project;
11099		if (!project || !force && !project._needsUpdate)
11100			return false;
11101		var ctx = this._context,
11102			size = this._viewSize;
11103		ctx.clearRect(0, 0, size.width + 1, size.height + 1);
11104		project.draw(ctx, this._matrix, this._pixelRatio);
11105		project._needsUpdate = false;
11106		return true;
11107	}
11108}, new function() {
11109
11110	var downPoint,
11111		lastPoint,
11112		overPoint,
11113		downItem,
11114		lastItem,
11115		overItem,
11116		dragItem,
11117		dblClick,
11118		clickTime;
11119
11120	function callEvent(view, type, event, point, target, lastPoint) {
11121		var item = target,
11122			mouseEvent;
11123
11124		function call(obj) {
11125			if (obj.responds(type)) {
11126				if (!mouseEvent) {
11127					mouseEvent = new MouseEvent(type, event, point, target,
11128							lastPoint ? point.subtract(lastPoint) : null);
11129				}
11130				if (obj.emit(type, mouseEvent) && mouseEvent.isStopped) {
11131					event.preventDefault();
11132					return true;
11133				}
11134			}
11135		}
11136
11137		while (item) {
11138			if (call(item))
11139				return true;
11140			item = item.getParent();
11141		}
11142		if (call(view))
11143			return true;
11144		return false;
11145	}
11146
11147	return {
11148		_handleEvent: function(type, point, event) {
11149			if (!this._eventCounters[type])
11150				return;
11151			var project = this._project,
11152				hit = project.hitTest(point, {
11153					tolerance: 0,
11154					fill: true,
11155					stroke: true
11156				}),
11157				item = hit && hit.item,
11158				stopped = false;
11159			switch (type) {
11160			case 'mousedown':
11161				stopped = callEvent(this, type, event, point, item);
11162				dblClick = lastItem == item && (Date.now() - clickTime < 300);
11163				downItem = lastItem = item;
11164				downPoint = lastPoint = overPoint = point;
11165				dragItem = !stopped && item;
11166				while (dragItem && !dragItem.responds('mousedrag'))
11167					dragItem = dragItem._parent;
11168				break;
11169			case 'mouseup':
11170				stopped = callEvent(this, type, event, point, item, downPoint);
11171				if (dragItem) {
11172					if (lastPoint && !lastPoint.equals(point))
11173						callEvent(this, 'mousedrag', event, point, dragItem,
11174								lastPoint);
11175					if (item !== dragItem) {
11176						overPoint = point;
11177						callEvent(this, 'mousemove', event, point, item,
11178								overPoint);
11179					}
11180				}
11181				if (!stopped && item && item === downItem) {
11182					clickTime = Date.now();
11183					callEvent(this, dblClick && downItem.responds('doubleclick')
11184							? 'doubleclick' : 'click', event, downPoint, item);
11185					dblClick = false;
11186				}
11187				downItem = dragItem = null;
11188				break;
11189			case 'mousemove':
11190				if (dragItem)
11191					stopped = callEvent(this, 'mousedrag', event, point,
11192							dragItem, lastPoint);
11193				if (!stopped) {
11194					if (item !== overItem)
11195						overPoint = point;
11196					stopped = callEvent(this, type, event, point, item,
11197							overPoint);
11198				}
11199				lastPoint = overPoint = point;
11200				if (item !== overItem) {
11201					callEvent(this, 'mouseleave', event, point, overItem);
11202					overItem = item;
11203					callEvent(this, 'mouseenter', event, point, item);
11204				}
11205				break;
11206			}
11207			return stopped;
11208		}
11209	};
11210});
11211
11212var Event = Base.extend({
11213	_class: 'Event',
11214
11215	initialize: function Event(event) {
11216		this.event = event;
11217	},
11218
11219	isPrevented: false,
11220	isStopped: false,
11221
11222	preventDefault: function() {
11223		this.isPrevented = true;
11224		this.event.preventDefault();
11225	},
11226
11227	stopPropagation: function() {
11228		this.isStopped = true;
11229		this.event.stopPropagation();
11230	},
11231
11232	stop: function() {
11233		this.stopPropagation();
11234		this.preventDefault();
11235	},
11236
11237	getModifiers: function() {
11238		return Key.modifiers;
11239	}
11240});
11241
11242var KeyEvent = Event.extend({
11243	_class: 'KeyEvent',
11244
11245	initialize: function KeyEvent(down, key, character, event) {
11246		Event.call(this, event);
11247		this.type = down ? 'keydown' : 'keyup';
11248		this.key = key;
11249		this.character = character;
11250	},
11251
11252	toString: function() {
11253		return "{ type: '" + this.type
11254				+ "', key: '" + this.key
11255				+ "', character: '" + this.character
11256				+ "', modifiers: " + this.getModifiers()
11257				+ " }";
11258	}
11259});
11260
11261var Key = new function() {
11262
11263	var specialKeys = {
11264		8: 'backspace',
11265		9: 'tab',
11266		13: 'enter',
11267		16: 'shift',
11268		17: 'control',
11269		18: 'option',
11270		19: 'pause',
11271		20: 'caps-lock',
11272		27: 'escape',
11273		32: 'space',
11274		35: 'end',
11275		36: 'home',
11276		37: 'left',
11277		38: 'up',
11278		39: 'right',
11279		40: 'down',
11280		46: 'delete',
11281		91: 'command',
11282		93: 'command',
11283		224: 'command'
11284	},
11285
11286	specialChars = {
11287		9: true,
11288		13: true,
11289		32: true
11290	},
11291
11292	modifiers = new Base({
11293		shift: false,
11294		control: false,
11295		option: false,
11296		command: false,
11297		capsLock: false,
11298		space: false
11299	}),
11300
11301	charCodeMap = {},
11302	keyMap = {},
11303	commandFixMap,
11304	downCode;
11305
11306	function handleKey(down, keyCode, charCode, event) {
11307		var character = charCode ? String.fromCharCode(charCode) : '',
11308			specialKey = specialKeys[keyCode],
11309			key = specialKey || character.toLowerCase(),
11310			type = down ? 'keydown' : 'keyup',
11311			view = View._focused,
11312			scope = view && view.isVisible() && view._scope,
11313			tool = scope && scope.tool,
11314			name;
11315		keyMap[key] = down;
11316		if (down) {
11317			charCodeMap[keyCode] = charCode;
11318		} else {
11319			delete charCodeMap[keyCode];
11320		}
11321		if (specialKey && (name = Base.camelize(specialKey)) in modifiers) {
11322			modifiers[name] = down;
11323			var browser = paper.browser;
11324			if (name === 'command' && browser && browser.mac) {
11325				if (down) {
11326					commandFixMap = {};
11327				} else {
11328					for (var code in commandFixMap) {
11329						if (code in charCodeMap)
11330							handleKey(false, code, commandFixMap[code], event);
11331					}
11332					commandFixMap = null;
11333				}
11334			}
11335		} else if (down && commandFixMap) {
11336			commandFixMap[keyCode] = charCode;
11337		}
11338		if (tool && tool.responds(type)) {
11339			paper = scope;
11340			tool.emit(type, new KeyEvent(down, key, character, event));
11341			if (view)
11342				view.update();
11343		}
11344	}
11345
11346	DomEvent.add(document, {
11347		keydown: function(event) {
11348			var code = event.which || event.keyCode;
11349			if (code in specialKeys || modifiers.command) {
11350				handleKey(true, code,
11351						code in specialChars || modifiers.command ? code : 0,
11352						event);
11353			} else {
11354				downCode = code;
11355			}
11356		},
11357
11358		keypress: function(event) {
11359			if (downCode != null) {
11360				handleKey(true, downCode, event.which || event.keyCode, event);
11361				downCode = null;
11362			}
11363		},
11364
11365		keyup: function(event) {
11366			var code = event.which || event.keyCode;
11367			if (code in charCodeMap)
11368				handleKey(false, code, charCodeMap[code], event);
11369		}
11370	});
11371
11372	DomEvent.add(window, {
11373		blur: function(event) {
11374			for (var code in charCodeMap)
11375				handleKey(false, code, charCodeMap[code], event);
11376		}
11377	});
11378
11379	return {
11380		modifiers: modifiers,
11381
11382		isDown: function(key) {
11383			return !!keyMap[key];
11384		}
11385	};
11386};
11387
11388var MouseEvent = Event.extend({
11389	_class: 'MouseEvent',
11390
11391	initialize: function MouseEvent(type, event, point, target, delta) {
11392		Event.call(this, event);
11393		this.type = type;
11394		this.point = point;
11395		this.target = target;
11396		this.delta = delta;
11397	},
11398
11399	toString: function() {
11400		return "{ type: '" + this.type
11401				+ "', point: " + this.point
11402				+ ', target: ' + this.target
11403				+ (this.delta ? ', delta: ' + this.delta : '')
11404				+ ', modifiers: ' + this.getModifiers()
11405				+ ' }';
11406	}
11407});
11408
11409var ToolEvent = Event.extend({
11410	_class: 'ToolEvent',
11411	_item: null,
11412
11413	initialize: function ToolEvent(tool, type, event) {
11414		this.tool = tool;
11415		this.type = type;
11416		this.event = event;
11417	},
11418
11419	_choosePoint: function(point, toolPoint) {
11420		return point ? point : toolPoint ? toolPoint.clone() : null;
11421	},
11422
11423	getPoint: function() {
11424		return this._choosePoint(this._point, this.tool._point);
11425	},
11426
11427	setPoint: function(point) {
11428		this._point = point;
11429	},
11430
11431	getLastPoint: function() {
11432		return this._choosePoint(this._lastPoint, this.tool._lastPoint);
11433	},
11434
11435	setLastPoint: function(lastPoint) {
11436		this._lastPoint = lastPoint;
11437	},
11438
11439	getDownPoint: function() {
11440		return this._choosePoint(this._downPoint, this.tool._downPoint);
11441	},
11442
11443	setDownPoint: function(downPoint) {
11444		this._downPoint = downPoint;
11445	},
11446
11447	getMiddlePoint: function() {
11448		if (!this._middlePoint && this.tool._lastPoint) {
11449			return this.tool._point.add(this.tool._lastPoint).divide(2);
11450		}
11451		return this._middlePoint;
11452	},
11453
11454	setMiddlePoint: function(middlePoint) {
11455		this._middlePoint = middlePoint;
11456	},
11457
11458	getDelta: function() {
11459		return !this._delta && this.tool._lastPoint
11460				? this.tool._point.subtract(this.tool._lastPoint)
11461				: this._delta;
11462	},
11463
11464	setDelta: function(delta) {
11465		this._delta = delta;
11466	},
11467
11468	getCount: function() {
11469		return /^mouse(down|up)$/.test(this.type)
11470				? this.tool._downCount
11471				: this.tool._count;
11472	},
11473
11474	setCount: function(count) {
11475		this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
11476			= count;
11477	},
11478
11479	getItem: function() {
11480		if (!this._item) {
11481			var result = this.tool._scope.project.hitTest(this.getPoint());
11482			if (result) {
11483				var item = result.item,
11484					parent = item._parent;
11485				while (/^(Group|CompoundPath)$/.test(parent._class)) {
11486					item = parent;
11487					parent = parent._parent;
11488				}
11489				this._item = item;
11490			}
11491		}
11492		return this._item;
11493	},
11494
11495	setItem: function(item) {
11496		this._item = item;
11497	},
11498
11499	toString: function() {
11500		return '{ type: ' + this.type
11501				+ ', point: ' + this.getPoint()
11502				+ ', count: ' + this.getCount()
11503				+ ', modifiers: ' + this.getModifiers()
11504				+ ' }';
11505	}
11506});
11507
11508var Tool = PaperScopeItem.extend({
11509	_class: 'Tool',
11510	_list: 'tools',
11511	_reference: 'tool',
11512	_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
11513			'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
11514			'onKeyDown', 'onKeyUp' ],
11515
11516	initialize: function Tool(props) {
11517		PaperScopeItem.call(this);
11518		this._firstMove = true;
11519		this._count = 0;
11520		this._downCount = 0;
11521		this._set(props);
11522	},
11523
11524	getMinDistance: function() {
11525		return this._minDistance;
11526	},
11527
11528	setMinDistance: function(minDistance) {
11529		this._minDistance = minDistance;
11530		if (minDistance != null && this._maxDistance != null
11531				&& minDistance > this._maxDistance) {
11532			this._maxDistance = minDistance;
11533		}
11534	},
11535
11536	getMaxDistance: function() {
11537		return this._maxDistance;
11538	},
11539
11540	setMaxDistance: function(maxDistance) {
11541		this._maxDistance = maxDistance;
11542		if (this._minDistance != null && maxDistance != null
11543				&& maxDistance < this._minDistance) {
11544			this._minDistance = maxDistance;
11545		}
11546	},
11547
11548	getFixedDistance: function() {
11549		return this._minDistance == this._maxDistance
11550			? this._minDistance : null;
11551	},
11552
11553	setFixedDistance: function(distance) {
11554		this._minDistance = distance;
11555		this._maxDistance = distance;
11556	},
11557
11558	_updateEvent: function(type, point, minDistance, maxDistance, start,
11559			needsChange, matchMaxDistance) {
11560		if (!start) {
11561			if (minDistance != null || maxDistance != null) {
11562				var minDist = minDistance != null ? minDistance : 0,
11563					vector = point.subtract(this._point),
11564					distance = vector.getLength();
11565				if (distance < minDist)
11566					return false;
11567				if (maxDistance != null && maxDistance != 0) {
11568					if (distance > maxDistance) {
11569						point = this._point.add(vector.normalize(maxDistance));
11570					} else if (matchMaxDistance) {
11571						return false;
11572					}
11573				}
11574			}
11575			if (needsChange && point.equals(this._point))
11576				return false;
11577		}
11578		this._lastPoint = start && type == 'mousemove' ? point : this._point;
11579		this._point = point;
11580		switch (type) {
11581		case 'mousedown':
11582			this._lastPoint = this._downPoint;
11583			this._downPoint = this._point;
11584			this._downCount++;
11585			break;
11586		case 'mouseup':
11587			this._lastPoint = this._downPoint;
11588			break;
11589		}
11590		this._count = start ? 0 : this._count + 1;
11591		return true;
11592	},
11593
11594	_fireEvent: function(type, event) {
11595		var sets = paper.project._removeSets;
11596		if (sets) {
11597			if (type === 'mouseup')
11598				sets.mousedrag = null;
11599			var set = sets[type];
11600			if (set) {
11601				for (var id in set) {
11602					var item = set[id];
11603					for (var key in sets) {
11604						var other = sets[key];
11605						if (other && other != set)
11606							delete other[item._id];
11607					}
11608					item.remove();
11609				}
11610				sets[type] = null;
11611			}
11612		}
11613		return this.responds(type)
11614				&& this.emit(type, new ToolEvent(this, type, event));
11615	},
11616
11617	_handleEvent: function(type, point, event) {
11618		paper = this._scope;
11619		var called = false;
11620		switch (type) {
11621		case 'mousedown':
11622			this._updateEvent(type, point, null, null, true, false, false);
11623			called = this._fireEvent(type, event);
11624			break;
11625		case 'mousedrag':
11626			var needsChange = false,
11627				matchMaxDistance = false;
11628			while (this._updateEvent(type, point, this.minDistance,
11629					this.maxDistance, false, needsChange, matchMaxDistance)) {
11630				called = this._fireEvent(type, event) || called;
11631				needsChange = true;
11632				matchMaxDistance = true;
11633			}
11634			break;
11635		case 'mouseup':
11636			if (!point.equals(this._point)
11637					&& this._updateEvent('mousedrag', point, this.minDistance,
11638							this.maxDistance, false, false, false)) {
11639				called = this._fireEvent('mousedrag', event);
11640			}
11641			this._updateEvent(type, point, null, this.maxDistance, false,
11642					false, false);
11643			called = this._fireEvent(type, event) || called;
11644			this._updateEvent(type, point, null, null, true, false, false);
11645			this._firstMove = true;
11646			break;
11647		case 'mousemove':
11648			while (this._updateEvent(type, point, this.minDistance,
11649					this.maxDistance, this._firstMove, true, false)) {
11650				called = this._fireEvent(type, event) || called;
11651				this._firstMove = false;
11652			}
11653			break;
11654		}
11655		if (called)
11656			event.preventDefault();
11657		return called;
11658	}
11659
11660});
11661
11662var Http = {
11663	request: function(method, url, callback, async) {
11664		async = (async === undefined) ? true : async;
11665		var xhr = new (window.ActiveXObject || XMLHttpRequest)(
11666					'Microsoft.XMLHTTP');
11667		xhr.open(method.toUpperCase(), url, async);
11668		if ('overrideMimeType' in xhr)
11669			xhr.overrideMimeType('text/plain');
11670		xhr.onreadystatechange = function() {
11671			if (xhr.readyState === 4) {
11672				var status = xhr.status;
11673				if (status === 0 || status === 200) {
11674					callback.call(xhr, xhr.responseText);
11675				} else {
11676					throw new Error('Could not load ' + url + ' (Error '
11677							+ status + ')');
11678				}
11679			}
11680		};
11681		return xhr.send(null);
11682	}
11683};
11684
11685var CanvasProvider = {
11686	canvases: [],
11687
11688	getCanvas: function(width, height) {
11689		var canvas,
11690			clear = true;
11691		if (typeof width === 'object') {
11692			height = width.height;
11693			width = width.width;
11694		}
11695		if (this.canvases.length) {
11696			canvas = this.canvases.pop();
11697		} else {
11698			canvas = document.createElement('canvas');
11699		}
11700		var ctx = canvas.getContext('2d');
11701		if (canvas.width === width && canvas.height === height) {
11702			if (clear)
11703				ctx.clearRect(0, 0, width + 1, height + 1);
11704		} else {
11705			canvas.width = width;
11706			canvas.height = height;
11707		}
11708		ctx.save();
11709		return canvas;
11710	},
11711
11712	getContext: function(width, height) {
11713		return this.getCanvas(width, height).getContext('2d');
11714	},
11715
11716	release: function(obj) {
11717		var canvas = obj.canvas ? obj.canvas : obj;
11718		canvas.getContext('2d').restore();
11719		this.canvases.push(canvas);
11720	}
11721};
11722
11723var BlendMode = new function() {
11724	var min = Math.min,
11725		max = Math.max,
11726		abs = Math.abs,
11727		sr, sg, sb, sa,
11728		br, bg, bb, ba,
11729		dr, dg, db;
11730
11731	function getLum(r, g, b) {
11732		return 0.2989 * r + 0.587 * g + 0.114 * b;
11733	}
11734
11735	function setLum(r, g, b, l) {
11736		var d = l - getLum(r, g, b);
11737		dr = r + d;
11738		dg = g + d;
11739		db = b + d;
11740		var l = getLum(dr, dg, db),
11741			mn = min(dr, dg, db),
11742			mx = max(dr, dg, db);
11743		if (mn < 0) {
11744			var lmn = l - mn;
11745			dr = l + (dr - l) * l / lmn;
11746			dg = l + (dg - l) * l / lmn;
11747			db = l + (db - l) * l / lmn;
11748		}
11749		if (mx > 255) {
11750			var ln = 255 - l,
11751				mxl = mx - l;
11752			dr = l + (dr - l) * ln / mxl;
11753			dg = l + (dg - l) * ln / mxl;
11754			db = l + (db - l) * ln / mxl;
11755		}
11756	}
11757
11758	function getSat(r, g, b) {
11759		return max(r, g, b) - min(r, g, b);
11760	}
11761
11762	function setSat(r, g, b, s) {
11763		var col = [r, g, b],
11764			mx = max(r, g, b),
11765			mn = min(r, g, b),
11766			md;
11767		mn = mn === r ? 0 : mn === g ? 1 : 2;
11768		mx = mx === r ? 0 : mx === g ? 1 : 2;
11769		md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;
11770		if (col[mx] > col[mn]) {
11771			col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]);
11772			col[mx] = s;
11773		} else {
11774			col[md] = col[mx] = 0;
11775		}
11776		col[mn] = 0;
11777		dr = col[0];
11778		dg = col[1];
11779		db = col[2];
11780	}
11781
11782	var modes = {
11783		multiply: function() {
11784			dr = br * sr / 255;
11785			dg = bg * sg / 255;
11786			db = bb * sb / 255;
11787		},
11788
11789		screen: function() {
11790			dr = br + sr - (br * sr / 255);
11791			dg = bg + sg - (bg * sg / 255);
11792			db = bb + sb - (bb * sb / 255);
11793		},
11794
11795		overlay: function() {
vendor: 4,622 bytes, lines 11796-11962
11796			dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255;
11797			dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255;
11798			db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255;
11799		},
11800
11801		'soft-light': function() {
11802			var t = sr * br / 255;
11803			dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255;
11804			t = sg * bg / 255;
11805			dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255;
11806			t = sb * bb / 255;
11807			db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255;
11808		},
11809
11810		'hard-light': function() {
11811			dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255;
11812			dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255;
11813			db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255;
11814		},
11815
11816		'color-dodge': function() {
11817			dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr));
11818			dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg));
11819			db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb));
11820		},
11821
11822		'color-burn': function() {
11823			dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr);
11824			dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg);
11825			db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb);
11826		},
11827
11828		darken: function() {
11829			dr = br < sr ? br : sr;
11830			dg = bg < sg ? bg : sg;
11831			db = bb < sb ? bb : sb;
11832		},
11833
11834		lighten: function() {
11835			dr = br > sr ? br : sr;
11836			dg = bg > sg ? bg : sg;
11837			db = bb > sb ? bb : sb;
11838		},
11839
11840		difference: function() {
11841			dr = br - sr;
11842			if (dr < 0)
11843				dr = -dr;
11844			dg = bg - sg;
11845			if (dg < 0)
11846				dg = -dg;
11847			db = bb - sb;
11848			if (db < 0)
11849				db = -db;
11850		},
11851
11852		exclusion: function() {
11853			dr = br + sr * (255 - br - br) / 255;
11854			dg = bg + sg * (255 - bg - bg) / 255;
11855			db = bb + sb * (255 - bb - bb) / 255;
11856		},
11857
11858		hue: function() {
11859			setSat(sr, sg, sb, getSat(br, bg, bb));
11860			setLum(dr, dg, db, getLum(br, bg, bb));
11861		},
11862
11863		saturation: function() {
11864			setSat(br, bg, bb, getSat(sr, sg, sb));
11865			setLum(dr, dg, db, getLum(br, bg, bb));
11866		},
11867
11868		luminosity: function() {
11869			setLum(br, bg, bb, getLum(sr, sg, sb));
11870		},
11871
11872		color: function() {
11873			setLum(sr, sg, sb, getLum(br, bg, bb));
11874		},
11875
11876		add: function() {
11877			dr = min(br + sr, 255);
11878			dg = min(bg + sg, 255);
11879			db = min(bb + sb, 255);
11880		},
11881
11882		subtract: function() {
11883			dr = max(br - sr, 0);
11884			dg = max(bg - sg, 0);
11885			db = max(bb - sb, 0);
11886		},
11887
11888		average: function() {
11889			dr = (br + sr) / 2;
11890			dg = (bg + sg) / 2;
11891			db = (bb + sb) / 2;
11892		},
11893
11894		negation: function() {
11895			dr = 255 - abs(255 - sr - br);
11896			dg = 255 - abs(255 - sg - bg);
11897			db = 255 - abs(255 - sb - bb);
11898		}
11899	};
11900
11901	var nativeModes = this.nativeModes = Base.each([
11902		'source-over', 'source-in', 'source-out', 'source-atop',
11903		'destination-over', 'destination-in', 'destination-out',
11904		'destination-atop', 'lighter', 'darker', 'copy', 'xor'
11905	], function(mode) {
11906		this[mode] = true;
11907	}, {});
11908
11909	var ctx = CanvasProvider.getContext(1, 1);
11910	Base.each(modes, function(func, mode) {
11911		var darken = mode === 'darken',
11912			ok = false;
11913		ctx.save();
11914		try {
11915			ctx.fillStyle = darken ? '#300' : '#a00';
11916			ctx.fillRect(0, 0, 1, 1);
11917			ctx.globalCompositeOperation = mode;
11918			if (ctx.globalCompositeOperation === mode) {
11919				ctx.fillStyle = darken ? '#a00' : '#300';
11920				ctx.fillRect(0, 0, 1, 1);
11921				ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51;
11922			}
11923		} catch (e) {}
11924		ctx.restore();
11925		nativeModes[mode] = ok;
11926	});
11927	CanvasProvider.release(ctx);
11928
11929	this.process = function(mode, srcContext, dstContext, alpha, offset) {
11930		var srcCanvas = srcContext.canvas,
11931			normal = mode === 'normal';
11932		if (normal || nativeModes[mode]) {
11933			dstContext.save();
11934			dstContext.setTransform(1, 0, 0, 1, 0, 0);
11935			dstContext.globalAlpha = alpha;
11936			if (!normal)
11937				dstContext.globalCompositeOperation = mode;
11938			dstContext.drawImage(srcCanvas, offset.x, offset.y);
11939			dstContext.restore();
11940		} else {
11941			var process = modes[mode];
11942			if (!process)
11943				return;
11944			var dstData = dstContext.getImageData(offset.x, offset.y,
11945					srcCanvas.width, srcCanvas.height),
11946				dst = dstData.data,
11947				src = srcContext.getImageData(0, 0,
11948					srcCanvas.width, srcCanvas.height).data;
11949			for (var i = 0, l = dst.length; i < l; i += 4) {
11950				sr = src[i];
11951				br = dst[i];
11952				sg = src[i + 1];
11953				bg = dst[i + 1];
11954				sb = src[i + 2];
11955				bb = dst[i + 2];
11956				sa = src[i + 3];
11957				ba = dst[i + 3];
11958				process();
11959				var a1 = sa * alpha / 255,
11960					a2 = 1 - a1;
11961				dst[i] = a1 * dr + a2 * br;
11962				dst[i + 1] = a1 * dg + a2 * bg;
11963				dst[i + 2] = a1 * db + a2 * bb;
11964				dst[i + 3] = sa * alpha + a2 * ba;
11965			}
11966			dstContext.putImageData(dstData, offset.x, offset.y);
11967		}
11968	};
11969};
11970
11971var SVGStyles = Base.each({
11972	fillColor: ['fill', 'color'],
11973	strokeColor: ['stroke', 'color'],
11974	strokeWidth: ['stroke-width', 'number'],
11975	strokeCap: ['stroke-linecap', 'string'],
11976	strokeJoin: ['stroke-linejoin', 'string'],
11977	strokeScaling: ['vector-effect', 'lookup', {
11978		true: 'none',
11979		false: 'non-scaling-stroke'
11980	}, function(item, value) {
11981		return !value
11982				&& (item instanceof PathItem
11983					|| item instanceof Shape
11984					|| item instanceof TextItem);
11985	}],
11986	miterLimit: ['stroke-miterlimit', 'number'],
11987	dashArray: ['stroke-dasharray', 'array'],
11988	dashOffset: ['stroke-dashoffset', 'number'],
11989	fontFamily: ['font-family', 'string'],
11990	fontWeight: ['font-weight', 'string'],
11991	fontSize: ['font-size', 'number'],
11992	justification: ['text-anchor', 'lookup', {
11993		left: 'start',
11994		center: 'middle',
11995		right: 'end'
11996	}],
11997	opacity: ['opacity', 'number'],
11998	blendMode: ['mix-blend-mode', 'string']
11999}, function(entry, key) {
12000	var part = Base.capitalize(key),
12001		lookup = entry[2];
12002	this[key] = {
12003		type: entry[1],
12004		property: key,
12005		attribute: entry[0],
12006		toSVG: lookup,
12007		fromSVG: lookup && Base.each(lookup, function(value, name) {
12008			this[value] = name;
12009		}, {}),
12010		exportFilter: entry[3],
12011		get: 'get' + part,
12012		set: 'set' + part
12013	};
12014}, {});
12015
12016var SVGNamespaces = {
12017	href: 'http://www.w3.org/1999/xlink',
12018	xlink: 'http://www.w3.org/2000/xmlns'
12019};
12020
12021new function() {
12022	var formatter;
12023
12024	function setAttributes(node, attrs) {
12025		for (var key in attrs) {
12026			var val = attrs[key],
12027				namespace = SVGNamespaces[key];
12028			if (typeof val === 'number')
12029				val = formatter.number(val);
12030			if (namespace) {
12031				node.setAttributeNS(namespace, key, val);
12032			} else {
12033				node.setAttribute(key, val);
12034			}
12035		}
12036		return node;
12037	}
12038
12039	function createElement(tag, attrs) {
12040		return setAttributes(
12041			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
12042	}
12043
12044	function getTransform(matrix, coordinates, center) {
12045		var attrs = new Base(),
12046			trans = matrix.getTranslation();
12047		if (coordinates) {
12048			matrix = matrix.shiftless();
12049			var point = matrix._inverseTransform(trans);
12050			attrs[center ? 'cx' : 'x'] = point.x;
12051			attrs[center ? 'cy' : 'y'] = point.y;
12052			trans = null;
12053		}
12054		if (!matrix.isIdentity()) {
12055			var decomposed = matrix.decompose();
12056			if (decomposed && !decomposed.shearing) {
12057				var parts = [],
12058					angle = decomposed.rotation,
12059					scale = decomposed.scaling;
12060				if (trans && !trans.isZero())
12061					parts.push('translate(' + formatter.point(trans) + ')');
12062				if (!Numerical.isZero(scale.x - 1)
12063						|| !Numerical.isZero(scale.y - 1))
12064					parts.push('scale(' + formatter.point(scale) +')');
12065				if (angle)
12066					parts.push('rotate(' + formatter.number(angle) + ')');
12067				attrs.transform = parts.join(' ');
12068			} else {
12069				attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
12070			}
12071		}
12072		return attrs;
12073	}
12074
12075	function exportGroup(item, options) {
12076		var attrs = getTransform(item._matrix),
12077			children = item._children;
12078		var node = createElement('g', attrs);
12079		for (var i = 0, l = children.length; i < l; i++) {
12080			var child = children[i];
12081			var childNode = exportSVG(child, options);
12082			if (childNode) {
12083				if (child.isClipMask()) {
12084					var clip = createElement('clipPath');
12085					clip.appendChild(childNode);
12086					setDefinition(child, clip, 'clip');
12087					setAttributes(node, {
12088						'clip-path': 'url(#' + clip.id + ')'
12089					});
12090				} else {
12091					node.appendChild(childNode);
12092				}
12093			}
12094		}
12095		return node;
12096	}
12097
12098	function exportRaster(item, options) {
12099		var attrs = getTransform(item._matrix, true),
12100			size = item.getSize(),
12101			image = item.getImage();
12102		attrs.x -= size.width / 2;
12103		attrs.y -= size.height / 2;
12104		attrs.width = size.width;
12105		attrs.height = size.height;
12106		attrs.href = options.embedImages === false && image && image.src
12107				|| item.toDataURL();
12108		return createElement('image', attrs);
12109	}
12110
12111	function exportPath(item, options) {
12112		var matchShapes = options.matchShapes;
12113		if (matchShapes) {
12114			var shape = item.toShape(false);
12115			if (shape)
12116				return exportShape(shape, options);
12117		}
12118		var segments = item._segments,
12119			type,
12120			attrs = getTransform(item._matrix);
12121		if (segments.length === 0)
12122			return null;
12123		if (matchShapes && !item.hasHandles()) {
12124			if (segments.length >= 3) {
12125				type = item._closed ? 'polygon' : 'polyline';
12126				var parts = [];
12127				for(var i = 0, l = segments.length; i < l; i++)
12128					parts.push(formatter.point(segments[i]._point));
12129				attrs.points = parts.join(' ');
12130			} else {
12131				type = 'line';
12132				var first = segments[0]._point,
12133					last = segments[segments.length - 1]._point;
12134				attrs.set({
12135					x1: first.x,
12136					y1: first.y,
12137					x2: last.x,
12138					y2: last.y
12139				});
12140			}
12141		} else {
12142			type = 'path';
12143			attrs.d = item.getPathData(null, options.precision);
12144		}
12145		return createElement(type, attrs);
12146	}
12147
12148	function exportShape(item) {
12149		var type = item._type,
12150			radius = item._radius,
12151			attrs = getTransform(item._matrix, true, type !== 'rectangle');
12152		if (type === 'rectangle') {
12153			type = 'rect';
12154			var size = item._size,
12155				width = size.width,
12156				height = size.height;
12157			attrs.x -= width / 2;
12158			attrs.y -= height / 2;
12159			attrs.width = width;
12160			attrs.height = height;
12161			if (radius.isZero())
12162				radius = null;
12163		}
12164		if (radius) {
12165			if (type === 'circle') {
12166				attrs.r = radius;
12167			} else {
12168				attrs.rx = radius.width;
12169				attrs.ry = radius.height;
12170			}
12171		}
12172		return createElement(type, attrs);
12173	}
12174
12175	function exportCompoundPath(item, options) {
12176		var attrs = getTransform(item._matrix);
12177		var data = item.getPathData(null, options.precision);
12178		if (data)
12179			attrs.d = data;
12180		return createElement('path', attrs);
12181	}
12182
12183	function exportPlacedSymbol(item, options) {
12184		var attrs = getTransform(item._matrix, true),
12185			symbol = item.getSymbol(),
12186			symbolNode = getDefinition(symbol, 'symbol'),
12187			definition = symbol.getDefinition(),
12188			bounds = definition.getBounds();
12189		if (!symbolNode) {
12190			symbolNode = createElement('symbol', {
12191				viewBox: formatter.rectangle(bounds)
12192			});
12193			symbolNode.appendChild(exportSVG(definition, options));
12194			setDefinition(symbol, symbolNode, 'symbol');
12195		}
12196		attrs.href = '#' + symbolNode.id;
12197		attrs.x += bounds.x;
12198		attrs.y += bounds.y;
12199		attrs.width = formatter.number(bounds.width);
12200		attrs.height = formatter.number(bounds.height);
12201		attrs.overflow = 'visible';
12202		return createElement('use', attrs);
12203	}
12204
12205	function exportGradient(color) {
12206		var gradientNode = getDefinition(color, 'color');
12207		if (!gradientNode) {
12208			var gradient = color.getGradient(),
12209				radial = gradient._radial,
12210				origin = color.getOrigin().transform(),
12211				destination = color.getDestination().transform(),
12212				attrs;
12213			if (radial) {
12214				attrs = {
12215					cx: origin.x,
12216					cy: origin.y,
12217					r: origin.getDistance(destination)
12218				};
12219				var highlight = color.getHighlight();
12220				if (highlight) {
12221					highlight = highlight.transform();
12222					attrs.fx = highlight.x;
12223					attrs.fy = highlight.y;
12224				}
12225			} else {
12226				attrs = {
12227					x1: origin.x,
12228					y1: origin.y,
12229					x2: destination.x,
12230					y2: destination.y
12231				};
12232			}
12233			attrs.gradientUnits = 'userSpaceOnUse';
12234			gradientNode = createElement(
12235					(radial ? 'radial' : 'linear') + 'Gradient', attrs);
12236			var stops = gradient._stops;
12237			for (var i = 0, l = stops.length; i < l; i++) {
12238				var stop = stops[i],
12239					stopColor = stop._color,
12240					alpha = stopColor.getAlpha();
12241				attrs = {
12242					offset: stop._rampPoint,
12243					'stop-color': stopColor.toCSS(true)
12244				};
12245				if (alpha < 1)
12246					attrs['stop-opacity'] = alpha;
12247				gradientNode.appendChild(createElement('stop', attrs));
12248			}
12249			setDefinition(color, gradientNode, 'color');
12250		}
12251		return 'url(#' + gradientNode.id + ')';
12252	}
12253
12254	function exportText(item) {
12255		var node = createElement('text', getTransform(item._matrix, true));
12256		node.textContent = item._content;
12257		return node;
12258	}
12259
12260	var exporters = {
12261		Group: exportGroup,
12262		Layer: exportGroup,
12263		Raster: exportRaster,
12264		Path: exportPath,
12265		Shape: exportShape,
12266		CompoundPath: exportCompoundPath,
12267		PlacedSymbol: exportPlacedSymbol,
12268		PointText: exportText
12269	};
12270
12271	function applyStyle(item, node, isRoot) {
12272		var attrs = {},
12273			parent = !isRoot && item.getParent();
12274
12275		if (item._name != null)
12276			attrs.id = item._name;
12277
12278		Base.each(SVGStyles, function(entry) {
12279			var get = entry.get,
12280				type = entry.type,
12281				value = item[get]();
12282			if (entry.exportFilter
12283					? entry.exportFilter(item, value)
12284					: !parent || !Base.equals(parent[get](), value)) {
12285				if (type === 'color' && value != null) {
12286					var alpha = value.getAlpha();
12287					if (alpha < 1)
12288						attrs[entry.attribute + '-opacity'] = alpha;
12289				}
12290				attrs[entry.attribute] = value == null
12291					? 'none'
12292					: type === 'number'
12293						? formatter.number(value)
12294						: type === 'color'
12295							? value.gradient
12296								? exportGradient(value, item)
12297								: value.toCSS(true)
12298							: type === 'array'
12299								? value.join(',')
12300								: type === 'lookup'
12301									? entry.toSVG[value]
12302									: value;
12303			}
12304		});
12305
12306		if (attrs.opacity === 1)
12307			delete attrs.opacity;
12308
12309		if (!item._visible)
12310			attrs.visibility = 'hidden';
12311
12312		return setAttributes(node, attrs);
12313	}
12314
12315	var definitions;
12316	function getDefinition(item, type) {
12317		if (!definitions)
12318			definitions = { ids: {}, svgs: {} };
12319		return item && definitions.svgs[type + '-' + item._id];
12320	}
12321
12322	function setDefinition(item, node, type) {
12323		if (!definitions)
12324			getDefinition();
12325		var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
12326		node.id = type + '-' + id;
12327		definitions.svgs[type + '-' + item._id] = node;
12328	}
12329
12330	function exportDefinitions(node, options) {
12331		var svg = node,
12332			defs = null;
12333		if (definitions) {
12334			svg = node.nodeName.toLowerCase() === 'svg' && node;
12335			for (var i in definitions.svgs) {
12336				if (!defs) {
12337					if (!svg) {
12338						svg = createElement('svg');
12339						svg.appendChild(node);
12340					}
12341					defs = svg.insertBefore(createElement('defs'),
12342							svg.firstChild);
12343				}
12344				defs.appendChild(definitions.svgs[i]);
12345			}
12346			definitions = null;
12347		}
12348		return options.asString
12349				? new XMLSerializer().serializeToString(svg)
12350				: svg;
12351	}
12352
12353	function exportSVG(item, options, isRoot) {
12354		var exporter = exporters[item._class],
12355			node = exporter && exporter(item, options);
12356		if (node) {
12357			var onExport = options.onExport;
12358			if (onExport)
12359				node = onExport(item, node, options) || node;
12360			var data = JSON.stringify(item._data);
12361			if (data && data !== '{}' && data !== 'null')
12362				node.setAttribute('data-paper-data', data);
12363		}
12364		return node && applyStyle(item, node, isRoot);
12365	}
12366
12367	function setOptions(options) {
12368		if (!options)
12369			options = {};
12370		formatter = new Formatter(options.precision);
12371		return options;
12372	}
12373
12374	Item.inject({
12375		exportSVG: function(options) {
12376			options = setOptions(options);
12377			return exportDefinitions(exportSVG(this, options, true), options);
12378		}
12379	});
12380
12381	Project.inject({
12382		exportSVG: function(options) {
12383			options = setOptions(options);
12384			var layers = this.layers,
12385				view = this.getView(),
12386				size = view.getViewSize(),
12387				node = createElement('svg', {
12388					x: 0,
12389					y: 0,
12390					width: size.width,
12391					height: size.height,
12392					version: '1.1',
12393					xmlns: 'http://www.w3.org/2000/svg',
12394					'xmlns:xlink': 'http://www.w3.org/1999/xlink'
12395				}),
12396				parent = node,
12397				matrix = view._matrix;
12398			if (!matrix.isIdentity())
12399				parent = node.appendChild(
12400						createElement('g', getTransform(matrix)));
12401			for (var i = 0, l = layers.length; i < l; i++)
12402				parent.appendChild(exportSVG(layers[i], options, true));
12403			return exportDefinitions(node, options);
12404		}
12405	});
12406};
12407
12408new function() {
12409
12410	function getValue(node, name, isString, allowNull) {
12411		var namespace = SVGNamespaces[name],
12412			value = namespace
12413				? node.getAttributeNS(namespace, name)
12414				: node.getAttribute(name);
12415		if (value === 'null')
12416			value = null;
12417		return value == null
12418				? allowNull
12419					? null
12420					: isString
12421						? ''
12422						: 0
12423				: isString
12424					? value
12425					: parseFloat(value);
12426	}
12427
12428	function getPoint(node, x, y, allowNull) {
12429		x = getValue(node, x, false, allowNull);
12430		y = getValue(node, y, false, allowNull);
12431		return allowNull && (x == null || y == null) ? null
12432				: new Point(x, y);
12433	}
12434
12435	function getSize(node, w, h, allowNull) {
12436		w = getValue(node, w, false, allowNull);
12437		h = getValue(node, h, false, allowNull);
12438		return allowNull && (w == null || h == null) ? null
12439				: new Size(w, h);
12440	}
12441
12442	function convertValue(value, type, lookup) {
12443		return value === 'none'
12444				? null
12445				: type === 'number'
12446					? parseFloat(value)
12447					: type === 'array'
12448						? value ? value.split(/[\s,]+/g).map(parseFloat) : []
12449						: type === 'color'
12450							? getDefinition(value) || value
12451							: type === 'lookup'
12452								? lookup[value]
12453								: value;
12454	}
12455
12456	function importGroup(node, type, options, isRoot) {
12457		var nodes = node.childNodes,
12458			isClip = type === 'clippath',
12459			item = new Group(),
12460			project = item._project,
12461			currentStyle = project._currentStyle,
12462			children = [];
12463		if (!isClip) {
12464			item = applyAttributes(item, node, isRoot);
12465			project._currentStyle = item._style.clone();
12466		}
12467		if (isRoot) {
12468			var defs = node.querySelectorAll('defs');
12469			for (var i = 0, l = defs.length; i < l; i++) {
12470				importSVG(defs[i], options, false);
12471			}
12472		}
12473		for (var i = 0, l = nodes.length; i < l; i++) {
12474			var childNode = nodes[i],
12475				child;
12476			if (childNode.nodeType === 1
12477					&& childNode.nodeName.toLowerCase() !== 'defs'
12478					&& (child = importSVG(childNode, options, false))
12479					&& !(child instanceof Symbol))
12480				children.push(child);
12481		}
12482		item.addChildren(children);
12483		if (isClip)
12484			item = applyAttributes(item.reduce(), node, isRoot);
12485		project._currentStyle = currentStyle;
12486		if (isClip || type === 'defs') {
12487			item.remove();
12488			item = null;
12489		}
12490		return item;
12491	}
12492
12493	function importPoly(node, type) {
12494		var coords = node.getAttribute('points').match(
12495					/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g),
12496			points = [];
12497		for (var i = 0, l = coords.length; i < l; i += 2)
12498			points.push(new Point(
12499					parseFloat(coords[i]),
12500					parseFloat(coords[i + 1])));
12501		var path = new Path(points);
12502		if (type === 'polygon')
12503			path.closePath();
12504		return path;
12505	}
12506
12507	function importPath(node) {
12508		var data = node.getAttribute('d'),
12509			param = { pathData: data };
12510		return (data.match(/m/gi) || []).length > 1 || /z\S+/i.test(data)
12511				? new CompoundPath(param)
12512				: new Path(param);
12513	}
12514
12515	function importGradient(node, type) {
12516		var id = (getValue(node, 'href', true) || '').substring(1),
12517			isRadial = type === 'radialgradient',
12518			gradient;
12519		if (id) {
12520			gradient = definitions[id].getGradient();
12521		} else {
12522			var nodes = node.childNodes,
12523				stops = [];
12524			for (var i = 0, l = nodes.length; i < l; i++) {
12525				var child = nodes[i];
12526				if (child.nodeType === 1)
12527					stops.push(applyAttributes(new GradientStop(), child));
12528			}
12529			gradient = new Gradient(stops, isRadial);
12530		}
12531		var origin, destination, highlight;
12532		if (isRadial) {
12533			origin = getPoint(node, 'cx', 'cy');
12534			destination = origin.add(getValue(node, 'r'), 0);
12535			highlight = getPoint(node, 'fx', 'fy', true);
12536		} else {
12537			origin = getPoint(node, 'x1', 'y1');
12538			destination = getPoint(node, 'x2', 'y2');
12539		}
12540		applyAttributes(
12541			new Color(gradient, origin, destination, highlight), node);
12542		return null;
12543	}
12544
12545	var importers = {
12546		'#document': function (node, type, options, isRoot) {
12547			var nodes = node.childNodes;
12548			for (var i = 0, l = nodes.length; i < l; i++) {
12549				var child = nodes[i];
12550				if (child.nodeType === 1) {
12551					var next = child.nextSibling;
12552					document.body.appendChild(child);
12553					var item = importSVG(child, options, isRoot);
12554					if (next) {
12555						node.insertBefore(child, next);
12556					} else {
12557						node.appendChild(child);
12558					}
12559					return item;
12560				}
12561			}
12562		},
12563		g: importGroup,
12564		svg: importGroup,
12565		clippath: importGroup,
12566		polygon: importPoly,
12567		polyline: importPoly,
12568		path: importPath,
12569		lineargradient: importGradient,
12570		radialgradient: importGradient,
12571
12572		image: function (node) {
12573			var raster = new Raster(getValue(node, 'href', true));
12574			raster.on('load', function() {
12575				var size = getSize(node, 'width', 'height');
12576				this.setSize(size);
12577				var center = this._matrix._transformPoint(
12578						getPoint(node, 'x', 'y').add(size.divide(2)));
12579				this.translate(center);
12580			});
12581			return raster;
12582		},
12583
12584		symbol: function(node, type, options, isRoot) {
12585			return new Symbol(importGroup(node, type, options, isRoot), true);
12586		},
12587
12588		defs: importGroup,
12589
12590		use: function(node) {
12591			var id = (getValue(node, 'href', true) || '').substring(1),
12592				definition = definitions[id],
12593				point = getPoint(node, 'x', 'y');
12594			return definition
12595					? definition instanceof Symbol
12596						? definition.place(point)
12597						: definition.clone().translate(point)
12598					: null;
12599		},
12600
12601		circle: function(node) {
12602			return new Shape.Circle(getPoint(node, 'cx', 'cy'),
12603					getValue(node, 'r'));
12604		},
12605
12606		ellipse: function(node) {
12607			return new Shape.Ellipse({
12608				center: getPoint(node, 'cx', 'cy'),
12609				radius: getSize(node, 'rx', 'ry')
12610			});
12611		},
12612
12613		rect: function(node) {
12614			var point = getPoint(node, 'x', 'y'),
12615				size = getSize(node, 'width', 'height'),
12616				radius = getSize(node, 'rx', 'ry');
12617			return new Shape.Rectangle(new Rectangle(point, size), radius);
12618		},
12619
12620		line: function(node) {
12621			return new Path.Line(getPoint(node, 'x1', 'y1'),
12622					getPoint(node, 'x2', 'y2'));
12623		},
12624
12625		text: function(node) {
12626			var text = new PointText(getPoint(node, 'x', 'y')
12627					.add(getPoint(node, 'dx', 'dy')));
12628			text.setContent(node.textContent.trim() || '');
12629			return text;
12630		}
12631	};
12632
12633	function applyTransform(item, value, name, node) {
12634		var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
12635			matrix = new Matrix();
12636		for (var i = 0, l = transforms.length; i < l; i++) {
12637			var transform = transforms[i];
12638			if (!transform)
12639				break;
12640			var parts = transform.split(/\(\s*/),
12641				command = parts[0],
12642				v = parts[1].split(/[\s,]+/g);
12643			for (var j = 0, m = v.length; j < m; j++)
12644				v[j] = parseFloat(v[j]);
12645			switch (command) {
12646			case 'matrix':
12647				matrix.concatenate(
12648						new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
12649				break;
12650			case 'rotate':
12651				matrix.rotate(v[0], v[1], v[2]);
12652				break;
12653			case 'translate':
12654				matrix.translate(v[0], v[1]);
12655				break;
12656			case 'scale':
12657				matrix.scale(v);
12658				break;
12659			case 'skewX':
12660				matrix.skew(v[0], 0);
12661				break;
12662			case 'skewY':
12663				matrix.skew(0, v[0]);
12664				break;
12665			}
12666		}
12667		item.transform(matrix);
12668	}
12669
12670	function applyOpacity(item, value, name) {
12671		var color = item[name === 'fill-opacity' ? 'getFillColor'
12672				: 'getStrokeColor']();
12673		if (color)
12674			color.setAlpha(parseFloat(value));
12675	}
12676
12677	var attributes = Base.set(Base.each(SVGStyles, function(entry) {
12678		this[entry.attribute] = function(item, value) {
12679			item[entry.set](convertValue(value, entry.type, entry.fromSVG));
12680			if (entry.type === 'color' && item instanceof Shape) {
12681				var color = item[entry.get]();
12682				if (color)
12683					color.transform(new Matrix().translate(
12684							item.getPosition(true).negate()));
12685			}
12686		};
12687	}, {}), {
12688		id: function(item, value) {
12689			definitions[value] = item;
12690			if (item.setName)
12691				item.setName(value);
12692		},
12693
12694		'clip-path': function(item, value) {
12695			var clip = getDefinition(value);
12696			if (clip) {
12697				clip = clip.clone();
12698				clip.setClipMask(true);
12699				if (item instanceof Group) {
12700					item.insertChild(0, clip);
12701				} else {
12702					return new Group(clip, item);
12703				}
12704			}
12705		},
12706
12707		gradientTransform: applyTransform,
12708		transform: applyTransform,
12709
12710		'fill-opacity': applyOpacity,
12711		'stroke-opacity': applyOpacity,
12712
12713		visibility: function(item, value) {
12714			item.setVisible(value === 'visible');
12715		},
12716
12717		display: function(item, value) {
12718			item.setVisible(value !== null);
12719		},
12720
12721		'stop-color': function(item, value) {
12722			if (item.setColor)
12723				item.setColor(value);
12724		},
12725
12726		'stop-opacity': function(item, value) {
12727			if (item._color)
12728				item._color.setAlpha(parseFloat(value));
12729		},
12730
12731		offset: function(item, value) {
12732			var percentage = value.match(/(.*)%$/);
12733			item.setRampPoint(percentage
12734					? percentage[1] / 100
12735					: parseFloat(value));
12736		},
12737
12738		viewBox: function(item, value, name, node, styles) {
12739			var rect = new Rectangle(convertValue(value, 'array')),
12740				size = getSize(node, 'width', 'height', true);
12741			if (item instanceof Group) {
12742				var scale = size ? rect.getSize().divide(size) : 1,
12743					matrix = new Matrix().translate(rect.getPoint()).scale(scale);
12744				item.transform(matrix.inverted());
12745			} else if (item instanceof Symbol) {
12746				if (size)
12747					rect.setSize(size);
12748				var clip = getAttribute(node, 'overflow', styles) != 'visible',
12749					group = item._definition;
12750				if (clip && !rect.contains(group.getBounds())) {
12751					clip = new Shape.Rectangle(rect).transform(group._matrix);
12752					clip.setClipMask(true);
12753					group.addChild(clip);
12754				}
12755			}
12756		}
12757	});
12758
12759	function getAttribute(node, name, styles) {
12760		var attr = node.attributes[name],
12761			value = attr && attr.value;
12762		if (!value) {
12763			var style = Base.camelize(name);
12764			value = node.style[style];
12765			if (!value && styles.node[style] !== styles.parent[style])
12766				value = styles.node[style];
12767		}
12768		return !value
12769				? undefined
12770				: value === 'none'
12771					? null
12772					: value;
12773	}
12774
12775	function applyAttributes(item, node, isRoot) {
12776		var styles = {
12777			node: DomElement.getStyles(node) || {},
12778			parent: !isRoot && DomElement.getStyles(node.parentNode) || {}
12779		};
12780		Base.each(attributes, function(apply, name) {
12781			var value = getAttribute(node, name, styles);
12782			if (value !== undefined)
12783				item = Base.pick(apply(item, value, name, node, styles), item);
12784		});
12785		return item;
12786	}
12787
12788	var definitions = {};
12789	function getDefinition(value) {
12790		var match = value && value.match(/\((?:#|)([^)']+)/);
12791		return match && definitions[match[1]];
12792	}
12793
12794	function importSVG(source, options, isRoot) {
12795		if (!source)
12796			return null;
12797		if (!options) {
12798			options = {};
12799		} else if (typeof options === 'function') {
12800			options = { onLoad: options };
12801		}
12802
12803		var node = source,
12804			scope = paper;
12805
12806		function onLoadCallback(svg) {
12807			paper = scope;
12808			var item = importSVG(svg, options, isRoot),
12809				onLoad = options.onLoad,
12810				view = scope.project && scope.getView();
12811			if (onLoad)
12812				onLoad.call(this, item);
12813			view.update();
12814		}
12815
12816		if (isRoot) {
12817			if (typeof source === 'string' && !/^.*</.test(source)) {
12818				node = document.getElementById(source);
12819				if (node) {
12820					source = null;
12821				} else {
12822					return Http.request('get', source, onLoadCallback);
12823				}
12824			} else if (typeof File !== 'undefined' && source instanceof File) {
12825				var reader = new FileReader();
12826				reader.onload = function() {
12827					onLoadCallback(reader.result);
12828				};
12829				return reader.readAsText(source);
12830			}
12831		}
12832
12833		if (typeof source === 'string')
12834			node = new DOMParser().parseFromString(source, 'image/svg+xml');
12835		if (!node.nodeName)
12836			throw new Error('Unsupported SVG source: ' + source);
12837		var type = node.nodeName.toLowerCase(),
12838			importer = importers[type],
12839			item,
12840			data = node.getAttribute && node.getAttribute('data-paper-data'),
12841			settings = scope.settings,
12842			applyMatrix = settings.applyMatrix;
12843		settings.applyMatrix = false;
12844		item = importer && importer(node, type, options, isRoot) || null;
12845		settings.applyMatrix = applyMatrix;
12846		if (item) {
12847			if (type !== '#document' && !(item instanceof Group))
12848				item = applyAttributes(item, node, isRoot);
12849			var onImport = options.onImport;
12850			if (onImport)
12851				item = onImport(node, item, options) || item;
12852			if (options.expandShapes && item instanceof Shape) {
12853				item.remove();
12854				item = item.toPath();
12855			}
12856			if (data)
12857				item._data = JSON.parse(data);
12858		}
12859		if (isRoot) {
12860			definitions = {};
12861			if (item && Base.pick(options.applyMatrix, applyMatrix))
12862				item.matrix.apply(true, true);
12863		}
12864		return item;
12865	}
12866
12867	Item.inject({
12868		importSVG: function(node, options) {
12869			return this.addChild(importSVG(node, options, true));
12870		}
12871	});
12872
12873	Project.inject({
12874		importSVG: function(node, options) {
12875			this.activate();
12876			return importSVG(node, options, true);
12877		}
12878	});
12879};
12880
12881Base.exports.PaperScript = (function() {
12882	var exports, define,
12883		scope = this;
12884!function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in mr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=mr[r]);hr=fr.sourceFile||null}function t(e,r){var t=vr(dr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=dr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(dr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,dr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=dr.charCodeAt(br+=2);pr>br&&10!==t&&13!==t&&8232!==t&&8233!==t;)++br,t=dr.charCodeAt(br);fr.onComment&&fr.onComment(!1,dr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;pr>br;){var e=dr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=dr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e||8232===e||8233===e)++br,fr.locations&&(++Ar,Sr=br);else if(e>8&&14>e)++br;else if(47===e){var r=dr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=dr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=dr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function d(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Dt,1)}function p(e){var r=dr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Tt,1)}function h(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Vt,1)}function m(e){var r=dr.charCodeAt(br+1);return r===e?45==r&&62==dr.charCodeAt(br+2)&&Gt.test(dr.slice(Lr,br))?(br+=3,c(),u(),g()):x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=dr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===dr.charCodeAt(br+2)?3:2,61===dr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):33==r&&60==e&&45==dr.charCodeAt(br+2)&&45==dr.charCodeAt(br+3)?(br+=4,c(),u(),g()):(61===r&&(t=61===dr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=dr.charCodeAt(br+1);return 61===r?x(qt,61===dr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(mt);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(dt);case 123:return++br,i(pt);case 125:return++br,i(ht);case 58:return++br,i(gt);case 63:return++br,i(kt);
12884case 48:var r=dr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return d();case 124:case 38:return p(e);case 94:return h();case 43:case 45:return m(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=pr)return i(Br);var r=dr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=dr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=pr&&t(a,"Unterminated regular expression");var o=dr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=dr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=dr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===dr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===dr.charCodeAt(br)&&(++br,w(10),n=!0);var o=dr.charCodeAt(br);(69===o||101===o)&&(o=dr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=dr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Tr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=pr&&t(yr,"Unterminated string constant");var n=dr.charCodeAt(br);if(n===e)return++br,i(Dr,r);if(92===n){n=dr.charCodeAt(++br);var a=/^[0-7]+/.exec(dr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Tr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+="	";break;case 98:r+="\b";break;case 118:r+="�";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===dr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8233===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=dr.charCodeAt(br);if(Yt(a))Bt&&(e+=dr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=dr.slice(n,br)),Bt=!0,117!=dr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:dr.slice(n,br)}function L(){var e=I(),r=Fr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Tr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){if(Tr=e,br=Lr,fr.locations)for(;Sr>br;)Sr=dr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function V(){this.type=null,this.start=yr,this.end=null}function T(){this.start=xr,this.end=null,null!==hr&&(this.source=hr)}function q(){var e=new V;return fr.locations&&(e.loc=new T),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new V;return r.start=e.start,fr.locations&&(r.loc=new T,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function D(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function F(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===ht||Gt.test(dr.slice(Lr,yr)))}function M(){F(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}
12884function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Tr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Tr=null,Vr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&D(n)&&R(!0),t=!1}return j(r,"Program")}function J(){(wr===wt||wr===Et&&"/="==Cr)&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;F(yt)||B()?r.label=null:wr!==Fr?X():(r.label=lr(),M());for(var a=0;a<Vr.length;++a){var o=Vr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Vr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Vr.push(Zt),r.body=J(),Vr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Vr.push(Zt),z(mt),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),j(i,"VariableDeclaration"),1===i.declarations.length&&F(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return F(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=F(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),F(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(pt),Vr.push(en);for(var s,c;wr!=ht;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Vr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(dr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(mt),l.param=lr(),Tr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=F($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),G(r),M(),j(r,"VariableDeclaration");case tt:return U(),r.test=P(),Vr.push(Zt),r.body=J(),Vr.pop(),j(r,"WhileStatement");case nt:return Tr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case pt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,d=K();if(e===Fr&&"Identifier"===d.type&&F(gt)){for(var a=0;a<Vr.length;++a)Vr[a].name===f&&t(d.start,"Label '"+f+"' is already declared");var p=wr.isLoop?"loop":wr===Yr?"switch":null;return Vr.push({name:f,kind:p}),r.body=J(),Vr.pop(),r.label=d,j(r,"LabeledStatement")}return r.expression=d,M(),j(r,"ExpressionStatement")}}function P(){z(mt);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(pt);!F(ht);){var o=J();t.body.push(o),n&&e&&D(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Tr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=F(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!F(bt))break}return e}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];F(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(F(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var o=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(o,r,t)}return e}
12884function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,Er=!0,U(),e.argument=rr(),r?N(e.argument):Tr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(F(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(F(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(dt),nr(j(t,"MemberExpression"),r)}if(!r&&F(mt)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Fr:return lr();case Or:case Dr:case jr:var e=q();return e.value=Cr,e.raw=dr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case mt:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(dt,!0,!0),j(e,"ArrayExpression");case pt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=F(mt)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!F(ht);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&F(ht))break;var a,o={key:sr()},i=!1;if(F(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==mt&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Tr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Tr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Dr?ar():lr(!0)}function cr(e,r){wr===Fr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(mt);!F(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Vr;if(Rr=!0,Vr=[],e.body=H(!0),Rr=a,Vr=o,Tr||e.body.body.length&&D(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!F(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&F(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Fr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),Er=!1,U(),j(r,"Identifier")}e.version="0.4.0";var fr,dr,pr,hr;e.parse=function(e,t){return dr=String(e),pr=dr.length,r(t),o(),W(fr.program)};var mr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}dr=String(e),pr=dr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(dr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Vr,Tr,qr=[],Or={type:"num"},jr={type:"regexp"},Dr={type:"string"},Fr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"}
12884,tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},dt={type:"]"},pt={type:"{",beforeExpr:!0},ht={type:"}"},mt={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Vt={binop:4,beforeExpr:!0},Tt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Dt={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:dt,braceL:pt,braceR:ht,parenL:mt,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Fr,eof:Br,num:Or,regexp:jr,string:Dr};for(var Ft in lt)e.tokTypes["_"+Ft]=lt[Ft];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u
1288419c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc",Ht="\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f",$t=new RegExp("["+Pt+"]"),_t=new RegExp("["+Pt+Ht+"]"),Gt=/[\n\r\u2028\u2029]/,Kt=/\r\n|[\n\r\u2028\u2029]/g,Qt=e.isIdentifierStart=function(e){return 65>e?36===e:91>e?!0:97>e?95===e:123>e?!0:e>=170&&$t.test(String.fromCharCode(e))},Yt=e.isIdentifierChar=function(e){return 48>e?36===e:58>e?!0:65>e?!1:91>e?!0:97>e?95===e:123>e?!0:e>=170&&_t.test(String.fromCharCode(e))},Zt={kind:"loop"},en={kind:"switch"}});
12885
12886	var binaryOperators = {
12887		'+': '__add',
12888		'-': '__subtract',
12889		'*': '__multiply',
12890		'/': '__divide',
12891		'%': '__modulo',
12892		'==': 'equals',
12893		'!=': 'equals'
12894	};
12895
12896	var unaryOperators = {
12897		'-': '__negate',
12898		'+': null
12899	};
12900
12901	var fields = Base.each(
12902		['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
12903		function(name) {
12904			this['__' + name] = '#' + name;
12905		},
12906		{}
12907	);
12908	Point.inject(fields);
12909	Size.inject(fields);
12910	Color.inject(fields);
12911
12912	function __$__(left, operator, right) {
12913		var handler = binaryOperators[operator];
12914		if (left && left[handler]) {
12915			var res = left[handler](right);
12916			return operator === '!=' ? !res : res;
12917		}
12918		switch (operator) {
12919		case '+': return left + right;
12920		case '-': return left - right;
12921		case '*': return left * right;
12922		case '/': return left / right;
12923		case '%': return left % right;
12924		case '==': return left == right;
12925		case '!=': return left != right;
12926		}
12927	}
12928
12929	function $__(operator, value) {
12930		var handler = unaryOperators[operator];
12931		if (handler && value && value[handler])
12932			return value[handler]();
12933		switch (operator) {
12934		case '+': return +value;
12935		case '-': return -value;
12936		}
12937	}
12938
12939	function parse(code, options) {
12940		return scope.acorn.parse(code, options);
12941	}
12942
12943	function compile(code, url, options) {
12944		if (!code)
12945			return '';
12946		options = options || {};
12947		url = url || '';
12948
12949		var insertions = [];
12950
12951		function getOffset(offset) {
12952			for (var i = 0, l = insertions.length; i < l; i++) {
12953				var insertion = insertions[i];
12954				if (insertion[0] >= offset)
12955					break;
12956				offset += insertion[1];
12957			}
12958			return offset;
12959		}
12960
12961		function getCode(node) {
12962			return code.substring(getOffset(node.range[0]),
12963					getOffset(node.range[1]));
12964		}
12965
12966		function getBetween(left, right) {
12967			return code.substring(getOffset(left.range[1]),
12968					getOffset(right.range[0]));
12969		}
12970
12971		function replaceCode(node, str) {
12972			var start = getOffset(node.range[0]),
12973				end = getOffset(node.range[1]),
12974				insert = 0;
12975			for (var i = insertions.length - 1; i >= 0; i--) {
12976				if (start > insertions[i][0]) {
12977					insert = i + 1;
12978					break;
12979				}
12980			}
12981			insertions.splice(insert, 0, [start, str.length - end + start]);
12982			code = code.substring(0, start) + str + code.substring(end);
12983		}
12984
12985		function walkAST(node, parent) {
12986			if (!node)
12987				return;
12988			for (var key in node) {
12989				if (key === 'range' || key === 'loc')
12990					continue;
12991				var value = node[key];
12992				if (Array.isArray(value)) {
12993					for (var i = 0, l = value.length; i < l; i++)
12994						walkAST(value[i], node);
12995				} else if (value && typeof value === 'object') {
12996					walkAST(value, node);
12997				}
12998			}
12999			switch (node.type) {
13000			case 'UnaryExpression':
13001				if (node.operator in unaryOperators
13002						&& node.argument.type !== 'Literal') {
13003					var arg = getCode(node.argument);
13004					replaceCode(node, '$__("' + node.operator + '", '
13005							+ arg + ')');
13006				}
13007				break;
13008			case 'BinaryExpression':
13009				if (node.operator in binaryOperators
13010						&& node.left.type !== 'Literal') {
13011					var left = getCode(node.left),
13012						right = getCode(node.right),
13013						between = getBetween(node.left, node.right),
13014						operator = node.operator;
13015					replaceCode(node, '__$__(' + left + ','
13016							+ between.replace(new RegExp('\\' + operator),
13017								'"' + operator + '"')
13018							+ ', ' + right + ')');
13019				}
13020				break;
13021			case 'UpdateExpression':
13022			case 'AssignmentExpression':
13023				var parentType = parent && parent.type;
13024				if (!(
13025						parentType === 'ForStatement'
13026						|| parentType === 'BinaryExpression'
13027							&& /^[=!<>]/.test(parent.operator)
13028						|| parentType === 'MemberExpression' && parent.computed
13029				)) {
13030					if (node.type === 'UpdateExpression') {
13031						var arg = getCode(node.argument),
13032							exp = '__$__(' + arg + ', "' + node.operator[0]
13033									+ '", 1)',
13034							str = arg + ' = ' + exp;
13035						if (!node.prefix
13036								&& (parentType === 'AssignmentExpression'
13037									|| parentType === 'VariableDeclarator')) {
13038							if (getCode(parent.left || parent.id) === arg)
13039								str = exp;
13040							str = arg + '; ' + str;
13041						}
13042						replaceCode(node, str);
13043					} else {
13044						if (/^.=$/.test(node.operator)
13045								&& node.left.type !== 'Literal') {
13046							var left = getCode(node.left),
13047								right = getCode(node.right);
13048							replaceCode(node, left + ' = __$__(' + left + ', "'
13049									+ node.operator[0] + '", ' + right + ')');
13050						}
13051					}
13052				}
13053				break;
13054			}
13055		}
13056		var sourceMap = null,
13057			browser = paper.browser,
13058			version = browser.versionNumber,
13059			lineBreaks = /\r\n|\n|\r/mg;
13060		if (browser.chrome && version >= 30
13061				|| browser.webkit && version >= 537.76
13062				|| browser.firefox && version >= 23) {
13063			var offset = 0;
13064			if (window.location.href.indexOf(url) === 0) {
13065				var html = document.getElementsByTagName('html')[0].innerHTML;
13066				offset = html.substr(0, html.indexOf(code) + 1).match(
13067						lineBreaks).length + 1;
13068			}
13069			var mappings = ['AAAA'];
13070			mappings.length = (code.match(lineBreaks) || []).length + 1 + offset;
13071			sourceMap = {
13072				version: 3,
13073				file: url,
13074				names:[],
13075				mappings: mappings.join(';AACA'),
13076				sourceRoot: '',
13077				sources: [url]
13078			};
13079			var source = options.source || !url && code;
13080			if (source)
13081				sourceMap.sourcesContent = [source];
13082		}
13083		walkAST(parse(code, { ranges: true }));
13084		if (sourceMap) {
13085			code = new Array(offset + 1).join('\n') + code
13086					+ "\n//# sourceMappingURL=data:application/json;base64,"
13087					+ (btoa(unescape(encodeURIComponent(
13088						JSON.stringify(sourceMap)))))
13089					+ "\n//# sourceURL=" + (url || 'paperscript');
13090		}
13091		return code;
13092	}
13093
13094	function execute(code, scope, url, options) {
13095		paper = scope;
13096		var view = scope.getView(),
13097			tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code)
13098					? new Tool()
13099					: null,
13100			toolHandlers = tool ? tool._events : [],
13101			handlers = ['onFrame', 'onResize'].concat(toolHandlers),
13102			params = [],
13103			args = [],
13104			func;
13105		code = compile(code, url, options);
13106		function expose(scope, hidden) {
13107			for (var key in scope) {
13108				if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)'
13109						+ key.replace(/\$/g, '\\$') + '\\b').test(code)) {
13110					params.push(key);
13111					args.push(scope[key]);
13112				}
13113			}
13114		}
13115		expose({ __$__: __$__, $__: $__, paper: scope, view: view, tool: tool },
13116				true);
13117		expose(scope);
13118		handlers = Base.each(handlers, function(key) {
13119			if (new RegExp('\\s+' + key + '\\b').test(code)) {
13120				params.push(key);
13121				this.push(key + ': ' + key);
13122			}
13123		}, []).join(', ');
13124		if (handlers)
13125			code += '\nreturn { ' + handlers + ' };';
13126		var browser = paper.browser;
13127		if (browser.chrome || browser.firefox) {
13128			var script = document.createElement('script'),
13129				head = document.head || document.getElementsByTagName('head')[0];
13130			if (browser.firefox)
13131				code = '\n' + code;
13132			script.appendChild(document.createTextNode(
13133				'paper._execute = function(' + params + ') {' + code + '\n}'
13134			));
13135			head.appendChild(script);
13136			func = paper._execute;
13137			delete paper._execute;
13138			head.removeChild(script);
13139		} else {
13140			func = Function(params, code);
13141		}
13142		var res = func.apply(scope, args) || {};
13143		Base.each(toolHandlers, function(key) {
13144			var value = res[key];
13145			if (value)
13146				tool[key] = value;
13147		});
13148		if (view) {
13149			if (res.onResize)
13150				view.setOnResize(res.onResize);
13151			view.emit('resize', {
13152				size: view.size,
13153				delta: new Point()
13154			});
13155			if (res.onFrame)
13156				view.setOnFrame(res.onFrame);
13157			view.update();
13158		}
13159	}
13160
13161	function loadScript(script) {
13162		if (/^text\/(?:x-|)paperscript$/.test(script.type)
13163				&& PaperScope.getAttribute(script, 'ignore') !== 'true') {
13164			var canvasId = PaperScope.getAttribute(script, 'canvas'),
13165				canvas = document.getElementById(canvasId),
13166				src = script.src || script.getAttribute('data-src'),
13167				async = PaperScope.hasAttribute(script, 'async'),
13168				scopeAttribute = 'data-paper-scope';
13169			if (!canvas)
13170				throw new Error('Unable to find canvas with id "'
13171						+ canvasId + '"');
13172			var scope = PaperScope.get(canvas.getAttribute(scopeAttribute))
13173						|| new PaperScope().setup(canvas);
13174			canvas.setAttribute(scopeAttribute, scope._id);
13175			if (src) {
13176				Http.request('get', src, function(code) {
13177					execute(code, scope, src);
13178				}, async);
13179			} else {
13180				execute(script.innerHTML, scope, script.baseURI);
13181			}
13182			script.setAttribute('data-paper-ignore', 'true');
13183			return scope;
13184		}
13185	}
13186
13187	function loadAll() {
13188		Base.each(document.getElementsByTagName('script'), loadScript);
13189	}
13190
13191	function load(script) {
13192		return script ? loadScript(script) : loadAll();
13193	}
13194
13195	if (document.readyState === 'complete') {
13196		setTimeout(loadAll);
13197	} else {
13198		DomEvent.add(window, { load: loadAll });
13199	}
13200
13201	return {
13202		compile: compile,
13203		execute: execute,
13204		load: load,
13205		parse: parse
13206	};
13207
13208}).call(this);
13209
13210paper = new (PaperScope.inject(Base.exports, {
13211	enumerable: true,
13212	Base: Base,
13213	Numerical: Numerical,
13214	Key: Key
13215}))();
13216
13217if (typeof define === 'function' && define.amd) {
13218	define('paper', paper);
13219} else if (typeof module === 'object' && module) {
13220	module.exports = paper;
13221}
13222
13223return paper;
13224};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.