client/lib/paper.js
changeset 344 e616c4fff297
parent 343 e2a56a651526
child 345 cb5c0007c9f9
equal deleted inserted replaced
343:e2a56a651526 344:e616c4fff297
     1 /*!
       
     2  * Paper.js v0.9.20 - The Swiss Army Knife of Vector Graphics Scripting.
       
     3  * http://paperjs.org/
       
     4  *
       
     5  * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
       
     6  * http://scratchdisk.com/ & http://jonathanpuckey.com/
       
     7  *
       
     8  * Distributed under the MIT license. See LICENSE file for details.
       
     9  *
       
    10  * All rights reserved.
       
    11  *
       
    12  * Date: Mon Aug 25 14:21:13 2014 +0200
       
    13  *
       
    14  ***
       
    15  *
       
    16  * Straps.js - Class inheritance library with support for bean-style accessors
       
    17  *
       
    18  * Copyright (c) 2006 - 2013 Juerg Lehni
       
    19  * http://scratchdisk.com/
       
    20  *
       
    21  * Distributed under the MIT license.
       
    22  *
       
    23  ***
       
    24  *
       
    25  * Acorn.js
       
    26  * http://marijnhaverbeke.nl/acorn/
       
    27  *
       
    28  * Acorn is a tiny, fast JavaScript parser written in JavaScript,
       
    29  * created by Marijn Haverbeke and released under an MIT license.
       
    30  *
       
    31  */
       
    32 
       
    33 var paper = new function(undefined) {
       
    34 
       
    35 var Base = new function() {
       
    36 	var hidden = /^(statics|enumerable|beans|preserve)$/,
       
    37 
       
    38 		forEach = [].forEach || function(iter, bind) {
       
    39 			for (var i = 0, l = this.length; i < l; i++)
       
    40 				iter.call(bind, this[i], i, this);
       
    41 		},
       
    42 
       
    43 		forIn = function(iter, bind) {
       
    44 			for (var i in this)
       
    45 				if (this.hasOwnProperty(i))
       
    46 					iter.call(bind, this[i], i, this);
       
    47 		},
       
    48 
       
    49 		create = Object.create || function(proto) {
       
    50 			return { __proto__: proto };
       
    51 		},
       
    52 
       
    53 		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
       
    54 			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
       
    55 			return get
       
    56 					? { get: get, set: obj.__lookupSetter__(name),
       
    57 						enumerable: true, configurable: true }
       
    58 					: obj.hasOwnProperty(name)
       
    59 						? { value: obj[name], enumerable: true,
       
    60 							configurable: true, writable: true }
       
    61 						: null;
       
    62 		},
       
    63 
       
    64 		_define = Object.defineProperty || function(obj, name, desc) {
       
    65 			if ((desc.get || desc.set) && obj.__defineGetter__) {
       
    66 				if (desc.get)
       
    67 					obj.__defineGetter__(name, desc.get);
       
    68 				if (desc.set)
       
    69 					obj.__defineSetter__(name, desc.set);
       
    70 			} else {
       
    71 				obj[name] = desc.value;
       
    72 			}
       
    73 			return obj;
       
    74 		},
       
    75 
       
    76 		define = function(obj, name, desc) {
       
    77 			delete obj[name];
       
    78 			return _define(obj, name, desc);
       
    79 		};
       
    80 
       
    81 	function inject(dest, src, enumerable, beans, preserve) {
       
    82 		var beansNames = {};
       
    83 
       
    84 		function field(name, val) {
       
    85 			val = val || (val = describe(src, name))
       
    86 					&& (val.get ? val : val.value);
       
    87 			if (typeof val === 'string' && val[0] === '#')
       
    88 				val = dest[val.substring(1)] || val;
       
    89 			var isFunc = typeof val === 'function',
       
    90 				res = val,
       
    91 				prev = preserve || isFunc
       
    92 						? (val && val.get ? name in dest : dest[name])
       
    93 						: null,
       
    94 				bean;
       
    95 			if (!preserve || !prev) {
       
    96 				if (isFunc && prev)
       
    97 					val.base = prev;
       
    98 				if (isFunc && beans !== false
       
    99 						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
       
   100 					beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
       
   101 				if (!res || isFunc || !res.get || typeof res.get !== 'function'
       
   102 						|| !Base.isPlainObject(res))
       
   103 					res = { value: res, writable: true };
       
   104 				if ((describe(dest, name)
       
   105 						|| { configurable: true }).configurable) {
       
   106 					res.configurable = true;
       
   107 					res.enumerable = enumerable;
       
   108 				}
       
   109 				define(dest, name, res);
       
   110 			}
       
   111 		}
       
   112 		if (src) {
       
   113 			for (var name in src) {
       
   114 				if (src.hasOwnProperty(name) && !hidden.test(name))
       
   115 					field(name);
       
   116 			}
       
   117 			for (var name in beansNames) {
       
   118 				var part = beansNames[name],
       
   119 					set = dest['set' + part],
       
   120 					get = dest['get' + part] || set && dest['is' + part];
       
   121 				if (get && (beans === true || get.length === 0))
       
   122 					field(name, { get: get, set: set });
       
   123 			}
       
   124 		}
       
   125 		return dest;
       
   126 	}
       
   127 
       
   128 	function each(obj, iter, bind) {
       
   129 		if (obj)
       
   130 			('length' in obj && !obj.getLength
       
   131 					&& typeof obj.length === 'number'
       
   132 				? forEach
       
   133 				: forIn).call(obj, iter, bind = bind || obj);
       
   134 		return bind;
       
   135 	}
       
   136 
       
   137 	function set(obj, props) {
       
   138 		for (var i in props)
       
   139 			if (props.hasOwnProperty(i))
       
   140 				obj[i] = props[i];
       
   141 		return obj;
       
   142 	}
       
   143 
       
   144 	return inject(function Base() {
       
   145 		for (var i = 0, l = arguments.length; i < l; i++)
       
   146 			set(this, arguments[i]);
       
   147 	}, {
       
   148 		inject: function(src) {
       
   149 			if (src) {
       
   150 				var statics = src.statics === true ? src : src.statics,
       
   151 					beans = src.beans,
       
   152 					preserve = src.preserve;
       
   153 				if (statics !== src)
       
   154 					inject(this.prototype, src, src.enumerable, beans, preserve);
       
   155 				inject(this, statics, true, beans, preserve);
       
   156 			}
       
   157 			for (var i = 1, l = arguments.length; i < l; i++)
       
   158 				this.inject(arguments[i]);
       
   159 			return this;
       
   160 		},
       
   161 
       
   162 		extend: function() {
       
   163 			var base = this,
       
   164 				ctor;
       
   165 			for (var i = 0, l = arguments.length; i < l; i++)
       
   166 				if (ctor = arguments[i].initialize)
       
   167 					break;
       
   168 			ctor = ctor || function() {
       
   169 				base.apply(this, arguments);
       
   170 			};
       
   171 			ctor.prototype = create(this.prototype);
       
   172 			ctor.base = base;
       
   173 			define(ctor.prototype, 'constructor',
       
   174 					{ value: ctor, writable: true, configurable: true });
       
   175 			inject(ctor, this, true);
       
   176 			return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
       
   177 		}
       
   178 	}, true).inject({
       
   179 		inject: function() {
       
   180 			for (var i = 0, l = arguments.length; i < l; i++) {
       
   181 				var src = arguments[i];
       
   182 				if (src)
       
   183 					inject(this, src, src.enumerable, src.beans, src.preserve);
       
   184 			}
       
   185 			return this;
       
   186 		},
       
   187 
       
   188 		extend: function() {
       
   189 			var res = create(this);
       
   190 			return res.inject.apply(res, arguments);
       
   191 		},
       
   192 
       
   193 		each: function(iter, bind) {
       
   194 			return each(this, iter, bind);
       
   195 		},
       
   196 
       
   197 		set: function(props) {
       
   198 			return set(this, props);
       
   199 		},
       
   200 
       
   201 		clone: function() {
       
   202 			return new this.constructor(this);
       
   203 		},
       
   204 
       
   205 		statics: {
       
   206 			each: each,
       
   207 			create: create,
       
   208 			define: define,
       
   209 			describe: describe,
       
   210 			set: set,
       
   211 
       
   212 			clone: function(obj) {
       
   213 				return set(new obj.constructor(), obj);
       
   214 			},
       
   215 
       
   216 			isPlainObject: function(obj) {
       
   217 				var ctor = obj != null && obj.constructor;
       
   218 				return ctor && (ctor === Object || ctor === Base
       
   219 						|| ctor.name === 'Object');
       
   220 			},
       
   221 
       
   222 			pick: function() {
       
   223 				for (var i = 0, l = arguments.length; i < l; i++)
       
   224 					if (arguments[i] !== undefined)
       
   225 						return arguments[i];
       
   226 			}
       
   227 		}
       
   228 	});
       
   229 };
       
   230 
       
   231 if (typeof module !== 'undefined')
       
   232 	module.exports = Base;
       
   233 
       
   234 if (!Array.isArray) {
       
   235 	Array.isArray = function(obj) {
       
   236 		return Object.prototype.toString.call(obj) === '[object Array]';
       
   237 	};
       
   238 }
       
   239 
       
   240 if (!document.head) {
       
   241 	document.head = document.getElementsByTagName('head')[0];
       
   242 }
       
   243 
       
   244 Base.inject({
       
   245 	toString: function() {
       
   246 		return this._id != null
       
   247 			?  (this._class || 'Object') + (this._name
       
   248 				? " '" + this._name + "'"
       
   249 				: ' @' + this._id)
       
   250 			: '{ ' + Base.each(this, function(value, key) {
       
   251 				if (!/^_/.test(key)) {
       
   252 					var type = typeof value;
       
   253 					this.push(key + ': ' + (type === 'number'
       
   254 							? Formatter.instance.number(value)
       
   255 							: type === 'string' ? "'" + value + "'" : value));
       
   256 				}
       
   257 			}, []).join(', ') + ' }';
       
   258 	},
       
   259 
       
   260 	exportJSON: function(options) {
       
   261 		return Base.exportJSON(this, options);
       
   262 	},
       
   263 
       
   264 	toJSON: function() {
       
   265 		return Base.serialize(this);
       
   266 	},
       
   267 
       
   268 	_set: function(props, exclude, dontCheck) {
       
   269 		if (props && (dontCheck || Base.isPlainObject(props))) {
       
   270 			var orig = props._filtering || props;
       
   271 			for (var key in orig) {
       
   272 				if (key in this && orig.hasOwnProperty(key)
       
   273 						&& (!exclude || !exclude[key])) {
       
   274 					var value = props[key];
       
   275 					if (value !== undefined)
       
   276 						this[key] = value;
       
   277 				}
       
   278 			}
       
   279 			return true;
       
   280 		}
       
   281 	},
       
   282 
       
   283 	statics: {
       
   284 
       
   285 		exports: {
       
   286 			enumerable: true
       
   287 		},
       
   288 
       
   289 		extend: function extend() {
       
   290 			var res = extend.base.apply(this, arguments),
       
   291 				name = res.prototype._class;
       
   292 			if (name && !Base.exports[name])
       
   293 				Base.exports[name] = res;
       
   294 			return res;
       
   295 		},
       
   296 
       
   297 		equals: function(obj1, obj2) {
       
   298 			function checkKeys(o1, o2) {
       
   299 				for (var i in o1)
       
   300 					if (o1.hasOwnProperty(i) && !o2.hasOwnProperty(i))
       
   301 						return false;
       
   302 				return true;
       
   303 			}
       
   304 			if (obj1 === obj2)
       
   305 				return true;
       
   306 			if (obj1 && obj1.equals)
       
   307 				return obj1.equals(obj2);
       
   308 			if (obj2 && obj2.equals)
       
   309 				return obj2.equals(obj1);
       
   310 			if (Array.isArray(obj1) && Array.isArray(obj2)) {
       
   311 				if (obj1.length !== obj2.length)
       
   312 					return false;
       
   313 				for (var i = 0, l = obj1.length; i < l; i++) {
       
   314 					if (!Base.equals(obj1[i], obj2[i]))
       
   315 						return false;
       
   316 				}
       
   317 				return true;
       
   318 			}
       
   319 			if (obj1 && typeof obj1 === 'object'
       
   320 					&& obj2 && typeof obj2 === 'object') {
       
   321 				if (!checkKeys(obj1, obj2) || !checkKeys(obj2, obj1))
       
   322 					return false;
       
   323 				for (var i in obj1) {
       
   324 					if (obj1.hasOwnProperty(i)
       
   325 							&& !Base.equals(obj1[i], obj2[i]))
       
   326 						return false;
       
   327 				}
       
   328 				return true;
       
   329 			}
       
   330 			return false;
       
   331 		},
       
   332 
       
   333 		read: function(list, start, options, length) {
       
   334 			if (this === Base) {
       
   335 				var value = this.peek(list, start);
       
   336 				list.__index++;
       
   337 				return value;
       
   338 			}
       
   339 			var proto = this.prototype,
       
   340 				readIndex = proto._readIndex,
       
   341 				index = start || readIndex && list.__index || 0;
       
   342 			if (!length)
       
   343 				length = list.length - index;
       
   344 			var obj = list[index];
       
   345 			if (obj instanceof this
       
   346 				|| options && options.readNull && obj == null && length <= 1) {
       
   347 				if (readIndex)
       
   348 					list.__index = index + 1;
       
   349 				return obj && options && options.clone ? obj.clone() : obj;
       
   350 			}
       
   351 			obj = Base.create(this.prototype);
       
   352 			if (readIndex)
       
   353 				obj.__read = true;
       
   354 			obj = obj.initialize.apply(obj, index > 0 || length < list.length
       
   355 				? Array.prototype.slice.call(list, index, index + length)
       
   356 				: list) || obj;
       
   357 			if (readIndex) {
       
   358 				list.__index = index + obj.__read;
       
   359 				obj.__read = undefined;
       
   360 			}
       
   361 			return obj;
       
   362 		},
       
   363 
       
   364 		peek: function(list, start) {
       
   365 			return list[list.__index = start || list.__index || 0];
       
   366 		},
       
   367 
       
   368 		remain: function(list) {
       
   369 			return list.length - (list.__index || 0);
       
   370 		},
       
   371 
       
   372 		readAll: function(list, start, options) {
       
   373 			var res = [],
       
   374 				entry;
       
   375 			for (var i = start || 0, l = list.length; i < l; i++) {
       
   376 				res.push(Array.isArray(entry = list[i])
       
   377 						? this.read(entry, 0, options)
       
   378 						: this.read(list, i, options, 1));
       
   379 			}
       
   380 			return res;
       
   381 		},
       
   382 
       
   383 		readNamed: function(list, name, start, options, length) {
       
   384 			var value = this.getNamed(list, name),
       
   385 				hasObject = value !== undefined;
       
   386 			if (hasObject) {
       
   387 				var filtered = list._filtered;
       
   388 				if (!filtered) {
       
   389 					filtered = list._filtered = Base.create(list[0]);
       
   390 					filtered._filtering = list[0];
       
   391 				}
       
   392 				filtered[name] = undefined;
       
   393 			}
       
   394 			return this.read(hasObject ? [value] : list, start, options, length);
       
   395 		},
       
   396 
       
   397 		getNamed: function(list, name) {
       
   398 			var arg = list[0];
       
   399 			if (list._hasObject === undefined)
       
   400 				list._hasObject = list.length === 1 && Base.isPlainObject(arg);
       
   401 			if (list._hasObject)
       
   402 				return name ? arg[name] : list._filtered || arg;
       
   403 		},
       
   404 
       
   405 		hasNamed: function(list, name) {
       
   406 			return !!this.getNamed(list, name);
       
   407 		},
       
   408 
       
   409 		isPlainValue: function(obj, asString) {
       
   410 			return this.isPlainObject(obj) || Array.isArray(obj)
       
   411 					|| asString && typeof obj === 'string';
       
   412 		},
       
   413 
       
   414 		serialize: function(obj, options, compact, dictionary) {
       
   415 			options = options || {};
       
   416 
       
   417 			var root = !dictionary,
       
   418 				res;
       
   419 			if (root) {
       
   420 				options.formatter = new Formatter(options.precision);
       
   421 				dictionary = {
       
   422 					length: 0,
       
   423 					definitions: {},
       
   424 					references: {},
       
   425 					add: function(item, create) {
       
   426 						var id = '#' + item._id,
       
   427 							ref = this.references[id];
       
   428 						if (!ref) {
       
   429 							this.length++;
       
   430 							var res = create.call(item),
       
   431 								name = item._class;
       
   432 							if (name && res[0] !== name)
       
   433 								res.unshift(name);
       
   434 							this.definitions[id] = res;
       
   435 							ref = this.references[id] = [id];
       
   436 						}
       
   437 						return ref;
       
   438 					}
       
   439 				};
       
   440 			}
       
   441 			if (obj && obj._serialize) {
       
   442 				res = obj._serialize(options, dictionary);
       
   443 				var name = obj._class;
       
   444 				if (name && !compact && !res._compact && res[0] !== name)
       
   445 					res.unshift(name);
       
   446 			} else if (Array.isArray(obj)) {
       
   447 				res = [];
       
   448 				for (var i = 0, l = obj.length; i < l; i++)
       
   449 					res[i] = Base.serialize(obj[i], options, compact,
       
   450 							dictionary);
       
   451 				if (compact)
       
   452 					res._compact = true;
       
   453 			} else if (Base.isPlainObject(obj)) {
       
   454 				res = {};
       
   455 				for (var i in obj)
       
   456 					if (obj.hasOwnProperty(i))
       
   457 						res[i] = Base.serialize(obj[i], options, compact,
       
   458 								dictionary);
       
   459 			} else if (typeof obj === 'number') {
       
   460 				res = options.formatter.number(obj, options.precision);
       
   461 			} else {
       
   462 				res = obj;
       
   463 			}
       
   464 			return root && dictionary.length > 0
       
   465 					? [['dictionary', dictionary.definitions], res]
       
   466 					: res;
       
   467 		},
       
   468 
       
   469 		deserialize: function(json, create, _data) {
       
   470 			var res = json;
       
   471 			_data = _data || {};
       
   472 			if (Array.isArray(json)) {
       
   473 				var type = json[0],
       
   474 					isDictionary = type === 'dictionary';
       
   475 				if (!isDictionary) {
       
   476 					if (_data.dictionary && json.length == 1 && /^#/.test(type))
       
   477 						return _data.dictionary[type];
       
   478 					type = Base.exports[type];
       
   479 				}
       
   480 				res = [];
       
   481 				for (var i = type ? 1 : 0, l = json.length; i < l; i++)
       
   482 					res.push(Base.deserialize(json[i], create, _data));
       
   483 				if (isDictionary) {
       
   484 					_data.dictionary = res[0];
       
   485 				} else if (type) {
       
   486 					var args = res;
       
   487 					if (create) {
       
   488 						res = create(type, args);
       
   489 					} else {
       
   490 						res = Base.create(type.prototype);
       
   491 						type.apply(res, args);
       
   492 					}
       
   493 				}
       
   494 			} else if (Base.isPlainObject(json)) {
       
   495 				res = {};
       
   496 				for (var key in json)
       
   497 					res[key] = Base.deserialize(json[key], create, _data);
       
   498 			}
       
   499 			return res;
       
   500 		},
       
   501 
       
   502 		exportJSON: function(obj, options) {
       
   503 			var json = Base.serialize(obj, options);
       
   504 			return options && options.asString === false
       
   505 					? json
       
   506 					: JSON.stringify(json);
       
   507 		},
       
   508 
       
   509 		importJSON: function(json, target) {
       
   510 			return Base.deserialize(
       
   511 					typeof json === 'string' ? JSON.parse(json) : json,
       
   512 					function(type, args) {
       
   513 						var obj = target && target.constructor === type
       
   514 								? target
       
   515 								: Base.create(type.prototype),
       
   516 							isTarget = obj === target;
       
   517 						if (args.length === 1 && obj instanceof Item
       
   518 								&& (isTarget || !(obj instanceof Layer))) {
       
   519 							var arg = args[0];
       
   520 							if (Base.isPlainObject(arg))
       
   521 								arg.insert = false;
       
   522 						}
       
   523 						type.apply(obj, args);
       
   524 						if (isTarget)
       
   525 							target = null;
       
   526 						return obj;
       
   527 					});
       
   528 		},
       
   529 
       
   530 		splice: function(list, items, index, remove) {
       
   531 			var amount = items && items.length,
       
   532 				append = index === undefined;
       
   533 			index = append ? list.length : index;
       
   534 			if (index > list.length)
       
   535 				index = list.length;
       
   536 			for (var i = 0; i < amount; i++)
       
   537 				items[i]._index = index + i;
       
   538 			if (append) {
       
   539 				list.push.apply(list, items);
       
   540 				return [];
       
   541 			} else {
       
   542 				var args = [index, remove];
       
   543 				if (items)
       
   544 					args.push.apply(args, items);
       
   545 				var removed = list.splice.apply(list, args);
       
   546 				for (var i = 0, l = removed.length; i < l; i++)
       
   547 					removed[i]._index = undefined;
       
   548 				for (var i = index + amount, l = list.length; i < l; i++)
       
   549 					list[i]._index = i;
       
   550 				return removed;
       
   551 			}
       
   552 		},
       
   553 
       
   554 		capitalize: function(str) {
       
   555 			return str.replace(/\b[a-z]/g, function(match) {
       
   556 				return match.toUpperCase();
       
   557 			});
       
   558 		},
       
   559 
       
   560 		camelize: function(str) {
       
   561 			return str.replace(/-(.)/g, function(all, chr) {
       
   562 				return chr.toUpperCase();
       
   563 			});
       
   564 		},
       
   565 
       
   566 		hyphenate: function(str) {
       
   567 			return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
       
   568 		}
       
   569 	}
       
   570 });
       
   571 
       
   572 var Callback = {
       
   573 	attach: function(type, func) {
       
   574 		if (typeof type !== 'string') {
       
   575 			Base.each(type, function(value, key) {
       
   576 				this.attach(key, value);
       
   577 			}, this);
       
   578 			return;
       
   579 		}
       
   580 		var entry = this._eventTypes[type];
       
   581 		if (entry) {
       
   582 			var handlers = this._handlers = this._handlers || {};
       
   583 			handlers = handlers[type] = handlers[type] || [];
       
   584 			if (handlers.indexOf(func) == -1) {
       
   585 				handlers.push(func);
       
   586 				if (entry.install && handlers.length == 1)
       
   587 					entry.install.call(this, type);
       
   588 			}
       
   589 		}
       
   590 	},
       
   591 
       
   592 	detach: function(type, func) {
       
   593 		if (typeof type !== 'string') {
       
   594 			Base.each(type, function(value, key) {
       
   595 				this.detach(key, value);
       
   596 			}, this);
       
   597 			return;
       
   598 		}
       
   599 		var entry = this._eventTypes[type],
       
   600 			handlers = this._handlers && this._handlers[type],
       
   601 			index;
       
   602 		if (entry && handlers) {
       
   603 			if (!func || (index = handlers.indexOf(func)) != -1
       
   604 					&& handlers.length == 1) {
       
   605 				if (entry.uninstall)
       
   606 					entry.uninstall.call(this, type);
       
   607 				delete this._handlers[type];
       
   608 			} else if (index != -1) {
       
   609 				handlers.splice(index, 1);
       
   610 			}
       
   611 		}
       
   612 	},
       
   613 
       
   614 	once: function(type, func) {
       
   615 		this.attach(type, function() {
       
   616 			func.apply(this, arguments);
       
   617 			this.detach(type, func);
       
   618 		});
       
   619 	},
       
   620 
       
   621 	fire: function(type, event) {
       
   622 		var handlers = this._handlers && this._handlers[type];
       
   623 		if (!handlers)
       
   624 			return false;
       
   625 		var args = [].slice.call(arguments, 1),
       
   626 			that = this;
       
   627 		for (var i = 0, l = handlers.length; i < l; i++) {
       
   628 			if (handlers[i].apply(that, args) === false
       
   629 					&& event && event.stop) {
       
   630 				event.stop();
       
   631 				break;
       
   632 			}
       
   633 		}
       
   634 		return true;
       
   635 	},
       
   636 
       
   637 	responds: function(type) {
       
   638 		return !!(this._handlers && this._handlers[type]);
       
   639 	},
       
   640 
       
   641 	on: '#attach',
       
   642 	off: '#detach',
       
   643 	trigger: '#fire',
       
   644 
       
   645 	_installEvents: function(install) {
       
   646 		var handlers = this._handlers,
       
   647 			key = install ? 'install' : 'uninstall';
       
   648 		for (var type in handlers) {
       
   649 			if (handlers[type].length > 0) {
       
   650 				var entry = this._eventTypes[type],
       
   651 					func = entry[key];
       
   652 				if (func)
       
   653 					func.call(this, type);
       
   654 			}
       
   655 		}
       
   656 	},
       
   657 
       
   658 	statics: {
       
   659 		inject: function inject() {
       
   660 			for (var i = 0, l = arguments.length; i < l; i++) {
       
   661 				var src = arguments[i],
       
   662 					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.detach(type, prev);
       
   679 							if (func)
       
   680 								this.attach(type, func);
       
   681 							this[name] = func;
       
   682 						};
       
   683 					});
       
   684 					src._eventTypes = types;
       
   685 				}
       
   686 				inject.base.call(this, src);
       
   687 			}
       
   688 			return this;
       
   689 		}
       
   690 	}
       
   691 };
       
   692 
       
   693 var PaperScope = Base.extend({
       
   694 	_class: 'PaperScope',
       
   695 
       
   696 	initialize: function PaperScope() {
       
   697 		paper = this;
       
   698 		this.settings = new Base({
       
   699 			applyMatrix: true,
       
   700 			handleSize: 4,
       
   701 			hitTolerance: 0
       
   702 		});
       
   703 		this.project = null;
       
   704 		this.projects = [];
       
   705 		this.tools = [];
       
   706 		this.palettes = [];
       
   707 		this._id = PaperScope._id++;
       
   708 		PaperScope._scopes[this._id] = this;
       
   709 		if (!this.support) {
       
   710 			var ctx = CanvasProvider.getContext(1, 1);
       
   711 			PaperScope.prototype.support = {
       
   712 				nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
       
   713 				nativeBlendModes: BlendMode.nativeModes
       
   714 			};
       
   715 			CanvasProvider.release(ctx);
       
   716 		}
       
   717 	},
       
   718 
       
   719 	version: '0.9.20',
       
   720 
       
   721 	getView: function() {
       
   722 		return this.project && this.project.getView();
       
   723 	},
       
   724 
       
   725 	getPaper: function() {
       
   726 		return this;
       
   727 	},
       
   728 
       
   729 	execute: function(code, url, options) {
       
   730 		paper.PaperScript.execute(code, this, url, options);
       
   731 		View.updateFocus();
       
   732 	},
       
   733 
       
   734 	install: function(scope) {
       
   735 		var that = this;
       
   736 		Base.each(['project', 'view', 'tool'], function(key) {
       
   737 			Base.define(scope, key, {
       
   738 				configurable: true,
       
   739 				get: function() {
       
   740 					return that[key];
       
   741 				}
       
   742 			});
       
   743 		});
       
   744 		for (var key in this)
       
   745 			if (!/^_/.test(key) && this[key])
       
   746 				scope[key] = this[key];
       
   747 	},
       
   748 
       
   749 	setup: function(element) {
       
   750 		paper = this;
       
   751 		this.project = new Project(element);
       
   752 		return this;
       
   753 	},
       
   754 
       
   755 	activate: function() {
       
   756 		paper = this;
       
   757 	},
       
   758 
       
   759 	clear: function() {
       
   760 		for (var i = this.projects.length - 1; i >= 0; i--)
       
   761 			this.projects[i].remove();
       
   762 		for (var i = this.tools.length - 1; i >= 0; i--)
       
   763 			this.tools[i].remove();
       
   764 		for (var i = this.palettes.length - 1; i >= 0; i--)
       
   765 			this.palettes[i].remove();
       
   766 	},
       
   767 
       
   768 	remove: function() {
       
   769 		this.clear();
       
   770 		delete PaperScope._scopes[this._id];
       
   771 	},
       
   772 
       
   773 	statics: new function() {
       
   774 		function handleAttribute(name) {
       
   775 			name += 'Attribute';
       
   776 			return function(el, attr) {
       
   777 				return el[name](attr) || el[name]('data-paper-' + attr);
       
   778 			};
       
   779 		}
       
   780 
       
   781 		return {
       
   782 			_scopes: {},
       
   783 			_id: 0,
       
   784 
       
   785 			get: function(id) {
       
   786 				return this._scopes[id] || null;
       
   787 			},
       
   788 
       
   789 			getAttribute: handleAttribute('get'),
       
   790 			hasAttribute: handleAttribute('has')
       
   791 		};
       
   792 	}
       
   793 });
       
   794 
       
   795 var PaperScopeItem = Base.extend(Callback, {
       
   796 
       
   797 	initialize: function(activate) {
       
   798 		this._scope = paper;
       
   799 		this._index = this._scope[this._list].push(this) - 1;
       
   800 		if (activate || !this._scope[this._reference])
       
   801 			this.activate();
       
   802 	},
       
   803 
       
   804 	activate: function() {
       
   805 		if (!this._scope)
       
   806 			return false;
       
   807 		var prev = this._scope[this._reference];
       
   808 		if (prev && prev !== this)
       
   809 			prev.fire('deactivate');
       
   810 		this._scope[this._reference] = this;
       
   811 		this.fire('activate', prev);
       
   812 		return true;
       
   813 	},
       
   814 
       
   815 	isActive: function() {
       
   816 		return this._scope[this._reference] === this;
       
   817 	},
       
   818 
       
   819 	remove: function() {
       
   820 		if (this._index == null)
       
   821 			return false;
       
   822 		Base.splice(this._scope[this._list], null, this._index, 1);
       
   823 		if (this._scope[this._reference] == this)
       
   824 			this._scope[this._reference] = null;
       
   825 		this._scope = null;
       
   826 		return true;
       
   827 	}
       
   828 });
       
   829 
       
   830 var Formatter = Base.extend({
       
   831 	initialize: function(precision) {
       
   832 		this.precision = precision || 5;
       
   833 		this.multiplier = Math.pow(10, this.precision);
       
   834 	},
       
   835 
       
   836 	number: function(val) {
       
   837 		return Math.round(val * this.multiplier) / this.multiplier;
       
   838 	},
       
   839 
       
   840 	pair: function(val1, val2, separator) {
       
   841 		return this.number(val1) + (separator || ',') + this.number(val2);
       
   842 	},
       
   843 
       
   844 	point: function(val, separator) {
       
   845 		return this.number(val.x) + (separator || ',') + this.number(val.y);
       
   846 	},
       
   847 
       
   848 	size: function(val, separator) {
       
   849 		return this.number(val.width) + (separator || ',')
       
   850 				+ this.number(val.height);
       
   851 	},
       
   852 
       
   853 	rectangle: function(val, separator) {
       
   854 		return this.point(val, separator) + (separator || ',')
       
   855 				+ this.size(val, separator);
       
   856 	}
       
   857 });
       
   858 
       
   859 Formatter.instance = new Formatter();
       
   860 
       
   861 var Numerical = new function() {
       
   862 
       
   863 	var abscissas = [
       
   864 		[  0.5773502691896257645091488],
       
   865 		[0,0.7745966692414833770358531],
       
   866 		[  0.3399810435848562648026658,0.8611363115940525752239465],
       
   867 		[0,0.5384693101056830910363144,0.9061798459386639927976269],
       
   868 		[  0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016],
       
   869 		[0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897],
       
   870 		[  0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609],
       
   871 		[0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762],
       
   872 		[  0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640],
       
   873 		[0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380],
       
   874 		[  0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491],
       
   875 		[0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294],
       
   876 		[  0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973],
       
   877 		[0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657],
       
   878 		[  0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542]
       
   879 	];
       
   880 
       
   881 	var weights = [
       
   882 		[1],
       
   883 		[0.8888888888888888888888889,0.5555555555555555555555556],
       
   884 		[0.6521451548625461426269361,0.3478548451374538573730639],
       
   885 		[0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640],
       
   886 		[0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],
       
   887 		[0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114],
       
   888 		[0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314],
       
   889 		[0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922],
       
   890 		[0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688],
       
   891 		[0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537],
       
   892 		[0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160],
       
   893 		[0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216],
       
   894 		[0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329],
       
   895 		[0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284],
       
   896 		[0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806]
       
   897 	];
       
   898 
       
   899 	var abs = Math.abs,
       
   900 		sqrt = Math.sqrt,
       
   901 		pow = Math.pow,
       
   902 		cos = Math.cos,
       
   903 		PI = Math.PI,
       
   904 		TOLERANCE = 10e-6,
       
   905 		EPSILON = 10e-12;
       
   906 
       
   907 	function setupRoots(roots, min, max) {
       
   908 		var unbound = min === undefined,
       
   909 			minE = min - EPSILON,
       
   910 			maxE = max + EPSILON,
       
   911 			count = 0;
       
   912 		return function(root) {
       
   913 			if (unbound || root > minE && root < maxE)
       
   914 				roots[count++] = root < min ? min : root > max ? max : root;
       
   915 			return count;
       
   916 		};
       
   917 	}
       
   918 
       
   919 	return {
       
   920 		TOLERANCE: TOLERANCE,
       
   921 		EPSILON: EPSILON,
       
   922 		KAPPA: 4 * (sqrt(2) - 1) / 3,
       
   923 
       
   924 		isZero: function(val) {
       
   925 			return abs(val) <= EPSILON;
       
   926 		},
       
   927 
       
   928 		integrate: function(f, a, b, n) {
       
   929 			var x = abscissas[n - 2],
       
   930 				w = weights[n - 2],
       
   931 				A = 0.5 * (b - a),
       
   932 				B = A + a,
       
   933 				i = 0,
       
   934 				m = (n + 1) >> 1,
       
   935 				sum = n & 1 ? w[i++] * f(B) : 0;
       
   936 			while (i < m) {
       
   937 				var Ax = A * x[i];
       
   938 				sum += w[i++] * (f(B + Ax) + f(B - Ax));
       
   939 			}
       
   940 			return A * sum;
       
   941 		},
       
   942 
       
   943 		findRoot: function(f, df, x, a, b, n, tolerance) {
       
   944 			for (var i = 0; i < n; i++) {
       
   945 				var fx = f(x),
       
   946 					dx = fx / df(x),
       
   947 					nx = x - dx;
       
   948 				if (abs(dx) < tolerance)
       
   949 					return nx;
       
   950 				if (fx > 0) {
       
   951 					b = x;
       
   952 					x = nx <= a ? 0.5 * (a + b) : nx;
       
   953 				} else {
       
   954 					a = x;
       
   955 					x = nx >= b ? 0.5 * (a + b) : nx;
       
   956 				}
       
   957 			}
       
   958 			return x;
       
   959 		},
       
   960 
       
   961 		solveQuadratic: function(a, b, c, roots, min, max) {
       
   962 			var add = setupRoots(roots, min, max);
       
   963 
       
   964 			if (abs(a) < EPSILON) {
       
   965 				if (abs(b) >= EPSILON)
       
   966 					return add(-c / b);
       
   967 				return abs(c) < EPSILON ? -1 : 0;
       
   968 			}
       
   969 			var p = b / (2 * a);
       
   970 			var q = c / a;
       
   971 			var p2 = p * p;
       
   972 			if (p2 < q - EPSILON)
       
   973 				return 0;
       
   974 			var s = p2 > q ? sqrt(p2 - q) : 0,
       
   975 				count = add(s - p);
       
   976 			if (s > 0)
       
   977 				count = add(-s - p);
       
   978 			return count;
       
   979 		},
       
   980 
       
   981 		solveCubic: function(a, b, c, d, roots, min, max) {
       
   982 			if (abs(a) < EPSILON)
       
   983 				return Numerical.solveQuadratic(b, c, d, roots, min, max);
       
   984 
       
   985 			b /= a;
       
   986 			c /= a;
       
   987 			d /= a;
       
   988 			var add = setupRoots(roots, min, max),
       
   989 				bb = b * b,
       
   990 				p = (bb - 3 * c) / 9,
       
   991 				q = (2 * bb * b - 9 * b * c + 27 * d) / 54,
       
   992 				ppp = p * p * p,
       
   993 				D = q * q - ppp;
       
   994 			b /= 3;
       
   995 			if (abs(D) < EPSILON) {
       
   996 				if (abs(q) < EPSILON)
       
   997 					return add(-b);
       
   998 				var sqp = sqrt(p),
       
   999 					snq = q > 0 ? 1 : -1;
       
  1000 				add(-snq * 2 * sqp - b);
       
  1001 				return add(snq * sqp - b);
       
  1002 			}
       
  1003 			if (D < 0) {
       
  1004 				var sqp = sqrt(p),
       
  1005 					phi = Math.acos(q / (sqp * sqp * sqp)) / 3,
       
  1006 					t = -2 * sqp,
       
  1007 					o = 2 * PI / 3;
       
  1008 				add(t * cos(phi) - b);
       
  1009 				add(t * cos(phi + o) - b);
       
  1010 				return add(t * cos(phi - o) - b);
       
  1011 			}
       
  1012 			var A = (q > 0 ? -1 : 1) * pow(abs(q) + sqrt(D), 1 / 3);
       
  1013 			return add(A + p / A - b);
       
  1014 		}
       
  1015 	};
       
  1016 };
       
  1017 
       
  1018 var Point = Base.extend({
       
  1019 	_class: 'Point',
       
  1020 	_readIndex: true,
       
  1021 
       
  1022 	initialize: function Point(arg0, arg1) {
       
  1023 		var type = typeof arg0;
       
  1024 		if (type === 'number') {
       
  1025 			var hasY = typeof arg1 === 'number';
       
  1026 			this.x = arg0;
       
  1027 			this.y = hasY ? arg1 : arg0;
       
  1028 			if (this.__read)
       
  1029 				this.__read = hasY ? 2 : 1;
       
  1030 		} else if (type === 'undefined' || arg0 === null) {
       
  1031 			this.x = this.y = 0;
       
  1032 			if (this.__read)
       
  1033 				this.__read = arg0 === null ? 1 : 0;
       
  1034 		} else {
       
  1035 			if (Array.isArray(arg0)) {
       
  1036 				this.x = arg0[0];
       
  1037 				this.y = arg0.length > 1 ? arg0[1] : arg0[0];
       
  1038 			} else if (arg0.x != null) {
       
  1039 				this.x = arg0.x;
       
  1040 				this.y = arg0.y;
       
  1041 			} else if (arg0.width != null) {
       
  1042 				this.x = arg0.width;
       
  1043 				this.y = arg0.height;
       
  1044 			} else if (arg0.angle != null) {
       
  1045 				this.x = arg0.length;
       
  1046 				this.y = 0;
       
  1047 				this.setAngle(arg0.angle);
       
  1048 			} else {
       
  1049 				this.x = this.y = 0;
       
  1050 				if (this.__read)
       
  1051 					this.__read = 0;
       
  1052 			}
       
  1053 			if (this.__read)
       
  1054 				this.__read = 1;
       
  1055 		}
       
  1056 	},
       
  1057 
       
  1058 	set: function(x, y) {
       
  1059 		this.x = x;
       
  1060 		this.y = y;
       
  1061 		return this;
       
  1062 	},
       
  1063 
       
  1064 	equals: function(point) {
       
  1065 		return this === point || point
       
  1066 				&& (this.x === point.x && this.y === point.y
       
  1067 					|| Array.isArray(point)
       
  1068 						&& this.x === point[0] && this.y === point[1])
       
  1069 				|| false;
       
  1070 	},
       
  1071 
       
  1072 	clone: function() {
       
  1073 		return new Point(this.x, this.y);
       
  1074 	},
       
  1075 
       
  1076 	toString: function() {
       
  1077 		var f = Formatter.instance;
       
  1078 		return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';
       
  1079 	},
       
  1080 
       
  1081 	_serialize: function(options) {
       
  1082 		var f = options.formatter;
       
  1083 		return [f.number(this.x), f.number(this.y)];
       
  1084 	},
       
  1085 
       
  1086 	getLength: function() {
       
  1087 		return Math.sqrt(this.x * this.x + this.y * this.y);
       
  1088 	},
       
  1089 
       
  1090 	setLength: function(length) {
       
  1091 		if (this.isZero()) {
       
  1092 			var angle = this._angle || 0;
       
  1093 			this.set(
       
  1094 				Math.cos(angle) * length,
       
  1095 				Math.sin(angle) * length
       
  1096 			);
       
  1097 		} else {
       
  1098 			var scale = length / this.getLength();
       
  1099 			if (Numerical.isZero(scale))
       
  1100 				this.getAngle();
       
  1101 			this.set(
       
  1102 				this.x * scale,
       
  1103 				this.y * scale
       
  1104 			);
       
  1105 		}
       
  1106 	},
       
  1107 	getAngle: function() {
       
  1108 		return this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI;
       
  1109 	},
       
  1110 
       
  1111 	setAngle: function(angle) {
       
  1112 		this.setAngleInRadians.call(this, angle * Math.PI / 180);
       
  1113 	},
       
  1114 
       
  1115 	getAngleInDegrees: '#getAngle',
       
  1116 	setAngleInDegrees: '#setAngle',
       
  1117 
       
  1118 	getAngleInRadians: function() {
       
  1119 		if (!arguments.length) {
       
  1120 			return this.isZero()
       
  1121 					? this._angle || 0
       
  1122 					: this._angle = Math.atan2(this.y, this.x);
       
  1123 		} else {
       
  1124 			var point = Point.read(arguments),
       
  1125 				div = this.getLength() * point.getLength();
       
  1126 			if (Numerical.isZero(div)) {
       
  1127 				return NaN;
       
  1128 			} else {
       
  1129 				var a = this.dot(point) / div;
       
  1130 				return Math.acos(a < -1 ? -1 : a > 1 ? 1 : a);
       
  1131 			}
       
  1132 		}
       
  1133 	},
       
  1134 
       
  1135 	setAngleInRadians: function(angle) {
       
  1136 		this._angle = angle;
       
  1137 		if (!this.isZero()) {
       
  1138 			var length = this.getLength();
       
  1139 			this.set(
       
  1140 				Math.cos(angle) * length,
       
  1141 				Math.sin(angle) * length
       
  1142 			);
       
  1143 		}
       
  1144 	},
       
  1145 
       
  1146 	getQuadrant: function() {
       
  1147 		return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;
       
  1148 	}
       
  1149 }, {
       
  1150 	beans: false,
       
  1151 
       
  1152 	getDirectedAngle: function() {
       
  1153 		var point = Point.read(arguments);
       
  1154 		return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;
       
  1155 	},
       
  1156 
       
  1157 	getDistance: function() {
       
  1158 		var point = Point.read(arguments),
       
  1159 			x = point.x - this.x,
       
  1160 			y = point.y - this.y,
       
  1161 			d = x * x + y * y,
       
  1162 			squared = Base.read(arguments);
       
  1163 		return squared ? d : Math.sqrt(d);
       
  1164 	},
       
  1165 
       
  1166 	normalize: function(length) {
       
  1167 		if (length === undefined)
       
  1168 			length = 1;
       
  1169 		var current = this.getLength(),
       
  1170 			scale = current !== 0 ? length / current : 0,
       
  1171 			point = new Point(this.x * scale, this.y * scale);
       
  1172 		if (scale >= 0)
       
  1173 			point._angle = this._angle;
       
  1174 		return point;
       
  1175 	},
       
  1176 
       
  1177 	rotate: function(angle, center) {
       
  1178 		if (angle === 0)
       
  1179 			return this.clone();
       
  1180 		angle = angle * Math.PI / 180;
       
  1181 		var point = center ? this.subtract(center) : this,
       
  1182 			s = Math.sin(angle),
       
  1183 			c = Math.cos(angle);
       
  1184 		point = new Point(
       
  1185 			point.x * c - point.y * s,
       
  1186 			point.x * s + point.y * c
       
  1187 		);
       
  1188 		return center ? point.add(center) : point;
       
  1189 	},
       
  1190 
       
  1191 	transform: function(matrix) {
       
  1192 		return matrix ? matrix._transformPoint(this) : this;
       
  1193 	},
       
  1194 
       
  1195 	add: function() {
       
  1196 		var point = Point.read(arguments);
       
  1197 		return new Point(this.x + point.x, this.y + point.y);
       
  1198 	},
       
  1199 
       
  1200 	subtract: function() {
       
  1201 		var point = Point.read(arguments);
       
  1202 		return new Point(this.x - point.x, this.y - point.y);
       
  1203 	},
       
  1204 
       
  1205 	multiply: function() {
       
  1206 		var point = Point.read(arguments);
       
  1207 		return new Point(this.x * point.x, this.y * point.y);
       
  1208 	},
       
  1209 
       
  1210 	divide: function() {
       
  1211 		var point = Point.read(arguments);
       
  1212 		return new Point(this.x / point.x, this.y / point.y);
       
  1213 	},
       
  1214 
       
  1215 	modulo: function() {
       
  1216 		var point = Point.read(arguments);
       
  1217 		return new Point(this.x % point.x, this.y % point.y);
       
  1218 	},
       
  1219 
       
  1220 	negate: function() {
       
  1221 		return new Point(-this.x, -this.y);
       
  1222 	},
       
  1223 
       
  1224 	isInside: function(rect) {
       
  1225 		return rect.contains(this);
       
  1226 	},
       
  1227 
       
  1228 	isClose: function(point, tolerance) {
       
  1229 		return this.getDistance(point) < tolerance;
       
  1230 	},
       
  1231 
       
  1232 	isColinear: function(point) {
       
  1233 		return Math.abs(this.cross(point)) < 0.00001;
       
  1234 	},
       
  1235 
       
  1236 	isOrthogonal: function(point) {
       
  1237 		return Math.abs(this.dot(point)) < 0.00001;
       
  1238 	},
       
  1239 
       
  1240 	isZero: function() {
       
  1241 		return Numerical.isZero(this.x) && Numerical.isZero(this.y);
       
  1242 	},
       
  1243 
       
  1244 	isNaN: function() {
       
  1245 		return isNaN(this.x) || isNaN(this.y);
       
  1246 	},
       
  1247 
       
  1248 	dot: function() {
       
  1249 		var point = Point.read(arguments);
       
  1250 		return this.x * point.x + this.y * point.y;
       
  1251 	},
       
  1252 
       
  1253 	cross: function() {
       
  1254 		var point = Point.read(arguments);
       
  1255 		return this.x * point.y - this.y * point.x;
       
  1256 	},
       
  1257 
       
  1258 	project: function() {
       
  1259 		var point = Point.read(arguments);
       
  1260 		if (point.isZero()) {
       
  1261 			return new Point(0, 0);
       
  1262 		} else {
       
  1263 			var scale = this.dot(point) / point.dot(point);
       
  1264 			return new Point(
       
  1265 				point.x * scale,
       
  1266 				point.y * scale
       
  1267 			);
       
  1268 		}
       
  1269 	},
       
  1270 
       
  1271 	statics: {
       
  1272 		min: function() {
       
  1273 			var point1 = Point.read(arguments),
       
  1274 				point2 = Point.read(arguments);
       
  1275 			return new Point(
       
  1276 				Math.min(point1.x, point2.x),
       
  1277 				Math.min(point1.y, point2.y)
       
  1278 			);
       
  1279 		},
       
  1280 
       
  1281 		max: function() {
       
  1282 			var point1 = Point.read(arguments),
       
  1283 				point2 = Point.read(arguments);
       
  1284 			return new Point(
       
  1285 				Math.max(point1.x, point2.x),
       
  1286 				Math.max(point1.y, point2.y)
       
  1287 			);
       
  1288 		},
       
  1289 
       
  1290 		random: function() {
       
  1291 			return new Point(Math.random(), Math.random());
       
  1292 		}
       
  1293 	}
       
  1294 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
       
  1295 	var op = Math[name];
       
  1296 	this[name] = function() {
       
  1297 		return new Point(op(this.x), op(this.y));
       
  1298 	};
       
  1299 }, {}));
       
  1300 
       
  1301 var LinkedPoint = Point.extend({
       
  1302 	initialize: function Point(x, y, owner, setter) {
       
  1303 		this._x = x;
       
  1304 		this._y = y;
       
  1305 		this._owner = owner;
       
  1306 		this._setter = setter;
       
  1307 	},
       
  1308 
       
  1309 	set: function(x, y, _dontNotify) {
       
  1310 		this._x = x;
       
  1311 		this._y = y;
       
  1312 		if (!_dontNotify)
       
  1313 			this._owner[this._setter](this);
       
  1314 		return this;
       
  1315 	},
       
  1316 
       
  1317 	getX: function() {
       
  1318 		return this._x;
       
  1319 	},
       
  1320 
       
  1321 	setX: function(x) {
       
  1322 		this._x = x;
       
  1323 		this._owner[this._setter](this);
       
  1324 	},
       
  1325 
       
  1326 	getY: function() {
       
  1327 		return this._y;
       
  1328 	},
       
  1329 
       
  1330 	setY: function(y) {
       
  1331 		this._y = y;
       
  1332 		this._owner[this._setter](this);
       
  1333 	}
       
  1334 });
       
  1335 
       
  1336 var Size = Base.extend({
       
  1337 	_class: 'Size',
       
  1338 	_readIndex: true,
       
  1339 
       
  1340 	initialize: function Size(arg0, arg1) {
       
  1341 		var type = typeof arg0;
       
  1342 		if (type === 'number') {
       
  1343 			var hasHeight = typeof arg1 === 'number';
       
  1344 			this.width = arg0;
       
  1345 			this.height = hasHeight ? arg1 : arg0;
       
  1346 			if (this.__read)
       
  1347 				this.__read = hasHeight ? 2 : 1;
       
  1348 		} else if (type === 'undefined' || arg0 === null) {
       
  1349 			this.width = this.height = 0;
       
  1350 			if (this.__read)
       
  1351 				this.__read = arg0 === null ? 1 : 0;
       
  1352 		} else {
       
  1353 			if (Array.isArray(arg0)) {
       
  1354 				this.width = arg0[0];
       
  1355 				this.height = arg0.length > 1 ? arg0[1] : arg0[0];
       
  1356 			} else if (arg0.width != null) {
       
  1357 				this.width = arg0.width;
       
  1358 				this.height = arg0.height;
       
  1359 			} else if (arg0.x != null) {
       
  1360 				this.width = arg0.x;
       
  1361 				this.height = arg0.y;
       
  1362 			} else {
       
  1363 				this.width = this.height = 0;
       
  1364 				if (this.__read)
       
  1365 					this.__read = 0;
       
  1366 			}
       
  1367 			if (this.__read)
       
  1368 				this.__read = 1;
       
  1369 		}
       
  1370 	},
       
  1371 
       
  1372 	set: function(width, height) {
       
  1373 		this.width = width;
       
  1374 		this.height = height;
       
  1375 		return this;
       
  1376 	},
       
  1377 
       
  1378 	equals: function(size) {
       
  1379 		return size === this || size && (this.width === size.width
       
  1380 				&& this.height === size.height
       
  1381 				|| Array.isArray(size) && this.width === size[0]
       
  1382 					&& this.height === size[1]) || false;
       
  1383 	},
       
  1384 
       
  1385 	clone: function() {
       
  1386 		return new Size(this.width, this.height);
       
  1387 	},
       
  1388 
       
  1389 	toString: function() {
       
  1390 		var f = Formatter.instance;
       
  1391 		return '{ width: ' + f.number(this.width)
       
  1392 				+ ', height: ' + f.number(this.height) + ' }';
       
  1393 	},
       
  1394 
       
  1395 	_serialize: function(options) {
       
  1396 		var f = options.formatter;
       
  1397 		return [f.number(this.width),
       
  1398 				f.number(this.height)];
       
  1399 	},
       
  1400 
       
  1401 	add: function() {
       
  1402 		var size = Size.read(arguments);
       
  1403 		return new Size(this.width + size.width, this.height + size.height);
       
  1404 	},
       
  1405 
       
  1406 	subtract: function() {
       
  1407 		var size = Size.read(arguments);
       
  1408 		return new Size(this.width - size.width, this.height - size.height);
       
  1409 	},
       
  1410 
       
  1411 	multiply: function() {
       
  1412 		var size = Size.read(arguments);
       
  1413 		return new Size(this.width * size.width, this.height * size.height);
       
  1414 	},
       
  1415 
       
  1416 	divide: function() {
       
  1417 		var size = Size.read(arguments);
       
  1418 		return new Size(this.width / size.width, this.height / size.height);
       
  1419 	},
       
  1420 
       
  1421 	modulo: function() {
       
  1422 		var size = Size.read(arguments);
       
  1423 		return new Size(this.width % size.width, this.height % size.height);
       
  1424 	},
       
  1425 
       
  1426 	negate: function() {
       
  1427 		return new Size(-this.width, -this.height);
       
  1428 	},
       
  1429 
       
  1430 	isZero: function() {
       
  1431 		return Numerical.isZero(this.width) && Numerical.isZero(this.height);
       
  1432 	},
       
  1433 
       
  1434 	isNaN: function() {
       
  1435 		return isNaN(this.width) || isNaN(this.height);
       
  1436 	},
       
  1437 
       
  1438 	statics: {
       
  1439 		min: function(size1, size2) {
       
  1440 			return new Size(
       
  1441 				Math.min(size1.width, size2.width),
       
  1442 				Math.min(size1.height, size2.height));
       
  1443 		},
       
  1444 
       
  1445 		max: function(size1, size2) {
       
  1446 			return new Size(
       
  1447 				Math.max(size1.width, size2.width),
       
  1448 				Math.max(size1.height, size2.height));
       
  1449 		},
       
  1450 
       
  1451 		random: function() {
       
  1452 			return new Size(Math.random(), Math.random());
       
  1453 		}
       
  1454 	}
       
  1455 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
       
  1456 	var op = Math[name];
       
  1457 	this[name] = function() {
       
  1458 		return new Size(op(this.width), op(this.height));
       
  1459 	};
       
  1460 }, {}));
       
  1461 
       
  1462 var LinkedSize = Size.extend({
       
  1463 	initialize: function Size(width, height, owner, setter) {
       
  1464 		this._width = width;
       
  1465 		this._height = height;
       
  1466 		this._owner = owner;
       
  1467 		this._setter = setter;
       
  1468 	},
       
  1469 
       
  1470 	set: function(width, height, _dontNotify) {
       
  1471 		this._width = width;
       
  1472 		this._height = height;
       
  1473 		if (!_dontNotify)
       
  1474 			this._owner[this._setter](this);
       
  1475 		return this;
       
  1476 	},
       
  1477 
       
  1478 	getWidth: function() {
       
  1479 		return this._width;
       
  1480 	},
       
  1481 
       
  1482 	setWidth: function(width) {
       
  1483 		this._width = width;
       
  1484 		this._owner[this._setter](this);
       
  1485 	},
       
  1486 
       
  1487 	getHeight: function() {
       
  1488 		return this._height;
       
  1489 	},
       
  1490 
       
  1491 	setHeight: function(height) {
       
  1492 		this._height = height;
       
  1493 		this._owner[this._setter](this);
       
  1494 	}
       
  1495 });
       
  1496 
       
  1497 var Rectangle = Base.extend({
       
  1498 	_class: 'Rectangle',
       
  1499 	_readIndex: true,
       
  1500 	beans: true,
       
  1501 
       
  1502 	initialize: function Rectangle(arg0, arg1, arg2, arg3) {
       
  1503 		var type = typeof arg0,
       
  1504 			read = 0;
       
  1505 		if (type === 'number') {
       
  1506 			this.x = arg0;
       
  1507 			this.y = arg1;
       
  1508 			this.width = arg2;
       
  1509 			this.height = arg3;
       
  1510 			read = 4;
       
  1511 		} else if (type === 'undefined' || arg0 === null) {
       
  1512 			this.x = this.y = this.width = this.height = 0;
       
  1513 			read = arg0 === null ? 1 : 0;
       
  1514 		} else if (arguments.length === 1) {
       
  1515 			if (Array.isArray(arg0)) {
       
  1516 				this.x = arg0[0];
       
  1517 				this.y = arg0[1];
       
  1518 				this.width = arg0[2];
       
  1519 				this.height = arg0[3];
       
  1520 				read = 1;
       
  1521 			} else if (arg0.x !== undefined || arg0.width !== undefined) {
       
  1522 				this.x = arg0.x || 0;
       
  1523 				this.y = arg0.y || 0;
       
  1524 				this.width = arg0.width || 0;
       
  1525 				this.height = arg0.height || 0;
       
  1526 				read = 1;
       
  1527 			} else if (arg0.from === undefined && arg0.to === undefined) {
       
  1528 				this.x = this.y = this.width = this.height = 0;
       
  1529 				this._set(arg0);
       
  1530 				read = 1;
       
  1531 			}
       
  1532 		}
       
  1533 		if (!read) {
       
  1534 			var point = Point.readNamed(arguments, 'from'),
       
  1535 				next = Base.peek(arguments);
       
  1536 			this.x = point.x;
       
  1537 			this.y = point.y;
       
  1538 			if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
       
  1539 				var to = Point.readNamed(arguments, 'to');
       
  1540 				this.width = to.x - point.x;
       
  1541 				this.height = to.y - point.y;
       
  1542 				if (this.width < 0) {
       
  1543 					this.x = to.x;
       
  1544 					this.width = -this.width;
       
  1545 				}
       
  1546 				if (this.height < 0) {
       
  1547 					this.y = to.y;
       
  1548 					this.height = -this.height;
       
  1549 				}
       
  1550 			} else {
       
  1551 				var size = Size.read(arguments);
       
  1552 				this.width = size.width;
       
  1553 				this.height = size.height;
       
  1554 			}
       
  1555 			read = arguments.__index;
       
  1556 		}
       
  1557 		if (this.__read)
       
  1558 			this.__read = read;
       
  1559 	},
       
  1560 
       
  1561 	set: function(x, y, width, height) {
       
  1562 		this.x = x;
       
  1563 		this.y = y;
       
  1564 		this.width = width;
       
  1565 		this.height = height;
       
  1566 		return this;
       
  1567 	},
       
  1568 
       
  1569 	clone: function() {
       
  1570 		return new Rectangle(this.x, this.y, this.width, this.height);
       
  1571 	},
       
  1572 
       
  1573 	equals: function(rect) {
       
  1574 		var rt = Base.isPlainValue(rect)
       
  1575 				? Rectangle.read(arguments)
       
  1576 				: rect;
       
  1577 		return rt === this
       
  1578 				|| rt && this.x === rt.x && this.y === rt.y
       
  1579 					&& this.width === rt.width && this.height === rt.height
       
  1580 				|| false;
       
  1581 	},
       
  1582 
       
  1583 	toString: function() {
       
  1584 		var f = Formatter.instance;
       
  1585 		return '{ x: ' + f.number(this.x)
       
  1586 				+ ', y: ' + f.number(this.y)
       
  1587 				+ ', width: ' + f.number(this.width)
       
  1588 				+ ', height: ' + f.number(this.height)
       
  1589 				+ ' }';
       
  1590 	},
       
  1591 
       
  1592 	_serialize: function(options) {
       
  1593 		var f = options.formatter;
       
  1594 		return [f.number(this.x),
       
  1595 				f.number(this.y),
       
  1596 				f.number(this.width),
       
  1597 				f.number(this.height)];
       
  1598 	},
       
  1599 
       
  1600 	getPoint: function(_dontLink) {
       
  1601 		var ctor = _dontLink ? Point : LinkedPoint;
       
  1602 		return new ctor(this.x, this.y, this, 'setPoint');
       
  1603 	},
       
  1604 
       
  1605 	setPoint: function() {
       
  1606 		var point = Point.read(arguments);
       
  1607 		this.x = point.x;
       
  1608 		this.y = point.y;
       
  1609 	},
       
  1610 
       
  1611 	getSize: function(_dontLink) {
       
  1612 		var ctor = _dontLink ? Size : LinkedSize;
       
  1613 		return new ctor(this.width, this.height, this, 'setSize');
       
  1614 	},
       
  1615 
       
  1616 	setSize: function() {
       
  1617 		var size = Size.read(arguments);
       
  1618 		if (this._fixX)
       
  1619 			this.x += (this.width - size.width) * this._fixX;
       
  1620 		if (this._fixY)
       
  1621 			this.y += (this.height - size.height) * this._fixY;
       
  1622 		this.width = size.width;
       
  1623 		this.height = size.height;
       
  1624 		this._fixW = 1;
       
  1625 		this._fixH = 1;
       
  1626 	},
       
  1627 
       
  1628 	getLeft: function() {
       
  1629 		return this.x;
       
  1630 	},
       
  1631 
       
  1632 	setLeft: function(left) {
       
  1633 		if (!this._fixW)
       
  1634 			this.width -= left - this.x;
       
  1635 		this.x = left;
       
  1636 		this._fixX = 0;
       
  1637 	},
       
  1638 
       
  1639 	getTop: function() {
       
  1640 		return this.y;
       
  1641 	},
       
  1642 
       
  1643 	setTop: function(top) {
       
  1644 		if (!this._fixH)
       
  1645 			this.height -= top - this.y;
       
  1646 		this.y = top;
       
  1647 		this._fixY = 0;
       
  1648 	},
       
  1649 
       
  1650 	getRight: function() {
       
  1651 		return this.x + this.width;
       
  1652 	},
       
  1653 
       
  1654 	setRight: function(right) {
       
  1655 		if (this._fixX !== undefined && this._fixX !== 1)
       
  1656 			this._fixW = 0;
       
  1657 		if (this._fixW)
       
  1658 			this.x = right - this.width;
       
  1659 		else
       
  1660 			this.width = right - this.x;
       
  1661 		this._fixX = 1;
       
  1662 	},
       
  1663 
       
  1664 	getBottom: function() {
       
  1665 		return this.y + this.height;
       
  1666 	},
       
  1667 
       
  1668 	setBottom: function(bottom) {
       
  1669 		if (this._fixY !== undefined && this._fixY !== 1)
       
  1670 			this._fixH = 0;
       
  1671 		if (this._fixH)
       
  1672 			this.y = bottom - this.height;
       
  1673 		else
       
  1674 			this.height = bottom - this.y;
       
  1675 		this._fixY = 1;
       
  1676 	},
       
  1677 
       
  1678 	getCenterX: function() {
       
  1679 		return this.x + this.width * 0.5;
       
  1680 	},
       
  1681 
       
  1682 	setCenterX: function(x) {
       
  1683 		this.x = x - this.width * 0.5;
       
  1684 		this._fixX = 0.5;
       
  1685 	},
       
  1686 
       
  1687 	getCenterY: function() {
       
  1688 		return this.y + this.height * 0.5;
       
  1689 	},
       
  1690 
       
  1691 	setCenterY: function(y) {
       
  1692 		this.y = y - this.height * 0.5;
       
  1693 		this._fixY = 0.5;
       
  1694 	},
       
  1695 
       
  1696 	getCenter: function(_dontLink) {
       
  1697 		var ctor = _dontLink ? Point : LinkedPoint;
       
  1698 		return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');
       
  1699 	},
       
  1700 
       
  1701 	setCenter: function() {
       
  1702 		var point = Point.read(arguments);
       
  1703 		this.setCenterX(point.x);
       
  1704 		this.setCenterY(point.y);
       
  1705 		return this;
       
  1706 	},
       
  1707 
       
  1708 	getArea: function() {
       
  1709 		return this.width * this.height;
       
  1710 	},
       
  1711 
       
  1712 	isEmpty: function() {
       
  1713 		return this.width === 0 || this.height === 0;
       
  1714 	},
       
  1715 
       
  1716 	contains: function(arg) {
       
  1717 		return arg && arg.width !== undefined
       
  1718 				|| (Array.isArray(arg) ? arg : arguments).length == 4
       
  1719 				? this._containsRectangle(Rectangle.read(arguments))
       
  1720 				: this._containsPoint(Point.read(arguments));
       
  1721 	},
       
  1722 
       
  1723 	_containsPoint: function(point) {
       
  1724 		var x = point.x,
       
  1725 			y = point.y;
       
  1726 		return x >= this.x && y >= this.y
       
  1727 				&& x <= this.x + this.width
       
  1728 				&& y <= this.y + this.height;
       
  1729 	},
       
  1730 
       
  1731 	_containsRectangle: function(rect) {
       
  1732 		var x = rect.x,
       
  1733 			y = rect.y;
       
  1734 		return x >= this.x && y >= this.y
       
  1735 				&& x + rect.width <= this.x + this.width
       
  1736 				&& y + rect.height <= this.y + this.height;
       
  1737 	},
       
  1738 
       
  1739 	intersects: function() {
       
  1740 		var rect = Rectangle.read(arguments);
       
  1741 		return rect.x + rect.width > this.x
       
  1742 				&& rect.y + rect.height > this.y
       
  1743 				&& rect.x < this.x + this.width
       
  1744 				&& rect.y < this.y + this.height;
       
  1745 	},
       
  1746 
       
  1747 	touches: function() {
       
  1748 		var rect = Rectangle.read(arguments);
       
  1749 		return rect.x + rect.width >= this.x
       
  1750 				&& rect.y + rect.height >= this.y
       
  1751 				&& rect.x <= this.x + this.width
       
  1752 				&& rect.y <= this.y + this.height;
       
  1753 	},
       
  1754 
       
  1755 	intersect: function() {
       
  1756 		var rect = Rectangle.read(arguments),
       
  1757 			x1 = Math.max(this.x, rect.x),
       
  1758 			y1 = Math.max(this.y, rect.y),
       
  1759 			x2 = Math.min(this.x + this.width, rect.x + rect.width),
       
  1760 			y2 = Math.min(this.y + this.height, rect.y + rect.height);
       
  1761 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1762 	},
       
  1763 
       
  1764 	unite: function() {
       
  1765 		var rect = Rectangle.read(arguments),
       
  1766 			x1 = Math.min(this.x, rect.x),
       
  1767 			y1 = Math.min(this.y, rect.y),
       
  1768 			x2 = Math.max(this.x + this.width, rect.x + rect.width),
       
  1769 			y2 = Math.max(this.y + this.height, rect.y + rect.height);
       
  1770 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1771 	},
       
  1772 
       
  1773 	include: function() {
       
  1774 		var point = Point.read(arguments);
       
  1775 		var x1 = Math.min(this.x, point.x),
       
  1776 			y1 = Math.min(this.y, point.y),
       
  1777 			x2 = Math.max(this.x + this.width, point.x),
       
  1778 			y2 = Math.max(this.y + this.height, point.y);
       
  1779 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1780 	},
       
  1781 
       
  1782 	expand: function() {
       
  1783 		var amount = Size.read(arguments),
       
  1784 			hor = amount.width,
       
  1785 			ver = amount.height;
       
  1786 		return new Rectangle(this.x - hor / 2, this.y - ver / 2,
       
  1787 				this.width + hor, this.height + ver);
       
  1788 	},
       
  1789 
       
  1790 	scale: function(hor, ver) {
       
  1791 		return this.expand(this.width * hor - this.width,
       
  1792 				this.height * (ver === undefined ? hor : ver) - this.height);
       
  1793 	}
       
  1794 }, Base.each([
       
  1795 		['Top', 'Left'], ['Top', 'Right'],
       
  1796 		['Bottom', 'Left'], ['Bottom', 'Right'],
       
  1797 		['Left', 'Center'], ['Top', 'Center'],
       
  1798 		['Right', 'Center'], ['Bottom', 'Center']
       
  1799 	],
       
  1800 	function(parts, index) {
       
  1801 		var part = parts.join('');
       
  1802 		var xFirst = /^[RL]/.test(part);
       
  1803 		if (index >= 4)
       
  1804 			parts[1] += xFirst ? 'Y' : 'X';
       
  1805 		var x = parts[xFirst ? 0 : 1],
       
  1806 			y = parts[xFirst ? 1 : 0],
       
  1807 			getX = 'get' + x,
       
  1808 			getY = 'get' + y,
       
  1809 			setX = 'set' + x,
       
  1810 			setY = 'set' + y,
       
  1811 			get = 'get' + part,
       
  1812 			set = 'set' + part;
       
  1813 		this[get] = function(_dontLink) {
       
  1814 			var ctor = _dontLink ? Point : LinkedPoint;
       
  1815 			return new ctor(this[getX](), this[getY](), this, set);
       
  1816 		};
       
  1817 		this[set] = function() {
       
  1818 			var point = Point.read(arguments);
       
  1819 			this[setX](point.x);
       
  1820 			this[setY](point.y);
       
  1821 		};
       
  1822 	}, {
       
  1823 		beans: true
       
  1824 	}
       
  1825 ));
       
  1826 
       
  1827 var LinkedRectangle = Rectangle.extend({
       
  1828 	initialize: function Rectangle(x, y, width, height, owner, setter) {
       
  1829 		this.set(x, y, width, height, true);
       
  1830 		this._owner = owner;
       
  1831 		this._setter = setter;
       
  1832 	},
       
  1833 
       
  1834 	set: function(x, y, width, height, _dontNotify) {
       
  1835 		this._x = x;
       
  1836 		this._y = y;
       
  1837 		this._width = width;
       
  1838 		this._height = height;
       
  1839 		if (!_dontNotify)
       
  1840 			this._owner[this._setter](this);
       
  1841 		return this;
       
  1842 	}
       
  1843 }, new function() {
       
  1844 	var proto = Rectangle.prototype;
       
  1845 
       
  1846 	return Base.each(['x', 'y', 'width', 'height'], function(key) {
       
  1847 		var part = Base.capitalize(key);
       
  1848 		var internal = '_' + key;
       
  1849 		this['get' + part] = function() {
       
  1850 			return this[internal];
       
  1851 		};
       
  1852 
       
  1853 		this['set' + part] = function(value) {
       
  1854 			this[internal] = value;
       
  1855 			if (!this._dontNotify)
       
  1856 				this._owner[this._setter](this);
       
  1857 		};
       
  1858 	}, Base.each(['Point', 'Size', 'Center',
       
  1859 			'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',
       
  1860 			'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
       
  1861 			'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],
       
  1862 		function(key) {
       
  1863 			var name = 'set' + key;
       
  1864 			this[name] = function() {
       
  1865 				this._dontNotify = true;
       
  1866 				proto[name].apply(this, arguments);
       
  1867 				this._dontNotify = false;
       
  1868 				this._owner[this._setter](this);
       
  1869 			};
       
  1870 		}, {
       
  1871 			isSelected: function() {
       
  1872 				return this._owner._boundsSelected;
       
  1873 			},
       
  1874 
       
  1875 			setSelected: function(selected) {
       
  1876 				var owner = this._owner;
       
  1877 				if (owner.setSelected) {
       
  1878 					owner._boundsSelected = selected;
       
  1879 					owner.setSelected(selected || owner._selectedSegmentState > 0);
       
  1880 				}
       
  1881 			}
       
  1882 		})
       
  1883 	);
       
  1884 });
       
  1885 
       
  1886 var Matrix = Base.extend({
       
  1887 	_class: 'Matrix',
       
  1888 
       
  1889 	initialize: function Matrix(arg) {
       
  1890 		var count = arguments.length,
       
  1891 			ok = true;
       
  1892 		if (count === 6) {
       
  1893 			this.set.apply(this, arguments);
       
  1894 		} else if (count === 1) {
       
  1895 			if (arg instanceof Matrix) {
       
  1896 				this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
       
  1897 			} else if (Array.isArray(arg)) {
       
  1898 				this.set.apply(this, arg);
       
  1899 			} else {
       
  1900 				ok = false;
       
  1901 			}
       
  1902 		} else if (count === 0) {
       
  1903 			this.reset();
       
  1904 		} else {
       
  1905 			ok = false;
       
  1906 		}
       
  1907 		if (!ok)
       
  1908 			throw new Error('Unsupported matrix parameters');
       
  1909 	},
       
  1910 
       
  1911 	set: function(a, c, b, d, tx, ty, _dontNotify) {
       
  1912 		this._a = a;
       
  1913 		this._c = c;
       
  1914 		this._b = b;
       
  1915 		this._d = d;
       
  1916 		this._tx = tx;
       
  1917 		this._ty = ty;
       
  1918 		if (!_dontNotify)
       
  1919 			this._changed();
       
  1920 		return this;
       
  1921 	},
       
  1922 
       
  1923 	_serialize: function(options) {
       
  1924 		return Base.serialize(this.getValues(), options);
       
  1925 	},
       
  1926 
       
  1927 	_changed: function() {
       
  1928 		var owner = this._owner;
       
  1929 		if (owner) {
       
  1930 			if (owner._applyMatrix) {
       
  1931 				owner.transform(null, true);
       
  1932 			} else {
       
  1933 				owner._changed(9);
       
  1934 			}
       
  1935 		}
       
  1936 	},
       
  1937 
       
  1938 	clone: function() {
       
  1939 		return new Matrix(this._a, this._c, this._b, this._d,
       
  1940 				this._tx, this._ty);
       
  1941 	},
       
  1942 
       
  1943 	equals: function(mx) {
       
  1944 		return mx === this || mx && this._a === mx._a && this._b === mx._b
       
  1945 				&& this._c === mx._c && this._d === mx._d
       
  1946 				&& this._tx === mx._tx && this._ty === mx._ty
       
  1947 				|| false;
       
  1948 	},
       
  1949 
       
  1950 	toString: function() {
       
  1951 		var f = Formatter.instance;
       
  1952 		return '[[' + [f.number(this._a), f.number(this._b),
       
  1953 					f.number(this._tx)].join(', ') + '], ['
       
  1954 				+ [f.number(this._c), f.number(this._d),
       
  1955 					f.number(this._ty)].join(', ') + ']]';
       
  1956 	},
       
  1957 
       
  1958 	reset: function(_dontNotify) {
       
  1959 		this._a = this._d = 1;
       
  1960 		this._c = this._b = this._tx = this._ty = 0;
       
  1961 		if (!_dontNotify)
       
  1962 			this._changed();
       
  1963 		return this;
       
  1964 	},
       
  1965 
       
  1966 	apply: function() {
       
  1967 		var owner = this._owner;
       
  1968 		if (owner) {
       
  1969 			owner.transform(null, true);
       
  1970 			return this.isIdentity();
       
  1971 		}
       
  1972 		return false;
       
  1973 	},
       
  1974 
       
  1975 	translate: function() {
       
  1976 		var point = Point.read(arguments),
       
  1977 			x = point.x,
       
  1978 			y = point.y;
       
  1979 		this._tx += x * this._a + y * this._b;
       
  1980 		this._ty += x * this._c + y * this._d;
       
  1981 		this._changed();
       
  1982 		return this;
       
  1983 	},
       
  1984 
       
  1985 	scale: function() {
       
  1986 		var scale = Point.read(arguments),
       
  1987 			center = Point.read(arguments, 0, { readNull: true });
       
  1988 		if (center)
       
  1989 			this.translate(center);
       
  1990 		this._a *= scale.x;
       
  1991 		this._c *= scale.x;
       
  1992 		this._b *= scale.y;
       
  1993 		this._d *= scale.y;
       
  1994 		if (center)
       
  1995 			this.translate(center.negate());
       
  1996 		this._changed();
       
  1997 		return this;
       
  1998 	},
       
  1999 
       
  2000 	rotate: function(angle ) {
       
  2001 		angle *= Math.PI / 180;
       
  2002 		var center = Point.read(arguments, 1),
       
  2003 			x = center.x,
       
  2004 			y = center.y,
       
  2005 			cos = Math.cos(angle),
       
  2006 			sin = Math.sin(angle),
       
  2007 			tx = x - x * cos + y * sin,
       
  2008 			ty = y - x * sin - y * cos,
       
  2009 			a = this._a,
       
  2010 			b = this._b,
       
  2011 			c = this._c,
       
  2012 			d = this._d;
       
  2013 		this._a = cos * a + sin * b;
       
  2014 		this._b = -sin * a + cos * b;
       
  2015 		this._c = cos * c + sin * d;
       
  2016 		this._d = -sin * c + cos * d;
       
  2017 		this._tx += tx * a + ty * b;
       
  2018 		this._ty += tx * c + ty * d;
       
  2019 		this._changed();
       
  2020 		return this;
       
  2021 	},
       
  2022 
       
  2023 	shear: function() {
       
  2024 		var shear = Point.read(arguments),
       
  2025 			center = Point.read(arguments, 0, { readNull: true });
       
  2026 		if (center)
       
  2027 			this.translate(center);
       
  2028 		var a = this._a,
       
  2029 			c = this._c;
       
  2030 		this._a += shear.y * this._b;
       
  2031 		this._c += shear.y * this._d;
       
  2032 		this._b += shear.x * a;
       
  2033 		this._d += shear.x * c;
       
  2034 		if (center)
       
  2035 			this.translate(center.negate());
       
  2036 		this._changed();
       
  2037 		return this;
       
  2038 	},
       
  2039 
       
  2040 	skew: function() {
       
  2041 		var skew = Point.read(arguments),
       
  2042 			center = Point.read(arguments, 0, { readNull: true }),
       
  2043 			toRadians = Math.PI / 180,
       
  2044 			shear = new Point(Math.tan(skew.x * toRadians),
       
  2045 				Math.tan(skew.y * toRadians));
       
  2046 		return this.shear(shear, center);
       
  2047 	},
       
  2048 
       
  2049 	concatenate: function(mx) {
       
  2050 		var a1 = this._a,
       
  2051 			b1 = this._b,
       
  2052 			c1 = this._c,
       
  2053 			d1 = this._d,
       
  2054 			a2 = mx._a,
       
  2055 			b2 = mx._b,
       
  2056 			c2 = mx._c,
       
  2057 			d2 = mx._d,
       
  2058 			tx2 = mx._tx,
       
  2059 			ty2 = mx._ty;
       
  2060 		this._a = a2 * a1 + c2 * b1;
       
  2061 		this._b = b2 * a1 + d2 * b1;
       
  2062 		this._c = a2 * c1 + c2 * d1;
       
  2063 		this._d = b2 * c1 + d2 * d1;
       
  2064 		this._tx += tx2 * a1 + ty2 * b1;
       
  2065 		this._ty += tx2 * c1 + ty2 * d1;
       
  2066 		this._changed();
       
  2067 		return this;
       
  2068 	},
       
  2069 
       
  2070 	preConcatenate: function(mx) {
       
  2071 		var a1 = this._a,
       
  2072 			b1 = this._b,
       
  2073 			c1 = this._c,
       
  2074 			d1 = this._d,
       
  2075 			tx1 = this._tx,
       
  2076 			ty1 = this._ty,
       
  2077 			a2 = mx._a,
       
  2078 			b2 = mx._b,
       
  2079 			c2 = mx._c,
       
  2080 			d2 = mx._d,
       
  2081 			tx2 = mx._tx,
       
  2082 			ty2 = mx._ty;
       
  2083 		this._a = a2 * a1 + b2 * c1;
       
  2084 		this._b = a2 * b1 + b2 * d1;
       
  2085 		this._c = c2 * a1 + d2 * c1;
       
  2086 		this._d = c2 * b1 + d2 * d1;
       
  2087 		this._tx = a2 * tx1 + b2 * ty1 + tx2;
       
  2088 		this._ty = c2 * tx1 + d2 * ty1 + ty2;
       
  2089 		this._changed();
       
  2090 		return this;
       
  2091 	},
       
  2092 
       
  2093 	chain: function(mx) {
       
  2094 		var a1 = this._a,
       
  2095 			b1 = this._b,
       
  2096 			c1 = this._c,
       
  2097 			d1 = this._d,
       
  2098 			tx1 = this._tx,
       
  2099 			ty1 = this._ty,
       
  2100 			a2 = mx._a,
       
  2101 			b2 = mx._b,
       
  2102 			c2 = mx._c,
       
  2103 			d2 = mx._d,
       
  2104 			tx2 = mx._tx,
       
  2105 			ty2 = mx._ty;
       
  2106 		return new Matrix(
       
  2107 				a2 * a1 + c2 * b1,
       
  2108 				a2 * c1 + c2 * d1,
       
  2109 				b2 * a1 + d2 * b1,
       
  2110 				b2 * c1 + d2 * d1,
       
  2111 				tx1 + tx2 * a1 + ty2 * b1,
       
  2112 				ty1 + tx2 * c1 + ty2 * d1);
       
  2113 	},
       
  2114 
       
  2115 	isIdentity: function() {
       
  2116 		return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
       
  2117 				&& this._tx === 0 && this._ty === 0;
       
  2118 	},
       
  2119 
       
  2120 	orNullIfIdentity: function() {
       
  2121 		return this.isIdentity() ? null : this;
       
  2122 	},
       
  2123 
       
  2124 	isInvertible: function() {
       
  2125 		return !!this._getDeterminant();
       
  2126 	},
       
  2127 
       
  2128 	isSingular: function() {
       
  2129 		return !this._getDeterminant();
       
  2130 	},
       
  2131 
       
  2132 	transform: function( src, dst, count) {
       
  2133 		return arguments.length < 3
       
  2134 			? this._transformPoint(Point.read(arguments))
       
  2135 			: this._transformCoordinates(src, dst, count);
       
  2136 	},
       
  2137 
       
  2138 	_transformPoint: function(point, dest, _dontNotify) {
       
  2139 		var x = point.x,
       
  2140 			y = point.y;
       
  2141 		if (!dest)
       
  2142 			dest = new Point();
       
  2143 		return dest.set(
       
  2144 			x * this._a + y * this._b + this._tx,
       
  2145 			x * this._c + y * this._d + this._ty,
       
  2146 			_dontNotify
       
  2147 		);
       
  2148 	},
       
  2149 
       
  2150 	_transformCoordinates: function(src, dst, count) {
       
  2151 		var i = 0,
       
  2152 			j = 0,
       
  2153 			max = 2 * count;
       
  2154 		while (i < max) {
       
  2155 			var x = src[i++],
       
  2156 				y = src[i++];
       
  2157 			dst[j++] = x * this._a + y * this._b + this._tx;
       
  2158 			dst[j++] = x * this._c + y * this._d + this._ty;
       
  2159 		}
       
  2160 		return dst;
       
  2161 	},
       
  2162 
       
  2163 	_transformCorners: function(rect) {
       
  2164 		var x1 = rect.x,
       
  2165 			y1 = rect.y,
       
  2166 			x2 = x1 + rect.width,
       
  2167 			y2 = y1 + rect.height,
       
  2168 			coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
       
  2169 		return this._transformCoordinates(coords, coords, 4);
       
  2170 	},
       
  2171 
       
  2172 	_transformBounds: function(bounds, dest, _dontNotify) {
       
  2173 		var coords = this._transformCorners(bounds),
       
  2174 			min = coords.slice(0, 2),
       
  2175 			max = coords.slice();
       
  2176 		for (var i = 2; i < 8; i++) {
       
  2177 			var val = coords[i],
       
  2178 				j = i & 1;
       
  2179 			if (val < min[j])
       
  2180 				min[j] = val;
       
  2181 			else if (val > max[j])
       
  2182 				max[j] = val;
       
  2183 		}
       
  2184 		if (!dest)
       
  2185 			dest = new Rectangle();
       
  2186 		return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
       
  2187 				_dontNotify);
       
  2188 	},
       
  2189 
       
  2190 	inverseTransform: function() {
       
  2191 		return this._inverseTransform(Point.read(arguments));
       
  2192 	},
       
  2193 
       
  2194 	_getDeterminant: function() {
       
  2195 		var det = this._a * this._d - this._b * this._c;
       
  2196 		return isFinite(det) && !Numerical.isZero(det)
       
  2197 				&& isFinite(this._tx) && isFinite(this._ty)
       
  2198 				? det : null;
       
  2199 	},
       
  2200 
       
  2201 	_inverseTransform: function(point, dest, _dontNotify) {
       
  2202 		var det = this._getDeterminant();
       
  2203 		if (!det)
       
  2204 			return null;
       
  2205 		var x = point.x - this._tx,
       
  2206 			y = point.y - this._ty;
       
  2207 		if (!dest)
       
  2208 			dest = new Point();
       
  2209 		return dest.set(
       
  2210 			(x * this._d - y * this._b) / det,
       
  2211 			(y * this._a - x * this._c) / det,
       
  2212 			_dontNotify
       
  2213 		);
       
  2214 	},
       
  2215 
       
  2216 	decompose: function() {
       
  2217 		var a = this._a, b = this._b, c = this._c, d = this._d;
       
  2218 		if (Numerical.isZero(a * d - b * c))
       
  2219 			return null;
       
  2220 
       
  2221 		var scaleX = Math.sqrt(a * a + b * b);
       
  2222 		a /= scaleX;
       
  2223 		b /= scaleX;
       
  2224 
       
  2225 		var shear = a * c + b * d;
       
  2226 		c -= a * shear;
       
  2227 		d -= b * shear;
       
  2228 
       
  2229 		var scaleY = Math.sqrt(c * c + d * d);
       
  2230 		c /= scaleY;
       
  2231 		d /= scaleY;
       
  2232 		shear /= scaleY;
       
  2233 
       
  2234 		if (a * d < b * c) {
       
  2235 			a = -a;
       
  2236 			b = -b;
       
  2237 			shear = -shear;
       
  2238 			scaleX = -scaleX;
       
  2239 		}
       
  2240 
       
  2241 		return {
       
  2242 			scaling: new Point(scaleX, scaleY),
       
  2243 			rotation: -Math.atan2(b, a) * 180 / Math.PI,
       
  2244 			shearing: shear
       
  2245 		};
       
  2246 	},
       
  2247 
       
  2248 	getValues: function() {
       
  2249 		return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
       
  2250 	},
       
  2251 
       
  2252 	getTranslation: function() {
       
  2253 		return new Point(this._tx, this._ty);
       
  2254 	},
       
  2255 
       
  2256 	getScaling: function() {
       
  2257 		return (this.decompose() || {}).scaling;
       
  2258 	},
       
  2259 
       
  2260 	getRotation: function() {
       
  2261 		return (this.decompose() || {}).rotation;
       
  2262 	},
       
  2263 
       
  2264 	inverted: function() {
       
  2265 		var det = this._getDeterminant();
       
  2266 		return det && new Matrix(
       
  2267 				this._d / det,
       
  2268 				-this._c / det,
       
  2269 				-this._b / det,
       
  2270 				this._a / det,
       
  2271 				(this._b * this._ty - this._d * this._tx) / det,
       
  2272 				(this._c * this._tx - this._a * this._ty) / det);
       
  2273 	},
       
  2274 
       
  2275 	shiftless: function() {
       
  2276 		return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
       
  2277 	},
       
  2278 
       
  2279 	applyToContext: function(ctx) {
       
  2280 		ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
       
  2281 	}
       
  2282 }, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) {
       
  2283 	var part = Base.capitalize(name),
       
  2284 		prop = '_' + name;
       
  2285 	this['get' + part] = function() {
       
  2286 		return this[prop];
       
  2287 	};
       
  2288 	this['set' + part] = function(value) {
       
  2289 		this[prop] = value;
       
  2290 		this._changed();
       
  2291 	};
       
  2292 }, {}));
       
  2293 
       
  2294 var Line = Base.extend({
       
  2295 	_class: 'Line',
       
  2296 
       
  2297 	initialize: function Line(arg0, arg1, arg2, arg3, arg4) {
       
  2298 		var asVector = false;
       
  2299 		if (arguments.length >= 4) {
       
  2300 			this._px = arg0;
       
  2301 			this._py = arg1;
       
  2302 			this._vx = arg2;
       
  2303 			this._vy = arg3;
       
  2304 			asVector = arg4;
       
  2305 		} else {
       
  2306 			this._px = arg0.x;
       
  2307 			this._py = arg0.y;
       
  2308 			this._vx = arg1.x;
       
  2309 			this._vy = arg1.y;
       
  2310 			asVector = arg2;
       
  2311 		}
       
  2312 		if (!asVector) {
       
  2313 			this._vx -= this._px;
       
  2314 			this._vy -= this._py;
       
  2315 		}
       
  2316 	},
       
  2317 
       
  2318 	getPoint: function() {
       
  2319 		return new Point(this._px, this._py);
       
  2320 	},
       
  2321 
       
  2322 	getVector: function() {
       
  2323 		return new Point(this._vx, this._vy);
       
  2324 	},
       
  2325 
       
  2326 	getLength: function() {
       
  2327 		return this.getVector().getLength();
       
  2328 	},
       
  2329 
       
  2330 	intersect: function(line, isInfinite) {
       
  2331 		return Line.intersect(
       
  2332 				this._px, this._py, this._vx, this._vy,
       
  2333 				line._px, line._py, line._vx, line._vy,
       
  2334 				true, isInfinite);
       
  2335 	},
       
  2336 
       
  2337 	getSide: function(point) {
       
  2338 		return Line.getSide(
       
  2339 				this._px, this._py, this._vx, this._vy,
       
  2340 				point.x, point.y, true);
       
  2341 	},
       
  2342 
       
  2343 	getDistance: function(point) {
       
  2344 		return Math.abs(Line.getSignedDistance(
       
  2345 				this._px, this._py, this._vx, this._vy,
       
  2346 				point.x, point.y, true));
       
  2347 	},
       
  2348 
       
  2349 	statics: {
       
  2350 		intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector,
       
  2351 				isInfinite) {
       
  2352 			if (!asVector) {
       
  2353 				avx -= apx;
       
  2354 				avy -= apy;
       
  2355 				bvx -= bpx;
       
  2356 				bvy -= bpy;
       
  2357 			}
       
  2358 			var cross = bvy * avx - bvx * avy;
       
  2359 			if (!Numerical.isZero(cross)) {
       
  2360 				var dx = apx - bpx,
       
  2361 					dy = apy - bpy,
       
  2362 					ta = (bvx * dy - bvy * dx) / cross,
       
  2363 					tb = (avx * dy - avy * dx) / cross;
       
  2364 				if ((isInfinite || 0 <= ta && ta <= 1)
       
  2365 						&& (isInfinite || 0 <= tb && tb <= 1))
       
  2366 					return new Point(
       
  2367 								apx + ta * avx,
       
  2368 								apy + ta * avy);
       
  2369 			}
       
  2370 		},
       
  2371 
       
  2372 		getSide: function(px, py, vx, vy, x, y, asVector) {
       
  2373 			if (!asVector) {
       
  2374 				vx -= px;
       
  2375 				vy -= py;
       
  2376 			}
       
  2377 			var v2x = x - px,
       
  2378 				v2y = y - py,
       
  2379 				ccw = v2x * vy - v2y * vx;
       
  2380 			if (ccw === 0) {
       
  2381 				ccw = v2x * vx + v2y * vy;
       
  2382 				if (ccw > 0) {
       
  2383 					v2x -= vx;
       
  2384 					v2y -= vy;
       
  2385 					ccw = v2x * vx + v2y * vy;
       
  2386 					if (ccw < 0)
       
  2387 						ccw = 0;
       
  2388 				}
       
  2389 			}
       
  2390 			return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
       
  2391 		},
       
  2392 
       
  2393 		getSignedDistance: function(px, py, vx, vy, x, y, asVector) {
       
  2394 			if (!asVector) {
       
  2395 				vx -= px;
       
  2396 				vy -= py;
       
  2397 			}
       
  2398 			var m = vy / vx,
       
  2399 				b = py - m * px;
       
  2400 			return (y - (m * x) - b) / Math.sqrt(m * m + 1);
       
  2401 		}
       
  2402 	}
       
  2403 });
       
  2404 
       
  2405 var Project = PaperScopeItem.extend({
       
  2406 	_class: 'Project',
       
  2407 	_list: 'projects',
       
  2408 	_reference: 'project',
       
  2409 
       
  2410 	initialize: function Project(element) {
       
  2411 		PaperScopeItem.call(this, true);
       
  2412 		this.layers = [];
       
  2413 		this.symbols = [];
       
  2414 		this._currentStyle = new Style(null, null, this);
       
  2415 		this.activeLayer = new Layer();
       
  2416 		this._view = View.create(this,
       
  2417 				element || CanvasProvider.getCanvas(1, 1));
       
  2418 		this._selectedItems = {};
       
  2419 		this._selectedItemCount = 0;
       
  2420 		this._updateVersion = 0;
       
  2421 	},
       
  2422 
       
  2423 	_serialize: function(options, dictionary) {
       
  2424 		return Base.serialize(this.layers, options, true, dictionary);
       
  2425 	},
       
  2426 
       
  2427 	clear: function() {
       
  2428 		for (var i = this.layers.length - 1; i >= 0; i--)
       
  2429 			this.layers[i].remove();
       
  2430 		this.symbols = [];
       
  2431 	},
       
  2432 
       
  2433 	isEmpty: function() {
       
  2434 		return this.layers.length <= 1
       
  2435 			&& (!this.activeLayer || this.activeLayer.isEmpty());
       
  2436 	},
       
  2437 
       
  2438 	remove: function remove() {
       
  2439 		if (!remove.base.call(this))
       
  2440 			return false;
       
  2441 		if (this._view)
       
  2442 			this._view.remove();
       
  2443 		return true;
       
  2444 	},
       
  2445 
       
  2446 	getView: function() {
       
  2447 		return this._view;
       
  2448 	},
       
  2449 
       
  2450 	getCurrentStyle: function() {
       
  2451 		return this._currentStyle;
       
  2452 	},
       
  2453 
       
  2454 	setCurrentStyle: function(style) {
       
  2455 		this._currentStyle.initialize(style);
       
  2456 	},
       
  2457 
       
  2458 	getIndex: function() {
       
  2459 		return this._index;
       
  2460 	},
       
  2461 
       
  2462 	addChild: function(child) {
       
  2463 		if (child instanceof Layer) {
       
  2464 			Base.splice(this.layers, [child]);
       
  2465 			if (!this.activeLayer)
       
  2466 				this.activeLayer = child;
       
  2467 		} else if (child instanceof Item) {
       
  2468 			(this.activeLayer
       
  2469 				|| this.addChild(new Layer(Item.NO_INSERT))).addChild(child);
       
  2470 		} else {
       
  2471 			child = null;
       
  2472 		}
       
  2473 		return child;
       
  2474 	},
       
  2475 
       
  2476 	getSelectedItems: function() {
       
  2477 		var items = [];
       
  2478 		for (var id in this._selectedItems) {
       
  2479 			var item = this._selectedItems[id];
       
  2480 			if (item.isInserted())
       
  2481 				items.push(item);
       
  2482 		}
       
  2483 		return items;
       
  2484 	},
       
  2485 
       
  2486 	getOptions: function() {
       
  2487 		return this._scope.settings;
       
  2488 	},
       
  2489 
       
  2490 	_updateSelection: function(item) {
       
  2491 		var id = item._id,
       
  2492 			selectedItems = this._selectedItems;
       
  2493 		if (item._selected) {
       
  2494 			if (selectedItems[id] !== item) {
       
  2495 				this._selectedItemCount++;
       
  2496 				selectedItems[id] = item;
       
  2497 			}
       
  2498 		} else if (selectedItems[id] === item) {
       
  2499 			this._selectedItemCount--;
       
  2500 			delete selectedItems[id];
       
  2501 		}
       
  2502 	},
       
  2503 
       
  2504 	selectAll: function() {
       
  2505 		var layers = this.layers;
       
  2506 		for (var i = 0, l = layers.length; i < l; i++)
       
  2507 			layers[i].setFullySelected(true);
       
  2508 	},
       
  2509 
       
  2510 	deselectAll: function() {
       
  2511 		var selectedItems = this._selectedItems;
       
  2512 		for (var i in selectedItems)
       
  2513 			selectedItems[i].setFullySelected(false);
       
  2514 	},
       
  2515 
       
  2516 	hitTest: function() {
       
  2517 		var point = Point.read(arguments),
       
  2518 			options = HitResult.getOptions(Base.read(arguments));
       
  2519 		for (var i = this.layers.length - 1; i >= 0; i--) {
       
  2520 			var res = this.layers[i]._hitTest(point, options);
       
  2521 			if (res) return res;
       
  2522 		}
       
  2523 		return null;
       
  2524 	},
       
  2525 
       
  2526 	getItems: function(match) {
       
  2527 		return Item._getItems(this.layers, match, true);
       
  2528 	},
       
  2529 
       
  2530 	getItem: function(match) {
       
  2531 		return Item._getItems(this.layers, match, false);
       
  2532 	},
       
  2533 
       
  2534 	importJSON: function(json) {
       
  2535 		this.activate();
       
  2536 		var layer = this.activeLayer;
       
  2537 		return Base.importJSON(json, layer && layer.isEmpty() && layer);
       
  2538 	},
       
  2539 
       
  2540 	draw: function(ctx, matrix, pixelRatio) {
       
  2541 		this._updateVersion++;
       
  2542 		ctx.save();
       
  2543 		matrix.applyToContext(ctx);
       
  2544 		var param = new Base({
       
  2545 			offset: new Point(0, 0),
       
  2546 			pixelRatio: pixelRatio,
       
  2547 			viewMatrix: matrix.isIdentity() ? null : matrix,
       
  2548 			matrices: [new Matrix()],
       
  2549 			updateMatrix: true
       
  2550 		});
       
  2551 		for (var i = 0, layers = this.layers, l = layers.length; i < l; i++)
       
  2552 			layers[i].draw(ctx, param);
       
  2553 		ctx.restore();
       
  2554 
       
  2555 		if (this._selectedItemCount > 0) {
       
  2556 			ctx.save();
       
  2557 			ctx.strokeWidth = 1;
       
  2558 			var items = this._selectedItems,
       
  2559 				size = this._scope.settings.handleSize,
       
  2560 				version = this._updateVersion;
       
  2561 			for (var id in items)
       
  2562 				items[id]._drawSelection(ctx, matrix, size, items, version);
       
  2563 			ctx.restore();
       
  2564 		}
       
  2565 	}
       
  2566 });
       
  2567 
       
  2568 var Symbol = Base.extend({
       
  2569 	_class: 'Symbol',
       
  2570 
       
  2571 	initialize: function Symbol(item, dontCenter) {
       
  2572 		this._id = Symbol._id = (Symbol._id || 0) + 1;
       
  2573 		this.project = paper.project;
       
  2574 		this.project.symbols.push(this);
       
  2575 		if (item)
       
  2576 			this.setDefinition(item, dontCenter);
       
  2577 	},
       
  2578 
       
  2579 	_serialize: function(options, dictionary) {
       
  2580 		return dictionary.add(this, function() {
       
  2581 			return Base.serialize([this._class, this._definition],
       
  2582 					options, false, dictionary);
       
  2583 		});
       
  2584 	},
       
  2585 
       
  2586 	_changed: function(flags) {
       
  2587 		if (flags & 8) {
       
  2588 			Item._clearBoundsCache(this);
       
  2589 		}
       
  2590 		if (flags & 1) {
       
  2591 			this.project._needsUpdate = true;
       
  2592 		}
       
  2593 	},
       
  2594 
       
  2595 	getDefinition: function() {
       
  2596 		return this._definition;
       
  2597 	},
       
  2598 
       
  2599 	setDefinition: function(item, _dontCenter) {
       
  2600 		if (item._parentSymbol)
       
  2601 			item = item.clone();
       
  2602 		if (this._definition)
       
  2603 			this._definition._parentSymbol = null;
       
  2604 		this._definition = item;
       
  2605 		item.remove();
       
  2606 		item.setSelected(false);
       
  2607 		if (!_dontCenter)
       
  2608 			item.setPosition(new Point());
       
  2609 		item._parentSymbol = this;
       
  2610 		this._changed(9);
       
  2611 	},
       
  2612 
       
  2613 	place: function(position) {
       
  2614 		return new PlacedSymbol(this, position);
       
  2615 	},
       
  2616 
       
  2617 	clone: function() {
       
  2618 		return new Symbol(this._definition.clone(false));
       
  2619 	}
       
  2620 });
       
  2621 
       
  2622 var Item = Base.extend(Callback, {
       
  2623 	statics: {
       
  2624 		extend: function extend(src) {
       
  2625 			if (src._serializeFields)
       
  2626 				src._serializeFields = new Base(
       
  2627 						this.prototype._serializeFields, src._serializeFields);
       
  2628 			return extend.base.apply(this, arguments);
       
  2629 		},
       
  2630 
       
  2631 		NO_INSERT: { insert: false }
       
  2632 	},
       
  2633 
       
  2634 	_class: 'Item',
       
  2635 	_applyMatrix: true,
       
  2636 	_canApplyMatrix: true,
       
  2637 	_boundsSelected: false,
       
  2638 	_selectChildren: false,
       
  2639 	_serializeFields: {
       
  2640 		name: null,
       
  2641 		applyMatrix: null,
       
  2642 		matrix: new Matrix(),
       
  2643 		pivot: null,
       
  2644 		locked: false,
       
  2645 		visible: true,
       
  2646 		blendMode: 'normal',
       
  2647 		opacity: 1,
       
  2648 		guide: false,
       
  2649 		selected: false,
       
  2650 		clipMask: false,
       
  2651 		data: {}
       
  2652 	},
       
  2653 
       
  2654 	initialize: function Item() {
       
  2655 	},
       
  2656 
       
  2657 	_initialize: function(props, point) {
       
  2658 		var hasProps = props && Base.isPlainObject(props),
       
  2659 			internal = hasProps && props.internal === true,
       
  2660 			matrix = this._matrix = new Matrix(),
       
  2661 			project = paper.project;
       
  2662 		if (!internal)
       
  2663 			this._id = Item._id = (Item._id || 0) + 1;
       
  2664 		this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix;
       
  2665 		if (point)
       
  2666 			matrix.translate(point);
       
  2667 		matrix._owner = this;
       
  2668 		this._style = new Style(project._currentStyle, this, project);
       
  2669 		if (!this._project) {
       
  2670 			if (internal || hasProps && props.insert === false) {
       
  2671 				this._setProject(project);
       
  2672 			} else if (hasProps && props.parent) {
       
  2673 				this.setParent(props.parent);
       
  2674 			} else {
       
  2675 				(project.activeLayer || new Layer()).addChild(this);
       
  2676 			}
       
  2677 		}
       
  2678 		if (hasProps && props !== Item.NO_INSERT)
       
  2679 			this._set(props, { insert: true, parent: true }, true);
       
  2680 		return hasProps;
       
  2681 	},
       
  2682 
       
  2683 	_events: new function() {
       
  2684 
       
  2685 		var mouseFlags = {
       
  2686 			mousedown: {
       
  2687 				mousedown: 1,
       
  2688 				mousedrag: 1,
       
  2689 				click: 1,
       
  2690 				doubleclick: 1
       
  2691 			},
       
  2692 			mouseup: {
       
  2693 				mouseup: 1,
       
  2694 				mousedrag: 1,
       
  2695 				click: 1,
       
  2696 				doubleclick: 1
       
  2697 			},
       
  2698 			mousemove: {
       
  2699 				mousedrag: 1,
       
  2700 				mousemove: 1,
       
  2701 				mouseenter: 1,
       
  2702 				mouseleave: 1
       
  2703 			}
       
  2704 		};
       
  2705 
       
  2706 		var mouseEvent = {
       
  2707 			install: function(type) {
       
  2708 				var counters = this.getView()._eventCounters;
       
  2709 				if (counters) {
       
  2710 					for (var key in mouseFlags) {
       
  2711 						counters[key] = (counters[key] || 0)
       
  2712 								+ (mouseFlags[key][type] || 0);
       
  2713 					}
       
  2714 				}
       
  2715 			},
       
  2716 			uninstall: function(type) {
       
  2717 				var counters = this.getView()._eventCounters;
       
  2718 				if (counters) {
       
  2719 					for (var key in mouseFlags)
       
  2720 						counters[key] -= mouseFlags[key][type] || 0;
       
  2721 				}
       
  2722 			}
       
  2723 		};
       
  2724 
       
  2725 		return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
       
  2726 			'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
       
  2727 			function(name) {
       
  2728 				this[name] = mouseEvent;
       
  2729 			}, {
       
  2730 				onFrame: {
       
  2731 					install: function() {
       
  2732 						this._animateItem(true);
       
  2733 					},
       
  2734 					uninstall: function() {
       
  2735 						this._animateItem(false);
       
  2736 					}
       
  2737 				},
       
  2738 
       
  2739 				onLoad: {}
       
  2740 			}
       
  2741 		);
       
  2742 	},
       
  2743 
       
  2744 	_animateItem: function(animate) {
       
  2745 		this.getView()._animateItem(this, animate);
       
  2746 	},
       
  2747 
       
  2748 	_serialize: function(options, dictionary) {
       
  2749 		var props = {},
       
  2750 			that = this;
       
  2751 
       
  2752 		function serialize(fields) {
       
  2753 			for (var key in fields) {
       
  2754 				var value = that[key];
       
  2755 				if (!Base.equals(value, key === 'leading'
       
  2756 						? fields.fontSize * 1.2 : fields[key])) {
       
  2757 					props[key] = Base.serialize(value, options,
       
  2758 							key !== 'data', dictionary);
       
  2759 				}
       
  2760 			}
       
  2761 		}
       
  2762 
       
  2763 		serialize(this._serializeFields);
       
  2764 		if (!(this instanceof Group))
       
  2765 			serialize(this._style._defaults);
       
  2766 		return [ this._class, props ];
       
  2767 	},
       
  2768 
       
  2769 	_changed: function(flags) {
       
  2770 		var symbol = this._parentSymbol,
       
  2771 			cacheParent = this._parent || symbol,
       
  2772 			project = this._project;
       
  2773 		if (flags & 8) {
       
  2774 			this._bounds = this._position = this._decomposed =
       
  2775 					this._globalMatrix = this._currentPath = undefined;
       
  2776 		}
       
  2777 		if (cacheParent
       
  2778 				&& (flags & 40)) {
       
  2779 			Item._clearBoundsCache(cacheParent);
       
  2780 		}
       
  2781 		if (flags & 2) {
       
  2782 			Item._clearBoundsCache(this);
       
  2783 		}
       
  2784 		if (project) {
       
  2785 			if (flags & 1) {
       
  2786 				project._needsUpdate = true;
       
  2787 			}
       
  2788 			if (project._changes) {
       
  2789 				var entry = project._changesById[this._id];
       
  2790 				if (entry) {
       
  2791 					entry.flags |= flags;
       
  2792 				} else {
       
  2793 					entry = { item: this, flags: flags };
       
  2794 					project._changesById[this._id] = entry;
       
  2795 					project._changes.push(entry);
       
  2796 				}
       
  2797 			}
       
  2798 		}
       
  2799 		if (symbol)
       
  2800 			symbol._changed(flags);
       
  2801 	},
       
  2802 
       
  2803 	set: function(props) {
       
  2804 		if (props)
       
  2805 			this._set(props);
       
  2806 		return this;
       
  2807 	},
       
  2808 
       
  2809 	getId: function() {
       
  2810 		return this._id;
       
  2811 	},
       
  2812 
       
  2813 	getClassName: function() {
       
  2814 		return this._class;
       
  2815 	},
       
  2816 
       
  2817 	getName: function() {
       
  2818 		return this._name;
       
  2819 	},
       
  2820 
       
  2821 	setName: function(name, unique) {
       
  2822 
       
  2823 		if (this._name)
       
  2824 			this._removeNamed();
       
  2825 		if (name === (+name) + '')
       
  2826 			throw new Error(
       
  2827 					'Names consisting only of numbers are not supported.');
       
  2828 		var parent = this._parent;
       
  2829 		if (name && parent) {
       
  2830 			var children = parent._children,
       
  2831 				namedChildren = parent._namedChildren,
       
  2832 				orig = name,
       
  2833 				i = 1;
       
  2834 			while (unique && children[name])
       
  2835 				name = orig + ' ' + (i++);
       
  2836 			(namedChildren[name] = namedChildren[name] || []).push(this);
       
  2837 			children[name] = this;
       
  2838 		}
       
  2839 		this._name = name || undefined;
       
  2840 		this._changed(128);
       
  2841 	},
       
  2842 
       
  2843 	getStyle: function() {
       
  2844 		return this._style;
       
  2845 	},
       
  2846 
       
  2847 	setStyle: function(style) {
       
  2848 		this.getStyle().set(style);
       
  2849 	}
       
  2850 }, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
       
  2851 	function(name) {
       
  2852 		var part = Base.capitalize(name),
       
  2853 			name = '_' + name;
       
  2854 		this['get' + part] = function() {
       
  2855 			return this[name];
       
  2856 		};
       
  2857 		this['set' + part] = function(value) {
       
  2858 			if (value != this[name]) {
       
  2859 				this[name] = value;
       
  2860 				this._changed(name === '_locked'
       
  2861 						? 128 : 129);
       
  2862 			}
       
  2863 		};
       
  2864 	},
       
  2865 {}), {
       
  2866 	beans: true,
       
  2867 
       
  2868 	_locked: false,
       
  2869 
       
  2870 	_visible: true,
       
  2871 
       
  2872 	_blendMode: 'normal',
       
  2873 
       
  2874 	_opacity: 1,
       
  2875 
       
  2876 	_guide: false,
       
  2877 
       
  2878 	isSelected: function() {
       
  2879 		if (this._selectChildren) {
       
  2880 			var children = this._children;
       
  2881 			for (var i = 0, l = children.length; i < l; i++)
       
  2882 				if (children[i].isSelected())
       
  2883 					return true;
       
  2884 		}
       
  2885 		return this._selected;
       
  2886 	},
       
  2887 
       
  2888 	setSelected: function(selected, noChildren) {
       
  2889 		if (!noChildren && this._selectChildren) {
       
  2890 			var children = this._children;
       
  2891 			for (var i = 0, l = children.length; i < l; i++)
       
  2892 				children[i].setSelected(selected);
       
  2893 		}
       
  2894 		if ((selected = !!selected) ^ this._selected) {
       
  2895 			this._selected = selected;
       
  2896 			this._project._updateSelection(this);
       
  2897 			this._changed(129);
       
  2898 		}
       
  2899 	},
       
  2900 
       
  2901 	_selected: false,
       
  2902 
       
  2903 	isFullySelected: function() {
       
  2904 		var children = this._children;
       
  2905 		if (children && this._selected) {
       
  2906 			for (var i = 0, l = children.length; i < l; i++)
       
  2907 				if (!children[i].isFullySelected())
       
  2908 					return false;
       
  2909 			return true;
       
  2910 		}
       
  2911 		return this._selected;
       
  2912 	},
       
  2913 
       
  2914 	setFullySelected: function(selected) {
       
  2915 		var children = this._children;
       
  2916 		if (children) {
       
  2917 			for (var i = 0, l = children.length; i < l; i++)
       
  2918 				children[i].setFullySelected(selected);
       
  2919 		}
       
  2920 		this.setSelected(selected, true);
       
  2921 	},
       
  2922 
       
  2923 	isClipMask: function() {
       
  2924 		return this._clipMask;
       
  2925 	},
       
  2926 
       
  2927 	setClipMask: function(clipMask) {
       
  2928 		if (this._clipMask != (clipMask = !!clipMask)) {
       
  2929 			this._clipMask = clipMask;
       
  2930 			if (clipMask) {
       
  2931 				this.setFillColor(null);
       
  2932 				this.setStrokeColor(null);
       
  2933 			}
       
  2934 			this._changed(129);
       
  2935 			if (this._parent)
       
  2936 				this._parent._changed(1024);
       
  2937 		}
       
  2938 	},
       
  2939 
       
  2940 	_clipMask: false,
       
  2941 
       
  2942 	getData: function() {
       
  2943 		if (!this._data)
       
  2944 			this._data = {};
       
  2945 		return this._data;
       
  2946 	},
       
  2947 
       
  2948 	setData: function(data) {
       
  2949 		this._data = data;
       
  2950 	},
       
  2951 
       
  2952 	getPosition: function(_dontLink) {
       
  2953 		var position = this._position,
       
  2954 			ctor = _dontLink ? Point : LinkedPoint;
       
  2955 		if (!position) {
       
  2956 			var pivot = this._pivot;
       
  2957 			position = this._position = pivot
       
  2958 					? this._matrix._transformPoint(pivot)
       
  2959 					: this.getBounds().getCenter(true);
       
  2960 		}
       
  2961 		return new ctor(position.x, position.y, this, 'setPosition');
       
  2962 	},
       
  2963 
       
  2964 	setPosition: function() {
       
  2965 		this.translate(Point.read(arguments).subtract(this.getPosition(true)));
       
  2966 	},
       
  2967 
       
  2968 	getPivot: function(_dontLink) {
       
  2969 		var pivot = this._pivot;
       
  2970 		if (pivot) {
       
  2971 			var ctor = _dontLink ? Point : LinkedPoint;
       
  2972 			pivot = new ctor(pivot.x, pivot.y, this, 'setPivot');
       
  2973 		}
       
  2974 		return pivot;
       
  2975 	},
       
  2976 
       
  2977 	setPivot: function() {
       
  2978 		this._pivot = Point.read(arguments);
       
  2979 		this._position = undefined;
       
  2980 	},
       
  2981 
       
  2982 	_pivot: null,
       
  2983 
       
  2984 	getRegistration: '#getPivot',
       
  2985 	setRegistration: '#setPivot'
       
  2986 }, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds',
       
  2987 		'internalBounds', 'internalRoughBounds'],
       
  2988 	function(key) {
       
  2989 		var getter = 'get' + Base.capitalize(key),
       
  2990 			match = key.match(/^internal(.*)$/),
       
  2991 			internalGetter = match ? 'get' + match[1] : null;
       
  2992 		this[getter] = function(_matrix) {
       
  2993 			var boundsGetter = this._boundsGetter,
       
  2994 				name = !internalGetter && (typeof boundsGetter === 'string'
       
  2995 						? boundsGetter : boundsGetter && boundsGetter[getter])
       
  2996 						|| getter,
       
  2997 				bounds = this._getCachedBounds(name, _matrix, this,
       
  2998 						internalGetter);
       
  2999 			return key === 'bounds'
       
  3000 					? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
       
  3001 							bounds.height, this, 'setBounds')
       
  3002 					: bounds;
       
  3003 		};
       
  3004 	},
       
  3005 {
       
  3006 	beans: true,
       
  3007 
       
  3008 	_getBounds: function(getter, matrix, cacheItem) {
       
  3009 		var children = this._children;
       
  3010 		if (!children || children.length == 0)
       
  3011 			return new Rectangle();
       
  3012 		var x1 = Infinity,
       
  3013 			x2 = -x1,
       
  3014 			y1 = x1,
       
  3015 			y2 = x2;
       
  3016 		for (var i = 0, l = children.length; i < l; i++) {
       
  3017 			var child = children[i];
       
  3018 			if (child._visible && !child.isEmpty()) {
       
  3019 				var rect = child._getCachedBounds(getter, matrix, cacheItem);
       
  3020 				x1 = Math.min(rect.x, x1);
       
  3021 				y1 = Math.min(rect.y, y1);
       
  3022 				x2 = Math.max(rect.x + rect.width, x2);
       
  3023 				y2 = Math.max(rect.y + rect.height, y2);
       
  3024 			}
       
  3025 		}
       
  3026 		return isFinite(x1)
       
  3027 				? new Rectangle(x1, y1, x2 - x1, y2 - y1)
       
  3028 				: new Rectangle();
       
  3029 	},
       
  3030 
       
  3031 	setBounds: function() {
       
  3032 		var rect = Rectangle.read(arguments),
       
  3033 			bounds = this.getBounds(),
       
  3034 			matrix = new Matrix(),
       
  3035 			center = rect.getCenter();
       
  3036 		matrix.translate(center);
       
  3037 		if (rect.width != bounds.width || rect.height != bounds.height) {
       
  3038 			matrix.scale(
       
  3039 					bounds.width != 0 ? rect.width / bounds.width : 1,
       
  3040 					bounds.height != 0 ? rect.height / bounds.height : 1);
       
  3041 		}
       
  3042 		center = bounds.getCenter();
       
  3043 		matrix.translate(-center.x, -center.y);
       
  3044 		this.transform(matrix);
       
  3045 	},
       
  3046 
       
  3047 	_getCachedBounds: function(getter, matrix, cacheItem, internalGetter) {
       
  3048 		matrix = matrix && matrix.orNullIfIdentity();
       
  3049 		var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(),
       
  3050 			cache = (!matrix || matrix.equals(_matrix)) && getter;
       
  3051 		var cacheParent = this._parent || this._parentSymbol;
       
  3052 		if (cacheParent) {
       
  3053 			var id = cacheItem._id,
       
  3054 				ref = cacheParent._boundsCache = cacheParent._boundsCache || {
       
  3055 					ids: {},
       
  3056 					list: []
       
  3057 				};
       
  3058 			if (!ref.ids[id]) {
       
  3059 				ref.list.push(cacheItem);
       
  3060 				ref.ids[id] = cacheItem;
       
  3061 			}
       
  3062 		}
       
  3063 		if (cache && this._bounds && this._bounds[cache])
       
  3064 			return this._bounds[cache].clone();
       
  3065 		matrix = !matrix
       
  3066 				? _matrix
       
  3067 				: _matrix
       
  3068 					? matrix.chain(_matrix)
       
  3069 					: matrix;
       
  3070 		var bounds = this._getBounds(internalGetter || getter, matrix,
       
  3071 				cacheItem);
       
  3072 		if (cache) {
       
  3073 			if (!this._bounds)
       
  3074 				this._bounds = {};
       
  3075 			var cached = this._bounds[cache] = bounds.clone();
       
  3076 			cached._internal = !!internalGetter;
       
  3077 		}
       
  3078 		return bounds;
       
  3079 	},
       
  3080 
       
  3081 	statics: {
       
  3082 		_clearBoundsCache: function(item) {
       
  3083 			var cache = item._boundsCache;
       
  3084 			if (cache) {
       
  3085 				item._bounds = item._position = item._boundsCache = undefined;
       
  3086 				for (var i = 0, list = cache.list, l = list.length; i < l; i++) {
       
  3087 					var other = list[i];
       
  3088 					if (other !== item) {
       
  3089 						other._bounds = other._position = undefined;
       
  3090 						if (other._boundsCache)
       
  3091 							Item._clearBoundsCache(other);
       
  3092 					}
       
  3093 				}
       
  3094 			}
       
  3095 		}
       
  3096 	}
       
  3097 
       
  3098 }), {
       
  3099 	beans: true,
       
  3100 
       
  3101 	_decompose: function() {
       
  3102 		return this._decomposed = this._matrix.decompose();
       
  3103 	},
       
  3104 
       
  3105 	getRotation: function() {
       
  3106 		var decomposed = this._decomposed || this._decompose();
       
  3107 		return decomposed && decomposed.rotation;
       
  3108 	},
       
  3109 
       
  3110 	setRotation: function(rotation) {
       
  3111 		var current = this.getRotation();
       
  3112 		if (current != null && rotation != null) {
       
  3113 			var decomposed = this._decomposed;
       
  3114 			this.rotate(rotation - current);
       
  3115 			decomposed.rotation = rotation;
       
  3116 			this._decomposed = decomposed;
       
  3117 		}
       
  3118 	},
       
  3119 
       
  3120 	getScaling: function() {
       
  3121 		var decomposed = this._decomposed || this._decompose();
       
  3122 		return decomposed && decomposed.scaling;
       
  3123 	},
       
  3124 
       
  3125 	setScaling: function() {
       
  3126 		var current = this.getScaling();
       
  3127 		if (current != null) {
       
  3128 			var scaling = Point.read(arguments, 0, { clone: true }),
       
  3129 				decomposed = this._decomposed;
       
  3130 			this.scale(scaling.x / current.x, scaling.y / current.y);
       
  3131 			decomposed.scaling = scaling;
       
  3132 			this._decomposed = decomposed;
       
  3133 		}
       
  3134 	},
       
  3135 
       
  3136 	getMatrix: function() {
       
  3137 		return this._matrix;
       
  3138 	},
       
  3139 
       
  3140 	setMatrix: function(matrix) {
       
  3141 		this._matrix.initialize(matrix);
       
  3142 		if (this._applyMatrix) {
       
  3143 			this.transform(null, true);
       
  3144 		} else {
       
  3145 			this._changed(9);
       
  3146 		}
       
  3147 	},
       
  3148 
       
  3149 	getGlobalMatrix: function(_dontClone) {
       
  3150 		var matrix = this._globalMatrix,
       
  3151 			updateVersion = this._project._updateVersion;
       
  3152 		if (matrix && matrix._updateVersion !== updateVersion)
       
  3153 			matrix = null;
       
  3154 		if (!matrix) {
       
  3155 			matrix = this._globalMatrix = this._matrix.clone();
       
  3156 			var parent = this._parent;
       
  3157 			if (parent)
       
  3158 				matrix.preConcatenate(parent.getGlobalMatrix(true));
       
  3159 			matrix._updateVersion = updateVersion;
       
  3160 		}
       
  3161 		return _dontClone ? matrix : matrix.clone();
       
  3162 	},
       
  3163 
       
  3164 	getApplyMatrix: function() {
       
  3165 		return this._applyMatrix;
       
  3166 	},
       
  3167 
       
  3168 	setApplyMatrix: function(transform) {
       
  3169 		if (this._applyMatrix = this._canApplyMatrix && !!transform)
       
  3170 			this.transform(null, true);
       
  3171 	},
       
  3172 
       
  3173 	getTransformContent: '#getApplyMatrix',
       
  3174 	setTransformContent: '#setApplyMatrix',
       
  3175 }, {
       
  3176 	getProject: function() {
       
  3177 		return this._project;
       
  3178 	},
       
  3179 
       
  3180 	_setProject: function(project, installEvents) {
       
  3181 		if (this._project !== project) {
       
  3182 			if (this._project)
       
  3183 				this._installEvents(false);
       
  3184 			this._project = project;
       
  3185 			var children = this._children;
       
  3186 			for (var i = 0, l = children && children.length; i < l; i++)
       
  3187 				children[i]._setProject(project);
       
  3188 			installEvents = true;
       
  3189 		}
       
  3190 		if (installEvents)
       
  3191 			this._installEvents(true);
       
  3192 	},
       
  3193 
       
  3194 	getView: function() {
       
  3195 		return this._project.getView();
       
  3196 	},
       
  3197 
       
  3198 	_installEvents: function _installEvents(install) {
       
  3199 		_installEvents.base.call(this, install);
       
  3200 		var children = this._children;
       
  3201 		for (var i = 0, l = children && children.length; i < l; i++)
       
  3202 			children[i]._installEvents(install);
       
  3203 	},
       
  3204 
       
  3205 	getLayer: function() {
       
  3206 		var parent = this;
       
  3207 		while (parent = parent._parent) {
       
  3208 			if (parent instanceof Layer)
       
  3209 				return parent;
       
  3210 		}
       
  3211 		return null;
       
  3212 	},
       
  3213 
       
  3214 	getParent: function() {
       
  3215 		return this._parent;
       
  3216 	},
       
  3217 
       
  3218 	setParent: function(item) {
       
  3219 		return item.addChild(this);
       
  3220 	},
       
  3221 
       
  3222 	getChildren: function() {
       
  3223 		return this._children;
       
  3224 	},
       
  3225 
       
  3226 	setChildren: function(items) {
       
  3227 		this.removeChildren();
       
  3228 		this.addChildren(items);
       
  3229 	},
       
  3230 
       
  3231 	getFirstChild: function() {
       
  3232 		return this._children && this._children[0] || null;
       
  3233 	},
       
  3234 
       
  3235 	getLastChild: function() {
       
  3236 		return this._children && this._children[this._children.length - 1]
       
  3237 				|| null;
       
  3238 	},
       
  3239 
       
  3240 	getNextSibling: function() {
       
  3241 		return this._parent && this._parent._children[this._index + 1] || null;
       
  3242 	},
       
  3243 
       
  3244 	getPreviousSibling: function() {
       
  3245 		return this._parent && this._parent._children[this._index - 1] || null;
       
  3246 	},
       
  3247 
       
  3248 	getIndex: function() {
       
  3249 		return this._index;
       
  3250 	},
       
  3251 
       
  3252 	equals: function(item) {
       
  3253 		return item === this || item && this._class === item._class
       
  3254 				&& this._style.equals(item._style)
       
  3255 				&& this._matrix.equals(item._matrix)
       
  3256 				&& this._locked === item._locked
       
  3257 				&& this._visible === item._visible
       
  3258 				&& this._blendMode === item._blendMode
       
  3259 				&& this._opacity === item._opacity
       
  3260 				&& this._clipMask === item._clipMask
       
  3261 				&& this._guide === item._guide
       
  3262 				&& this._equals(item)
       
  3263 				|| false;
       
  3264 	},
       
  3265 
       
  3266 	_equals: function(item) {
       
  3267 		return Base.equals(this._children, item._children);
       
  3268 	},
       
  3269 
       
  3270 	clone: function(insert) {
       
  3271 		return this._clone(new this.constructor(Item.NO_INSERT), insert);
       
  3272 	},
       
  3273 
       
  3274 	_clone: function(copy, insert) {
       
  3275 		copy.setStyle(this._style);
       
  3276 		if (this._children) {
       
  3277 			for (var i = 0, l = this._children.length; i < l; i++)
       
  3278 				copy.addChild(this._children[i].clone(false), true);
       
  3279 		}
       
  3280 		if (insert || insert === undefined)
       
  3281 			copy.insertAbove(this);
       
  3282 		var keys = ['_locked', '_visible', '_blendMode', '_opacity',
       
  3283 				'_clipMask', '_guide', '_applyMatrix'];
       
  3284 		for (var i = 0, l = keys.length; i < l; i++) {
       
  3285 			var key = keys[i];
       
  3286 			if (this.hasOwnProperty(key))
       
  3287 				copy[key] = this[key];
       
  3288 		}
       
  3289 		copy._matrix.initialize(this._matrix);
       
  3290 		copy._data = this._data ? Base.clone(this._data) : null;
       
  3291 		copy.setSelected(this._selected);
       
  3292 		if (this._name)
       
  3293 			copy.setName(this._name, true);
       
  3294 		return copy;
       
  3295 	},
       
  3296 
       
  3297 	copyTo: function(itemOrProject) {
       
  3298 		return itemOrProject.addChild(this.clone(false));
       
  3299 	},
       
  3300 
       
  3301 	rasterize: function(resolution) {
       
  3302 		var bounds = this.getStrokeBounds(),
       
  3303 			scale = (resolution || this.getView().getResolution()) / 72,
       
  3304 			topLeft = bounds.getTopLeft().floor(),
       
  3305 			bottomRight = bounds.getBottomRight().ceil(),
       
  3306 			size = new Size(bottomRight.subtract(topLeft)),
       
  3307 			canvas = CanvasProvider.getCanvas(size.multiply(scale)),
       
  3308 			ctx = canvas.getContext('2d'),
       
  3309 			matrix = new Matrix().scale(scale).translate(topLeft.negate());
       
  3310 		ctx.save();
       
  3311 		matrix.applyToContext(ctx);
       
  3312 		this.draw(ctx, new Base({ matrices: [matrix] }));
       
  3313 		ctx.restore();
       
  3314 		var raster = new Raster(Item.NO_INSERT);
       
  3315 		raster.setCanvas(canvas);
       
  3316 		raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
       
  3317 				.scale(1 / scale));
       
  3318 		raster.insertAbove(this);
       
  3319 		return raster;
       
  3320 	},
       
  3321 
       
  3322 	contains: function() {
       
  3323 		return !!this._contains(
       
  3324 				this._matrix._inverseTransform(Point.read(arguments)));
       
  3325 	},
       
  3326 
       
  3327 	_contains: function(point) {
       
  3328 		if (this._children) {
       
  3329 			for (var i = this._children.length - 1; i >= 0; i--) {
       
  3330 				if (this._children[i].contains(point))
       
  3331 					return true;
       
  3332 			}
       
  3333 			return false;
       
  3334 		}
       
  3335 		return point.isInside(this.getInternalBounds());
       
  3336 	},
       
  3337 
       
  3338 	hitTest: function() {
       
  3339 		return this._hitTest(
       
  3340 				Point.read(arguments),
       
  3341 				HitResult.getOptions(Base.read(arguments)));
       
  3342 	},
       
  3343 
       
  3344 	_hitTest: function(point, options) {
       
  3345 		if (this._locked || !this._visible || this._guide && !options.guides
       
  3346 				|| this.isEmpty())
       
  3347 			return null;
       
  3348 
       
  3349 		var matrix = this._matrix,
       
  3350 			parentTotalMatrix = options._totalMatrix,
       
  3351 			view = this.getView(),
       
  3352 			totalMatrix = options._totalMatrix = parentTotalMatrix
       
  3353 					? parentTotalMatrix.chain(matrix)
       
  3354 					: this.getGlobalMatrix().preConcatenate(view._matrix),
       
  3355 			tolerancePadding = options._tolerancePadding = new Size(
       
  3356 						Path._getPenPadding(1, totalMatrix.inverted())
       
  3357 					).multiply(
       
  3358 						Math.max(options.tolerance, 0.00001)
       
  3359 					);
       
  3360 		point = matrix._inverseTransform(point);
       
  3361 
       
  3362 		if (!this._children && !this.getInternalRoughBounds()
       
  3363 				.expand(tolerancePadding.multiply(2))._containsPoint(point))
       
  3364 			return null;
       
  3365 		var checkSelf = !(options.guides && !this._guide
       
  3366 				|| options.selected && !this._selected
       
  3367 				|| options.type && options.type !== Base.hyphenate(this._class)
       
  3368 				|| options.class && !(this instanceof options.class)),
       
  3369 			that = this,
       
  3370 			res;
       
  3371 
       
  3372 		function checkBounds(type, part) {
       
  3373 			var pt = bounds['get' + part]();
       
  3374 			if (point.subtract(pt).divide(tolerancePadding).length <= 1)
       
  3375 				return new HitResult(type, that,
       
  3376 						{ name: Base.hyphenate(part), point: pt });
       
  3377 		}
       
  3378 
       
  3379 		if (checkSelf && (options.center || options.bounds) && this._parent) {
       
  3380 			var bounds = this.getInternalBounds();
       
  3381 			if (options.center)
       
  3382 				res = checkBounds('center', 'Center');
       
  3383 			if (!res && options.bounds) {
       
  3384 				var points = [
       
  3385 					'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
       
  3386 					'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
       
  3387 				];
       
  3388 				for (var i = 0; i < 8 && !res; i++)
       
  3389 					res = checkBounds('bounds', points[i]);
       
  3390 			}
       
  3391 		}
       
  3392 
       
  3393 		var children = !res && this._children;
       
  3394 		if (children) {
       
  3395 			var opts = this._getChildHitTestOptions(options);
       
  3396 			for (var i = children.length - 1; i >= 0 && !res; i--)
       
  3397 				res = children[i]._hitTest(point, opts);
       
  3398 		}
       
  3399 		if (!res && checkSelf)
       
  3400 			res = this._hitTestSelf(point, options);
       
  3401 		if (res && res.point)
       
  3402 			res.point = matrix.transform(res.point);
       
  3403 		options._totalMatrix = parentTotalMatrix;
       
  3404 		return res;
       
  3405 	},
       
  3406 
       
  3407 	_getChildHitTestOptions: function(options) {
       
  3408 		return options;
       
  3409 	},
       
  3410 
       
  3411 	_hitTestSelf: function(point, options) {
       
  3412 		if (options.fill && this.hasFill() && this._contains(point))
       
  3413 			return new HitResult('fill', this);
       
  3414 	},
       
  3415 
       
  3416 	matches: function(match) {
       
  3417 		function matchObject(obj1, obj2) {
       
  3418 			for (var i in obj1) {
       
  3419 				if (obj1.hasOwnProperty(i)) {
       
  3420 					var val1 = obj1[i],
       
  3421 						val2 = obj2[i];
       
  3422 					if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {
       
  3423 						if (!matchObject(val1, val2))
       
  3424 							return false;
       
  3425 					} else if (!Base.equals(val1, val2)) {
       
  3426 						return false;
       
  3427 					}
       
  3428 				}
       
  3429 			}
       
  3430 			return true;
       
  3431 		}
       
  3432 		for (var key in match) {
       
  3433 			if (match.hasOwnProperty(key)) {
       
  3434 				var value = this[key],
       
  3435 					compare = match[key];
       
  3436 				if (value === undefined && key === 'type')
       
  3437 					value = Base.hyphenate(this._class);
       
  3438 				if (/^(constructor|class)$/.test(key)) {
       
  3439 					if (!(this instanceof compare))
       
  3440 						return false;
       
  3441 				} else if (compare instanceof RegExp) {
       
  3442 					if (!compare.test(value))
       
  3443 						return false;
       
  3444 				} else if (typeof compare === 'function') {
       
  3445 					if (!compare(value))
       
  3446 						return false;
       
  3447 				} else if (Base.isPlainObject(compare)) {
       
  3448 					if (!matchObject(compare, value))
       
  3449 						return false;
       
  3450 				} else if (!Base.equals(value, compare)) {
       
  3451 					return false;
       
  3452 				}
       
  3453 			}
       
  3454 		}
       
  3455 		return true;
       
  3456 	},
       
  3457 
       
  3458 	getItems: function(match) {
       
  3459 		return Item._getItems(this._children, match, true);
       
  3460 	},
       
  3461 
       
  3462 	getItem: function(match) {
       
  3463 		return Item._getItems(this._children, match, false);
       
  3464 	},
       
  3465 
       
  3466 	statics: {
       
  3467 		_getItems: function _getItems(children, match, list) {
       
  3468 			var items = list && [];
       
  3469 			for (var i = 0, l = children && children.length; i < l; i++) {
       
  3470 				var child = children[i];
       
  3471 				if (child.matches(match)) {
       
  3472 					if (list) {
       
  3473 						items.push(child);
       
  3474 					} else {
       
  3475 						return child;
       
  3476 					}
       
  3477 				}
       
  3478 				var res = _getItems(child._children, match, list);
       
  3479 				if (list) {
       
  3480 					items.push.apply(items, res);
       
  3481 				} else if (res) {
       
  3482 					return res;
       
  3483 				}
       
  3484 			}
       
  3485 			return list ? items : null;
       
  3486 		}
       
  3487 	}
       
  3488 }, {
       
  3489 
       
  3490 	importJSON: function(json) {
       
  3491 		var res = Base.importJSON(json, this);
       
  3492 		return res !== this
       
  3493 				? this.addChild(res)
       
  3494 				: res;
       
  3495 	},
       
  3496 
       
  3497 	addChild: function(item, _preserve) {
       
  3498 		return this.insertChild(undefined, item, _preserve);
       
  3499 	},
       
  3500 
       
  3501 	insertChild: function(index, item, _preserve) {
       
  3502 		var res = this.insertChildren(index, [item], _preserve);
       
  3503 		return res && res[0];
       
  3504 	},
       
  3505 
       
  3506 	addChildren: function(items, _preserve) {
       
  3507 		return this.insertChildren(this._children.length, items, _preserve);
       
  3508 	},
       
  3509 
       
  3510 	insertChildren: function(index, items, _preserve, _proto) {
       
  3511 		var children = this._children;
       
  3512 		if (children && items && items.length > 0) {
       
  3513 			items = Array.prototype.slice.apply(items);
       
  3514 			for (var i = items.length - 1; i >= 0; i--) {
       
  3515 				var item = items[i];
       
  3516 				if (_proto && !(item instanceof _proto)) {
       
  3517 					items.splice(i, 1);
       
  3518 				} else {
       
  3519 					item._remove(false, true);
       
  3520 				}
       
  3521 			}
       
  3522 			Base.splice(children, items, index, 0);
       
  3523 			var project = this._project,
       
  3524 				notifySelf = project && project._changes;
       
  3525 			for (var i = 0, l = items.length; i < l; i++) {
       
  3526 				var item = items[i];
       
  3527 				item._parent = this;
       
  3528 				item._setProject(this._project, true);
       
  3529 				if (item._name)
       
  3530 					item.setName(item._name);
       
  3531 				if (notifySelf)
       
  3532 					this._changed(5);
       
  3533 			}
       
  3534 			this._changed(11);
       
  3535 		} else {
       
  3536 			items = null;
       
  3537 		}
       
  3538 		return items;
       
  3539 	},
       
  3540 
       
  3541 	_insert: function(above, item, _preserve) {
       
  3542 		if (!item._parent)
       
  3543 			return null;
       
  3544 		var index = item._index + (above ? 1 : 0);
       
  3545 		if (item._parent === this._parent && index > this._index)
       
  3546 			index--;
       
  3547 		return item._parent.insertChild(index, this, _preserve);
       
  3548 	},
       
  3549 
       
  3550 	insertAbove: function(item, _preserve) {
       
  3551 		return this._insert(true, item, _preserve);
       
  3552 	},
       
  3553 
       
  3554 	insertBelow: function(item, _preserve) {
       
  3555 		return this._insert(false, item, _preserve);
       
  3556 	},
       
  3557 
       
  3558 	sendToBack: function() {
       
  3559 		return this._parent.insertChild(0, this);
       
  3560 	},
       
  3561 
       
  3562 	bringToFront: function() {
       
  3563 		return this._parent.addChild(this);
       
  3564 	},
       
  3565 
       
  3566 	appendTop: '#addChild',
       
  3567 
       
  3568 	appendBottom: function(item) {
       
  3569 		return this.insertChild(0, item);
       
  3570 	},
       
  3571 
       
  3572 	moveAbove: '#insertAbove',
       
  3573 
       
  3574 	moveBelow: '#insertBelow',
       
  3575 
       
  3576 	reduce: function() {
       
  3577 		if (this._children && this._children.length === 1) {
       
  3578 			var child = this._children[0].reduce();
       
  3579 			child.insertAbove(this);
       
  3580 			child.setStyle(this._style);
       
  3581 			this.remove();
       
  3582 			return child;
       
  3583 		}
       
  3584 		return this;
       
  3585 	},
       
  3586 
       
  3587 	_removeNamed: function() {
       
  3588 		var parent = this._parent;
       
  3589 		if (parent) {
       
  3590 			var children = parent._children,
       
  3591 				namedChildren = parent._namedChildren,
       
  3592 				name = this._name,
       
  3593 				namedArray = namedChildren[name],
       
  3594 				index = namedArray ? namedArray.indexOf(this) : -1;
       
  3595 			if (index !== -1) {
       
  3596 				if (children[name] == this)
       
  3597 					delete children[name];
       
  3598 				namedArray.splice(index, 1);
       
  3599 				if (namedArray.length) {
       
  3600 					children[name] = namedArray[namedArray.length - 1];
       
  3601 				} else {
       
  3602 					delete namedChildren[name];
       
  3603 				}
       
  3604 			}
       
  3605 		}
       
  3606 	},
       
  3607 
       
  3608 	_remove: function(notifySelf, notifyParent) {
       
  3609 		var parent = this._parent;
       
  3610 		if (parent) {
       
  3611 			if (this._name)
       
  3612 				this._removeNamed();
       
  3613 			if (this._index != null)
       
  3614 				Base.splice(parent._children, null, this._index, 1);
       
  3615 			this._installEvents(false);
       
  3616 			if (notifySelf) {
       
  3617 				var project = this._project;
       
  3618 				if (project && project._changes)
       
  3619 					this._changed(5);
       
  3620 			}
       
  3621 			if (notifyParent)
       
  3622 				parent._changed(11);
       
  3623 			this._parent = null;
       
  3624 			return true;
       
  3625 		}
       
  3626 		return false;
       
  3627 	},
       
  3628 
       
  3629 	remove: function() {
       
  3630 		return this._remove(true, true);
       
  3631 	},
       
  3632 
       
  3633 	removeChildren: function(from, to) {
       
  3634 		if (!this._children)
       
  3635 			return null;
       
  3636 		from = from || 0;
       
  3637 		to = Base.pick(to, this._children.length);
       
  3638 		var removed = Base.splice(this._children, null, from, to - from);
       
  3639 		for (var i = removed.length - 1; i >= 0; i--) {
       
  3640 			removed[i]._remove(true, false);
       
  3641 		}
       
  3642 		if (removed.length > 0)
       
  3643 			this._changed(11);
       
  3644 		return removed;
       
  3645 	},
       
  3646 
       
  3647 	clear: '#removeChildren',
       
  3648 
       
  3649 	reverseChildren: function() {
       
  3650 		if (this._children) {
       
  3651 			this._children.reverse();
       
  3652 			for (var i = 0, l = this._children.length; i < l; i++)
       
  3653 				this._children[i]._index = i;
       
  3654 			this._changed(11);
       
  3655 		}
       
  3656 	},
       
  3657 
       
  3658 	isEmpty: function() {
       
  3659 		return !this._children || this._children.length === 0;
       
  3660 	},
       
  3661 
       
  3662 	isEditable: function() {
       
  3663 		var item = this;
       
  3664 		while (item) {
       
  3665 			if (!item._visible || item._locked)
       
  3666 				return false;
       
  3667 			item = item._parent;
       
  3668 		}
       
  3669 		return true;
       
  3670 	},
       
  3671 
       
  3672 	hasFill: function() {
       
  3673 		return this.getStyle().hasFill();
       
  3674 	},
       
  3675 
       
  3676 	hasStroke: function() {
       
  3677 		return this.getStyle().hasStroke();
       
  3678 	},
       
  3679 
       
  3680 	hasShadow: function() {
       
  3681 		return this.getStyle().hasShadow();
       
  3682 	},
       
  3683 
       
  3684 	_getOrder: function(item) {
       
  3685 		function getList(item) {
       
  3686 			var list = [];
       
  3687 			do {
       
  3688 				list.unshift(item);
       
  3689 			} while (item = item._parent);
       
  3690 			return list;
       
  3691 		}
       
  3692 		var list1 = getList(this),
       
  3693 			list2 = getList(item);
       
  3694 		for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {
       
  3695 			if (list1[i] != list2[i]) {
       
  3696 				return list1[i]._index < list2[i]._index ? 1 : -1;
       
  3697 			}
       
  3698 		}
       
  3699 		return 0;
       
  3700 	},
       
  3701 
       
  3702 	hasChildren: function() {
       
  3703 		return this._children && this._children.length > 0;
       
  3704 	},
       
  3705 
       
  3706 	isInserted: function() {
       
  3707 		return this._parent ? this._parent.isInserted() : false;
       
  3708 	},
       
  3709 
       
  3710 	isAbove: function(item) {
       
  3711 		return this._getOrder(item) === -1;
       
  3712 	},
       
  3713 
       
  3714 	isBelow: function(item) {
       
  3715 		return this._getOrder(item) === 1;
       
  3716 	},
       
  3717 
       
  3718 	isParent: function(item) {
       
  3719 		return this._parent === item;
       
  3720 	},
       
  3721 
       
  3722 	isChild: function(item) {
       
  3723 		return item && item._parent === this;
       
  3724 	},
       
  3725 
       
  3726 	isDescendant: function(item) {
       
  3727 		var parent = this;
       
  3728 		while (parent = parent._parent) {
       
  3729 			if (parent == item)
       
  3730 				return true;
       
  3731 		}
       
  3732 		return false;
       
  3733 	},
       
  3734 
       
  3735 	isAncestor: function(item) {
       
  3736 		return item ? item.isDescendant(this) : false;
       
  3737 	},
       
  3738 
       
  3739 	isGroupedWith: function(item) {
       
  3740 		var parent = this._parent;
       
  3741 		while (parent) {
       
  3742 			if (parent._parent
       
  3743 				&& /^(Group|Layer|CompoundPath)$/.test(parent._class)
       
  3744 				&& item.isDescendant(parent))
       
  3745 					return true;
       
  3746 			parent = parent._parent;
       
  3747 		}
       
  3748 		return false;
       
  3749 	},
       
  3750 
       
  3751 	translate: function() {
       
  3752 		var mx = new Matrix();
       
  3753 		return this.transform(mx.translate.apply(mx, arguments));
       
  3754 	},
       
  3755 
       
  3756 	rotate: function(angle ) {
       
  3757 		return this.transform(new Matrix().rotate(angle,
       
  3758 				Point.read(arguments, 1, { readNull: true })
       
  3759 					|| this.getPosition(true)));
       
  3760 	}
       
  3761 }, Base.each(['scale', 'shear', 'skew'], function(name) {
       
  3762 	this[name] = function() {
       
  3763 		var point = Point.read(arguments),
       
  3764 			center = Point.read(arguments, 0, { readNull: true });
       
  3765 		return this.transform(new Matrix()[name](point,
       
  3766 				center || this.getPosition(true)));
       
  3767 	};
       
  3768 }, {
       
  3769 
       
  3770 }), {
       
  3771 	transform: function(matrix, _applyMatrix) {
       
  3772 		if (matrix && matrix.isIdentity())
       
  3773 			matrix = null;
       
  3774 		var _matrix = this._matrix,
       
  3775 			applyMatrix = (_applyMatrix || this._applyMatrix)
       
  3776 				&& (!_matrix.isIdentity() || matrix);
       
  3777 		if (!matrix && !applyMatrix)
       
  3778 			return this;
       
  3779 		if (matrix)
       
  3780 			_matrix.preConcatenate(matrix);
       
  3781 		if (applyMatrix = applyMatrix && this._transformContent(_matrix)) {
       
  3782 			var pivot = this._pivot,
       
  3783 				style = this._style,
       
  3784 				fillColor = style.getFillColor(true),
       
  3785 				strokeColor = style.getStrokeColor(true);
       
  3786 			if (pivot)
       
  3787 				_matrix._transformPoint(pivot, pivot, true);
       
  3788 			if (fillColor)
       
  3789 				fillColor.transform(_matrix);
       
  3790 			if (strokeColor)
       
  3791 				strokeColor.transform(_matrix);
       
  3792 			_matrix.reset(true);
       
  3793 		}
       
  3794 		var bounds = this._bounds,
       
  3795 			position = this._position;
       
  3796 		this._changed(9);
       
  3797 		var decomp = bounds && matrix && matrix.decompose();
       
  3798 		if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
       
  3799 			for (var key in bounds) {
       
  3800 				var rect = bounds[key];
       
  3801 				if (applyMatrix || !rect._internal)
       
  3802 					matrix._transformBounds(rect, rect);
       
  3803 			}
       
  3804 			var getter = this._boundsGetter,
       
  3805 				rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
       
  3806 			if (rect)
       
  3807 				this._position = rect.getCenter(true);
       
  3808 			this._bounds = bounds;
       
  3809 		} else if (matrix && position) {
       
  3810 			this._position = matrix._transformPoint(position, position);
       
  3811 		}
       
  3812 		return this;
       
  3813 	},
       
  3814 
       
  3815 	_transformContent: function(matrix) {
       
  3816 		var children = this._children;
       
  3817 		if (children) {
       
  3818 			for (var i = 0, l = children.length; i < l; i++)
       
  3819 				children[i].transform(matrix, true);
       
  3820 			return true;
       
  3821 		}
       
  3822 	},
       
  3823 
       
  3824 	globalToLocal: function() {
       
  3825 		return this.getGlobalMatrix(true)._inverseTransform(
       
  3826 				Point.read(arguments));
       
  3827 	},
       
  3828 
       
  3829 	localToGlobal: function() {
       
  3830 		return this.getGlobalMatrix(true)._transformPoint(
       
  3831 				Point.read(arguments));
       
  3832 	},
       
  3833 
       
  3834 	fitBounds: function(rectangle, fill) {
       
  3835 		rectangle = Rectangle.read(arguments);
       
  3836 		var bounds = this.getBounds(),
       
  3837 			itemRatio = bounds.height / bounds.width,
       
  3838 			rectRatio = rectangle.height / rectangle.width,
       
  3839 			scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
       
  3840 					? rectangle.width / bounds.width
       
  3841 					: rectangle.height / bounds.height,
       
  3842 			newBounds = new Rectangle(new Point(),
       
  3843 					new Size(bounds.width * scale, bounds.height * scale));
       
  3844 		newBounds.setCenter(rectangle.getCenter());
       
  3845 		this.setBounds(newBounds);
       
  3846 	},
       
  3847 
       
  3848 	_setStyles: function(ctx) {
       
  3849 		var style = this._style,
       
  3850 			fillColor = style.getFillColor(),
       
  3851 			strokeColor = style.getStrokeColor(),
       
  3852 			shadowColor = style.getShadowColor();
       
  3853 		if (fillColor)
       
  3854 			ctx.fillStyle = fillColor.toCanvasStyle(ctx);
       
  3855 		if (strokeColor) {
       
  3856 			var strokeWidth = style.getStrokeWidth();
       
  3857 			if (strokeWidth > 0) {
       
  3858 				ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
       
  3859 				ctx.lineWidth = strokeWidth;
       
  3860 				var strokeJoin = style.getStrokeJoin(),
       
  3861 					strokeCap = style.getStrokeCap(),
       
  3862 					miterLimit = style.getMiterLimit();
       
  3863 				if (strokeJoin)
       
  3864 					ctx.lineJoin = strokeJoin;
       
  3865 				if (strokeCap)
       
  3866 					ctx.lineCap = strokeCap;
       
  3867 				if (miterLimit)
       
  3868 					ctx.miterLimit = miterLimit;
       
  3869 				if (paper.support.nativeDash) {
       
  3870 					var dashArray = style.getDashArray(),
       
  3871 						dashOffset = style.getDashOffset();
       
  3872 					if (dashArray && dashArray.length) {
       
  3873 						if ('setLineDash' in ctx) {
       
  3874 							ctx.setLineDash(dashArray);
       
  3875 							ctx.lineDashOffset = dashOffset;
       
  3876 						} else {
       
  3877 							ctx.mozDash = dashArray;
       
  3878 							ctx.mozDashOffset = dashOffset;
       
  3879 						}
       
  3880 					}
       
  3881 				}
       
  3882 			}
       
  3883 		}
       
  3884 		if (shadowColor) {
       
  3885 			var shadowBlur = style.getShadowBlur();
       
  3886 			if (shadowBlur > 0) {
       
  3887 				ctx.shadowColor = shadowColor.toCanvasStyle(ctx);
       
  3888 				ctx.shadowBlur = shadowBlur;
       
  3889 				var offset = this.getShadowOffset();
       
  3890 				ctx.shadowOffsetX = offset.x;
       
  3891 				ctx.shadowOffsetY = offset.y;
       
  3892 			}
       
  3893 		}
       
  3894 	},
       
  3895 
       
  3896 	draw: function(ctx, param, parentStrokeMatrix) {
       
  3897 		var updateVersion = this._updateVersion = this._project._updateVersion;
       
  3898 		if (!this._visible || this._opacity === 0)
       
  3899 			return;
       
  3900 		var matrices = param.matrices,
       
  3901 			parentMatrix = matrices[matrices.length - 1],
       
  3902 			viewMatrix = param.viewMatrix,
       
  3903 			matrix = this._matrix,
       
  3904 			globalMatrix = parentMatrix.chain(matrix);
       
  3905 		if (!globalMatrix.isInvertible())
       
  3906 			return;
       
  3907 
       
  3908 		function getViewMatrix(matrix) {
       
  3909 			return viewMatrix ? viewMatrix.chain(matrix) : matrix;
       
  3910 		}
       
  3911 
       
  3912 		matrices.push(globalMatrix);
       
  3913 		if (param.updateMatrix) {
       
  3914 			globalMatrix._updateVersion = updateVersion;
       
  3915 			this._globalMatrix = globalMatrix;
       
  3916 		}
       
  3917 
       
  3918 		var blendMode = this._blendMode,
       
  3919 			opacity = this._opacity,
       
  3920 			normalBlend = blendMode === 'normal',
       
  3921 			nativeBlend = BlendMode.nativeModes[blendMode],
       
  3922 			direct = normalBlend && opacity === 1
       
  3923 					|| param.dontStart
       
  3924 					|| param.clip
       
  3925 					|| (nativeBlend || normalBlend && opacity < 1)
       
  3926 						&& this._canComposite(),
       
  3927 			pixelRatio = param.pixelRatio,
       
  3928 			mainCtx, itemOffset, prevOffset;
       
  3929 		if (!direct) {
       
  3930 			var bounds = this.getStrokeBounds(getViewMatrix(parentMatrix));
       
  3931 			if (!bounds.width || !bounds.height)
       
  3932 				return;
       
  3933 			prevOffset = param.offset;
       
  3934 			itemOffset = param.offset = bounds.getTopLeft().floor();
       
  3935 			mainCtx = ctx;
       
  3936 			ctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1)
       
  3937 					.multiply(pixelRatio));
       
  3938 			if (pixelRatio !== 1)
       
  3939 				ctx.scale(pixelRatio, pixelRatio);
       
  3940 		}
       
  3941 		ctx.save();
       
  3942 		var strokeMatrix = parentStrokeMatrix
       
  3943 				? parentStrokeMatrix.chain(matrix)
       
  3944 				: !this.getStrokeScaling(true) && getViewMatrix(globalMatrix),
       
  3945 			clip = !direct && param.clipItem,
       
  3946 			transform = !strokeMatrix || clip;
       
  3947 		if (direct) {
       
  3948 			ctx.globalAlpha = opacity;
       
  3949 			if (nativeBlend)
       
  3950 				ctx.globalCompositeOperation = blendMode;
       
  3951 		} else if (transform) {
       
  3952 			ctx.translate(-itemOffset.x, -itemOffset.y);
       
  3953 		}
       
  3954 		if (transform)
       
  3955 			(direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
       
  3956 		if (clip)
       
  3957 			param.clipItem.draw(ctx, param.extend({ clip: true }));
       
  3958 		if (strokeMatrix) {
       
  3959 			ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
       
  3960 			var offset = param.offset;
       
  3961 			if (offset)
       
  3962 				ctx.translate(-offset.x, -offset.y);
       
  3963 		}
       
  3964 		this._draw(ctx, param, strokeMatrix);
       
  3965 		ctx.restore();
       
  3966 		matrices.pop();
       
  3967 		if (param.clip && !param.dontFinish)
       
  3968 			ctx.clip();
       
  3969 		if (!direct) {
       
  3970 			BlendMode.process(blendMode, ctx, mainCtx, opacity,
       
  3971 					itemOffset.subtract(prevOffset).multiply(pixelRatio));
       
  3972 			CanvasProvider.release(ctx);
       
  3973 			param.offset = prevOffset;
       
  3974 		}
       
  3975 	},
       
  3976 
       
  3977 	_isUpdated: function(updateVersion) {
       
  3978 		var parent = this._parent;
       
  3979 		if (parent instanceof CompoundPath)
       
  3980 			return parent._isUpdated(updateVersion);
       
  3981 		var updated = this._updateVersion === updateVersion;
       
  3982 		if (!updated && parent && parent._visible
       
  3983 				&& parent._isUpdated(updateVersion)) {
       
  3984 			this._updateVersion = updateVersion;
       
  3985 			updated = true;
       
  3986 		}
       
  3987 		return updated;
       
  3988 	},
       
  3989 
       
  3990 	_drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
       
  3991 		if ((this._drawSelected || this._boundsSelected)
       
  3992 				&& this._isUpdated(updateVersion)) {
       
  3993 			var color = this.getSelectedColor(true)
       
  3994 					|| this.getLayer().getSelectedColor(true),
       
  3995 				mx = matrix.chain(this.getGlobalMatrix(true));
       
  3996 			ctx.strokeStyle = ctx.fillStyle = color
       
  3997 					? color.toCanvasStyle(ctx) : '#009dec';
       
  3998 			if (this._drawSelected)
       
  3999 				this._drawSelected(ctx, mx, selectedItems);
       
  4000 			if (this._boundsSelected) {
       
  4001 				var half = size / 2;
       
  4002 					coords = mx._transformCorners(this.getInternalBounds());
       
  4003 				ctx.beginPath();
       
  4004 				for (var i = 0; i < 8; i++)
       
  4005 					ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
       
  4006 				ctx.closePath();
       
  4007 				ctx.stroke();
       
  4008 				for (var i = 0; i < 8; i++)
       
  4009 					ctx.fillRect(coords[i] - half, coords[++i] - half,
       
  4010 							size, size);
       
  4011 			}
       
  4012 		}
       
  4013 	},
       
  4014 
       
  4015 	_canComposite: function() {
       
  4016 		return false;
       
  4017 	}
       
  4018 }, Base.each(['down', 'drag', 'up', 'move'], function(name) {
       
  4019 	this['removeOn' + Base.capitalize(name)] = function() {
       
  4020 		var hash = {};
       
  4021 		hash[name] = true;
       
  4022 		return this.removeOn(hash);
       
  4023 	};
       
  4024 }, {
       
  4025 
       
  4026 	removeOn: function(obj) {
       
  4027 		for (var name in obj) {
       
  4028 			if (obj[name]) {
       
  4029 				var key = 'mouse' + name,
       
  4030 					project = this._project,
       
  4031 					sets = project._removeSets = project._removeSets || {};
       
  4032 				sets[key] = sets[key] || {};
       
  4033 				sets[key][this._id] = this;
       
  4034 			}
       
  4035 		}
       
  4036 		return this;
       
  4037 	}
       
  4038 }));
       
  4039 
       
  4040 var Group = Item.extend({
       
  4041 	_class: 'Group',
       
  4042 	_selectChildren: true,
       
  4043 	_serializeFields: {
       
  4044 		children: []
       
  4045 	},
       
  4046 
       
  4047 	initialize: function Group(arg) {
       
  4048 		this._children = [];
       
  4049 		this._namedChildren = {};
       
  4050 		if (!this._initialize(arg))
       
  4051 			this.addChildren(Array.isArray(arg) ? arg : arguments);
       
  4052 	},
       
  4053 
       
  4054 	_changed: function _changed(flags) {
       
  4055 		_changed.base.call(this, flags);
       
  4056 		if (flags & 1026) {
       
  4057 			this._clipItem = undefined;
       
  4058 		}
       
  4059 	},
       
  4060 
       
  4061 	_getClipItem: function() {
       
  4062 		var clipItem = this._clipItem;
       
  4063 		if (clipItem === undefined) {
       
  4064 			clipItem = null;
       
  4065 			for (var i = 0, l = this._children.length; i < l; i++) {
       
  4066 				var child = this._children[i];
       
  4067 				if (child._clipMask) {
       
  4068 					clipItem = child;
       
  4069 					break;
       
  4070 				}
       
  4071 			}
       
  4072 			this._clipItem = clipItem;
       
  4073 		}
       
  4074 		return clipItem;
       
  4075 	},
       
  4076 
       
  4077 	isClipped: function() {
       
  4078 		return !!this._getClipItem();
       
  4079 	},
       
  4080 
       
  4081 	setClipped: function(clipped) {
       
  4082 		var child = this.getFirstChild();
       
  4083 		if (child)
       
  4084 			child.setClipMask(clipped);
       
  4085 	},
       
  4086 
       
  4087 	_draw: function(ctx, param) {
       
  4088 		var clip = param.clip,
       
  4089 			clipItem = !clip && this._getClipItem(),
       
  4090 			draw = true;
       
  4091 		param = param.extend({ clipItem: clipItem, clip: false });
       
  4092 		if (clip) {
       
  4093 			if (this._currentPath) {
       
  4094 				ctx.currentPath = this._currentPath;
       
  4095 				draw = false;
       
  4096 			} else {
       
  4097 				ctx.beginPath();
       
  4098 				param.dontStart = param.dontFinish = true;
       
  4099 			}
       
  4100 		} else if (clipItem) {
       
  4101 			clipItem.draw(ctx, param.extend({ clip: true }));
       
  4102 		}
       
  4103 		if (draw) {
       
  4104 			for (var i = 0, l = this._children.length; i < l; i++) {
       
  4105 				var item = this._children[i];
       
  4106 				if (item !== clipItem)
       
  4107 					item.draw(ctx, param);
       
  4108 			}
       
  4109 		}
       
  4110 		if (clip) {
       
  4111 			this._currentPath = ctx.currentPath;
       
  4112 		}
       
  4113 	}
       
  4114 });
       
  4115 
       
  4116 var Layer = Group.extend({
       
  4117 	_class: 'Layer',
       
  4118 
       
  4119 	initialize: function Layer(arg) {
       
  4120 		var props = Base.isPlainObject(arg)
       
  4121 				? new Base(arg)
       
  4122 				: { children: Array.isArray(arg) ? arg : arguments },
       
  4123 			insert = props.insert;
       
  4124 		props.insert = false;
       
  4125 		Group.call(this, props);
       
  4126 		if (insert || insert === undefined) {
       
  4127 			this._project.addChild(this);
       
  4128 			this.activate();
       
  4129 		}
       
  4130 	},
       
  4131 
       
  4132 	_remove: function _remove(notify) {
       
  4133 		if (this._parent)
       
  4134 			return _remove.base.call(this, notify);
       
  4135 		if (this._index != null) {
       
  4136 			if (this._project.activeLayer === this)
       
  4137 				this._project.activeLayer = this.getNextSibling()
       
  4138 						|| this.getPreviousSibling();
       
  4139 			Base.splice(this._project.layers, null, this._index, 1);
       
  4140 			this._installEvents(false);
       
  4141 			this._project._needsUpdate = true;
       
  4142 			return true;
       
  4143 		}
       
  4144 		return false;
       
  4145 	},
       
  4146 
       
  4147 	getNextSibling: function getNextSibling() {
       
  4148 		return this._parent ? getNextSibling.base.call(this)
       
  4149 				: this._project.layers[this._index + 1] || null;
       
  4150 	},
       
  4151 
       
  4152 	getPreviousSibling: function getPreviousSibling() {
       
  4153 		return this._parent ? getPreviousSibling.base.call(this)
       
  4154 				: this._project.layers[this._index - 1] || null;
       
  4155 	},
       
  4156 
       
  4157 	isInserted: function isInserted() {
       
  4158 		return this._parent ? isInserted.base.call(this) : this._index != null;
       
  4159 	},
       
  4160 
       
  4161 	activate: function() {
       
  4162 		this._project.activeLayer = this;
       
  4163 	},
       
  4164 
       
  4165 	_insert: function _insert(above, item, _preserve) {
       
  4166 		if (item instanceof Layer && !item._parent) {
       
  4167 			this._remove(true, true);
       
  4168 			Base.splice(item._project.layers, [this],
       
  4169 					item._index + (above ? 1 : 0), 0);
       
  4170 			this._setProject(item._project, true);
       
  4171 			return this;
       
  4172 		}
       
  4173 		return _insert.base.call(this, above, item, _preserve);
       
  4174 	}
       
  4175 });
       
  4176 
       
  4177 var Shape = Item.extend({
       
  4178 	_class: 'Shape',
       
  4179 	_applyMatrix: false,
       
  4180 	_canApplyMatrix: false,
       
  4181 	_boundsSelected: true,
       
  4182 	_serializeFields: {
       
  4183 		type: null,
       
  4184 		size: null,
       
  4185 		radius: null
       
  4186 	},
       
  4187 
       
  4188 	initialize: function Shape(props) {
       
  4189 		this._initialize(props);
       
  4190 	},
       
  4191 
       
  4192 	_equals: function(item) {
       
  4193 		return this._type === item._type
       
  4194 			&& this._size.equals(item._size)
       
  4195 			&& Base.equals(this._radius, item._radius);
       
  4196 	},
       
  4197 
       
  4198 	clone: function(insert) {
       
  4199 		var copy = new Shape(Item.NO_INSERT);
       
  4200 		copy.setType(this._type);
       
  4201 		copy.setSize(this._size);
       
  4202 		copy.setRadius(this._radius);
       
  4203 		return this._clone(copy, insert);
       
  4204 	},
       
  4205 
       
  4206 	getType: function() {
       
  4207 		return this._type;
       
  4208 	},
       
  4209 
       
  4210 	setType: function(type) {
       
  4211 		this._type = type;
       
  4212 	},
       
  4213 
       
  4214 	getShape: '#getType',
       
  4215 	setShape: '#setType',
       
  4216 
       
  4217 	getSize: function() {
       
  4218 		var size = this._size;
       
  4219 		return new LinkedSize(size.width, size.height, this, 'setSize');
       
  4220 	},
       
  4221 
       
  4222 	setSize: function() {
       
  4223 		var size = Size.read(arguments);
       
  4224 		if (!this._size) {
       
  4225 			this._size = size.clone();
       
  4226 		} else if (!this._size.equals(size)) {
       
  4227 			var type = this._type,
       
  4228 				width = size.width,
       
  4229 				height = size.height;
       
  4230 			if (type === 'rectangle') {
       
  4231 				var radius = Size.min(this._radius, size.divide(2));
       
  4232 				this._radius.set(radius.width, radius.height);
       
  4233 			} else if (type === 'circle') {
       
  4234 				width = height = (width + height) / 2;
       
  4235 				this._radius = width / 2;
       
  4236 			} else if (type === 'ellipse') {
       
  4237 				this._radius.set(width / 2, height / 2);
       
  4238 			}
       
  4239 			this._size.set(width, height);
       
  4240 			this._changed(9);
       
  4241 		}
       
  4242 	},
       
  4243 
       
  4244 	getRadius: function() {
       
  4245 		var rad = this._radius;
       
  4246 		return this._type === 'circle'
       
  4247 				? rad
       
  4248 				: new LinkedSize(rad.width, rad.height, this, 'setRadius');
       
  4249 	},
       
  4250 
       
  4251 	setRadius: function(radius) {
       
  4252 		var type = this._type;
       
  4253 		if (type === 'circle') {
       
  4254 			if (radius === this._radius)
       
  4255 				return;
       
  4256 			var size = radius * 2;
       
  4257 			this._radius = radius;
       
  4258 			this._size.set(size, size);
       
  4259 		} else {
       
  4260 			radius = Size.read(arguments);
       
  4261 			if (!this._radius) {
       
  4262 				this._radius = radius.clone();
       
  4263 			} else {
       
  4264 				if (this._radius.equals(radius))
       
  4265 					return;
       
  4266 				this._radius.set(radius.width, radius.height);
       
  4267 				if (type === 'rectangle') {
       
  4268 					var size = Size.max(this._size, radius.multiply(2));
       
  4269 					this._size.set(size.width, size.height);
       
  4270 				} else if (type === 'ellipse') {
       
  4271 					this._size.set(radius.width * 2, radius.height * 2);
       
  4272 				}
       
  4273 			}
       
  4274 		}
       
  4275 		this._changed(9);
       
  4276 	},
       
  4277 
       
  4278 	isEmpty: function() {
       
  4279 		return false;
       
  4280 	},
       
  4281 
       
  4282 	toPath: function(insert) {
       
  4283 		var path = new Path[Base.capitalize(this._type)]({
       
  4284 			center: new Point(),
       
  4285 			size: this._size,
       
  4286 			radius: this._radius,
       
  4287 			insert: false
       
  4288 		});
       
  4289 		path.setStyle(this._style);
       
  4290 		path.transform(this._matrix);
       
  4291 		if (insert || insert === undefined)
       
  4292 			path.insertAbove(this);
       
  4293 		return path;
       
  4294 	},
       
  4295 
       
  4296 	_draw: function(ctx, param, strokeMatrix) {
       
  4297 		var style = this._style,
       
  4298 			hasFill = style.hasFill(),
       
  4299 			hasStroke = style.hasStroke(),
       
  4300 			dontPaint = param.dontFinish || param.clip,
       
  4301 			untransformed = !strokeMatrix;
       
  4302 		if (hasFill || hasStroke || dontPaint) {
       
  4303 			var type = this._type,
       
  4304 				radius = this._radius,
       
  4305 				isCircle = type === 'circle';
       
  4306 			if (!param.dontStart)
       
  4307 				ctx.beginPath();
       
  4308 			if (untransformed && isCircle) {
       
  4309 				ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
       
  4310 			} else {
       
  4311 				var rx = isCircle ? radius : radius.width,
       
  4312 					ry = isCircle ? radius : radius.height,
       
  4313 					size = this._size,
       
  4314 					width = size.width,
       
  4315 					height = size.height;
       
  4316 				if (untransformed && type === 'rect' && rx === 0 && ry === 0) {
       
  4317 					ctx.rect(-width / 2, -height / 2, width, height);
       
  4318 				} else {
       
  4319 					var x = width / 2,
       
  4320 						y = height / 2,
       
  4321 						kappa = 1 - 0.5522847498307936,
       
  4322 						cx = rx * kappa,
       
  4323 						cy = ry * kappa,
       
  4324 						c = [
       
  4325 							-x, -y + ry,
       
  4326 							-x, -y + cy,
       
  4327 							-x + cx, -y,
       
  4328 							-x + rx, -y,
       
  4329 							x - rx, -y,
       
  4330 							x - cx, -y,
       
  4331 							x, -y + cy,
       
  4332 							x, -y + ry,
       
  4333 							x, y - ry,
       
  4334 							x, y - cy,
       
  4335 							x - cx, y,
       
  4336 							x - rx, y,
       
  4337 							-x + rx, y,
       
  4338 							-x + cx, y,
       
  4339 							-x, y - cy,
       
  4340 							-x, y - ry
       
  4341 						];
       
  4342 					if (strokeMatrix)
       
  4343 						strokeMatrix.transform(c, c, 32);
       
  4344 					ctx.moveTo(c[0], c[1]);
       
  4345 					ctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]);
       
  4346 					if (x !== rx)
       
  4347 						ctx.lineTo(c[8], c[9]);
       
  4348 					ctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]);
       
  4349 					if (y !== ry)
       
  4350 						ctx.lineTo(c[16], c[17]);
       
  4351 					ctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]);
       
  4352 					if (x !== rx)
       
  4353 						ctx.lineTo(c[24], c[25]);
       
  4354 					ctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]);
       
  4355 				}
       
  4356 			}
       
  4357 			ctx.closePath();
       
  4358 		}
       
  4359 		if (!dontPaint && (hasFill || hasStroke)) {
       
  4360 			this._setStyles(ctx);
       
  4361 			if (hasFill) {
       
  4362 				ctx.fill(style.getWindingRule());
       
  4363 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  4364 			}
       
  4365 			if (hasStroke)
       
  4366 				ctx.stroke();
       
  4367 		}
       
  4368 	},
       
  4369 
       
  4370 	_canComposite: function() {
       
  4371 		return !(this.hasFill() && this.hasStroke());
       
  4372 	},
       
  4373 
       
  4374 	_getBounds: function(getter, matrix) {
       
  4375 		var rect = new Rectangle(this._size).setCenter(0, 0);
       
  4376 		if (getter !== 'getBounds' && this.hasStroke())
       
  4377 			rect = rect.expand(this.getStrokeWidth());
       
  4378 		return matrix ? matrix._transformBounds(rect) : rect;
       
  4379 	}
       
  4380 },
       
  4381 new function() {
       
  4382 
       
  4383 	function getCornerCenter(that, point, expand) {
       
  4384 		var radius = that._radius;
       
  4385 		if (!radius.isZero()) {
       
  4386 			var halfSize = that._size.divide(2);
       
  4387 			for (var i = 0; i < 4; i++) {
       
  4388 				var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1),
       
  4389 					corner = dir.multiply(halfSize),
       
  4390 					center = corner.subtract(dir.multiply(radius)),
       
  4391 					rect = new Rectangle(corner, center);
       
  4392 				if ((expand ? rect.expand(expand) : rect).contains(point))
       
  4393 					return center;
       
  4394 			}
       
  4395 		}
       
  4396 	}
       
  4397 
       
  4398 	function getEllipseRadius(point, radius) {
       
  4399 		var angle = point.getAngleInRadians(),
       
  4400 			width = radius.width * 2,
       
  4401 			height = radius.height * 2,
       
  4402 			x = width * Math.sin(angle),
       
  4403 			y = height * Math.cos(angle);
       
  4404 		return width * height / (2 * Math.sqrt(x * x + y * y));
       
  4405 	}
       
  4406 
       
  4407 	return {
       
  4408 		_contains: function _contains(point) {
       
  4409 			if (this._type === 'rectangle') {
       
  4410 				var center = getCornerCenter(this, point);
       
  4411 				return center
       
  4412 						? point.subtract(center).divide(this._radius)
       
  4413 							.getLength() <= 1
       
  4414 						: _contains.base.call(this, point);
       
  4415 			} else {
       
  4416 				return point.divide(this.size).getLength() <= 0.5;
       
  4417 			}
       
  4418 		},
       
  4419 
       
  4420 		_hitTestSelf: function _hitTestSelf(point, options) {
       
  4421 			var hit = false;
       
  4422 			if (this.hasStroke()) {
       
  4423 				var type = this._type,
       
  4424 					radius = this._radius,
       
  4425 					strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
       
  4426 				if (type === 'rectangle') {
       
  4427 					var center = getCornerCenter(this, point, strokeWidth);
       
  4428 					if (center) {
       
  4429 						var pt = point.subtract(center);
       
  4430 						hit = 2 * Math.abs(pt.getLength()
       
  4431 								- getEllipseRadius(pt, radius)) <= strokeWidth;
       
  4432 					} else {
       
  4433 						var rect = new Rectangle(this._size).setCenter(0, 0),
       
  4434 							outer = rect.expand(strokeWidth),
       
  4435 							inner = rect.expand(-strokeWidth);
       
  4436 						hit = outer._containsPoint(point)
       
  4437 								&& !inner._containsPoint(point);
       
  4438 					}
       
  4439 				} else {
       
  4440 					if (type === 'ellipse')
       
  4441 						radius = getEllipseRadius(point, radius);
       
  4442 					hit = 2 * Math.abs(point.getLength() - radius)
       
  4443 							<= strokeWidth;
       
  4444 				}
       
  4445 			}
       
  4446 			return hit
       
  4447 					? new HitResult('stroke', this)
       
  4448 					: _hitTestSelf.base.apply(this, arguments);
       
  4449 		}
       
  4450 	};
       
  4451 }, {
       
  4452 
       
  4453 statics: new function() {
       
  4454 	function createShape(type, point, size, radius, args) {
       
  4455 		var item = new Shape(Base.getNamed(args));
       
  4456 		item._type = type;
       
  4457 		item._size = size;
       
  4458 		item._radius = radius;
       
  4459 		return item.translate(point);
       
  4460 	}
       
  4461 
       
  4462 	return {
       
  4463 		Circle: function() {
       
  4464 			var center = Point.readNamed(arguments, 'center'),
       
  4465 				radius = Base.readNamed(arguments, 'radius');
       
  4466 			return createShape('circle', center, new Size(radius * 2), radius,
       
  4467 					arguments);
       
  4468 		},
       
  4469 
       
  4470 		Rectangle: function() {
       
  4471 			var rect = Rectangle.readNamed(arguments, 'rectangle'),
       
  4472 				radius = Size.min(Size.readNamed(arguments, 'radius'),
       
  4473 						rect.getSize(true).divide(2));
       
  4474 			return createShape('rectangle', rect.getCenter(true),
       
  4475 					rect.getSize(true), radius, arguments);
       
  4476 		},
       
  4477 
       
  4478 		Ellipse: function() {
       
  4479 			var ellipse = Shape._readEllipse(arguments),
       
  4480 				radius = ellipse.radius;
       
  4481 			return createShape('ellipse', ellipse.center, radius.multiply(2),
       
  4482 					radius, arguments);
       
  4483 		},
       
  4484 
       
  4485 		_readEllipse: function(args) {
       
  4486 			var center,
       
  4487 				radius;
       
  4488 			if (Base.hasNamed(args, 'radius')) {
       
  4489 				center = Point.readNamed(args, 'center');
       
  4490 				radius = Size.readNamed(args, 'radius');
       
  4491 			} else {
       
  4492 				var rect = Rectangle.readNamed(args, 'rectangle');
       
  4493 				center = rect.getCenter(true);
       
  4494 				radius = rect.getSize(true).divide(2);
       
  4495 			}
       
  4496 			return { center: center, radius: radius };
       
  4497 		}
       
  4498 	};
       
  4499 }});
       
  4500 
       
  4501 var Raster = Item.extend({
       
  4502 	_class: 'Raster',
       
  4503 	_applyMatrix: false,
       
  4504 	_canApplyMatrix: false,
       
  4505 	_boundsGetter: 'getBounds',
       
  4506 	_boundsSelected: true,
       
  4507 	_serializeFields: {
       
  4508 		source: null
       
  4509 	},
       
  4510 
       
  4511 	initialize: function Raster(object, position) {
       
  4512 		if (!this._initialize(object,
       
  4513 				position !== undefined && Point.read(arguments, 1))) {
       
  4514 			if (typeof object === 'string') {
       
  4515 				this.setSource(object);
       
  4516 			} else {
       
  4517 				this.setImage(object);
       
  4518 			}
       
  4519 		}
       
  4520 		if (!this._size)
       
  4521 			this._size = new Size();
       
  4522 	},
       
  4523 
       
  4524 	_equals: function(item) {
       
  4525 		return this.getSource() === item.getSource();
       
  4526 	},
       
  4527 
       
  4528 	clone: function(insert) {
       
  4529 		var copy = new Raster(Item.NO_INSERT),
       
  4530 			image = this._image,
       
  4531 			canvas = this._canvas;
       
  4532 		if (image) {
       
  4533 			copy.setImage(image);
       
  4534 		} else if (canvas) {
       
  4535 			var copyCanvas = CanvasProvider.getCanvas(this._size);
       
  4536 			copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
       
  4537 			copy.setCanvas(copyCanvas);
       
  4538 		}
       
  4539 		return this._clone(copy, insert);
       
  4540 	},
       
  4541 
       
  4542 	getSize: function() {
       
  4543 		var size = this._size;
       
  4544 		return new LinkedSize(size.width, size.height, this, 'setSize');
       
  4545 	},
       
  4546 
       
  4547 	setSize: function() {
       
  4548 		var size = Size.read(arguments);
       
  4549 		if (!this._size.equals(size)) {
       
  4550 			var element = this.getElement();
       
  4551 			this.setCanvas(CanvasProvider.getCanvas(size));
       
  4552 			if (element)
       
  4553 				this.getContext(true).drawImage(element, 0, 0,
       
  4554 						size.width, size.height);
       
  4555 		}
       
  4556 	},
       
  4557 
       
  4558 	getWidth: function() {
       
  4559 		return this._size.width;
       
  4560 	},
       
  4561 
       
  4562 	getHeight: function() {
       
  4563 		return this._size.height;
       
  4564 	},
       
  4565 
       
  4566 	isEmpty: function() {
       
  4567 		return this._size.width === 0 && this._size.height === 0;
       
  4568 	},
       
  4569 
       
  4570 	getResolution: function() {
       
  4571 		var matrix = this._matrix,
       
  4572 			orig = new Point(0, 0).transform(matrix),
       
  4573 			u = new Point(1, 0).transform(matrix).subtract(orig),
       
  4574 			v = new Point(0, 1).transform(matrix).subtract(orig);
       
  4575 		return new Size(
       
  4576 			72 / u.getLength(),
       
  4577 			72 / v.getLength()
       
  4578 		);
       
  4579 	},
       
  4580 
       
  4581 	getPpi: '#getResolution',
       
  4582 
       
  4583 	getImage: function() {
       
  4584 		return this._image;
       
  4585 	},
       
  4586 
       
  4587 	setImage: function(image) {
       
  4588 		if (this._canvas)
       
  4589 			CanvasProvider.release(this._canvas);
       
  4590 		if (image && image.getContext) {
       
  4591 			this._image = null;
       
  4592 			this._canvas = image;
       
  4593 		} else {
       
  4594 			this._image = image;
       
  4595 			this._canvas = null;
       
  4596 		}
       
  4597 		this._size = new Size(
       
  4598 				image ? image.naturalWidth || image.width : 0,
       
  4599 				image ? image.naturalHeight || image.height : 0);
       
  4600 		this._context = null;
       
  4601 		this._changed(521);
       
  4602 	},
       
  4603 
       
  4604 	getCanvas: function() {
       
  4605 		if (!this._canvas) {
       
  4606 			var ctx = CanvasProvider.getContext(this._size);
       
  4607 			try {
       
  4608 				if (this._image)
       
  4609 					ctx.drawImage(this._image, 0, 0);
       
  4610 				this._canvas = ctx.canvas;
       
  4611 			} catch (e) {
       
  4612 				CanvasProvider.release(ctx);
       
  4613 			}
       
  4614 		}
       
  4615 		return this._canvas;
       
  4616 	},
       
  4617 
       
  4618 	setCanvas: '#setImage',
       
  4619 
       
  4620 	getContext: function(modify) {
       
  4621 		if (!this._context)
       
  4622 			this._context = this.getCanvas().getContext('2d');
       
  4623 		if (modify) {
       
  4624 			this._image = null;
       
  4625 			this._changed(513);
       
  4626 		}
       
  4627 		return this._context;
       
  4628 	},
       
  4629 
       
  4630 	setContext: function(context) {
       
  4631 		this._context = context;
       
  4632 	},
       
  4633 
       
  4634 	getSource: function() {
       
  4635 		return this._image && this._image.src || this.toDataURL();
       
  4636 	},
       
  4637 
       
  4638 	setSource: function(src) {
       
  4639 		var that = this,
       
  4640 			image;
       
  4641 
       
  4642 		function loaded() {
       
  4643 			var view = that.getView();
       
  4644 			if (view) {
       
  4645 				paper = view._scope;
       
  4646 				that.setImage(image);
       
  4647 				that.fire('load');
       
  4648 				view.update();
       
  4649 			}
       
  4650 		}
       
  4651 
       
  4652 			image = document.getElementById(src) || new Image();
       
  4653 
       
  4654 		if (image.naturalWidth && image.naturalHeight) {
       
  4655 			setTimeout(loaded, 0);
       
  4656 		} else {
       
  4657 			DomEvent.add(image, {
       
  4658 				load: loaded
       
  4659 			});
       
  4660 			if (!image.src)
       
  4661 				image.src = src;
       
  4662 		}
       
  4663 		this.setImage(image);
       
  4664 	},
       
  4665 
       
  4666 	getElement: function() {
       
  4667 		return this._canvas || this._image;
       
  4668 	}
       
  4669 }, {
       
  4670 	beans: false,
       
  4671 
       
  4672 	getSubCanvas: function() {
       
  4673 		var rect = Rectangle.read(arguments),
       
  4674 			ctx = CanvasProvider.getContext(rect.getSize());
       
  4675 		ctx.drawImage(this.getCanvas(), rect.x, rect.y,
       
  4676 				rect.width, rect.height, 0, 0, rect.width, rect.height);
       
  4677 		return ctx.canvas;
       
  4678 	},
       
  4679 
       
  4680 	getSubRaster: function() {
       
  4681 		var rect = Rectangle.read(arguments),
       
  4682 			raster = new Raster(Item.NO_INSERT);
       
  4683 		raster.setCanvas(this.getSubCanvas(rect));
       
  4684 		raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
       
  4685 		raster._matrix.preConcatenate(this._matrix);
       
  4686 		raster.insertAbove(this);
       
  4687 		return raster;
       
  4688 	},
       
  4689 
       
  4690 	toDataURL: function() {
       
  4691 		var src = this._image && this._image.src;
       
  4692 		if (/^data:/.test(src))
       
  4693 			return src;
       
  4694 		var canvas = this.getCanvas();
       
  4695 		return canvas ? canvas.toDataURL() : null;
       
  4696 	},
       
  4697 
       
  4698 	drawImage: function(image ) {
       
  4699 		var point = Point.read(arguments, 1);
       
  4700 		this.getContext(true).drawImage(image, point.x, point.y);
       
  4701 	},
       
  4702 
       
  4703 	getAverageColor: function(object) {
       
  4704 		var bounds, path;
       
  4705 		if (!object) {
       
  4706 			bounds = this.getBounds();
       
  4707 		} else if (object instanceof PathItem) {
       
  4708 			path = object;
       
  4709 			bounds = object.getBounds();
       
  4710 		} else if (object.width) {
       
  4711 			bounds = new Rectangle(object);
       
  4712 		} else if (object.x) {
       
  4713 			bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
       
  4714 		}
       
  4715 		var sampleSize = 32,
       
  4716 			width = Math.min(bounds.width, sampleSize),
       
  4717 			height = Math.min(bounds.height, sampleSize);
       
  4718 		var ctx = Raster._sampleContext;
       
  4719 		if (!ctx) {
       
  4720 			ctx = Raster._sampleContext = CanvasProvider.getContext(
       
  4721 					new Size(sampleSize));
       
  4722 		} else {
       
  4723 			ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);
       
  4724 		}
       
  4725 		ctx.save();
       
  4726 		var matrix = new Matrix()
       
  4727 				.scale(width / bounds.width, height / bounds.height)
       
  4728 				.translate(-bounds.x, -bounds.y);
       
  4729 		matrix.applyToContext(ctx);
       
  4730 		if (path)
       
  4731 			path.draw(ctx, new Base({ clip: true, matrices: [matrix] }));
       
  4732 		this._matrix.applyToContext(ctx);
       
  4733 		ctx.drawImage(this.getElement(),
       
  4734 				-this._size.width / 2, -this._size.height / 2);
       
  4735 		ctx.restore();
       
  4736 		var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
       
  4737 				Math.ceil(height)).data,
       
  4738 			channels = [0, 0, 0],
       
  4739 			total = 0;
       
  4740 		for (var i = 0, l = pixels.length; i < l; i += 4) {
       
  4741 			var alpha = pixels[i + 3];
       
  4742 			total += alpha;
       
  4743 			alpha /= 255;
       
  4744 			channels[0] += pixels[i] * alpha;
       
  4745 			channels[1] += pixels[i + 1] * alpha;
       
  4746 			channels[2] += pixels[i + 2] * alpha;
       
  4747 		}
       
  4748 		for (var i = 0; i < 3; i++)
       
  4749 			channels[i] /= total;
       
  4750 		return total ? Color.read(channels) : null;
       
  4751 	},
       
  4752 
       
  4753 	getPixel: function() {
       
  4754 		var point = Point.read(arguments);
       
  4755 		var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
       
  4756 		return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
       
  4757 				data[3] / 255);
       
  4758 	},
       
  4759 
       
  4760 	setPixel: function() {
       
  4761 		var point = Point.read(arguments),
       
  4762 			color = Color.read(arguments),
       
  4763 			components = color._convert('rgb'),
       
  4764 			alpha = color._alpha,
       
  4765 			ctx = this.getContext(true),
       
  4766 			imageData = ctx.createImageData(1, 1),
       
  4767 			data = imageData.data;
       
  4768 		data[0] = components[0] * 255;
       
  4769 		data[1] = components[1] * 255;
       
  4770 		data[2] = components[2] * 255;
       
  4771 		data[3] = alpha != null ? alpha * 255 : 255;
       
  4772 		ctx.putImageData(imageData, point.x, point.y);
       
  4773 	},
       
  4774 
       
  4775 	createImageData: function() {
       
  4776 		var size = Size.read(arguments);
       
  4777 		return this.getContext().createImageData(size.width, size.height);
       
  4778 	},
       
  4779 
       
  4780 	getImageData: function() {
       
  4781 		var rect = Rectangle.read(arguments);
       
  4782 		if (rect.isEmpty())
       
  4783 			rect = new Rectangle(this._size);
       
  4784 		return this.getContext().getImageData(rect.x, rect.y,
       
  4785 				rect.width, rect.height);
       
  4786 	},
       
  4787 
       
  4788 	setImageData: function(data ) {
       
  4789 		var point = Point.read(arguments, 1);
       
  4790 		this.getContext(true).putImageData(data, point.x, point.y);
       
  4791 	},
       
  4792 
       
  4793 	_getBounds: function(getter, matrix) {
       
  4794 		var rect = new Rectangle(this._size).setCenter(0, 0);
       
  4795 		return matrix ? matrix._transformBounds(rect) : rect;
       
  4796 	},
       
  4797 
       
  4798 	_hitTestSelf: function(point) {
       
  4799 		if (this._contains(point)) {
       
  4800 			var that = this;
       
  4801 			return new HitResult('pixel', that, {
       
  4802 				offset: point.add(that._size.divide(2)).round(),
       
  4803 				color: {
       
  4804 					get: function() {
       
  4805 						return that.getPixel(this.offset);
       
  4806 					}
       
  4807 				}
       
  4808 			});
       
  4809 		}
       
  4810 	},
       
  4811 
       
  4812 	_draw: function(ctx) {
       
  4813 		var element = this.getElement();
       
  4814 		if (element) {
       
  4815 			ctx.globalAlpha = this._opacity;
       
  4816 			ctx.drawImage(element,
       
  4817 					-this._size.width / 2, -this._size.height / 2);
       
  4818 		}
       
  4819 	},
       
  4820 
       
  4821 	_canComposite: function() {
       
  4822 		return true;
       
  4823 	}
       
  4824 });
       
  4825 
       
  4826 var PlacedSymbol = Item.extend({
       
  4827 	_class: 'PlacedSymbol',
       
  4828 	_applyMatrix: false,
       
  4829 	_canApplyMatrix: false,
       
  4830 	_boundsGetter: { getBounds: 'getStrokeBounds' },
       
  4831 	_boundsSelected: true,
       
  4832 	_serializeFields: {
       
  4833 		symbol: null
       
  4834 	},
       
  4835 
       
  4836 	initialize: function PlacedSymbol(arg0, arg1) {
       
  4837 		if (!this._initialize(arg0,
       
  4838 				arg1 !== undefined && Point.read(arguments, 1)))
       
  4839 			this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
       
  4840 	},
       
  4841 
       
  4842 	_equals: function(item) {
       
  4843 		return this._symbol === item._symbol;
       
  4844 	},
       
  4845 
       
  4846 	getSymbol: function() {
       
  4847 		return this._symbol;
       
  4848 	},
       
  4849 
       
  4850 	setSymbol: function(symbol) {
       
  4851 		this._symbol = symbol;
       
  4852 		this._changed(9);
       
  4853 	},
       
  4854 
       
  4855 	clone: function(insert) {
       
  4856 		var copy = new PlacedSymbol(Item.NO_INSERT);
       
  4857 		copy.setSymbol(this._symbol);
       
  4858 		return this._clone(copy, insert);
       
  4859 	},
       
  4860 
       
  4861 	isEmpty: function() {
       
  4862 		return this._symbol._definition.isEmpty();
       
  4863 	},
       
  4864 
       
  4865 	_getBounds: function(getter, matrix, cacheItem) {
       
  4866 		return this.symbol._definition._getCachedBounds(getter, matrix,
       
  4867 				cacheItem);
       
  4868 	},
       
  4869 
       
  4870 	_hitTestSelf: function(point, options) {
       
  4871 		var res = this._symbol._definition._hitTest(point, options);
       
  4872 		if (res)
       
  4873 			res.item = this;
       
  4874 		return res;
       
  4875 	},
       
  4876 
       
  4877 	_draw: function(ctx, param) {
       
  4878 		this.symbol._definition.draw(ctx, param);
       
  4879 	}
       
  4880 
       
  4881 });
       
  4882 
       
  4883 var HitResult = Base.extend({
       
  4884 	_class: 'HitResult',
       
  4885 
       
  4886 	initialize: function HitResult(type, item, values) {
       
  4887 		this.type = type;
       
  4888 		this.item = item;
       
  4889 		if (values) {
       
  4890 			values.enumerable = true;
       
  4891 			this.inject(values);
       
  4892 		}
       
  4893 	},
       
  4894 
       
  4895 	statics: {
       
  4896 		getOptions: function(options) {
       
  4897 			return new Base({
       
  4898 				type: null,
       
  4899 				tolerance: paper.settings.hitTolerance,
       
  4900 				fill: !options,
       
  4901 				stroke: !options,
       
  4902 				segments: !options,
       
  4903 				handles: false,
       
  4904 				ends: false,
       
  4905 				center: false,
       
  4906 				bounds: false,
       
  4907 				guides: false,
       
  4908 				selected: false
       
  4909 			}, options);
       
  4910 		}
       
  4911 	}
       
  4912 });
       
  4913 
       
  4914 var Segment = Base.extend({
       
  4915 	_class: 'Segment',
       
  4916 	beans: true,
       
  4917 
       
  4918 	initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
       
  4919 		var count = arguments.length,
       
  4920 			point, handleIn, handleOut;
       
  4921 		if (count === 0) {
       
  4922 		} else if (count === 1) {
       
  4923 			if (arg0.point) {
       
  4924 				point = arg0.point;
       
  4925 				handleIn = arg0.handleIn;
       
  4926 				handleOut = arg0.handleOut;
       
  4927 			} else {
       
  4928 				point = arg0;
       
  4929 			}
       
  4930 		} else if (count === 2 && typeof arg0 === 'number') {
       
  4931 			point = arguments;
       
  4932 		} else if (count <= 3) {
       
  4933 			point = arg0;
       
  4934 			handleIn = arg1;
       
  4935 			handleOut = arg2;
       
  4936 		} else {
       
  4937 			point = arg0 !== undefined ? [ arg0, arg1 ] : null;
       
  4938 			handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
       
  4939 			handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
       
  4940 		}
       
  4941 		new SegmentPoint(point, this, '_point');
       
  4942 		new SegmentPoint(handleIn, this, '_handleIn');
       
  4943 		new SegmentPoint(handleOut, this, '_handleOut');
       
  4944 	},
       
  4945 
       
  4946 	_serialize: function(options) {
       
  4947 		return Base.serialize(this.isLinear() ? this._point
       
  4948 				: [this._point, this._handleIn, this._handleOut],
       
  4949 				options, true);
       
  4950 	},
       
  4951 
       
  4952 	_changed: function(point) {
       
  4953 		var path = this._path;
       
  4954 		if (!path)
       
  4955 			return;
       
  4956 		var curves = path._curves,
       
  4957 			index = this._index,
       
  4958 			curveIn, curveOut;
       
  4959 		if (curves) {
       
  4960 			if ((!point || point === this._point || point === this._handleIn)
       
  4961 					&& (curveIn = curves[index - 1]
       
  4962 						|| path._closed && curves[curves.length - 1]))
       
  4963 				curveIn._changed();
       
  4964 			if ((!point || point === this._point || point === this._handleOut)
       
  4965 					&& (curveOut = curves[index]))
       
  4966 				curveOut._changed();
       
  4967 		}
       
  4968 		path._changed(25);
       
  4969 	},
       
  4970 
       
  4971 	getPoint: function() {
       
  4972 		return this._point;
       
  4973 	},
       
  4974 
       
  4975 	setPoint: function() {
       
  4976 		var point = Point.read(arguments);
       
  4977 		this._point.set(point.x, point.y);
       
  4978 	},
       
  4979 
       
  4980 	getHandleIn: function() {
       
  4981 		return this._handleIn;
       
  4982 	},
       
  4983 
       
  4984 	setHandleIn: function() {
       
  4985 		var point = Point.read(arguments);
       
  4986 		this._handleIn.set(point.x, point.y);
       
  4987 	},
       
  4988 
       
  4989 	getHandleOut: function() {
       
  4990 		return this._handleOut;
       
  4991 	},
       
  4992 
       
  4993 	setHandleOut: function() {
       
  4994 		var point = Point.read(arguments);
       
  4995 		this._handleOut.set(point.x, point.y);
       
  4996 	},
       
  4997 
       
  4998 	isLinear: function() {
       
  4999 		return this._handleIn.isZero() && this._handleOut.isZero();
       
  5000 	},
       
  5001 
       
  5002 	setLinear: function(linear) {
       
  5003 		if (linear) {
       
  5004 			this._handleIn.set(0, 0);
       
  5005 			this._handleOut.set(0, 0);
       
  5006 		} else {
       
  5007 		}
       
  5008 	},
       
  5009 
       
  5010 	isColinear: function(segment) {
       
  5011 		var next1 = this.getNext(),
       
  5012 			next2 = segment.getNext();
       
  5013 		return this._handleOut.isZero() && next1._handleIn.isZero()
       
  5014 				&& segment._handleOut.isZero() && next2._handleIn.isZero()
       
  5015 				&& next1._point.subtract(this._point).isColinear(
       
  5016 					next2._point.subtract(segment._point));
       
  5017 	},
       
  5018 
       
  5019 	isOrthogonal: function() {
       
  5020 		var prev = this.getPrevious(),
       
  5021 			next = this.getNext();
       
  5022 		return prev._handleOut.isZero() && this._handleIn.isZero()
       
  5023 			&& this._handleOut.isZero() && next._handleIn.isZero()
       
  5024 			&& this._point.subtract(prev._point).isOrthogonal(
       
  5025 					next._point.subtract(this._point));
       
  5026 	},
       
  5027 
       
  5028 	isArc: function() {
       
  5029 		var next = this.getNext(),
       
  5030 			handle1 = this._handleOut,
       
  5031 			handle2 = next._handleIn,
       
  5032 			kappa = 0.5522847498307936;
       
  5033 		if (handle1.isOrthogonal(handle2)) {
       
  5034 			var from = this._point,
       
  5035 				to = next._point,
       
  5036 				corner = new Line(from, handle1, true).intersect(
       
  5037 						new Line(to, handle2, true), true);
       
  5038 			return corner && Numerical.isZero(handle1.getLength() /
       
  5039 					corner.subtract(from).getLength() - kappa)
       
  5040 				&& Numerical.isZero(handle2.getLength() /
       
  5041 					corner.subtract(to).getLength() - kappa);
       
  5042 		}
       
  5043 		return false;
       
  5044 	},
       
  5045 
       
  5046 	_selectionState: 0,
       
  5047 
       
  5048 	isSelected: function(_point) {
       
  5049 		var state = this._selectionState;
       
  5050 		return !_point ? !!(state & 7)
       
  5051 			: _point === this._point ? !!(state & 4)
       
  5052 			: _point === this._handleIn ? !!(state & 1)
       
  5053 			: _point === this._handleOut ? !!(state & 2)
       
  5054 			: false;
       
  5055 	},
       
  5056 
       
  5057 	setSelected: function(selected, _point) {
       
  5058 		var path = this._path,
       
  5059 			selected = !!selected,
       
  5060 			state = this._selectionState,
       
  5061 			oldState = state,
       
  5062 			flag = !_point ? 7
       
  5063 					: _point === this._point ? 4
       
  5064 					: _point === this._handleIn ? 1
       
  5065 					: _point === this._handleOut ? 2
       
  5066 					: 0;
       
  5067 		if (selected) {
       
  5068 			state |= flag;
       
  5069 		} else {
       
  5070 			state &= ~flag;
       
  5071 		}
       
  5072 		this._selectionState = state;
       
  5073 		if (path && state !== oldState) {
       
  5074 			path._updateSelection(this, oldState, state);
       
  5075 			path._changed(129);
       
  5076 		}
       
  5077 	},
       
  5078 
       
  5079 	getIndex: function() {
       
  5080 		return this._index !== undefined ? this._index : null;
       
  5081 	},
       
  5082 
       
  5083 	getPath: function() {
       
  5084 		return this._path || null;
       
  5085 	},
       
  5086 
       
  5087 	getCurve: function() {
       
  5088 		var path = this._path,
       
  5089 			index = this._index;
       
  5090 		if (path) {
       
  5091 			if (index > 0 && !path._closed
       
  5092 					&& index === path._segments.length - 1)
       
  5093 				index--;
       
  5094 			return path.getCurves()[index] || null;
       
  5095 		}
       
  5096 		return null;
       
  5097 	},
       
  5098 
       
  5099 	getLocation: function() {
       
  5100 		var curve = this.getCurve();
       
  5101 		return curve
       
  5102 				? new CurveLocation(curve, this === curve._segment1 ? 0 : 1)
       
  5103 				: null;
       
  5104 	},
       
  5105 
       
  5106 	getNext: function() {
       
  5107 		var segments = this._path && this._path._segments;
       
  5108 		return segments && (segments[this._index + 1]
       
  5109 				|| this._path._closed && segments[0]) || null;
       
  5110 	},
       
  5111 
       
  5112 	getPrevious: function() {
       
  5113 		var segments = this._path && this._path._segments;
       
  5114 		return segments && (segments[this._index - 1]
       
  5115 				|| this._path._closed && segments[segments.length - 1]) || null;
       
  5116 	},
       
  5117 
       
  5118 	reverse: function() {
       
  5119 		return new Segment(this._point, this._handleOut, this._handleIn);
       
  5120 	},
       
  5121 
       
  5122 	remove: function() {
       
  5123 		return this._path ? !!this._path.removeSegment(this._index) : false;
       
  5124 	},
       
  5125 
       
  5126 	clone: function() {
       
  5127 		return new Segment(this._point, this._handleIn, this._handleOut);
       
  5128 	},
       
  5129 
       
  5130 	equals: function(segment) {
       
  5131 		return segment === this || segment && this._class === segment._class
       
  5132 				&& this._point.equals(segment._point)
       
  5133 				&& this._handleIn.equals(segment._handleIn)
       
  5134 				&& this._handleOut.equals(segment._handleOut)
       
  5135 				|| false;
       
  5136 	},
       
  5137 
       
  5138 	toString: function() {
       
  5139 		var parts = [ 'point: ' + this._point ];
       
  5140 		if (!this._handleIn.isZero())
       
  5141 			parts.push('handleIn: ' + this._handleIn);
       
  5142 		if (!this._handleOut.isZero())
       
  5143 			parts.push('handleOut: ' + this._handleOut);
       
  5144 		return '{ ' + parts.join(', ') + ' }';
       
  5145 	},
       
  5146 
       
  5147 	transform: function(matrix) {
       
  5148 		this._transformCoordinates(matrix, new Array(6), true);
       
  5149 		this._changed();
       
  5150 	},
       
  5151 
       
  5152 	_transformCoordinates: function(matrix, coords, change) {
       
  5153 		var point = this._point,
       
  5154 			handleIn = !change || !this._handleIn.isZero()
       
  5155 					? this._handleIn : null,
       
  5156 			handleOut = !change || !this._handleOut.isZero()
       
  5157 					? this._handleOut : null,
       
  5158 			x = point._x,
       
  5159 			y = point._y,
       
  5160 			i = 2;
       
  5161 		coords[0] = x;
       
  5162 		coords[1] = y;
       
  5163 		if (handleIn) {
       
  5164 			coords[i++] = handleIn._x + x;
       
  5165 			coords[i++] = handleIn._y + y;
       
  5166 		}
       
  5167 		if (handleOut) {
       
  5168 			coords[i++] = handleOut._x + x;
       
  5169 			coords[i++] = handleOut._y + y;
       
  5170 		}
       
  5171 		if (matrix) {
       
  5172 			matrix._transformCoordinates(coords, coords, i / 2);
       
  5173 			x = coords[0];
       
  5174 			y = coords[1];
       
  5175 			if (change) {
       
  5176 				point._x = x;
       
  5177 				point._y = y;
       
  5178 				i  = 2;
       
  5179 				if (handleIn) {
       
  5180 					handleIn._x = coords[i++] - x;
       
  5181 					handleIn._y = coords[i++] - y;
       
  5182 				}
       
  5183 				if (handleOut) {
       
  5184 					handleOut._x = coords[i++] - x;
       
  5185 					handleOut._y = coords[i++] - y;
       
  5186 				}
       
  5187 			} else {
       
  5188 				if (!handleIn) {
       
  5189 					coords[i++] = x;
       
  5190 					coords[i++] = y;
       
  5191 				}
       
  5192 				if (!handleOut) {
       
  5193 					coords[i++] = x;
       
  5194 					coords[i++] = y;
       
  5195 				}
       
  5196 			}
       
  5197 		}
       
  5198 		return coords;
       
  5199 	}
       
  5200 });
       
  5201 
       
  5202 var SegmentPoint = Point.extend({
       
  5203 	initialize: function SegmentPoint(point, owner, key) {
       
  5204 		var x, y, selected;
       
  5205 		if (!point) {
       
  5206 			x = y = 0;
       
  5207 		} else if ((x = point[0]) !== undefined) {
       
  5208 			y = point[1];
       
  5209 		} else {
       
  5210 			var pt = point;
       
  5211 			if ((x = pt.x) === undefined) {
       
  5212 				pt = Point.read(arguments);
       
  5213 				x = pt.x;
       
  5214 			}
       
  5215 			y = pt.y;
       
  5216 			selected = pt.selected;
       
  5217 		}
       
  5218 		this._x = x;
       
  5219 		this._y = y;
       
  5220 		this._owner = owner;
       
  5221 		owner[key] = this;
       
  5222 		if (selected)
       
  5223 			this.setSelected(true);
       
  5224 	},
       
  5225 
       
  5226 	set: function(x, y) {
       
  5227 		this._x = x;
       
  5228 		this._y = y;
       
  5229 		this._owner._changed(this);
       
  5230 		return this;
       
  5231 	},
       
  5232 
       
  5233 	_serialize: function(options) {
       
  5234 		var f = options.formatter,
       
  5235 			x = f.number(this._x),
       
  5236 			y = f.number(this._y);
       
  5237 		return this.isSelected()
       
  5238 				? { x: x, y: y, selected: true }
       
  5239 				: [x, y];
       
  5240 	},
       
  5241 
       
  5242 	getX: function() {
       
  5243 		return this._x;
       
  5244 	},
       
  5245 
       
  5246 	setX: function(x) {
       
  5247 		this._x = x;
       
  5248 		this._owner._changed(this);
       
  5249 	},
       
  5250 
       
  5251 	getY: function() {
       
  5252 		return this._y;
       
  5253 	},
       
  5254 
       
  5255 	setY: function(y) {
       
  5256 		this._y = y;
       
  5257 		this._owner._changed(this);
       
  5258 	},
       
  5259 
       
  5260 	isZero: function() {
       
  5261 		return Numerical.isZero(this._x) && Numerical.isZero(this._y);
       
  5262 	},
       
  5263 
       
  5264 	setSelected: function(selected) {
       
  5265 		this._owner.setSelected(selected, this);
       
  5266 	},
       
  5267 
       
  5268 	isSelected: function() {
       
  5269 		return this._owner.isSelected(this);
       
  5270 	}
       
  5271 });
       
  5272 
       
  5273 var Curve = Base.extend({
       
  5274 	_class: 'Curve',
       
  5275 	initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
       
  5276 		var count = arguments.length;
       
  5277 		if (count === 3) {
       
  5278 			this._path = arg0;
       
  5279 			this._segment1 = arg1;
       
  5280 			this._segment2 = arg2;
       
  5281 		} else if (count === 0) {
       
  5282 			this._segment1 = new Segment();
       
  5283 			this._segment2 = new Segment();
       
  5284 		} else if (count === 1) {
       
  5285 			this._segment1 = new Segment(arg0.segment1);
       
  5286 			this._segment2 = new Segment(arg0.segment2);
       
  5287 		} else if (count === 2) {
       
  5288 			this._segment1 = new Segment(arg0);
       
  5289 			this._segment2 = new Segment(arg1);
       
  5290 		} else {
       
  5291 			var point1, handle1, handle2, point2;
       
  5292 			if (count === 4) {
       
  5293 				point1 = arg0;
       
  5294 				handle1 = arg1;
       
  5295 				handle2 = arg2;
       
  5296 				point2 = arg3;
       
  5297 			} else if (count === 8) {
       
  5298 				point1 = [arg0, arg1];
       
  5299 				point2 = [arg6, arg7];
       
  5300 				handle1 = [arg2 - arg0, arg3 - arg1];
       
  5301 				handle2 = [arg4 - arg6, arg5 - arg7];
       
  5302 			}
       
  5303 			this._segment1 = new Segment(point1, null, handle1);
       
  5304 			this._segment2 = new Segment(point2, handle2, null);
       
  5305 		}
       
  5306 	},
       
  5307 
       
  5308 	_changed: function() {
       
  5309 		this._length = this._bounds = undefined;
       
  5310 	},
       
  5311 
       
  5312 	getPoint1: function() {
       
  5313 		return this._segment1._point;
       
  5314 	},
       
  5315 
       
  5316 	setPoint1: function() {
       
  5317 		var point = Point.read(arguments);
       
  5318 		this._segment1._point.set(point.x, point.y);
       
  5319 	},
       
  5320 
       
  5321 	getPoint2: function() {
       
  5322 		return this._segment2._point;
       
  5323 	},
       
  5324 
       
  5325 	setPoint2: function() {
       
  5326 		var point = Point.read(arguments);
       
  5327 		this._segment2._point.set(point.x, point.y);
       
  5328 	},
       
  5329 
       
  5330 	getHandle1: function() {
       
  5331 		return this._segment1._handleOut;
       
  5332 	},
       
  5333 
       
  5334 	setHandle1: function() {
       
  5335 		var point = Point.read(arguments);
       
  5336 		this._segment1._handleOut.set(point.x, point.y);
       
  5337 	},
       
  5338 
       
  5339 	getHandle2: function() {
       
  5340 		return this._segment2._handleIn;
       
  5341 	},
       
  5342 
       
  5343 	setHandle2: function() {
       
  5344 		var point = Point.read(arguments);
       
  5345 		this._segment2._handleIn.set(point.x, point.y);
       
  5346 	},
       
  5347 
       
  5348 	getSegment1: function() {
       
  5349 		return this._segment1;
       
  5350 	},
       
  5351 
       
  5352 	getSegment2: function() {
       
  5353 		return this._segment2;
       
  5354 	},
       
  5355 
       
  5356 	getPath: function() {
       
  5357 		return this._path;
       
  5358 	},
       
  5359 
       
  5360 	getIndex: function() {
       
  5361 		return this._segment1._index;
       
  5362 	},
       
  5363 
       
  5364 	getNext: function() {
       
  5365 		var curves = this._path && this._path._curves;
       
  5366 		return curves && (curves[this._segment1._index + 1]
       
  5367 				|| this._path._closed && curves[0]) || null;
       
  5368 	},
       
  5369 
       
  5370 	getPrevious: function() {
       
  5371 		var curves = this._path && this._path._curves;
       
  5372 		return curves && (curves[this._segment1._index - 1]
       
  5373 				|| this._path._closed && curves[curves.length - 1]) || null;
       
  5374 	},
       
  5375 
       
  5376 	isSelected: function() {
       
  5377 		return this.getPoint1().isSelected()
       
  5378 				&& this.getHandle2().isSelected()
       
  5379 				&& this.getHandle2().isSelected()
       
  5380 				&& this.getPoint2().isSelected();
       
  5381 	},
       
  5382 
       
  5383 	setSelected: function(selected) {
       
  5384 		this.getPoint1().setSelected(selected);
       
  5385 		this.getHandle1().setSelected(selected);
       
  5386 		this.getHandle2().setSelected(selected);
       
  5387 		this.getPoint2().setSelected(selected);
       
  5388 	},
       
  5389 
       
  5390 	getValues: function(matrix) {
       
  5391 		return Curve.getValues(this._segment1, this._segment2, matrix);
       
  5392 	},
       
  5393 
       
  5394 	getPoints: function() {
       
  5395 		var coords = this.getValues(),
       
  5396 			points = [];
       
  5397 		for (var i = 0; i < 8; i += 2)
       
  5398 			points.push(new Point(coords[i], coords[i + 1]));
       
  5399 		return points;
       
  5400 	},
       
  5401 
       
  5402 	getLength: function() {
       
  5403 		if (this._length == null) {
       
  5404 			this._length = this.isLinear()
       
  5405 				? this._segment2._point.getDistance(this._segment1._point)
       
  5406 				: Curve.getLength(this.getValues(), 0, 1);
       
  5407 		}
       
  5408 		return this._length;
       
  5409 	},
       
  5410 
       
  5411 	getArea: function() {
       
  5412 		return Curve.getArea(this.getValues());
       
  5413 	},
       
  5414 
       
  5415 	getPart: function(from, to) {
       
  5416 		return new Curve(Curve.getPart(this.getValues(), from, to));
       
  5417 	},
       
  5418 
       
  5419 	getPartLength: function(from, to) {
       
  5420 		return Curve.getLength(this.getValues(), from, to);
       
  5421 	},
       
  5422 
       
  5423 	isLinear: function() {
       
  5424 		return this._segment1._handleOut.isZero()
       
  5425 				&& this._segment2._handleIn.isZero();
       
  5426 	},
       
  5427 
       
  5428 	isHorizontal: function() {
       
  5429 		return this.isLinear() && Numerical.isZero(
       
  5430 				this._segment1._point._y - this._segment2._point._y);
       
  5431 	},
       
  5432 
       
  5433 	getIntersections: function(curve) {
       
  5434 		return Curve.getIntersections(this.getValues(), curve.getValues(),
       
  5435 				this, curve, []);
       
  5436 	},
       
  5437 
       
  5438 	_getParameter: function(offset, isParameter) {
       
  5439 		return isParameter
       
  5440 				? offset
       
  5441 				: offset && offset.curve === this
       
  5442 					? offset.parameter
       
  5443 					: offset === undefined && isParameter === undefined
       
  5444 						? 0.5
       
  5445 						: this.getParameterAt(offset, 0);
       
  5446 	},
       
  5447 
       
  5448 	divide: function(offset, isParameter, ignoreLinear) {
       
  5449 		var parameter = this._getParameter(offset, isParameter),
       
  5450 			tolerance = 0.00001,
       
  5451 			res = null;
       
  5452 		if (parameter > tolerance && parameter < 1 - tolerance) {
       
  5453 			var parts = Curve.subdivide(this.getValues(), parameter),
       
  5454 				isLinear = ignoreLinear ? false : this.isLinear(),
       
  5455 				left = parts[0],
       
  5456 				right = parts[1];
       
  5457 
       
  5458 			if (!isLinear) {
       
  5459 				this._segment1._handleOut.set(left[2] - left[0],
       
  5460 						left[3] - left[1]);
       
  5461 				this._segment2._handleIn.set(right[4] - right[6],
       
  5462 						right[5] - right[7]);
       
  5463 			}
       
  5464 
       
  5465 			var x = left[6], y = left[7],
       
  5466 				segment = new Segment(new Point(x, y),
       
  5467 						!isLinear && new Point(left[4] - x, left[5] - y),
       
  5468 						!isLinear && new Point(right[2] - x, right[3] - y));
       
  5469 
       
  5470 			if (this._path) {
       
  5471 				if (this._segment1._index > 0 && this._segment2._index === 0) {
       
  5472 					this._path.add(segment);
       
  5473 				} else {
       
  5474 					this._path.insert(this._segment2._index, segment);
       
  5475 				}
       
  5476 				res = this;
       
  5477 			} else {
       
  5478 				var end = this._segment2;
       
  5479 				this._segment2 = segment;
       
  5480 				res = new Curve(segment, end);
       
  5481 			}
       
  5482 		}
       
  5483 		return res;
       
  5484 	},
       
  5485 
       
  5486 	split: function(offset, isParameter) {
       
  5487 		return this._path
       
  5488 			? this._path.split(this._segment1._index,
       
  5489 					this._getParameter(offset, isParameter))
       
  5490 			: null;
       
  5491 	},
       
  5492 
       
  5493 	reverse: function() {
       
  5494 		return new Curve(this._segment2.reverse(), this._segment1.reverse());
       
  5495 	},
       
  5496 
       
  5497 	remove: function() {
       
  5498 		var removed = false;
       
  5499 		if (this._path) {
       
  5500 			var segment2 = this._segment2,
       
  5501 				handleOut = segment2._handleOut;
       
  5502 			removed = segment2.remove();
       
  5503 			if (removed)
       
  5504 				this._segment1._handleOut.set(handleOut.x, handleOut.y);
       
  5505 		}
       
  5506 		return removed;
       
  5507 	},
       
  5508 
       
  5509 	clone: function() {
       
  5510 		return new Curve(this._segment1, this._segment2);
       
  5511 	},
       
  5512 
       
  5513 	toString: function() {
       
  5514 		var parts = [ 'point1: ' + this._segment1._point ];
       
  5515 		if (!this._segment1._handleOut.isZero())
       
  5516 			parts.push('handle1: ' + this._segment1._handleOut);
       
  5517 		if (!this._segment2._handleIn.isZero())
       
  5518 			parts.push('handle2: ' + this._segment2._handleIn);
       
  5519 		parts.push('point2: ' + this._segment2._point);
       
  5520 		return '{ ' + parts.join(', ') + ' }';
       
  5521 	},
       
  5522 
       
  5523 statics: {
       
  5524 	getValues: function(segment1, segment2, matrix) {
       
  5525 		var p1 = segment1._point,
       
  5526 			h1 = segment1._handleOut,
       
  5527 			h2 = segment2._handleIn,
       
  5528 			p2 = segment2._point,
       
  5529 			values = [
       
  5530 				p1._x, p1._y,
       
  5531 				p1._x + h1._x, p1._y + h1._y,
       
  5532 				p2._x + h2._x, p2._y + h2._y,
       
  5533 				p2._x, p2._y
       
  5534 			];
       
  5535 		if (matrix)
       
  5536 			matrix._transformCoordinates(values, values, 6);
       
  5537 		return values;
       
  5538 	},
       
  5539 
       
  5540 	evaluate: function(v, t, type) {
       
  5541 		var p1x = v[0], p1y = v[1],
       
  5542 			c1x = v[2], c1y = v[3],
       
  5543 			c2x = v[4], c2y = v[5],
       
  5544 			p2x = v[6], p2y = v[7],
       
  5545 			tolerance = 0.00001,
       
  5546 			x, y;
       
  5547 
       
  5548 		if (type === 0 && (t < tolerance || t > 1 - tolerance)) {
       
  5549 			var isZero = t < tolerance;
       
  5550 			x = isZero ? p1x : p2x;
       
  5551 			y = isZero ? p1y : p2y;
       
  5552 		} else {
       
  5553 			var cx = 3 * (c1x - p1x),
       
  5554 				bx = 3 * (c2x - c1x) - cx,
       
  5555 				ax = p2x - p1x - cx - bx,
       
  5556 
       
  5557 				cy = 3 * (c1y - p1y),
       
  5558 				by = 3 * (c2y - c1y) - cy,
       
  5559 				ay = p2y - p1y - cy - by;
       
  5560 			if (type === 0) {
       
  5561 				x = ((ax * t + bx) * t + cx) * t + p1x;
       
  5562 				y = ((ay * t + by) * t + cy) * t + p1y;
       
  5563 			} else {
       
  5564 				if (t < tolerance && c1x === p1x && c1y === p1y
       
  5565 						|| t > 1 - tolerance && c2x === p2x && c2y === p2y) {
       
  5566 					x = p2x - p1x;
       
  5567 					y = p2y - p1y;
       
  5568 				} else if (t < tolerance) {
       
  5569 					x = cx;
       
  5570 					y = cy;
       
  5571 				} else if (t > 1 - tolerance) {
       
  5572 					x = 3 * (p2x - c2x);
       
  5573 					y = 3 * (p2y - c2y);
       
  5574 				} else {
       
  5575 					x = (3 * ax * t + 2 * bx) * t + cx;
       
  5576 					y = (3 * ay * t + 2 * by) * t + cy;
       
  5577 				}
       
  5578 				if (type === 3) {
       
  5579 					var x2 = 6 * ax * t + 2 * bx,
       
  5580 						y2 = 6 * ay * t + 2 * by;
       
  5581 					return (x * y2 - y * x2) / Math.pow(x * x + y * y, 3 / 2);
       
  5582 				}
       
  5583 			}
       
  5584 		}
       
  5585 		return type === 2 ? new Point(y, -x) : new Point(x, y);
       
  5586 	},
       
  5587 
       
  5588 	subdivide: function(v, t) {
       
  5589 		var p1x = v[0], p1y = v[1],
       
  5590 			c1x = v[2], c1y = v[3],
       
  5591 			c2x = v[4], c2y = v[5],
       
  5592 			p2x = v[6], p2y = v[7];
       
  5593 		if (t === undefined)
       
  5594 			t = 0.5;
       
  5595 		var u = 1 - t,
       
  5596 			p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y,
       
  5597 			p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y,
       
  5598 			p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y,
       
  5599 			p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y,
       
  5600 			p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
       
  5601 			p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
       
  5602 		return [
       
  5603 			[p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
       
  5604 			[p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
       
  5605 		];
       
  5606 	},
       
  5607 
       
  5608 	solveCubic: function (v, coord, val, roots, min, max) {
       
  5609 		var p1 = v[coord],
       
  5610 			c1 = v[coord + 2],
       
  5611 			c2 = v[coord + 4],
       
  5612 			p2 = v[coord + 6],
       
  5613 			c = 3 * (c1 - p1),
       
  5614 			b = 3 * (c2 - c1) - c,
       
  5615 			a = p2 - p1 - c - b;
       
  5616 		return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
       
  5617 	},
       
  5618 
       
  5619 	getParameterOf: function(v, x, y) {
       
  5620 		var tolerance = 0.00001;
       
  5621 		if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance)
       
  5622 			return 0;
       
  5623 		if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance)
       
  5624 			return 1;
       
  5625 		var txs = [],
       
  5626 			tys = [],
       
  5627 			sx = Curve.solveCubic(v, 0, x, txs),
       
  5628 			sy = Curve.solveCubic(v, 1, y, tys),
       
  5629 			tx, ty;
       
  5630 		for (var cx = 0;  sx == -1 || cx < sx;) {
       
  5631 			if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) {
       
  5632 				for (var cy = 0; sy == -1 || cy < sy;) {
       
  5633 					if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) {
       
  5634 						if (sx == -1) tx = ty;
       
  5635 						else if (sy == -1) ty = tx;
       
  5636 						if (Math.abs(tx - ty) < tolerance)
       
  5637 							return (tx + ty) * 0.5;
       
  5638 					}
       
  5639 				}
       
  5640 				if (sx == -1)
       
  5641 					break;
       
  5642 			}
       
  5643 		}
       
  5644 		return null;
       
  5645 	},
       
  5646 
       
  5647 	getPart: function(v, from, to) {
       
  5648 		if (from > 0)
       
  5649 			v = Curve.subdivide(v, from)[1];
       
  5650 		if (to < 1)
       
  5651 			v = Curve.subdivide(v, (to - from) / (1 - from))[0];
       
  5652 		return v;
       
  5653 	},
       
  5654 
       
  5655 	isLinear: function(v) {
       
  5656 		var isZero = Numerical.isZero;
       
  5657 		return isZero(v[0] - v[2]) && isZero(v[1] - v[3])
       
  5658 				&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]);
       
  5659 	},
       
  5660 
       
  5661 	isFlatEnough: function(v, tolerance) {
       
  5662 		var p1x = v[0], p1y = v[1],
       
  5663 			c1x = v[2], c1y = v[3],
       
  5664 			c2x = v[4], c2y = v[5],
       
  5665 			p2x = v[6], p2y = v[7],
       
  5666 			ux = 3 * c1x - 2 * p1x - p2x,
       
  5667 			uy = 3 * c1y - 2 * p1y - p2y,
       
  5668 			vx = 3 * c2x - 2 * p2x - p1x,
       
  5669 			vy = 3 * c2y - 2 * p2y - p1y;
       
  5670 		return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
       
  5671 				< 10 * tolerance * tolerance;
       
  5672 	},
       
  5673 
       
  5674 	getArea: function(v) {
       
  5675 		var p1x = v[0], p1y = v[1],
       
  5676 			c1x = v[2], c1y = v[3],
       
  5677 			c2x = v[4], c2y = v[5],
       
  5678 			p2x = v[6], p2y = v[7];
       
  5679 		return (  3.0 * c1y * p1x - 1.5 * c1y * c2x
       
  5680 				- 1.5 * c1y * p2x - 3.0 * p1y * c1x
       
  5681 				- 1.5 * p1y * c2x - 0.5 * p1y * p2x
       
  5682 				+ 1.5 * c2y * p1x + 1.5 * c2y * c1x
       
  5683 				- 3.0 * c2y * p2x + 0.5 * p2y * p1x
       
  5684 				+ 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10;
       
  5685 	},
       
  5686 
       
  5687 	getBounds: function(v) {
       
  5688 		var min = v.slice(0, 2),
       
  5689 			max = min.slice(),
       
  5690 			roots = [0, 0];
       
  5691 		for (var i = 0; i < 2; i++)
       
  5692 			Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],
       
  5693 					i, 0, min, max, roots);
       
  5694 		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
       
  5695 	},
       
  5696 
       
  5697 	_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {
       
  5698 		function add(value, padding) {
       
  5699 			var left = value - padding,
       
  5700 				right = value + padding;
       
  5701 			if (left < min[coord])
       
  5702 				min[coord] = left;
       
  5703 			if (right > max[coord])
       
  5704 				max[coord] = right;
       
  5705 		}
       
  5706 		var a = 3 * (v1 - v2) - v0 + v3,
       
  5707 			b = 2 * (v0 + v2) - 4 * v1,
       
  5708 			c = v1 - v0,
       
  5709 			count = Numerical.solveQuadratic(a, b, c, roots),
       
  5710 			tMin = 0.00001,
       
  5711 			tMax = 1 - tMin;
       
  5712 		add(v3, 0);
       
  5713 		for (var i = 0; i < count; i++) {
       
  5714 			var t = roots[i],
       
  5715 				u = 1 - t;
       
  5716 			if (tMin < t && t < tMax)
       
  5717 				add(u * u * u * v0
       
  5718 					+ 3 * u * u * t * v1
       
  5719 					+ 3 * u * t * t * v2
       
  5720 					+ t * t * t * v3,
       
  5721 					padding);
       
  5722 		}
       
  5723 	}
       
  5724 }}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
       
  5725 	function(name) {
       
  5726 		this[name] = function() {
       
  5727 			if (!this._bounds)
       
  5728 				this._bounds = {};
       
  5729 			var bounds = this._bounds[name];
       
  5730 			if (!bounds) {
       
  5731 				bounds = this._bounds[name] = Path[name]([this._segment1,
       
  5732 						this._segment2], false, this._path.getStyle());
       
  5733 			}
       
  5734 			return bounds.clone();
       
  5735 		};
       
  5736 	},
       
  5737 {
       
  5738 
       
  5739 }), Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'],
       
  5740 	function(name, index) {
       
  5741 		this[name + 'At'] = function(offset, isParameter) {
       
  5742 			var values = this.getValues();
       
  5743 			return Curve.evaluate(values, isParameter
       
  5744 					? offset : Curve.getParameterAt(values, offset, 0), index);
       
  5745 		};
       
  5746 		this[name] = function(parameter) {
       
  5747 			return Curve.evaluate(this.getValues(), parameter, index);
       
  5748 		};
       
  5749 	},
       
  5750 {
       
  5751 	beans: false,
       
  5752 
       
  5753 	getParameterAt: function(offset, start) {
       
  5754 		return Curve.getParameterAt(this.getValues(), offset,
       
  5755 				start !== undefined ? start : offset < 0 ? 1 : 0);
       
  5756 	},
       
  5757 
       
  5758 	getParameterOf: function() {
       
  5759 		var point = Point.read(arguments);
       
  5760 		return Curve.getParameterOf(this.getValues(), point.x, point.y);
       
  5761 	},
       
  5762 
       
  5763 	getLocationAt: function(offset, isParameter) {
       
  5764 		if (!isParameter)
       
  5765 			offset = this.getParameterAt(offset);
       
  5766 		return new CurveLocation(this, offset);
       
  5767 	},
       
  5768 
       
  5769 	getLocationOf: function() {
       
  5770 		var point = Point.read(arguments),
       
  5771 			t = this.getParameterOf(point);
       
  5772 		return t != null ? new CurveLocation(this, t) : null;
       
  5773 	},
       
  5774 
       
  5775 	getOffsetOf: function() {
       
  5776 		var loc = this.getLocationOf.apply(this, arguments);
       
  5777 		return loc ? loc.getOffset() : null;
       
  5778 	},
       
  5779 
       
  5780 	getNearestLocation: function() {
       
  5781 		var point = Point.read(arguments),
       
  5782 			values = this.getValues(),
       
  5783 			count = 100,
       
  5784 			minDist = Infinity,
       
  5785 			minT = 0;
       
  5786 
       
  5787 		function refine(t) {
       
  5788 			if (t >= 0 && t <= 1) {
       
  5789 				var dist = point.getDistance(
       
  5790 						Curve.evaluate(values, t, 0), true);
       
  5791 				if (dist < minDist) {
       
  5792 					minDist = dist;
       
  5793 					minT = t;
       
  5794 					return true;
       
  5795 				}
       
  5796 			}
       
  5797 		}
       
  5798 
       
  5799 		for (var i = 0; i <= count; i++)
       
  5800 			refine(i / count);
       
  5801 
       
  5802 		var step = 1 / (count * 2);
       
  5803 		while (step > 0.00001) {
       
  5804 			if (!refine(minT - step) && !refine(minT + step))
       
  5805 				step /= 2;
       
  5806 		}
       
  5807 		var pt = Curve.evaluate(values, minT, 0);
       
  5808 		return new CurveLocation(this, minT, pt, null, null, null,
       
  5809 				point.getDistance(pt));
       
  5810 	},
       
  5811 
       
  5812 	getNearestPoint: function() {
       
  5813 		return this.getNearestLocation.apply(this, arguments).getPoint();
       
  5814 	}
       
  5815 
       
  5816 }),
       
  5817 new function() {
       
  5818 
       
  5819 	function getLengthIntegrand(v) {
       
  5820 		var p1x = v[0], p1y = v[1],
       
  5821 			c1x = v[2], c1y = v[3],
       
  5822 			c2x = v[4], c2y = v[5],
       
  5823 			p2x = v[6], p2y = v[7],
       
  5824 
       
  5825 			ax = 9 * (c1x - c2x) + 3 * (p2x - p1x),
       
  5826 			bx = 6 * (p1x + c2x) - 12 * c1x,
       
  5827 			cx = 3 * (c1x - p1x),
       
  5828 
       
  5829 			ay = 9 * (c1y - c2y) + 3 * (p2y - p1y),
       
  5830 			by = 6 * (p1y + c2y) - 12 * c1y,
       
  5831 			cy = 3 * (c1y - p1y);
       
  5832 
       
  5833 		return function(t) {
       
  5834 			var dx = (ax * t + bx) * t + cx,
       
  5835 				dy = (ay * t + by) * t + cy;
       
  5836 			return Math.sqrt(dx * dx + dy * dy);
       
  5837 		};
       
  5838 	}
       
  5839 
       
  5840 	function getIterations(a, b) {
       
  5841 		return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));
       
  5842 	}
       
  5843 
       
  5844 	return {
       
  5845 		statics: true,
       
  5846 
       
  5847 		getLength: function(v, a, b) {
       
  5848 			if (a === undefined)
       
  5849 				a = 0;
       
  5850 			if (b === undefined)
       
  5851 				b = 1;
       
  5852 			var isZero = Numerical.isZero;
       
  5853 			if (a === 0 && b === 1
       
  5854 					&& isZero(v[0] - v[2]) && isZero(v[1] - v[3])
       
  5855 					&& isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
       
  5856 				var dx = v[6] - v[0],
       
  5857 					dy = v[7] - v[1];
       
  5858 				return Math.sqrt(dx * dx + dy * dy);
       
  5859 			}
       
  5860 			var ds = getLengthIntegrand(v);
       
  5861 			return Numerical.integrate(ds, a, b, getIterations(a, b));
       
  5862 		},
       
  5863 
       
  5864 		getParameterAt: function(v, offset, start) {
       
  5865 			if (offset === 0)
       
  5866 				return start;
       
  5867 			var forward = offset > 0,
       
  5868 				a = forward ? start : 0,
       
  5869 				b = forward ? 1 : start,
       
  5870 				offset = Math.abs(offset),
       
  5871 				ds = getLengthIntegrand(v),
       
  5872 				rangeLength = Numerical.integrate(ds, a, b,
       
  5873 						getIterations(a, b));
       
  5874 			if (offset >= rangeLength)
       
  5875 				return forward ? b : a;
       
  5876 			var guess = offset / rangeLength,
       
  5877 				length = 0;
       
  5878 			function f(t) {
       
  5879 				var count = getIterations(start, t);
       
  5880 				length += start < t
       
  5881 						? Numerical.integrate(ds, start, t, count)
       
  5882 						: -Numerical.integrate(ds, t, start, count);
       
  5883 				start = t;
       
  5884 				return length - offset;
       
  5885 			}
       
  5886 			return Numerical.findRoot(f, ds,
       
  5887 					forward ? a + guess : b - guess,
       
  5888 					a, b, 16, 0.00001);
       
  5889 		}
       
  5890 	};
       
  5891 }, new function() {
       
  5892 	function addLocation(locations, include, curve1, t1, point1, curve2, t2,
       
  5893 			point2) {
       
  5894 		var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2);
       
  5895 		if (!include || include(loc))
       
  5896 			locations.push(loc);
       
  5897 	}
       
  5898 
       
  5899 	function addCurveIntersections(v1, v2, curve1, curve2, locations, include,
       
  5900 			tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) {
       
  5901 		if (recursion > 20)
       
  5902 			return;
       
  5903 		var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
       
  5904 			tolerance = 0.00001,
       
  5905 			hullEpsilon = 1e-9,
       
  5906 			getSignedDistance = Line.getSignedDistance,
       
  5907 			d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0,
       
  5908 			d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0,
       
  5909 			factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
       
  5910 			dMin = factor * Math.min(0, d1, d2),
       
  5911 			dMax = factor * Math.max(0, d1, d2),
       
  5912 			dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]),
       
  5913 			dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),
       
  5914 			dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),
       
  5915 			dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),
       
  5916 			tMinNew, tMaxNew, tDiff;
       
  5917 		if (q0x === q3x && uMax - uMin <= hullEpsilon && recursion > 3) {
       
  5918 			tMinNew = (tMax + tMin) / 2;
       
  5919 			tMaxNew = tMinNew;
       
  5920 			tDiff = 0;
       
  5921 		} else {
       
  5922 			var hull = getConvexHull(dp0, dp1, dp2, dp3),
       
  5923 				top = hull[0],
       
  5924 				bottom = hull[1],
       
  5925 				tMinClip, tMaxClip;
       
  5926 			tMinClip = clipConvexHull(top, bottom, dMin, dMax);
       
  5927 			top.reverse();
       
  5928 			bottom.reverse();
       
  5929 			tMaxClip = clipConvexHull(top, bottom, dMin, dMax);
       
  5930 			if (tMinClip == null || tMaxClip == null)
       
  5931 				return false;
       
  5932 			v1 = Curve.getPart(v1, tMinClip, tMaxClip);
       
  5933 			tDiff = tMaxClip - tMinClip;
       
  5934 			tMinNew = tMax * tMinClip + tMin * (1 - tMinClip);
       
  5935 			tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip);
       
  5936 		}
       
  5937 		if (oldTDiff > 0.8 && tDiff > 0.8) {
       
  5938 			if (tMaxNew - tMinNew > uMax - uMin) {
       
  5939 				var parts = Curve.subdivide(v1, 0.5),
       
  5940 					t = tMinNew + (tMaxNew - tMinNew) / 2;
       
  5941 				addCurveIntersections(
       
  5942 					v2, parts[0], curve2, curve1, locations, include,
       
  5943 					uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion);
       
  5944 				addCurveIntersections(
       
  5945 					v2, parts[1], curve2, curve1, locations, include,
       
  5946 					uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion);
       
  5947 			} else {
       
  5948 				var parts = Curve.subdivide(v2, 0.5),
       
  5949 					t = uMin + (uMax - uMin) / 2;
       
  5950 				addCurveIntersections(
       
  5951 					parts[0], v1, curve2, curve1, locations, include,
       
  5952 					uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
       
  5953 				addCurveIntersections(
       
  5954 					parts[1], v1, curve2, curve1, locations, include,
       
  5955 					t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion);
       
  5956 			}
       
  5957 		} else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) {
       
  5958 			var t1 = tMinNew + (tMaxNew - tMinNew) / 2,
       
  5959 				t2 = uMin + (uMax - uMin) / 2;
       
  5960 			if (reverse) {
       
  5961 				addLocation(locations, include,
       
  5962 						curve2, t2, Curve.evaluate(v2, t2, 0),
       
  5963 						curve1, t1, Curve.evaluate(v1, t1, 0));
       
  5964 			} else {
       
  5965 				addLocation(locations, include,
       
  5966 						curve1, t1, Curve.evaluate(v1, t1, 0),
       
  5967 						curve2, t2, Curve.evaluate(v2, t2, 0));
       
  5968 			}
       
  5969 		} else {
       
  5970 			addCurveIntersections(v2, v1, curve2, curve1, locations, include,
       
  5971 					uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
       
  5972 		}
       
  5973 	}
       
  5974 
       
  5975 	function getConvexHull(dq0, dq1, dq2, dq3) {
       
  5976 		var p0 = [ 0, dq0 ],
       
  5977 			p1 = [ 1 / 3, dq1 ],
       
  5978 			p2 = [ 2 / 3, dq2 ],
       
  5979 			p3 = [ 1, dq3 ],
       
  5980 			getSignedDistance = Line.getSignedDistance,
       
  5981 			dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
       
  5982 			dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2),
       
  5983 			flip = false,
       
  5984 			hull;
       
  5985 		if (dist1 * dist2 < 0) {
       
  5986 			hull = [[p0, p1, p3], [p0, p2, p3]];
       
  5987 			flip = dist1 < 0;
       
  5988 		} else {
       
  5989 			var pmax, cross = 0,
       
  5990 				distZero = dist1 === 0 || dist2 === 0;
       
  5991 			if (Math.abs(dist1) > Math.abs(dist2)) {
       
  5992 				pmax = p1;
       
  5993 				cross = (dq3 - dq2 - (dq3 - dq0) / 3)
       
  5994 						* (2 * (dq3 - dq2) - dq3 + dq1) / 3;
       
  5995 			} else {
       
  5996 				pmax = p2;
       
  5997 				cross = (dq1 - dq0 + (dq0 - dq3) / 3)
       
  5998 						* (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
       
  5999 			}
       
  6000 			hull = cross < 0 || distZero
       
  6001 					? [[p0, pmax, p3], [p0, p3]]
       
  6002 					: [[p0, p1, p2, p3], [p0, p3]];
       
  6003 			flip = dist1 ? dist1 < 0 : dist2 < 0;
       
  6004 		}
       
  6005 		return flip ? hull.reverse() : hull;
       
  6006 	}
       
  6007 
       
  6008 	function clipConvexHull(hullTop, hullBottom, dMin, dMax) {
       
  6009 		var tProxy,
       
  6010 			tVal = null,
       
  6011 			px, py,
       
  6012 			qx, qy;
       
  6013 		for (var i = 0, l = hullBottom.length - 1; i < l; i++) {
       
  6014 			py = hullBottom[i][1];
       
  6015 			qy = hullBottom[i + 1][1];
       
  6016 			if (py < qy) {
       
  6017 				tProxy = null;
       
  6018 			} else if (qy <= dMax) {
       
  6019 				px = hullBottom[i][0];
       
  6020 				qx = hullBottom[i + 1][0];
       
  6021 				tProxy = px + (dMax - py) * (qx - px) / (qy - py);
       
  6022 			} else {
       
  6023 				continue;
       
  6024 			}
       
  6025 			break;
       
  6026 		}
       
  6027 		if (hullTop[0][1] <= dMax)
       
  6028 			tProxy = hullTop[0][0];
       
  6029 		for (var i = 0, l = hullTop.length - 1; i < l; i++) {
       
  6030 			py = hullTop[i][1];
       
  6031 			qy = hullTop[i + 1][1];
       
  6032 			if (py >= dMin) {
       
  6033 				tVal = tProxy;
       
  6034 			} else if (py > qy) {
       
  6035 				tVal = null;
       
  6036 			} else if (qy >= dMin) {
       
  6037 				px = hullTop[i][0];
       
  6038 				qx = hullTop[i + 1][0];
       
  6039 				tVal = px + (dMin  - py) * (qx - px) / (qy - py);
       
  6040 			} else {
       
  6041 				continue;
       
  6042 			}
       
  6043 			break;
       
  6044 		}
       
  6045 		return tVal;
       
  6046 	}
       
  6047 
       
  6048 	function addCurveLineIntersections(v1, v2, curve1, curve2, locations,
       
  6049 			include) {
       
  6050 		var flip = Curve.isLinear(v1),
       
  6051 			vc = flip ? v2 : v1,
       
  6052 			vl = flip ? v1 : v2,
       
  6053 			lx1 = vl[0], ly1 = vl[1],
       
  6054 			lx2 = vl[6], ly2 = vl[7],
       
  6055 			ldx = lx2 - lx1,
       
  6056 			ldy = ly2 - ly1,
       
  6057 			angle = Math.atan2(-ldy, ldx),
       
  6058 			sin = Math.sin(angle),
       
  6059 			cos = Math.cos(angle),
       
  6060 			rlx2 = ldx * cos - ldy * sin,
       
  6061 			rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0],
       
  6062 			rvc = [];
       
  6063 		for(var i = 0; i < 8; i += 2) {
       
  6064 			var x = vc[i] - lx1,
       
  6065 				y = vc[i + 1] - ly1;
       
  6066 			rvc.push(
       
  6067 				x * cos - y * sin,
       
  6068 				y * cos + x * sin);
       
  6069 		}
       
  6070 		var roots = [],
       
  6071 			count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1);
       
  6072 		for (var i = 0; i < count; i++) {
       
  6073 			var tc = roots[i],
       
  6074 				x = Curve.evaluate(rvc, tc, 0).x;
       
  6075 			if (x >= 0 && x <= rlx2) {
       
  6076 				var tl = Curve.getParameterOf(rvl, x, 0),
       
  6077 					t1 = flip ? tl : tc,
       
  6078 					t2 = flip ? tc : tl;
       
  6079 				addLocation(locations, include,
       
  6080 						curve1, t1, Curve.evaluate(v1, t1, 0),
       
  6081 						curve2, t2, Curve.evaluate(v2, t2, 0));
       
  6082 			}
       
  6083 		}
       
  6084 	}
       
  6085 
       
  6086 	function addLineIntersection(v1, v2, curve1, curve2, locations, include) {
       
  6087 		var point = Line.intersect(
       
  6088 				v1[0], v1[1], v1[6], v1[7],
       
  6089 				v2[0], v2[1], v2[6], v2[7]);
       
  6090 		if (point) {
       
  6091 			var x = point.x,
       
  6092 				y = point.y;
       
  6093 			addLocation(locations, include,
       
  6094 					curve1, Curve.getParameterOf(v1, x, y), point,
       
  6095 					curve2, Curve.getParameterOf(v2, x, y), point);
       
  6096 		}
       
  6097 	}
       
  6098 
       
  6099 	return { statics: {
       
  6100 		getIntersections: function(v1, v2, curve1, curve2, locations, include) {
       
  6101 			var linear1 = Curve.isLinear(v1),
       
  6102 				linear2 = Curve.isLinear(v2);
       
  6103 			(linear1 && linear2
       
  6104 				? addLineIntersection
       
  6105 				: linear1 || linear2
       
  6106 					? addCurveLineIntersections
       
  6107 					: addCurveIntersections)(
       
  6108 						v1, v2, curve1, curve2, locations, include,
       
  6109 						0, 1, 0, 1, 0, false, 0);
       
  6110 			return locations;
       
  6111 		}
       
  6112 	}};
       
  6113 });
       
  6114 
       
  6115 var CurveLocation = Base.extend({
       
  6116 	_class: 'CurveLocation',
       
  6117 	beans: true,
       
  6118 
       
  6119 	initialize: function CurveLocation(curve, parameter, point, _curve2,
       
  6120 			_parameter2, _point2, _distance) {
       
  6121 		this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
       
  6122 		this._curve = curve;
       
  6123 		this._segment1 = curve._segment1;
       
  6124 		this._segment2 = curve._segment2;
       
  6125 		this._parameter = parameter;
       
  6126 		this._point = point;
       
  6127 		this._curve2 = _curve2;
       
  6128 		this._parameter2 = _parameter2;
       
  6129 		this._point2 = _point2;
       
  6130 		this._distance = _distance;
       
  6131 	},
       
  6132 
       
  6133 	getSegment: function(_preferFirst) {
       
  6134 		if (!this._segment) {
       
  6135 			var curve = this.getCurve(),
       
  6136 				parameter = this.getParameter();
       
  6137 			if (parameter === 1) {
       
  6138 				this._segment = curve._segment2;
       
  6139 			} else if (parameter === 0 || _preferFirst) {
       
  6140 				this._segment = curve._segment1;
       
  6141 			} else if (parameter == null) {
       
  6142 				return null;
       
  6143 			} else {
       
  6144 				this._segment = curve.getPartLength(0, parameter)
       
  6145 					< curve.getPartLength(parameter, 1)
       
  6146 						? curve._segment1
       
  6147 						: curve._segment2;
       
  6148 			}
       
  6149 		}
       
  6150 		return this._segment;
       
  6151 	},
       
  6152 
       
  6153 	getCurve: function(_uncached) {
       
  6154 		if (!this._curve || _uncached) {
       
  6155 			this._curve = this._segment1.getCurve();
       
  6156 			if (this._curve.getParameterOf(this._point) == null)
       
  6157 				this._curve = this._segment2.getPrevious().getCurve();
       
  6158 		}
       
  6159 		return this._curve;
       
  6160 	},
       
  6161 
       
  6162 	getIntersection: function() {
       
  6163 		var intersection = this._intersection;
       
  6164 		if (!intersection && this._curve2) {
       
  6165 			var param = this._parameter2;
       
  6166 			this._intersection = intersection = new CurveLocation(
       
  6167 					this._curve2, param, this._point2 || this._point, this);
       
  6168 			intersection._intersection = this;
       
  6169 		}
       
  6170 		return intersection;
       
  6171 	},
       
  6172 
       
  6173 	getPath: function() {
       
  6174 		var curve = this.getCurve();
       
  6175 		return curve && curve._path;
       
  6176 	},
       
  6177 
       
  6178 	getIndex: function() {
       
  6179 		var curve = this.getCurve();
       
  6180 		return curve && curve.getIndex();
       
  6181 	},
       
  6182 
       
  6183 	getOffset: function() {
       
  6184 		var path = this.getPath();
       
  6185 		return path ? path._getOffset(this) : this.getCurveOffset();
       
  6186 	},
       
  6187 
       
  6188 	getCurveOffset: function() {
       
  6189 		var curve = this.getCurve(),
       
  6190 			parameter = this.getParameter();
       
  6191 		return parameter != null && curve && curve.getPartLength(0, parameter);
       
  6192 	},
       
  6193 
       
  6194 	getParameter: function(_uncached) {
       
  6195 		if ((this._parameter == null || _uncached) && this._point) {
       
  6196 			var curve = this.getCurve(_uncached && this._point);
       
  6197 			this._parameter = curve && curve.getParameterOf(this._point);
       
  6198 		}
       
  6199 		return this._parameter;
       
  6200 	},
       
  6201 
       
  6202 	getPoint: function(_uncached) {
       
  6203 		if ((!this._point || _uncached) && this._parameter != null) {
       
  6204 			var curve = this.getCurve();
       
  6205 			this._point = curve && curve.getPointAt(this._parameter, true);
       
  6206 		}
       
  6207 		return this._point;
       
  6208 	},
       
  6209 
       
  6210 	getDistance: function() {
       
  6211 		return this._distance;
       
  6212 	},
       
  6213 
       
  6214 	divide: function() {
       
  6215 		var curve = this.getCurve(true);
       
  6216 		return curve && curve.divide(this.getParameter(true), true);
       
  6217 	},
       
  6218 
       
  6219 	split: function() {
       
  6220 		var curve = this.getCurve(true);
       
  6221 		return curve && curve.split(this.getParameter(true), true);
       
  6222 	},
       
  6223 
       
  6224 	equals: function(loc) {
       
  6225 		var isZero = Numerical.isZero;
       
  6226 		return this === loc
       
  6227 				|| loc
       
  6228 					&& this._curve === loc._curve
       
  6229 					&& this._curve2 === loc._curve2
       
  6230 					&& isZero(this._parameter - loc._parameter)
       
  6231 					&& isZero(this._parameter2 - loc._parameter2)
       
  6232 				|| false;
       
  6233 	},
       
  6234 
       
  6235 	toString: function() {
       
  6236 		var parts = [],
       
  6237 			point = this.getPoint(),
       
  6238 			f = Formatter.instance;
       
  6239 		if (point)
       
  6240 			parts.push('point: ' + point);
       
  6241 		var index = this.getIndex();
       
  6242 		if (index != null)
       
  6243 			parts.push('index: ' + index);
       
  6244 		var parameter = this.getParameter();
       
  6245 		if (parameter != null)
       
  6246 			parts.push('parameter: ' + f.number(parameter));
       
  6247 		if (this._distance != null)
       
  6248 			parts.push('distance: ' + f.number(this._distance));
       
  6249 		return '{ ' + parts.join(', ') + ' }';
       
  6250 	}
       
  6251 }, Base.each(['Tangent', 'Normal', 'Curvature'],
       
  6252 	function(name) {
       
  6253 		var get = 'get' + name + 'At';
       
  6254 		this['get' + name] = function() {
       
  6255 			var parameter = this.getParameter(),
       
  6256 				curve = this.getCurve();
       
  6257 			return parameter != null && curve && curve[get](parameter, true);
       
  6258 		};
       
  6259 	}, {}
       
  6260 ));
       
  6261 
       
  6262 var PathItem = Item.extend({
       
  6263 	_class: 'PathItem',
       
  6264 
       
  6265 	initialize: function PathItem() {
       
  6266 	},
       
  6267 
       
  6268 	getIntersections: function(path, _expand) {
       
  6269 		if (this === path)
       
  6270 			path = null;
       
  6271 		if (path && !this.getBounds().touches(path.getBounds()))
       
  6272 			return [];
       
  6273 		var locations = [],
       
  6274 			curves1 = this.getCurves(),
       
  6275 			curves2 = path ? path.getCurves() : curves1,
       
  6276 			matrix1 = this._matrix.orNullIfIdentity(),
       
  6277 			matrix2 = path ? path._matrix.orNullIfIdentity() : matrix1,
       
  6278 			length1 = curves1.length,
       
  6279 			length2 = path ? curves2.length : length1,
       
  6280 			values2 = [],
       
  6281 			MIN = 1e-11,
       
  6282 			MAX = 1 - 1e-11;
       
  6283 		for (var i = 0; i < length2; i++)
       
  6284 			values2[i] = curves2[i].getValues(matrix2);
       
  6285 		for (var i = 0; i < length1; i++) {
       
  6286 			var curve1 = curves1[i],
       
  6287 				values1 = path ? curve1.getValues(matrix1) : values2[i];
       
  6288 			if (!path) {
       
  6289 				var seg1 = curve1.getSegment1(),
       
  6290 					seg2 = curve1.getSegment2(),
       
  6291 					h1 = seg1._handleOut,
       
  6292 					h2 = seg2._handleIn;
       
  6293 				if (new Line(seg1._point.subtract(h1), h1.multiply(2), true)
       
  6294 						.intersect(new Line(seg2._point.subtract(h2),
       
  6295 						h2.multiply(2), true), false)) {
       
  6296 					var parts = Curve.subdivide(values1);
       
  6297 					Curve.getIntersections(
       
  6298 						parts[0], parts[1], curve1, curve1, locations,
       
  6299 						function(loc) {
       
  6300 							if (loc._parameter <= MAX) {
       
  6301 								loc._parameter /= 2;
       
  6302 								loc._parameter2 = 0.5 + loc._parameter2 / 2;
       
  6303 								return true;
       
  6304 							}
       
  6305 						}
       
  6306 					);
       
  6307 				}
       
  6308 			}
       
  6309 			for (var j = path ? 0 : i + 1; j < length2; j++) {
       
  6310 				Curve.getIntersections(
       
  6311 					values1, values2[j], curve1, curves2[j], locations,
       
  6312 					!path && (j === i + 1 || j === length2 - 1 && i === 0)
       
  6313 						&& function(loc) {
       
  6314 							var t = loc._parameter;
       
  6315 							return t >= MIN && t <= MAX;
       
  6316 						}
       
  6317 				);
       
  6318 			}
       
  6319 		}
       
  6320 		var last = locations.length - 1;
       
  6321 		for (var i = last; i >= 0; i--) {
       
  6322 			var loc = locations[i],
       
  6323 				next = loc._curve.getNext(),
       
  6324 				next2 = loc._curve2.getNext();
       
  6325 			if (next && loc._parameter >= MAX) {
       
  6326 				loc._parameter = 0;
       
  6327 				loc._curve = next;
       
  6328 			}
       
  6329 			if (next2 && loc._parameter2 >= MAX) {
       
  6330 				loc._parameter2 = 0;
       
  6331 				loc._curve2 = next2;
       
  6332 			}
       
  6333 		}
       
  6334 
       
  6335 		function compare(loc1, loc2) {
       
  6336 			var path1 = loc1.getPath(),
       
  6337 				path2 = loc2.getPath();
       
  6338 			return path1 === path2
       
  6339 					? (loc1.getIndex() + loc1.getParameter())
       
  6340 							- (loc2.getIndex() + loc2.getParameter())
       
  6341 					: path1._id - path2._id;
       
  6342 		}
       
  6343 
       
  6344 		if (last > 0) {
       
  6345 			locations.sort(compare);
       
  6346 			for (var i = last; i >= 1; i--) {
       
  6347 				if (locations[i].equals(locations[i === 0 ? last : i - 1])) {
       
  6348 					locations.splice(i, 1);
       
  6349 					last--;
       
  6350 				}
       
  6351 			}
       
  6352 		}
       
  6353 		if (_expand) {
       
  6354 			for (var i = last; i >= 0; i--)
       
  6355 				locations.push(locations[i].getIntersection());
       
  6356 			locations.sort(compare);
       
  6357 		}
       
  6358 		return locations;
       
  6359 	},
       
  6360 
       
  6361 	setPathData: function(data) {
       
  6362 
       
  6363 		var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
       
  6364 			coords,
       
  6365 			relative = false,
       
  6366 			previous,
       
  6367 			control,
       
  6368 			current = new Point(),
       
  6369 			start = new Point();
       
  6370 
       
  6371 		function getCoord(index, coord) {
       
  6372 			var val = +coords[index];
       
  6373 			if (relative)
       
  6374 				val += current[coord];
       
  6375 			return val;
       
  6376 		}
       
  6377 
       
  6378 		function getPoint(index) {
       
  6379 			return new Point(
       
  6380 				getCoord(index, 'x'),
       
  6381 				getCoord(index + 1, 'y')
       
  6382 			);
       
  6383 		}
       
  6384 
       
  6385 		this.clear();
       
  6386 
       
  6387 		for (var i = 0, l = parts.length; i < l; i++) {
       
  6388 			var part = parts[i],
       
  6389 				command = part[0],
       
  6390 				lower = command.toLowerCase();
       
  6391 			coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
       
  6392 			var length = coords && coords.length;
       
  6393 			relative = command === lower;
       
  6394 			if (previous === 'z' && !/[mz]/.test(lower))
       
  6395 				this.moveTo(current = start);
       
  6396 			switch (lower) {
       
  6397 			case 'm':
       
  6398 			case 'l':
       
  6399 				var move = lower === 'm';
       
  6400 				if (move && previous && previous !== 'z')
       
  6401 					this.closePath(true);
       
  6402 				for (var j = 0; j < length; j += 2)
       
  6403 					this[j === 0 && move ? 'moveTo' : 'lineTo'](
       
  6404 							current = getPoint(j));
       
  6405 				control = current;
       
  6406 				if (move)
       
  6407 					start = current;
       
  6408 				break;
       
  6409 			case 'h':
       
  6410 			case 'v':
       
  6411 				var coord = lower === 'h' ? 'x' : 'y';
       
  6412 				for (var j = 0; j < length; j++) {
       
  6413 					current[coord] = getCoord(j, coord);
       
  6414 					this.lineTo(current);
       
  6415 				}
       
  6416 				control = current;
       
  6417 				break;
       
  6418 			case 'c':
       
  6419 				for (var j = 0; j < length; j += 6) {
       
  6420 					this.cubicCurveTo(
       
  6421 							getPoint(j),
       
  6422 							control = getPoint(j + 2),
       
  6423 							current = getPoint(j + 4));
       
  6424 				}
       
  6425 				break;
       
  6426 			case 's':
       
  6427 				for (var j = 0; j < length; j += 4) {
       
  6428 					this.cubicCurveTo(
       
  6429 							/[cs]/.test(previous)
       
  6430 									? current.multiply(2).subtract(control)
       
  6431 									: current,
       
  6432 							control = getPoint(j),
       
  6433 							current = getPoint(j + 2));
       
  6434 					previous = lower;
       
  6435 				}
       
  6436 				break;
       
  6437 			case 'q':
       
  6438 				for (var j = 0; j < length; j += 4) {
       
  6439 					this.quadraticCurveTo(
       
  6440 							control = getPoint(j),
       
  6441 							current = getPoint(j + 2));
       
  6442 				}
       
  6443 				break;
       
  6444 			case 't':
       
  6445 				for (var j = 0; j < length; j += 2) {
       
  6446 					this.quadraticCurveTo(
       
  6447 							control = (/[qt]/.test(previous)
       
  6448 									? current.multiply(2).subtract(control)
       
  6449 									: current),
       
  6450 							current = getPoint(j));
       
  6451 					previous = lower;
       
  6452 				}
       
  6453 				break;
       
  6454 			case 'a':
       
  6455 				for (var j = 0; j < length; j += 7) {
       
  6456 					this.arcTo(current = getPoint(j + 5),
       
  6457 							new Size(+coords[0], +coords[1]),
       
  6458 							+coords[2], +coords[4], +coords[3]);
       
  6459 				}
       
  6460 				break;
       
  6461 			case 'z':
       
  6462 				this.closePath(true);
       
  6463 				break;
       
  6464 			}
       
  6465 			previous = lower;
       
  6466 		}
       
  6467 	},
       
  6468 
       
  6469 	_canComposite: function() {
       
  6470 		return !(this.hasFill() && this.hasStroke());
       
  6471 	},
       
  6472 
       
  6473 	_contains: function(point) {
       
  6474 		var winding = this._getWinding(point, false, true);
       
  6475 		return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
       
  6476 	}
       
  6477 
       
  6478 });
       
  6479 
       
  6480 var Path = PathItem.extend({
       
  6481 	_class: 'Path',
       
  6482 	_serializeFields: {
       
  6483 		segments: [],
       
  6484 		closed: false
       
  6485 	},
       
  6486 
       
  6487 	initialize: function Path(arg) {
       
  6488 		this._closed = false;
       
  6489 		this._segments = [];
       
  6490 		var segments = Array.isArray(arg)
       
  6491 			? typeof arg[0] === 'object'
       
  6492 				? arg
       
  6493 				: arguments
       
  6494 			: arg && (arg.size === undefined && (arg.x !== undefined
       
  6495 					|| arg.point !== undefined))
       
  6496 				? arguments
       
  6497 				: null;
       
  6498 		if (segments && segments.length > 0) {
       
  6499 			this.setSegments(segments);
       
  6500 		} else {
       
  6501 			this._curves = undefined;
       
  6502 			this._selectedSegmentState = 0;
       
  6503 			if (!segments && typeof arg === 'string') {
       
  6504 				this.setPathData(arg);
       
  6505 				arg = null;
       
  6506 			}
       
  6507 		}
       
  6508 		this._initialize(!segments && arg);
       
  6509 	},
       
  6510 
       
  6511 	_equals: function(item) {
       
  6512 		return Base.equals(this._segments, item._segments);
       
  6513 	},
       
  6514 
       
  6515 	clone: function(insert) {
       
  6516 		var copy = new Path(Item.NO_INSERT);
       
  6517 		copy.setSegments(this._segments);
       
  6518 		copy._closed = this._closed;
       
  6519 		if (this._clockwise !== undefined)
       
  6520 			copy._clockwise = this._clockwise;
       
  6521 		return this._clone(copy, insert);
       
  6522 	},
       
  6523 
       
  6524 	_changed: function _changed(flags) {
       
  6525 		_changed.base.call(this, flags);
       
  6526 		if (flags & 8) {
       
  6527 			var parent = this._parent;
       
  6528 			if (parent)
       
  6529 				parent._currentPath = undefined;
       
  6530 			this._length = this._clockwise = undefined;
       
  6531 			if (this._curves && !(flags & 16)) {
       
  6532 				for (var i = 0, l = this._curves.length; i < l; i++)
       
  6533 					this._curves[i]._changed();
       
  6534 			}
       
  6535 			this._monoCurves = undefined;
       
  6536 		} else if (flags & 32) {
       
  6537 			this._bounds = undefined;
       
  6538 		}
       
  6539 	},
       
  6540 
       
  6541 	getStyle: function() {
       
  6542 		var parent = this._parent;
       
  6543 		return (parent instanceof CompoundPath ? parent : this)._style;
       
  6544 	},
       
  6545 
       
  6546 	getSegments: function() {
       
  6547 		return this._segments;
       
  6548 	},
       
  6549 
       
  6550 	setSegments: function(segments) {
       
  6551 		var fullySelected = this.isFullySelected();
       
  6552 		this._segments.length = 0;
       
  6553 		this._selectedSegmentState = 0;
       
  6554 		this._curves = undefined;
       
  6555 		if (segments && segments.length > 0)
       
  6556 			this._add(Segment.readAll(segments));
       
  6557 		if (fullySelected)
       
  6558 			this.setFullySelected(true);
       
  6559 	},
       
  6560 
       
  6561 	getFirstSegment: function() {
       
  6562 		return this._segments[0];
       
  6563 	},
       
  6564 
       
  6565 	getLastSegment: function() {
       
  6566 		return this._segments[this._segments.length - 1];
       
  6567 	},
       
  6568 
       
  6569 	getCurves: function() {
       
  6570 		var curves = this._curves,
       
  6571 			segments = this._segments;
       
  6572 		if (!curves) {
       
  6573 			var length = this._countCurves();
       
  6574 			curves = this._curves = new Array(length);
       
  6575 			for (var i = 0; i < length; i++)
       
  6576 				curves[i] = new Curve(this, segments[i],
       
  6577 					segments[i + 1] || segments[0]);
       
  6578 		}
       
  6579 		return curves;
       
  6580 	},
       
  6581 
       
  6582 	getFirstCurve: function() {
       
  6583 		return this.getCurves()[0];
       
  6584 	},
       
  6585 
       
  6586 	getLastCurve: function() {
       
  6587 		var curves = this.getCurves();
       
  6588 		return curves[curves.length - 1];
       
  6589 	},
       
  6590 
       
  6591 	isClosed: function() {
       
  6592 		return this._closed;
       
  6593 	},
       
  6594 
       
  6595 	setClosed: function(closed) {
       
  6596 		if (this._closed != (closed = !!closed)) {
       
  6597 			this._closed = closed;
       
  6598 			if (this._curves) {
       
  6599 				var length = this._curves.length = this._countCurves();
       
  6600 				if (closed)
       
  6601 					this._curves[length - 1] = new Curve(this,
       
  6602 						this._segments[length - 1], this._segments[0]);
       
  6603 			}
       
  6604 			this._changed(25);
       
  6605 		}
       
  6606 	}
       
  6607 }, {
       
  6608 	beans: true,
       
  6609 
       
  6610 	getPathData: function(_matrix, _precision) {
       
  6611 		var segments = this._segments,
       
  6612 			length = segments.length,
       
  6613 			f = new Formatter(_precision),
       
  6614 			coords = new Array(6),
       
  6615 			first = true,
       
  6616 			curX, curY,
       
  6617 			prevX, prevY,
       
  6618 			inX, inY,
       
  6619 			outX, outY,
       
  6620 			parts = [];
       
  6621 
       
  6622 		function addSegment(segment, skipLine) {
       
  6623 			segment._transformCoordinates(_matrix, coords, false);
       
  6624 			curX = coords[0];
       
  6625 			curY = coords[1];
       
  6626 			if (first) {
       
  6627 				parts.push('M' + f.pair(curX, curY));
       
  6628 				first = false;
       
  6629 			} else {
       
  6630 				inX = coords[2];
       
  6631 				inY = coords[3];
       
  6632 				if (inX === curX && inY === curY
       
  6633 						&& outX === prevX && outY === prevY) {
       
  6634 					if (!skipLine)
       
  6635 						parts.push('l' + f.pair(curX - prevX, curY - prevY));
       
  6636 				} else {
       
  6637 					parts.push('c' + f.pair(outX - prevX, outY - prevY)
       
  6638 							+ ' ' + f.pair(inX - prevX, inY - prevY)
       
  6639 							+ ' ' + f.pair(curX - prevX, curY - prevY));
       
  6640 				}
       
  6641 			}
       
  6642 			prevX = curX;
       
  6643 			prevY = curY;
       
  6644 			outX = coords[4];
       
  6645 			outY = coords[5];
       
  6646 		}
       
  6647 
       
  6648 		if (length === 0)
       
  6649 			return '';
       
  6650 
       
  6651 		for (var i = 0; i < length; i++)
       
  6652 			addSegment(segments[i]);
       
  6653 		if (this._closed && length > 0) {
       
  6654 			addSegment(segments[0], true);
       
  6655 			parts.push('z');
       
  6656 		}
       
  6657 		return parts.join('');
       
  6658 	}
       
  6659 }, {
       
  6660 
       
  6661 	isEmpty: function() {
       
  6662 		return this._segments.length === 0;
       
  6663 	},
       
  6664 
       
  6665 	isPolygon: function() {
       
  6666 		for (var i = 0, l = this._segments.length; i < l; i++) {
       
  6667 			if (!this._segments[i].isLinear())
       
  6668 				return false;
       
  6669 		}
       
  6670 		return true;
       
  6671 	},
       
  6672 
       
  6673 	_transformContent: function(matrix) {
       
  6674 		var coords = new Array(6);
       
  6675 		for (var i = 0, l = this._segments.length; i < l; i++)
       
  6676 			this._segments[i]._transformCoordinates(matrix, coords, true);
       
  6677 		return true;
       
  6678 	},
       
  6679 
       
  6680 	_add: function(segs, index) {
       
  6681 		var segments = this._segments,
       
  6682 			curves = this._curves,
       
  6683 			amount = segs.length,
       
  6684 			append = index == null,
       
  6685 			index = append ? segments.length : index;
       
  6686 		for (var i = 0; i < amount; i++) {
       
  6687 			var segment = segs[i];
       
  6688 			if (segment._path)
       
  6689 				segment = segs[i] = segment.clone();
       
  6690 			segment._path = this;
       
  6691 			segment._index = index + i;
       
  6692 			if (segment._selectionState)
       
  6693 				this._updateSelection(segment, 0, segment._selectionState);
       
  6694 		}
       
  6695 		if (append) {
       
  6696 			segments.push.apply(segments, segs);
       
  6697 		} else {
       
  6698 			segments.splice.apply(segments, [index, 0].concat(segs));
       
  6699 			for (var i = index + amount, l = segments.length; i < l; i++)
       
  6700 				segments[i]._index = i;
       
  6701 		}
       
  6702 		if (curves || segs._curves) {
       
  6703 			if (!curves)
       
  6704 				curves = this._curves = [];
       
  6705 			var from = index > 0 ? index - 1 : index,
       
  6706 				start = from,
       
  6707 				to = Math.min(from + amount, this._countCurves());
       
  6708 			if (segs._curves) {
       
  6709 				curves.splice.apply(curves, [from, 0].concat(segs._curves));
       
  6710 				start += segs._curves.length;
       
  6711 			}
       
  6712 			for (var i = start; i < to; i++)
       
  6713 				curves.splice(i, 0, new Curve(this, null, null));
       
  6714 			this._adjustCurves(from, to);
       
  6715 		}
       
  6716 		this._changed(25);
       
  6717 		return segs;
       
  6718 	},
       
  6719 
       
  6720 	_adjustCurves: function(from, to) {
       
  6721 		var segments = this._segments,
       
  6722 			curves = this._curves,
       
  6723 			curve;
       
  6724 		for (var i = from; i < to; i++) {
       
  6725 			curve = curves[i];
       
  6726 			curve._path = this;
       
  6727 			curve._segment1 = segments[i];
       
  6728 			curve._segment2 = segments[i + 1] || segments[0];
       
  6729 			curve._changed();
       
  6730 		}
       
  6731 		if (curve = curves[this._closed && from === 0 ? segments.length - 1
       
  6732 				: from - 1]) {
       
  6733 			curve._segment2 = segments[from] || segments[0];
       
  6734 			curve._changed();
       
  6735 		}
       
  6736 		if (curve = curves[to]) {
       
  6737 			curve._segment1 = segments[to];
       
  6738 			curve._changed();
       
  6739 		}
       
  6740 	},
       
  6741 
       
  6742 	_countCurves: function() {
       
  6743 		var length = this._segments.length;
       
  6744 		return !this._closed && length > 0 ? length - 1 : length;
       
  6745 	},
       
  6746 
       
  6747 	add: function(segment1 ) {
       
  6748 		return arguments.length > 1 && typeof segment1 !== 'number'
       
  6749 			? this._add(Segment.readAll(arguments))
       
  6750 			: this._add([ Segment.read(arguments) ])[0];
       
  6751 	},
       
  6752 
       
  6753 	insert: function(index, segment1 ) {
       
  6754 		return arguments.length > 2 && typeof segment1 !== 'number'
       
  6755 			? this._add(Segment.readAll(arguments, 1), index)
       
  6756 			: this._add([ Segment.read(arguments, 1) ], index)[0];
       
  6757 	},
       
  6758 
       
  6759 	addSegment: function() {
       
  6760 		return this._add([ Segment.read(arguments) ])[0];
       
  6761 	},
       
  6762 
       
  6763 	insertSegment: function(index ) {
       
  6764 		return this._add([ Segment.read(arguments, 1) ], index)[0];
       
  6765 	},
       
  6766 
       
  6767 	addSegments: function(segments) {
       
  6768 		return this._add(Segment.readAll(segments));
       
  6769 	},
       
  6770 
       
  6771 	insertSegments: function(index, segments) {
       
  6772 		return this._add(Segment.readAll(segments), index);
       
  6773 	},
       
  6774 
       
  6775 	removeSegment: function(index) {
       
  6776 		return this.removeSegments(index, index + 1)[0] || null;
       
  6777 	},
       
  6778 
       
  6779 	removeSegments: function(from, to, _includeCurves) {
       
  6780 		from = from || 0;
       
  6781 		to = Base.pick(to, this._segments.length);
       
  6782 		var segments = this._segments,
       
  6783 			curves = this._curves,
       
  6784 			count = segments.length,
       
  6785 			removed = segments.splice(from, to - from),
       
  6786 			amount = removed.length;
       
  6787 		if (!amount)
       
  6788 			return removed;
       
  6789 		for (var i = 0; i < amount; i++) {
       
  6790 			var segment = removed[i];
       
  6791 			if (segment._selectionState)
       
  6792 				this._updateSelection(segment, segment._selectionState, 0);
       
  6793 			segment._index = segment._path = null;
       
  6794 		}
       
  6795 		for (var i = from, l = segments.length; i < l; i++)
       
  6796 			segments[i]._index = i;
       
  6797 		if (curves) {
       
  6798 			var index = from > 0 && to === count + (this._closed ? 1 : 0)
       
  6799 					? from - 1
       
  6800 					: from,
       
  6801 				curves = curves.splice(index, amount);
       
  6802 			if (_includeCurves)
       
  6803 				removed._curves = curves.slice(1);
       
  6804 			this._adjustCurves(index, index);
       
  6805 		}
       
  6806 		this._changed(25);
       
  6807 		return removed;
       
  6808 	},
       
  6809 
       
  6810 	clear: '#removeSegments',
       
  6811 
       
  6812 	getLength: function() {
       
  6813 		if (this._length == null) {
       
  6814 			var curves = this.getCurves();
       
  6815 			this._length = 0;
       
  6816 			for (var i = 0, l = curves.length; i < l; i++)
       
  6817 				this._length += curves[i].getLength();
       
  6818 		}
       
  6819 		return this._length;
       
  6820 	},
       
  6821 
       
  6822 	getArea: function() {
       
  6823 		var curves = this.getCurves();
       
  6824 		var area = 0;
       
  6825 		for (var i = 0, l = curves.length; i < l; i++)
       
  6826 			area += curves[i].getArea();
       
  6827 		return area;
       
  6828 	},
       
  6829 
       
  6830 	isFullySelected: function() {
       
  6831 		var length = this._segments.length;
       
  6832 		return this._selected && length > 0 && this._selectedSegmentState
       
  6833 				=== length * 7;
       
  6834 	},
       
  6835 
       
  6836 	setFullySelected: function(selected) {
       
  6837 		if (selected)
       
  6838 			this._selectSegments(true);
       
  6839 		this.setSelected(selected);
       
  6840 	},
       
  6841 
       
  6842 	setSelected: function setSelected(selected) {
       
  6843 		if (!selected)
       
  6844 			this._selectSegments(false);
       
  6845 		setSelected.base.call(this, selected);
       
  6846 	},
       
  6847 
       
  6848 	_selectSegments: function(selected) {
       
  6849 		var length = this._segments.length;
       
  6850 		this._selectedSegmentState = selected
       
  6851 				? length * 7 : 0;
       
  6852 		for (var i = 0; i < length; i++)
       
  6853 			this._segments[i]._selectionState = selected
       
  6854 					? 7 : 0;
       
  6855 	},
       
  6856 
       
  6857 	_updateSelection: function(segment, oldState, newState) {
       
  6858 		segment._selectionState = newState;
       
  6859 		var total = this._selectedSegmentState += newState - oldState;
       
  6860 		if (total > 0)
       
  6861 			this.setSelected(true);
       
  6862 	},
       
  6863 
       
  6864 	flatten: function(maxDistance) {
       
  6865 		var flattener = new PathFlattener(this),
       
  6866 			pos = 0,
       
  6867 			step = flattener.length / Math.ceil(flattener.length / maxDistance),
       
  6868 			end = flattener.length + (this._closed ? -step : step) / 2;
       
  6869 		var segments = [];
       
  6870 		while (pos <= end) {
       
  6871 			segments.push(new Segment(flattener.evaluate(pos, 0)));
       
  6872 			pos += step;
       
  6873 		}
       
  6874 		this.setSegments(segments);
       
  6875 	},
       
  6876 
       
  6877 	reduce: function() {
       
  6878 		var curves = this.getCurves();
       
  6879 		for (var i = curves.length - 1; i >= 0; i--) {
       
  6880 			var curve = curves[i];
       
  6881 			if (curve.isLinear() && curve.getLength() === 0)
       
  6882 				curve.remove();
       
  6883 		}
       
  6884 		return this;
       
  6885 	},
       
  6886 
       
  6887 	simplify: function(tolerance) {
       
  6888 		if (this._segments.length > 2) {
       
  6889 			var fitter = new PathFitter(this, tolerance || 2.5);
       
  6890 			this.setSegments(fitter.fit());
       
  6891 		}
       
  6892 	},
       
  6893 
       
  6894 	split: function(index, parameter) {
       
  6895 		if (parameter === null)
       
  6896 			return;
       
  6897 		if (arguments.length === 1) {
       
  6898 			var arg = index;
       
  6899 			if (typeof arg === 'number')
       
  6900 				arg = this.getLocationAt(arg);
       
  6901 			index = arg.index;
       
  6902 			parameter = arg.parameter;
       
  6903 		}
       
  6904 		var tolerance = 0.00001;
       
  6905 		if (parameter >= 1 - tolerance) {
       
  6906 			index++;
       
  6907 			parameter--;
       
  6908 		}
       
  6909 		var curves = this.getCurves();
       
  6910 		if (index >= 0 && index < curves.length) {
       
  6911 			if (parameter > tolerance) {
       
  6912 				curves[index++].divide(parameter, true);
       
  6913 			}
       
  6914 			var segs = this.removeSegments(index, this._segments.length, true),
       
  6915 				path;
       
  6916 			if (this._closed) {
       
  6917 				this.setClosed(false);
       
  6918 				path = this;
       
  6919 			} else if (index > 0) {
       
  6920 				path = this._clone(new Path().insertAbove(this, true));
       
  6921 			}
       
  6922 			path._add(segs, 0);
       
  6923 			this.addSegment(segs[0]);
       
  6924 			return path;
       
  6925 		}
       
  6926 		return null;
       
  6927 	},
       
  6928 
       
  6929 	isClockwise: function() {
       
  6930 		if (this._clockwise !== undefined)
       
  6931 			return this._clockwise;
       
  6932 		return Path.isClockwise(this._segments);
       
  6933 	},
       
  6934 
       
  6935 	setClockwise: function(clockwise) {
       
  6936 		if (this.isClockwise() != (clockwise = !!clockwise))
       
  6937 			this.reverse();
       
  6938 		this._clockwise = clockwise;
       
  6939 	},
       
  6940 
       
  6941 	reverse: function() {
       
  6942 		this._segments.reverse();
       
  6943 		for (var i = 0, l = this._segments.length; i < l; i++) {
       
  6944 			var segment = this._segments[i];
       
  6945 			var handleIn = segment._handleIn;
       
  6946 			segment._handleIn = segment._handleOut;
       
  6947 			segment._handleOut = handleIn;
       
  6948 			segment._index = i;
       
  6949 		}
       
  6950 		this._curves = null;
       
  6951 		if (this._clockwise !== undefined)
       
  6952 			this._clockwise = !this._clockwise;
       
  6953 		this._changed(9);
       
  6954 	},
       
  6955 
       
  6956 	join: function(path) {
       
  6957 		if (path) {
       
  6958 			var segments = path._segments,
       
  6959 				last1 = this.getLastSegment(),
       
  6960 				last2 = path.getLastSegment();
       
  6961 			if (last1._point.equals(last2._point))
       
  6962 				path.reverse();
       
  6963 			var first1,
       
  6964 				first2 = path.getFirstSegment();
       
  6965 			if (last1._point.equals(first2._point)) {
       
  6966 				last1.setHandleOut(first2._handleOut);
       
  6967 				this._add(segments.slice(1));
       
  6968 			} else {
       
  6969 				first1 = this.getFirstSegment();
       
  6970 				if (first1._point.equals(first2._point))
       
  6971 					path.reverse();
       
  6972 				last2 = path.getLastSegment();
       
  6973 				if (first1._point.equals(last2._point)) {
       
  6974 					first1.setHandleIn(last2._handleIn);
       
  6975 					this._add(segments.slice(0, segments.length - 1), 0);
       
  6976 				} else {
       
  6977 					this._add(segments.slice());
       
  6978 				}
       
  6979 			}
       
  6980 			if (path.closed)
       
  6981 				this._add([segments[0]]);
       
  6982 			path.remove();
       
  6983 		}
       
  6984 		var first = this.getFirstSegment(),
       
  6985 			last = this.getLastSegment();
       
  6986 		if (first !== last && first._point.equals(last._point)) {
       
  6987 			first.setHandleIn(last._handleIn);
       
  6988 			last.remove();
       
  6989 			this.setClosed(true);
       
  6990 		}
       
  6991 	},
       
  6992 
       
  6993 	toShape: function(insert) {
       
  6994 		if (!this._closed)
       
  6995 			return null;
       
  6996 
       
  6997 		var segments = this._segments,
       
  6998 			type,
       
  6999 			size,
       
  7000 			radius,
       
  7001 			topCenter;
       
  7002 
       
  7003 		function isColinear(i, j) {
       
  7004 			return segments[i].isColinear(segments[j]);
       
  7005 		}
       
  7006 
       
  7007 		function isOrthogonal(i) {
       
  7008 			return segments[i].isOrthogonal();
       
  7009 		}
       
  7010 
       
  7011 		function isArc(i) {
       
  7012 			return segments[i].isArc();
       
  7013 		}
       
  7014 
       
  7015 		function getDistance(i, j) {
       
  7016 			return segments[i]._point.getDistance(segments[j]._point);
       
  7017 		}
       
  7018 
       
  7019 		if (this.isPolygon() && segments.length === 4
       
  7020 				&& isColinear(0, 2) && isColinear(1, 3) && isOrthogonal(1)) {
       
  7021 			type = Shape.Rectangle;
       
  7022 			size = new Size(getDistance(0, 3), getDistance(0, 1));
       
  7023 			topCenter = segments[1]._point.add(segments[2]._point).divide(2);
       
  7024 		} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)
       
  7025 				&& isArc(6) && isColinear(1, 5) && isColinear(3, 7)) {
       
  7026 			type = Shape.Rectangle;
       
  7027 			size = new Size(getDistance(1, 6), getDistance(0, 3));
       
  7028 			radius = size.subtract(new Size(getDistance(0, 7),
       
  7029 					getDistance(1, 2))).divide(2);
       
  7030 			topCenter = segments[3]._point.add(segments[4]._point).divide(2);
       
  7031 		} else if (segments.length === 4
       
  7032 				&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
       
  7033 			if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {
       
  7034 				type = Shape.Circle;
       
  7035 				radius = getDistance(0, 2) / 2;
       
  7036 			} else {
       
  7037 				type = Shape.Ellipse;
       
  7038 				radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);
       
  7039 			}
       
  7040 			topCenter = segments[1]._point;
       
  7041 		}
       
  7042 
       
  7043 		if (type) {
       
  7044 			var center = this.getPosition(true),
       
  7045 				shape = new type({
       
  7046 					center: center,
       
  7047 					size: size,
       
  7048 					radius: radius,
       
  7049 					insert: false
       
  7050 				});
       
  7051 			shape.rotate(topCenter.subtract(center).getAngle() + 90);
       
  7052 			shape.setStyle(this._style);
       
  7053 			if (insert || insert === undefined)
       
  7054 				shape.insertAbove(this);
       
  7055 			return shape;
       
  7056 		}
       
  7057 		return null;
       
  7058 	},
       
  7059 
       
  7060 	_hitTestSelf: function(point, options) {
       
  7061 		var that = this,
       
  7062 			style = this.getStyle(),
       
  7063 			segments = this._segments,
       
  7064 			numSegments = segments.length,
       
  7065 			closed = this._closed,
       
  7066 			tolerancePadding = options._tolerancePadding,
       
  7067 			strokePadding = tolerancePadding,
       
  7068 			join, cap, miterLimit,
       
  7069 			area, loc, res,
       
  7070 			hitStroke = options.stroke && style.hasStroke(),
       
  7071 			hitFill = options.fill && style.hasFill(),
       
  7072 			hitCurves = options.curves,
       
  7073 			radius = hitStroke
       
  7074 					? style.getStrokeWidth() / 2
       
  7075 					: hitFill && options.tolerance > 0 || hitCurves
       
  7076 						? 0 : null;
       
  7077 		if (radius !== null) {
       
  7078 			if (radius > 0) {
       
  7079 				join = style.getStrokeJoin();
       
  7080 				cap = style.getStrokeCap();
       
  7081 				miterLimit = radius * style.getMiterLimit();
       
  7082 				strokePadding = tolerancePadding.add(new Point(radius, radius));
       
  7083 			} else {
       
  7084 				join = cap = 'round';
       
  7085 			}
       
  7086 		}
       
  7087 
       
  7088 		function isCloseEnough(pt, padding) {
       
  7089 			return point.subtract(pt).divide(padding).length <= 1;
       
  7090 		}
       
  7091 
       
  7092 		function checkSegmentPoint(seg, pt, name) {
       
  7093 			if (!options.selected || pt.isSelected()) {
       
  7094 				var anchor = seg._point;
       
  7095 				if (pt !== anchor)
       
  7096 					pt = pt.add(anchor);
       
  7097 				if (isCloseEnough(pt, strokePadding)) {
       
  7098 					return new HitResult(name, that, {
       
  7099 						segment: seg,
       
  7100 						point: pt
       
  7101 					});
       
  7102 				}
       
  7103 			}
       
  7104 		}
       
  7105 
       
  7106 		function checkSegmentPoints(seg, ends) {
       
  7107 			return (ends || options.segments)
       
  7108 				&& checkSegmentPoint(seg, seg._point, 'segment')
       
  7109 				|| (!ends && options.handles) && (
       
  7110 					checkSegmentPoint(seg, seg._handleIn, 'handle-in') ||
       
  7111 					checkSegmentPoint(seg, seg._handleOut, 'handle-out'));
       
  7112 		}
       
  7113 
       
  7114 		function addToArea(point) {
       
  7115 			area.add(point);
       
  7116 		}
       
  7117 
       
  7118 		function checkSegmentStroke(segment) {
       
  7119 			if (join !== 'round' || cap !== 'round') {
       
  7120 				area = new Path({ internal: true, closed: true });
       
  7121 				if (closed || segment._index > 0
       
  7122 						&& segment._index < numSegments - 1) {
       
  7123 					if (join !== 'round' && (segment._handleIn.isZero()
       
  7124 							|| segment._handleOut.isZero()))
       
  7125 						Path._addBevelJoin(segment, join, radius, miterLimit,
       
  7126 								addToArea, true);
       
  7127 				} else if (cap !== 'round') {
       
  7128 					Path._addSquareCap(segment, cap, radius, addToArea, true);
       
  7129 				}
       
  7130 				if (!area.isEmpty()) {
       
  7131 					var loc;
       
  7132 					return area.contains(point)
       
  7133 						|| (loc = area.getNearestLocation(point))
       
  7134 							&& isCloseEnough(loc.getPoint(), tolerancePadding);
       
  7135 				}
       
  7136 			}
       
  7137 			return isCloseEnough(segment._point, strokePadding);
       
  7138 		}
       
  7139 
       
  7140 		if (options.ends && !options.segments && !closed) {
       
  7141 			if (res = checkSegmentPoints(segments[0], true)
       
  7142 					|| checkSegmentPoints(segments[numSegments - 1], true))
       
  7143 				return res;
       
  7144 		} else if (options.segments || options.handles) {
       
  7145 			for (var i = 0; i < numSegments; i++)
       
  7146 				if (res = checkSegmentPoints(segments[i]))
       
  7147 					return res;
       
  7148 		}
       
  7149 		if (radius !== null) {
       
  7150 			loc = this.getNearestLocation(point);
       
  7151 			if (loc) {
       
  7152 				var parameter = loc.getParameter();
       
  7153 				if (parameter === 0 || parameter === 1 && numSegments > 1) {
       
  7154 					if (!checkSegmentStroke(loc.getSegment()))
       
  7155 						loc = null;
       
  7156 				} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
       
  7157 					loc = null;
       
  7158 				}
       
  7159 			}
       
  7160 			if (!loc && join === 'miter' && numSegments > 1) {
       
  7161 				for (var i = 0; i < numSegments; i++) {
       
  7162 					var segment = segments[i];
       
  7163 					if (point.getDistance(segment._point) <= miterLimit
       
  7164 							&& checkSegmentStroke(segment)) {
       
  7165 						loc = segment.getLocation();
       
  7166 						break;
       
  7167 					}
       
  7168 				}
       
  7169 			}
       
  7170 		}
       
  7171 		return !loc && hitFill && this._contains(point)
       
  7172 				|| loc && !hitStroke && !hitCurves
       
  7173 					? new HitResult('fill', this)
       
  7174 					: loc
       
  7175 						? new HitResult(hitStroke ? 'stroke' : 'curve', this, {
       
  7176 							location: loc,
       
  7177 							point: loc.getPoint()
       
  7178 						})
       
  7179 						: null;
       
  7180 	}
       
  7181 
       
  7182 }, {
       
  7183 	beans: false,
       
  7184 
       
  7185 	_getOffset: function(location) {
       
  7186 		var index = location && location.getIndex();
       
  7187 		if (index != null) {
       
  7188 			var curves = this.getCurves(),
       
  7189 				offset = 0;
       
  7190 			for (var i = 0; i < index; i++)
       
  7191 				offset += curves[i].getLength();
       
  7192 			var curve = curves[index],
       
  7193 				parameter = location.getParameter();
       
  7194 			if (parameter > 0)
       
  7195 				offset += curve.getPartLength(0, parameter);
       
  7196 			return offset;
       
  7197 		}
       
  7198 		return null;
       
  7199 	},
       
  7200 
       
  7201 	getLocationOf: function() {
       
  7202 		var point = Point.read(arguments),
       
  7203 			curves = this.getCurves();
       
  7204 		for (var i = 0, l = curves.length; i < l; i++) {
       
  7205 			var loc = curves[i].getLocationOf(point);
       
  7206 			if (loc)
       
  7207 				return loc;
       
  7208 		}
       
  7209 		return null;
       
  7210 	},
       
  7211 
       
  7212 	getOffsetOf: function() {
       
  7213 		var loc = this.getLocationOf.apply(this, arguments);
       
  7214 		return loc ? loc.getOffset() : null;
       
  7215 	},
       
  7216 
       
  7217 	getLocationAt: function(offset, isParameter) {
       
  7218 		var curves = this.getCurves(),
       
  7219 			length = 0;
       
  7220 		if (isParameter) {
       
  7221 			var index = ~~offset;
       
  7222 			return curves[index].getLocationAt(offset - index, true);
       
  7223 		}
       
  7224 		for (var i = 0, l = curves.length; i < l; i++) {
       
  7225 			var start = length,
       
  7226 				curve = curves[i];
       
  7227 			length += curve.getLength();
       
  7228 			if (length > offset) {
       
  7229 				return curve.getLocationAt(offset - start);
       
  7230 			}
       
  7231 		}
       
  7232 		if (offset <= this.getLength())
       
  7233 			return new CurveLocation(curves[curves.length - 1], 1);
       
  7234 		return null;
       
  7235 	},
       
  7236 
       
  7237 	getPointAt: function(offset, isParameter) {
       
  7238 		var loc = this.getLocationAt(offset, isParameter);
       
  7239 		return loc && loc.getPoint();
       
  7240 	},
       
  7241 
       
  7242 	getTangentAt: function(offset, isParameter) {
       
  7243 		var loc = this.getLocationAt(offset, isParameter);
       
  7244 		return loc && loc.getTangent();
       
  7245 	},
       
  7246 
       
  7247 	getNormalAt: function(offset, isParameter) {
       
  7248 		var loc = this.getLocationAt(offset, isParameter);
       
  7249 		return loc && loc.getNormal();
       
  7250 	},
       
  7251 
       
  7252 	getNearestLocation: function() {
       
  7253 		var point = Point.read(arguments),
       
  7254 			curves = this.getCurves(),
       
  7255 			minDist = Infinity,
       
  7256 			minLoc = null;
       
  7257 		for (var i = 0, l = curves.length; i < l; i++) {
       
  7258 			var loc = curves[i].getNearestLocation(point);
       
  7259 			if (loc._distance < minDist) {
       
  7260 				minDist = loc._distance;
       
  7261 				minLoc = loc;
       
  7262 			}
       
  7263 		}
       
  7264 		return minLoc;
       
  7265 	},
       
  7266 
       
  7267 	getNearestPoint: function() {
       
  7268 		return this.getNearestLocation.apply(this, arguments).getPoint();
       
  7269 	}
       
  7270 }, new function() {
       
  7271 
       
  7272 	function drawHandles(ctx, segments, matrix, size) {
       
  7273 		var half = size / 2;
       
  7274 
       
  7275 		function drawHandle(index) {
       
  7276 			var hX = coords[index],
       
  7277 				hY = coords[index + 1];
       
  7278 			if (pX != hX || pY != hY) {
       
  7279 				ctx.beginPath();
       
  7280 				ctx.moveTo(pX, pY);
       
  7281 				ctx.lineTo(hX, hY);
       
  7282 				ctx.stroke();
       
  7283 				ctx.beginPath();
       
  7284 				ctx.arc(hX, hY, half, 0, Math.PI * 2, true);
       
  7285 				ctx.fill();
       
  7286 			}
       
  7287 		}
       
  7288 
       
  7289 		var coords = new Array(6);
       
  7290 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7291 			var segment = segments[i];
       
  7292 			segment._transformCoordinates(matrix, coords, false);
       
  7293 			var state = segment._selectionState,
       
  7294 				pX = coords[0],
       
  7295 				pY = coords[1];
       
  7296 			if (state & 1)
       
  7297 				drawHandle(2);
       
  7298 			if (state & 2)
       
  7299 				drawHandle(4);
       
  7300 			ctx.fillRect(pX - half, pY - half, size, size);
       
  7301 			if (!(state & 4)) {
       
  7302 				var fillStyle = ctx.fillStyle;
       
  7303 				ctx.fillStyle = '#ffffff';
       
  7304 				ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2);
       
  7305 				ctx.fillStyle = fillStyle;
       
  7306 			}
       
  7307 		}
       
  7308 	}
       
  7309 
       
  7310 	function drawSegments(ctx, path, matrix) {
       
  7311 		var segments = path._segments,
       
  7312 			length = segments.length,
       
  7313 			coords = new Array(6),
       
  7314 			first = true,
       
  7315 			curX, curY,
       
  7316 			prevX, prevY,
       
  7317 			inX, inY,
       
  7318 			outX, outY;
       
  7319 
       
  7320 		function drawSegment(segment) {
       
  7321 			if (matrix) {
       
  7322 				segment._transformCoordinates(matrix, coords, false);
       
  7323 				curX = coords[0];
       
  7324 				curY = coords[1];
       
  7325 			} else {
       
  7326 				var point = segment._point;
       
  7327 				curX = point._x;
       
  7328 				curY = point._y;
       
  7329 			}
       
  7330 			if (first) {
       
  7331 				ctx.moveTo(curX, curY);
       
  7332 				first = false;
       
  7333 			} else {
       
  7334 				if (matrix) {
       
  7335 					inX = coords[2];
       
  7336 					inY = coords[3];
       
  7337 				} else {
       
  7338 					var handle = segment._handleIn;
       
  7339 					inX = curX + handle._x;
       
  7340 					inY = curY + handle._y;
       
  7341 				}
       
  7342 				if (inX === curX && inY === curY
       
  7343 						&& outX === prevX && outY === prevY) {
       
  7344 					ctx.lineTo(curX, curY);
       
  7345 				} else {
       
  7346 					ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
       
  7347 				}
       
  7348 			}
       
  7349 			prevX = curX;
       
  7350 			prevY = curY;
       
  7351 			if (matrix) {
       
  7352 				outX = coords[4];
       
  7353 				outY = coords[5];
       
  7354 			} else {
       
  7355 				var handle = segment._handleOut;
       
  7356 				outX = prevX + handle._x;
       
  7357 				outY = prevY + handle._y;
       
  7358 			}
       
  7359 		}
       
  7360 
       
  7361 		for (var i = 0; i < length; i++)
       
  7362 			drawSegment(segments[i]);
       
  7363 		if (path._closed && length > 0)
       
  7364 			drawSegment(segments[0]);
       
  7365 	}
       
  7366 
       
  7367 	return {
       
  7368 		_draw: function(ctx, param, strokeMatrix) {
       
  7369 			var dontStart = param.dontStart,
       
  7370 				dontPaint = param.dontFinish || param.clip,
       
  7371 				style = this.getStyle(),
       
  7372 				hasFill = style.hasFill(),
       
  7373 				hasStroke = style.hasStroke(),
       
  7374 				dashArray = style.getDashArray(),
       
  7375 				dashLength = !paper.support.nativeDash && hasStroke
       
  7376 						&& dashArray && dashArray.length;
       
  7377 
       
  7378 			if (!dontStart)
       
  7379 				ctx.beginPath();
       
  7380 
       
  7381 			if (!dontStart && this._currentPath) {
       
  7382 				ctx.currentPath = this._currentPath;
       
  7383 			} else if (hasFill || hasStroke && !dashLength || dontPaint) {
       
  7384 				drawSegments(ctx, this, strokeMatrix);
       
  7385 				if (this._closed)
       
  7386 					ctx.closePath();
       
  7387 				if (!dontStart)
       
  7388 					this._currentPath = ctx.currentPath;
       
  7389 			}
       
  7390 
       
  7391 			function getOffset(i) {
       
  7392 				return dashArray[((i % dashLength) + dashLength) % dashLength];
       
  7393 			}
       
  7394 
       
  7395 			if (!dontPaint && (hasFill || hasStroke)) {
       
  7396 				this._setStyles(ctx);
       
  7397 				if (hasFill) {
       
  7398 					ctx.fill(style.getWindingRule());
       
  7399 					ctx.shadowColor = 'rgba(0,0,0,0)';
       
  7400 				}
       
  7401 				if (hasStroke) {
       
  7402 					if (dashLength) {
       
  7403 						if (!dontStart)
       
  7404 							ctx.beginPath();
       
  7405 						var flattener = new PathFlattener(this, strokeMatrix),
       
  7406 							length = flattener.length,
       
  7407 							from = -style.getDashOffset(), to,
       
  7408 							i = 0;
       
  7409 						from = from % length;
       
  7410 						while (from > 0) {
       
  7411 							from -= getOffset(i--) + getOffset(i--);
       
  7412 						}
       
  7413 						while (from < length) {
       
  7414 							to = from + getOffset(i++);
       
  7415 							if (from > 0 || to > 0)
       
  7416 								flattener.drawPart(ctx,
       
  7417 										Math.max(from, 0), Math.max(to, 0));
       
  7418 							from = to + getOffset(i++);
       
  7419 						}
       
  7420 					}
       
  7421 					ctx.stroke();
       
  7422 				}
       
  7423 			}
       
  7424 		},
       
  7425 
       
  7426 		_drawSelected: function(ctx, matrix) {
       
  7427 			ctx.beginPath();
       
  7428 			drawSegments(ctx, this, matrix);
       
  7429 			ctx.stroke();
       
  7430 			drawHandles(ctx, this._segments, matrix, paper.settings.handleSize);
       
  7431 		}
       
  7432 	};
       
  7433 }, new function() {
       
  7434 
       
  7435 	function getFirstControlPoints(rhs) {
       
  7436 		var n = rhs.length,
       
  7437 			x = [],
       
  7438 			tmp = [],
       
  7439 			b = 2;
       
  7440 		x[0] = rhs[0] / b;
       
  7441 		for (var i = 1; i < n; i++) {
       
  7442 			tmp[i] = 1 / b;
       
  7443 			b = (i < n - 1 ? 4 : 2) - tmp[i];
       
  7444 			x[i] = (rhs[i] - x[i - 1]) / b;
       
  7445 		}
       
  7446 		for (var i = 1; i < n; i++) {
       
  7447 			x[n - i - 1] -= tmp[n - i] * x[n - i];
       
  7448 		}
       
  7449 		return x;
       
  7450 	}
       
  7451 
       
  7452 	return {
       
  7453 		smooth: function() {
       
  7454 			var segments = this._segments,
       
  7455 				size = segments.length,
       
  7456 				closed = this._closed,
       
  7457 				n = size,
       
  7458 				overlap = 0;
       
  7459 			if (size <= 2)
       
  7460 				return;
       
  7461 			if (closed) {
       
  7462 				overlap = Math.min(size, 4);
       
  7463 				n += Math.min(size, overlap) * 2;
       
  7464 			}
       
  7465 			var knots = [];
       
  7466 			for (var i = 0; i < size; i++)
       
  7467 				knots[i + overlap] = segments[i]._point;
       
  7468 			if (closed) {
       
  7469 				for (var i = 0; i < overlap; i++) {
       
  7470 					knots[i] = segments[i + size - overlap]._point;
       
  7471 					knots[i + size + overlap] = segments[i]._point;
       
  7472 				}
       
  7473 			} else {
       
  7474 				n--;
       
  7475 			}
       
  7476 			var rhs = [];
       
  7477 
       
  7478 			for (var i = 1; i < n - 1; i++)
       
  7479 				rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
       
  7480 			rhs[0] = knots[0]._x + 2 * knots[1]._x;
       
  7481 			rhs[n - 1] = 3 * knots[n - 1]._x;
       
  7482 			var x = getFirstControlPoints(rhs);
       
  7483 
       
  7484 			for (var i = 1; i < n - 1; i++)
       
  7485 				rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
       
  7486 			rhs[0] = knots[0]._y + 2 * knots[1]._y;
       
  7487 			rhs[n - 1] = 3 * knots[n - 1]._y;
       
  7488 			var y = getFirstControlPoints(rhs);
       
  7489 
       
  7490 			if (closed) {
       
  7491 				for (var i = 0, j = size; i < overlap; i++, j++) {
       
  7492 					var f1 = i / overlap,
       
  7493 						f2 = 1 - f1,
       
  7494 						ie = i + overlap,
       
  7495 						je = j + overlap;
       
  7496 					x[j] = x[i] * f1 + x[j] * f2;
       
  7497 					y[j] = y[i] * f1 + y[j] * f2;
       
  7498 					x[je] = x[ie] * f2 + x[je] * f1;
       
  7499 					y[je] = y[ie] * f2 + y[je] * f1;
       
  7500 				}
       
  7501 				n--;
       
  7502 			}
       
  7503 			var handleIn = null;
       
  7504 			for (var i = overlap; i <= n - overlap; i++) {
       
  7505 				var segment = segments[i - overlap];
       
  7506 				if (handleIn)
       
  7507 					segment.setHandleIn(handleIn.subtract(segment._point));
       
  7508 				if (i < n) {
       
  7509 					segment.setHandleOut(
       
  7510 							new Point(x[i], y[i]).subtract(segment._point));
       
  7511 					handleIn = i < n - 1
       
  7512 							? new Point(
       
  7513 								2 * knots[i + 1]._x - x[i + 1],
       
  7514 								2 * knots[i + 1]._y - y[i + 1])
       
  7515 							: new Point(
       
  7516 								(knots[n]._x + x[n - 1]) / 2,
       
  7517 								(knots[n]._y + y[n - 1]) / 2);
       
  7518 				}
       
  7519 			}
       
  7520 			if (closed && handleIn) {
       
  7521 				var segment = this._segments[0];
       
  7522 				segment.setHandleIn(handleIn.subtract(segment._point));
       
  7523 			}
       
  7524 		}
       
  7525 	};
       
  7526 }, new function() {
       
  7527 	function getCurrentSegment(that) {
       
  7528 		var segments = that._segments;
       
  7529 		if (segments.length === 0)
       
  7530 			throw new Error('Use a moveTo() command first');
       
  7531 		return segments[segments.length - 1];
       
  7532 	}
       
  7533 
       
  7534 	return {
       
  7535 		moveTo: function() {
       
  7536 			var segments = this._segments;
       
  7537 			if (segments.length === 1)
       
  7538 				this.removeSegment(0);
       
  7539 			if (!segments.length)
       
  7540 				this._add([ new Segment(Point.read(arguments)) ]);
       
  7541 		},
       
  7542 
       
  7543 		moveBy: function() {
       
  7544 			throw new Error('moveBy() is unsupported on Path items.');
       
  7545 		},
       
  7546 
       
  7547 		lineTo: function() {
       
  7548 			this._add([ new Segment(Point.read(arguments)) ]);
       
  7549 		},
       
  7550 
       
  7551 		cubicCurveTo: function() {
       
  7552 			var handle1 = Point.read(arguments),
       
  7553 				handle2 = Point.read(arguments),
       
  7554 				to = Point.read(arguments),
       
  7555 				current = getCurrentSegment(this);
       
  7556 			current.setHandleOut(handle1.subtract(current._point));
       
  7557 			this._add([ new Segment(to, handle2.subtract(to)) ]);
       
  7558 		},
       
  7559 
       
  7560 		quadraticCurveTo: function() {
       
  7561 			var handle = Point.read(arguments),
       
  7562 				to = Point.read(arguments),
       
  7563 				current = getCurrentSegment(this)._point;
       
  7564 			this.cubicCurveTo(
       
  7565 				handle.add(current.subtract(handle).multiply(1 / 3)),
       
  7566 				handle.add(to.subtract(handle).multiply(1 / 3)),
       
  7567 				to
       
  7568 			);
       
  7569 		},
       
  7570 
       
  7571 		curveTo: function() {
       
  7572 			var through = Point.read(arguments),
       
  7573 				to = Point.read(arguments),
       
  7574 				t = Base.pick(Base.read(arguments), 0.5),
       
  7575 				t1 = 1 - t,
       
  7576 				current = getCurrentSegment(this)._point,
       
  7577 				handle = through.subtract(current.multiply(t1 * t1))
       
  7578 					.subtract(to.multiply(t * t)).divide(2 * t * t1);
       
  7579 			if (handle.isNaN())
       
  7580 				throw new Error(
       
  7581 					'Cannot put a curve through points with parameter = ' + t);
       
  7582 			this.quadraticCurveTo(handle, to);
       
  7583 		},
       
  7584 
       
  7585 		arcTo: function() {
       
  7586 			var current = getCurrentSegment(this),
       
  7587 				from = current._point,
       
  7588 				to = Point.read(arguments),
       
  7589 				through,
       
  7590 				peek = Base.peek(arguments),
       
  7591 				clockwise = Base.pick(peek, true),
       
  7592 				center, extent, vector, matrix;
       
  7593 			if (typeof clockwise === 'boolean') {
       
  7594 				var middle = from.add(to).divide(2),
       
  7595 				through = middle.add(middle.subtract(from).rotate(
       
  7596 						clockwise ? -90 : 90));
       
  7597 			} else if (Base.remain(arguments) <= 2) {
       
  7598 				through = to;
       
  7599 				to = Point.read(arguments);
       
  7600 			} else {
       
  7601 				var radius = Size.read(arguments);
       
  7602 				if (radius.isZero())
       
  7603 					return this.lineTo(to);
       
  7604 				var rotation = Base.read(arguments),
       
  7605 					clockwise = !!Base.read(arguments),
       
  7606 					large = !!Base.read(arguments),
       
  7607 					middle = from.add(to).divide(2),
       
  7608 					pt = from.subtract(middle).rotate(-rotation),
       
  7609 					x = pt.x,
       
  7610 					y = pt.y,
       
  7611 					abs = Math.abs,
       
  7612 					EPSILON = 1e-11,
       
  7613 					rx = abs(radius.width),
       
  7614 					ry = abs(radius.height),
       
  7615 					rxSq = rx * rx,
       
  7616 					rySq = ry * ry,
       
  7617 					xSq =  x * x,
       
  7618 					ySq =  y * y;
       
  7619 				var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
       
  7620 				if (factor > 1) {
       
  7621 					rx *= factor;
       
  7622 					ry *= factor;
       
  7623 					rxSq = rx * rx;
       
  7624 					rySq = ry * ry;
       
  7625 				}
       
  7626 				factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
       
  7627 						(rxSq * ySq + rySq * xSq);
       
  7628 				if (abs(factor) < EPSILON)
       
  7629 					factor = 0;
       
  7630 				if (factor < 0)
       
  7631 					throw new Error(
       
  7632 							'Cannot create an arc with the given arguments');
       
  7633 				center = new Point(rx * y / ry, -ry * x / rx)
       
  7634 						.multiply((large === clockwise ? -1 : 1)
       
  7635 							* Math.sqrt(factor))
       
  7636 						.rotate(rotation).add(middle);
       
  7637 				matrix = new Matrix().translate(center).rotate(rotation)
       
  7638 						.scale(rx, ry);
       
  7639 				vector = matrix._inverseTransform(from);
       
  7640 				extent = vector.getDirectedAngle(matrix._inverseTransform(to));
       
  7641 				if (!clockwise && extent > 0)
       
  7642 					extent -= 360;
       
  7643 				else if (clockwise && extent < 0)
       
  7644 					extent += 360;
       
  7645 			}
       
  7646 			if (through) {
       
  7647 				var l1 = new Line(from.add(through).divide(2),
       
  7648 							through.subtract(from).rotate(90), true),
       
  7649 					l2 = new Line(through.add(to).divide(2),
       
  7650 							to.subtract(through).rotate(90), true),
       
  7651 					line = new Line(from, to),
       
  7652 					throughSide = line.getSide(through);
       
  7653 				center = l1.intersect(l2, true);
       
  7654 				if (!center) {
       
  7655 					if (!throughSide)
       
  7656 						return this.lineTo(to);
       
  7657 					throw new Error(
       
  7658 							'Cannot create an arc with the given arguments');
       
  7659 				}
       
  7660 				vector = from.subtract(center);
       
  7661 				extent = vector.getDirectedAngle(to.subtract(center));
       
  7662 				var centerSide = line.getSide(center);
       
  7663 				if (centerSide === 0) {
       
  7664 					extent = throughSide * Math.abs(extent);
       
  7665 				} else if (throughSide === centerSide) {
       
  7666 					extent += extent < 0 ? 360 : -360;
       
  7667 				}
       
  7668 			}
       
  7669 			var ext = Math.abs(extent),
       
  7670 				count = ext >= 360 ? 4 : Math.ceil(ext / 90),
       
  7671 				inc = extent / count,
       
  7672 				half = inc * Math.PI / 360,
       
  7673 				z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
       
  7674 				segments = [];
       
  7675 			for (var i = 0; i <= count; i++) {
       
  7676 				var pt = to,
       
  7677 					out = null;
       
  7678 				if (i < count) {
       
  7679 					out = vector.rotate(90).multiply(z);
       
  7680 					if (matrix) {
       
  7681 						pt = matrix._transformPoint(vector);
       
  7682 						out = matrix._transformPoint(vector.add(out))
       
  7683 								.subtract(pt);
       
  7684 					} else {
       
  7685 						pt = center.add(vector);
       
  7686 					}
       
  7687 				}
       
  7688 				if (i === 0) {
       
  7689 					current.setHandleOut(out);
       
  7690 				} else {
       
  7691 					var _in = vector.rotate(-90).multiply(z);
       
  7692 					if (matrix) {
       
  7693 						_in = matrix._transformPoint(vector.add(_in))
       
  7694 								.subtract(pt);
       
  7695 					}
       
  7696 					segments.push(new Segment(pt, _in, out));
       
  7697 				}
       
  7698 				vector = vector.rotate(inc);
       
  7699 			}
       
  7700 			this._add(segments);
       
  7701 		},
       
  7702 
       
  7703 		lineBy: function() {
       
  7704 			var to = Point.read(arguments),
       
  7705 				current = getCurrentSegment(this)._point;
       
  7706 			this.lineTo(current.add(to));
       
  7707 		},
       
  7708 
       
  7709 		curveBy: function() {
       
  7710 			var through = Point.read(arguments),
       
  7711 				to = Point.read(arguments),
       
  7712 				parameter = Base.read(arguments),
       
  7713 				current = getCurrentSegment(this)._point;
       
  7714 			this.curveTo(current.add(through), current.add(to), parameter);
       
  7715 		},
       
  7716 
       
  7717 		cubicCurveBy: function() {
       
  7718 			var handle1 = Point.read(arguments),
       
  7719 				handle2 = Point.read(arguments),
       
  7720 				to = Point.read(arguments),
       
  7721 				current = getCurrentSegment(this)._point;
       
  7722 			this.cubicCurveTo(current.add(handle1), current.add(handle2),
       
  7723 					current.add(to));
       
  7724 		},
       
  7725 
       
  7726 		quadraticCurveBy: function() {
       
  7727 			var handle = Point.read(arguments),
       
  7728 				to = Point.read(arguments),
       
  7729 				current = getCurrentSegment(this)._point;
       
  7730 			this.quadraticCurveTo(current.add(handle), current.add(to));
       
  7731 		},
       
  7732 
       
  7733 		arcBy: function() {
       
  7734 			var current = getCurrentSegment(this)._point,
       
  7735 				point = current.add(Point.read(arguments)),
       
  7736 				clockwise = Base.pick(Base.peek(arguments), true);
       
  7737 			if (typeof clockwise === 'boolean') {
       
  7738 				this.arcTo(point, clockwise);
       
  7739 			} else {
       
  7740 				this.arcTo(point, current.add(Point.read(arguments)));
       
  7741 			}
       
  7742 		},
       
  7743 
       
  7744 		closePath: function(join) {
       
  7745 			this.setClosed(true);
       
  7746 			if (join)
       
  7747 				this.join();
       
  7748 		}
       
  7749 	};
       
  7750 }, {
       
  7751 
       
  7752 	_getBounds: function(getter, matrix) {
       
  7753 		return Path[getter](this._segments, this._closed, this.getStyle(),
       
  7754 				matrix);
       
  7755 	},
       
  7756 
       
  7757 statics: {
       
  7758 	isClockwise: function(segments) {
       
  7759 		var sum = 0;
       
  7760 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7761 			var v = Curve.getValues(
       
  7762 					segments[i], segments[i + 1 < l ? i + 1 : 0]);
       
  7763 			for (var j = 2; j < 8; j += 2)
       
  7764 				sum += (v[j - 2] - v[j]) * (v[j + 1] + v[j - 1]);
       
  7765 		}
       
  7766 		return sum > 0;
       
  7767 	},
       
  7768 
       
  7769 	getBounds: function(segments, closed, style, matrix, strokePadding) {
       
  7770 		var first = segments[0];
       
  7771 		if (!first)
       
  7772 			return new Rectangle();
       
  7773 		var coords = new Array(6),
       
  7774 			prevCoords = first._transformCoordinates(matrix, new Array(6), false),
       
  7775 			min = prevCoords.slice(0, 2),
       
  7776 			max = min.slice(),
       
  7777 			roots = new Array(2);
       
  7778 
       
  7779 		function processSegment(segment) {
       
  7780 			segment._transformCoordinates(matrix, coords, false);
       
  7781 			for (var i = 0; i < 2; i++) {
       
  7782 				Curve._addBounds(
       
  7783 					prevCoords[i],
       
  7784 					prevCoords[i + 4],
       
  7785 					coords[i + 2],
       
  7786 					coords[i],
       
  7787 					i, strokePadding ? strokePadding[i] : 0, min, max, roots);
       
  7788 			}
       
  7789 			var tmp = prevCoords;
       
  7790 			prevCoords = coords;
       
  7791 			coords = tmp;
       
  7792 		}
       
  7793 
       
  7794 		for (var i = 1, l = segments.length; i < l; i++)
       
  7795 			processSegment(segments[i]);
       
  7796 		if (closed)
       
  7797 			processSegment(first);
       
  7798 		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
       
  7799 	},
       
  7800 
       
  7801 	getStrokeBounds: function(segments, closed, style, matrix) {
       
  7802 		if (!style.hasStroke())
       
  7803 			return Path.getBounds(segments, closed, style, matrix);
       
  7804 		var length = segments.length - (closed ? 0 : 1),
       
  7805 			radius = style.getStrokeWidth() / 2,
       
  7806 			padding = Path._getPenPadding(radius, matrix),
       
  7807 			bounds = Path.getBounds(segments, closed, style, matrix, padding),
       
  7808 			join = style.getStrokeJoin(),
       
  7809 			cap = style.getStrokeCap(),
       
  7810 			miterLimit = radius * style.getMiterLimit();
       
  7811 		var joinBounds = new Rectangle(new Size(padding).multiply(2));
       
  7812 
       
  7813 		function add(point) {
       
  7814 			bounds = bounds.include(matrix
       
  7815 				? matrix._transformPoint(point, point) : point);
       
  7816 		}
       
  7817 
       
  7818 		function addRound(segment) {
       
  7819 			bounds = bounds.unite(joinBounds.setCenter(matrix
       
  7820 				? matrix._transformPoint(segment._point) : segment._point));
       
  7821 		}
       
  7822 
       
  7823 		function addJoin(segment, join) {
       
  7824 			var handleIn = segment._handleIn,
       
  7825 				handleOut = segment._handleOut;
       
  7826 			if (join === 'round' || !handleIn.isZero() && !handleOut.isZero()
       
  7827 					&& handleIn.isColinear(handleOut)) {
       
  7828 				addRound(segment);
       
  7829 			} else {
       
  7830 				Path._addBevelJoin(segment, join, radius, miterLimit, add);
       
  7831 			}
       
  7832 		}
       
  7833 
       
  7834 		function addCap(segment, cap) {
       
  7835 			if (cap === 'round') {
       
  7836 				addRound(segment);
       
  7837 			} else {
       
  7838 				Path._addSquareCap(segment, cap, radius, add);
       
  7839 			}
       
  7840 		}
       
  7841 
       
  7842 		for (var i = 1; i < length; i++)
       
  7843 			addJoin(segments[i], join);
       
  7844 		if (closed) {
       
  7845 			addJoin(segments[0], join);
       
  7846 		} else if (length > 0) {
       
  7847 			addCap(segments[0], cap);
       
  7848 			addCap(segments[segments.length - 1], cap);
       
  7849 		}
       
  7850 		return bounds;
       
  7851 	},
       
  7852 
       
  7853 	_getPenPadding: function(radius, matrix) {
       
  7854 		if (!matrix)
       
  7855 			return [radius, radius];
       
  7856 		var mx = matrix.shiftless(),
       
  7857 			hor = mx.transform(new Point(radius, 0)),
       
  7858 			ver = mx.transform(new Point(0, radius)),
       
  7859 			phi = hor.getAngleInRadians(),
       
  7860 			a = hor.getLength(),
       
  7861 			b = ver.getLength();
       
  7862 		var sin = Math.sin(phi),
       
  7863 			cos = Math.cos(phi),
       
  7864 			tan = Math.tan(phi),
       
  7865 			tx = -Math.atan(b * tan / a),
       
  7866 			ty = Math.atan(b / (tan * a));
       
  7867 		return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
       
  7868 				Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
       
  7869 	},
       
  7870 
       
  7871 	_addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) {
       
  7872 		var curve2 = segment.getCurve(),
       
  7873 			curve1 = curve2.getPrevious(),
       
  7874 			point = curve2.getPointAt(0, true),
       
  7875 			normal1 = curve1.getNormalAt(1, true),
       
  7876 			normal2 = curve2.getNormalAt(0, true),
       
  7877 			step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
       
  7878 		normal1.setLength(step);
       
  7879 		normal2.setLength(step);
       
  7880 		if (area) {
       
  7881 			addPoint(point);
       
  7882 			addPoint(point.add(normal1));
       
  7883 		}
       
  7884 		if (join === 'miter') {
       
  7885 			var corner = new Line(
       
  7886 					point.add(normal1),
       
  7887 					new Point(-normal1.y, normal1.x), true
       
  7888 				).intersect(new Line(
       
  7889 					point.add(normal2),
       
  7890 					new Point(-normal2.y, normal2.x), true
       
  7891 				), true);
       
  7892 			if (corner && point.getDistance(corner) <= miterLimit) {
       
  7893 				addPoint(corner);
       
  7894 				if (!area)
       
  7895 					return;
       
  7896 			}
       
  7897 		}
       
  7898 		if (!area)
       
  7899 			addPoint(point.add(normal1));
       
  7900 		addPoint(point.add(normal2));
       
  7901 	},
       
  7902 
       
  7903 	_addSquareCap: function(segment, cap, radius, addPoint, area) {
       
  7904 		var point = segment._point,
       
  7905 			loc = segment.getLocation(),
       
  7906 			normal = loc.getNormal().normalize(radius);
       
  7907 		if (area) {
       
  7908 			addPoint(point.subtract(normal));
       
  7909 			addPoint(point.add(normal));
       
  7910 		}
       
  7911 		if (cap === 'square')
       
  7912 			point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90));
       
  7913 		addPoint(point.add(normal));
       
  7914 		addPoint(point.subtract(normal));
       
  7915 	},
       
  7916 
       
  7917 	getHandleBounds: function(segments, closed, style, matrix, strokePadding,
       
  7918 			joinPadding) {
       
  7919 		var coords = new Array(6),
       
  7920 			x1 = Infinity,
       
  7921 			x2 = -x1,
       
  7922 			y1 = x1,
       
  7923 			y2 = x2;
       
  7924 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7925 			var segment = segments[i];
       
  7926 			segment._transformCoordinates(matrix, coords, false);
       
  7927 			for (var j = 0; j < 6; j += 2) {
       
  7928 				var padding = j === 0 ? joinPadding : strokePadding,
       
  7929 					paddingX = padding ? padding[0] : 0,
       
  7930 					paddingY = padding ? padding[1] : 0,
       
  7931 					x = coords[j],
       
  7932 					y = coords[j + 1],
       
  7933 					xn = x - paddingX,
       
  7934 					xx = x + paddingX,
       
  7935 					yn = y - paddingY,
       
  7936 					yx = y + paddingY;
       
  7937 				if (xn < x1) x1 = xn;
       
  7938 				if (xx > x2) x2 = xx;
       
  7939 				if (yn < y1) y1 = yn;
       
  7940 				if (yx > y2) y2 = yx;
       
  7941 			}
       
  7942 		}
       
  7943 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  7944 	},
       
  7945 
       
  7946 	getRoughBounds: function(segments, closed, style, matrix) {
       
  7947 		var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
       
  7948 			joinRadius = strokeRadius;
       
  7949 		if (strokeRadius > 0) {
       
  7950 			if (style.getStrokeJoin() === 'miter')
       
  7951 				joinRadius = strokeRadius * style.getMiterLimit();
       
  7952 			if (style.getStrokeCap() === 'square')
       
  7953 				joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
       
  7954 		}
       
  7955 		return Path.getHandleBounds(segments, closed, style, matrix,
       
  7956 				Path._getPenPadding(strokeRadius, matrix),
       
  7957 				Path._getPenPadding(joinRadius, matrix));
       
  7958 	}
       
  7959 }});
       
  7960 
       
  7961 Path.inject({ statics: new function() {
       
  7962 
       
  7963 	var kappa = 0.5522847498307936,
       
  7964 		ellipseSegments = [
       
  7965 			new Segment([-1, 0], [0, kappa ], [0, -kappa]),
       
  7966 			new Segment([0, -1], [-kappa, 0], [kappa, 0 ]),
       
  7967 			new Segment([1, 0], [0, -kappa], [0, kappa ]),
       
  7968 			new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
       
  7969 		];
       
  7970 
       
  7971 	function createPath(segments, closed, args) {
       
  7972 		var props = Base.getNamed(args),
       
  7973 			path = new Path(props && props.insert === false && Item.NO_INSERT);
       
  7974 		path._add(segments);
       
  7975 		path._closed = closed;
       
  7976 		return path.set(props);
       
  7977 	}
       
  7978 
       
  7979 	function createEllipse(center, radius, args) {
       
  7980 		var segments = new Array(4);
       
  7981 		for (var i = 0; i < 4; i++) {
       
  7982 			var segment = ellipseSegments[i];
       
  7983 			segments[i] = new Segment(
       
  7984 				segment._point.multiply(radius).add(center),
       
  7985 				segment._handleIn.multiply(radius),
       
  7986 				segment._handleOut.multiply(radius)
       
  7987 			);
       
  7988 		}
       
  7989 		return createPath(segments, true, args);
       
  7990 	}
       
  7991 
       
  7992 	return {
       
  7993 		Line: function() {
       
  7994 			return createPath([
       
  7995 				new Segment(Point.readNamed(arguments, 'from')),
       
  7996 				new Segment(Point.readNamed(arguments, 'to'))
       
  7997 			], false, arguments);
       
  7998 		},
       
  7999 
       
  8000 		Circle: function() {
       
  8001 			var center = Point.readNamed(arguments, 'center'),
       
  8002 				radius = Base.readNamed(arguments, 'radius');
       
  8003 			return createEllipse(center, new Size(radius), arguments);
       
  8004 		},
       
  8005 
       
  8006 		Rectangle: function() {
       
  8007 			var rect = Rectangle.readNamed(arguments, 'rectangle'),
       
  8008 				radius = Size.readNamed(arguments, 'radius', 0,
       
  8009 						{ readNull: true }),
       
  8010 				bl = rect.getBottomLeft(true),
       
  8011 				tl = rect.getTopLeft(true),
       
  8012 				tr = rect.getTopRight(true),
       
  8013 				br = rect.getBottomRight(true),
       
  8014 				segments;
       
  8015 			if (!radius || radius.isZero()) {
       
  8016 				segments = [
       
  8017 					new Segment(bl),
       
  8018 					new Segment(tl),
       
  8019 					new Segment(tr),
       
  8020 					new Segment(br)
       
  8021 				];
       
  8022 			} else {
       
  8023 				radius = Size.min(radius, rect.getSize(true).divide(2));
       
  8024 				var rx = radius.width,
       
  8025 					ry = radius.height,
       
  8026 					hx = rx * kappa,
       
  8027 					hy = ry * kappa;
       
  8028 				segments = [
       
  8029 					new Segment(bl.add(rx, 0), null, [-hx, 0]),
       
  8030 					new Segment(bl.subtract(0, ry), [0, hy]),
       
  8031 					new Segment(tl.add(0, ry), null, [0, -hy]),
       
  8032 					new Segment(tl.add(rx, 0), [-hx, 0], null),
       
  8033 					new Segment(tr.subtract(rx, 0), null, [hx, 0]),
       
  8034 					new Segment(tr.add(0, ry), [0, -hy], null),
       
  8035 					new Segment(br.subtract(0, ry), null, [0, hy]),
       
  8036 					new Segment(br.subtract(rx, 0), [hx, 0])
       
  8037 				];
       
  8038 			}
       
  8039 			return createPath(segments, true, arguments);
       
  8040 		},
       
  8041 
       
  8042 		RoundRectangle: '#Rectangle',
       
  8043 
       
  8044 		Ellipse: function() {
       
  8045 			var ellipse = Shape._readEllipse(arguments);
       
  8046 			return createEllipse(ellipse.center, ellipse.radius, arguments);
       
  8047 		},
       
  8048 
       
  8049 		Oval: '#Ellipse',
       
  8050 
       
  8051 		Arc: function() {
       
  8052 			var from = Point.readNamed(arguments, 'from'),
       
  8053 				through = Point.readNamed(arguments, 'through'),
       
  8054 				to = Point.readNamed(arguments, 'to'),
       
  8055 				props = Base.getNamed(arguments),
       
  8056 				path = new Path(props && props.insert === false
       
  8057 						&& Item.NO_INSERT);
       
  8058 			path.moveTo(from);
       
  8059 			path.arcTo(through, to);
       
  8060 			return path.set(props);
       
  8061 		},
       
  8062 
       
  8063 		RegularPolygon: function() {
       
  8064 			var center = Point.readNamed(arguments, 'center'),
       
  8065 				sides = Base.readNamed(arguments, 'sides'),
       
  8066 				radius = Base.readNamed(arguments, 'radius'),
       
  8067 				step = 360 / sides,
       
  8068 				three = !(sides % 3),
       
  8069 				vector = new Point(0, three ? -radius : radius),
       
  8070 				offset = three ? -1 : 0.5,
       
  8071 				segments = new Array(sides);
       
  8072 			for (var i = 0; i < sides; i++)
       
  8073 				segments[i] = new Segment(center.add(
       
  8074 					vector.rotate((i + offset) * step)));
       
  8075 			return createPath(segments, true, arguments);
       
  8076 		},
       
  8077 
       
  8078 		Star: function() {
       
  8079 			var center = Point.readNamed(arguments, 'center'),
       
  8080 				points = Base.readNamed(arguments, 'points') * 2,
       
  8081 				radius1 = Base.readNamed(arguments, 'radius1'),
       
  8082 				radius2 = Base.readNamed(arguments, 'radius2'),
       
  8083 				step = 360 / points,
       
  8084 				vector = new Point(0, -1),
       
  8085 				segments = new Array(points);
       
  8086 			for (var i = 0; i < points; i++)
       
  8087 				segments[i] = new Segment(center.add(vector.rotate(step * i)
       
  8088 						.multiply(i % 2 ? radius2 : radius1)));
       
  8089 			return createPath(segments, true, arguments);
       
  8090 		}
       
  8091 	};
       
  8092 }});
       
  8093 
       
  8094 var CompoundPath = PathItem.extend({
       
  8095 	_class: 'CompoundPath',
       
  8096 	_serializeFields: {
       
  8097 		children: []
       
  8098 	},
       
  8099 
       
  8100 	initialize: function CompoundPath(arg) {
       
  8101 		this._children = [];
       
  8102 		this._namedChildren = {};
       
  8103 		if (!this._initialize(arg)) {
       
  8104 			if (typeof arg === 'string') {
       
  8105 				this.setPathData(arg);
       
  8106 			} else {
       
  8107 				this.addChildren(Array.isArray(arg) ? arg : arguments);
       
  8108 			}
       
  8109 		}
       
  8110 	},
       
  8111 
       
  8112 	insertChildren: function insertChildren(index, items, _preserve) {
       
  8113 		items = insertChildren.base.call(this, index, items, _preserve, Path);
       
  8114 		for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
       
  8115 			var item = items[i];
       
  8116 			if (item._clockwise === undefined)
       
  8117 				item.setClockwise(item._index === 0);
       
  8118 		}
       
  8119 		return items;
       
  8120 	},
       
  8121 
       
  8122 	reverse: function() {
       
  8123 		var children = this._children;
       
  8124 		for (var i = 0, l = children.length; i < l; i++)
       
  8125 			children[i].reverse();
       
  8126 	},
       
  8127 
       
  8128 	smooth: function() {
       
  8129 		for (var i = 0, l = this._children.length; i < l; i++)
       
  8130 			this._children[i].smooth();
       
  8131 	},
       
  8132 
       
  8133 	isClockwise: function() {
       
  8134 		var child = this.getFirstChild();
       
  8135 		return child && child.isClockwise();
       
  8136 	},
       
  8137 
       
  8138 	setClockwise: function(clockwise) {
       
  8139 		if (this.isClockwise() !== !!clockwise)
       
  8140 			this.reverse();
       
  8141 	},
       
  8142 
       
  8143 	getFirstSegment: function() {
       
  8144 		var first = this.getFirstChild();
       
  8145 		return first && first.getFirstSegment();
       
  8146 	},
       
  8147 
       
  8148 	getLastSegment: function() {
       
  8149 		var last = this.getLastChild();
       
  8150 		return last && last.getLastSegment();
       
  8151 	},
       
  8152 
       
  8153 	getCurves: function() {
       
  8154 		var children = this._children,
       
  8155 			curves = [];
       
  8156 		for (var i = 0, l = children.length; i < l; i++)
       
  8157 			curves.push.apply(curves, children[i].getCurves());
       
  8158 		return curves;
       
  8159 	},
       
  8160 
       
  8161 	getFirstCurve: function() {
       
  8162 		var first = this.getFirstChild();
       
  8163 		return first && first.getFirstCurve();
       
  8164 	},
       
  8165 
       
  8166 	getLastCurve: function() {
       
  8167 		var last = this.getLastChild();
       
  8168 		return last && last.getFirstCurve();
       
  8169 	},
       
  8170 
       
  8171 	getArea: function() {
       
  8172 		var children = this._children,
       
  8173 			area = 0;
       
  8174 		for (var i = 0, l = children.length; i < l; i++)
       
  8175 			area += children[i].getArea();
       
  8176 		return area;
       
  8177 	}
       
  8178 }, {
       
  8179 	beans: true,
       
  8180 
       
  8181 	getPathData: function(_matrix, _precision) {
       
  8182 		var children = this._children,
       
  8183 			paths = [];
       
  8184 		for (var i = 0, l = children.length; i < l; i++) {
       
  8185 			var child = children[i],
       
  8186 				mx = child._matrix;
       
  8187 			paths.push(child.getPathData(_matrix && !mx.isIdentity()
       
  8188 					? _matrix.chain(mx) : mx, _precision));
       
  8189 		}
       
  8190 		return paths.join(' ');
       
  8191 	}
       
  8192 }, {
       
  8193 	_getChildHitTestOptions: function(options) {
       
  8194 		return options.class === Path || options.type === 'path'
       
  8195 				? options
       
  8196 				: new Base(options, { fill: false });
       
  8197 	},
       
  8198 
       
  8199 	_draw: function(ctx, param, strokeMatrix) {
       
  8200 		var children = this._children;
       
  8201 		if (children.length === 0)
       
  8202 			return;
       
  8203 
       
  8204 		if (this._currentPath) {
       
  8205 			ctx.currentPath = this._currentPath;
       
  8206 		} else {
       
  8207 			param = param.extend({ dontStart: true, dontFinish: true });
       
  8208 			ctx.beginPath();
       
  8209 			for (var i = 0, l = children.length; i < l; i++)
       
  8210 				children[i].draw(ctx, param, strokeMatrix);
       
  8211 			this._currentPath = ctx.currentPath;
       
  8212 		}
       
  8213 
       
  8214 		if (!param.clip) {
       
  8215 			this._setStyles(ctx);
       
  8216 			var style = this._style;
       
  8217 			if (style.hasFill()) {
       
  8218 				ctx.fill(style.getWindingRule());
       
  8219 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  8220 			}
       
  8221 			if (style.hasStroke())
       
  8222 				ctx.stroke();
       
  8223 		}
       
  8224 	},
       
  8225 
       
  8226 	_drawSelected: function(ctx, matrix, selectedItems) {
       
  8227 		var children = this._children;
       
  8228 		for (var i = 0, l = children.length; i < l; i++) {
       
  8229 			var child = children[i],
       
  8230 				mx = child._matrix;
       
  8231 			if (!selectedItems[child._id])
       
  8232 				child._drawSelected(ctx, mx.isIdentity() ? matrix
       
  8233 						: matrix.chain(mx));
       
  8234 		}
       
  8235 	}
       
  8236 }, new function() {
       
  8237 	function getCurrentPath(that, check) {
       
  8238 		var children = that._children;
       
  8239 		if (check && children.length === 0)
       
  8240 			throw new Error('Use a moveTo() command first');
       
  8241 		return children[children.length - 1];
       
  8242 	}
       
  8243 
       
  8244 	var fields = {
       
  8245 		moveTo: function() {
       
  8246 			var current = getCurrentPath(this),
       
  8247 				path = current && current.isEmpty() ? current : new Path();
       
  8248 			if (path !== current)
       
  8249 				this.addChild(path);
       
  8250 			path.moveTo.apply(path, arguments);
       
  8251 		},
       
  8252 
       
  8253 		moveBy: function() {
       
  8254 			var current = getCurrentPath(this, true),
       
  8255 				last = current && current.getLastSegment(),
       
  8256 				point = Point.read(arguments);
       
  8257 			this.moveTo(last ? point.add(last._point) : point);
       
  8258 		},
       
  8259 
       
  8260 		closePath: function(join) {
       
  8261 			getCurrentPath(this, true).closePath(join);
       
  8262 		}
       
  8263 	};
       
  8264 
       
  8265 	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
       
  8266 			'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
       
  8267 			function(key) {
       
  8268 				fields[key] = function() {
       
  8269 					var path = getCurrentPath(this, true);
       
  8270 					path[key].apply(path, arguments);
       
  8271 				};
       
  8272 			}
       
  8273 	);
       
  8274 
       
  8275 	return fields;
       
  8276 });
       
  8277 
       
  8278 PathItem.inject(new function() {
       
  8279 	function computeBoolean(path1, path2, operator, subtract) {
       
  8280 		function preparePath(path) {
       
  8281 			return path.clone(false).reduce().reorient().transform(null, true);
       
  8282 		}
       
  8283 
       
  8284 		var _path1 = preparePath(path1),
       
  8285 			_path2 = path2 && path1 !== path2 && preparePath(path2);
       
  8286 		if (!_path1.isClockwise())
       
  8287 			_path1.reverse();
       
  8288 		if (_path2 && !(subtract ^ _path2.isClockwise()))
       
  8289 			_path2.reverse();
       
  8290 		splitPath(_path1.getIntersections(_path2, true));
       
  8291 
       
  8292 		var chain = [],
       
  8293 			windings = [],
       
  8294 			lengths = [],
       
  8295 			segments = [],
       
  8296 			monoCurves = [];
       
  8297 
       
  8298 		function collect(paths) {
       
  8299 			for (var i = 0, l = paths.length; i < l; i++) {
       
  8300 				var path = paths[i];
       
  8301 				segments.push.apply(segments, path._segments);
       
  8302 				monoCurves.push.apply(monoCurves, path._getMonoCurves());
       
  8303 			}
       
  8304 		}
       
  8305 
       
  8306 		collect(_path1._children || [_path1]);
       
  8307 		if (_path2)
       
  8308 			collect(_path2._children || [_path2]);
       
  8309 		segments.sort(function(a, b) {
       
  8310 			var _a = a._intersection,
       
  8311 				_b = b._intersection;
       
  8312 			return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1;
       
  8313 		});
       
  8314 		for (var i = 0, l = segments.length; i < l; i++) {
       
  8315 			var segment = segments[i];
       
  8316 			if (segment._winding != null)
       
  8317 				continue;
       
  8318 			chain.length = windings.length = lengths.length = 0;
       
  8319 			var totalLength = 0,
       
  8320 				startSeg = segment;
       
  8321 			do {
       
  8322 				chain.push(segment);
       
  8323 				lengths.push(totalLength += segment.getCurve().getLength());
       
  8324 				segment = segment.getNext();
       
  8325 			} while (segment && !segment._intersection && segment !== startSeg);
       
  8326 			for (var j = 0; j < 3; j++) {
       
  8327 				var length = totalLength * Math.random(),
       
  8328 					amount = lengths.length,
       
  8329 					k = 0;
       
  8330 				do {
       
  8331 					if (lengths[k] >= length) {
       
  8332 						if (k > 0)
       
  8333 							length -= lengths[k - 1];
       
  8334 						break;
       
  8335 					}
       
  8336 				} while (++k < amount);
       
  8337 				var curve = chain[k].getCurve(),
       
  8338 					point = curve.getPointAt(length),
       
  8339 					hor = curve.isHorizontal(),
       
  8340 					path = curve._path;
       
  8341 				if (path._parent instanceof CompoundPath)
       
  8342 					path = path._parent;
       
  8343 				windings[j] = subtract && _path2
       
  8344 						&& (path === _path1 && _path2._getWinding(point, hor)
       
  8345 						|| path === _path2 && !_path1._getWinding(point, hor))
       
  8346 						? 0
       
  8347 						: getWinding(point, monoCurves, hor);
       
  8348 			}
       
  8349 			windings.sort();
       
  8350 			var winding = windings[1];
       
  8351 			for (var j = chain.length - 1; j >= 0; j--)
       
  8352 				chain[j]._winding = winding;
       
  8353 		}
       
  8354 		var result = new CompoundPath();
       
  8355 		result.addChildren(tracePaths(segments, operator), true);
       
  8356 		_path1.remove();
       
  8357 		if (_path2)
       
  8358 			_path2.remove();
       
  8359 		result = result.reduce();
       
  8360 		result.setStyle(path1._style);
       
  8361 		return result;
       
  8362 	}
       
  8363 
       
  8364 	function splitPath(intersections) {
       
  8365 		var TOLERANCE = 0.00001,
       
  8366 			linearSegments;
       
  8367 
       
  8368 		function resetLinear() {
       
  8369 			for (var i = 0, l = linearSegments.length; i < l; i++) {
       
  8370 				var segment = linearSegments[i];
       
  8371 				segment._handleOut.set(0, 0);
       
  8372 				segment._handleIn.set(0, 0);
       
  8373 			}
       
  8374 		}
       
  8375 
       
  8376 		for (var i = intersections.length - 1, curve, prevLoc; i >= 0; i--) {
       
  8377 			var loc = intersections[i],
       
  8378 				t = loc._parameter;
       
  8379 			if (prevLoc && prevLoc._curve === loc._curve
       
  8380 					&& prevLoc._parameter > 0) {
       
  8381 				t /= prevLoc._parameter;
       
  8382 			} else {
       
  8383 				if (linearSegments)
       
  8384 					resetLinear();
       
  8385 				curve = loc._curve;
       
  8386 				linearSegments = curve.isLinear() && [];
       
  8387 			}
       
  8388 			var newCurve,
       
  8389 				segment;
       
  8390 			if (newCurve = curve.divide(t, true, true)) {
       
  8391 				segment = newCurve._segment1;
       
  8392 				curve = newCurve.getPrevious();
       
  8393 			} else {
       
  8394 				segment = t < TOLERANCE
       
  8395 					? curve._segment1
       
  8396 					: t > 1 - TOLERANCE
       
  8397 						? curve._segment2
       
  8398 						: curve.getPartLength(0, t) < curve.getPartLength(t, 1)
       
  8399 							? curve._segment1
       
  8400 							: curve._segment2;
       
  8401 			}
       
  8402 			segment._intersection = loc.getIntersection();
       
  8403 			loc._segment = segment;
       
  8404 			if (linearSegments)
       
  8405 				linearSegments.push(segment);
       
  8406 			prevLoc = loc;
       
  8407 		}
       
  8408 		if (linearSegments)
       
  8409 			resetLinear();
       
  8410 	}
       
  8411 
       
  8412 	function getWinding(point, curves, horizontal, testContains) {
       
  8413 		var TOLERANCE = 0.00001,
       
  8414 			x = point.x,
       
  8415 			y = point.y,
       
  8416 			windLeft = 0,
       
  8417 			windRight = 0,
       
  8418 			roots = [],
       
  8419 			abs = Math.abs,
       
  8420 			MAX = 1 - TOLERANCE;
       
  8421 		if (horizontal) {
       
  8422 			var yTop = -Infinity,
       
  8423 				yBottom = Infinity,
       
  8424 				yBefore = y - TOLERANCE,
       
  8425 				yAfter = y + TOLERANCE;
       
  8426 			for (var i = 0, l = curves.length; i < l; i++) {
       
  8427 				var values = curves[i].values;
       
  8428 				if (Curve.solveCubic(values, 0, x, roots, 0, 1) > 0) {
       
  8429 					for (var j = roots.length - 1; j >= 0; j--) {
       
  8430 						var y0 = Curve.evaluate(values, roots[j], 0).y;
       
  8431 						if (y0 < yBefore && y0 > yTop) {
       
  8432 							yTop = y0;
       
  8433 						} else if (y0 > yAfter && y0 < yBottom) {
       
  8434 							yBottom = y0;
       
  8435 						}
       
  8436 					}
       
  8437 				}
       
  8438 			}
       
  8439 			yTop = (yTop + y) / 2;
       
  8440 			yBottom = (yBottom + y) / 2;
       
  8441 			if (yTop > -Infinity)
       
  8442 				windLeft = getWinding(new Point(x, yTop), curves);
       
  8443 			if (yBottom < Infinity)
       
  8444 				windRight = getWinding(new Point(x, yBottom), curves);
       
  8445 		} else {
       
  8446 			var xBefore = x - TOLERANCE,
       
  8447 				xAfter = x + TOLERANCE;
       
  8448 			for (var i = 0, l = curves.length; i < l; i++) {
       
  8449 				var curve = curves[i],
       
  8450 					values = curve.values,
       
  8451 					winding = curve.winding,
       
  8452 					next = curve.next;
       
  8453 				if (winding && (winding === 1
       
  8454 						&& y >= values[1] && y <= values[7]
       
  8455 						|| y >= values[7] && y <= values[1])
       
  8456 					&& Curve.solveCubic(values, 1, y, roots, 0,
       
  8457 						!next.winding && next.values[1] === y ? 1 : MAX) === 1){
       
  8458 					var t = roots[0],
       
  8459 						x0 = Curve.evaluate(values, t, 0).x,
       
  8460 						slope = Curve.evaluate(values, t, 1).y;
       
  8461 					if (abs(slope) < TOLERANCE && !Curve.isLinear(values)
       
  8462 							|| t < TOLERANCE && slope * Curve.evaluate(
       
  8463 								curve.previous.values, t, 1).y < 0) {
       
  8464 						if (testContains && x0 >= xBefore && x0 <= xAfter) {
       
  8465 							++windLeft;
       
  8466 							++windRight;
       
  8467 						}
       
  8468 					} else if (x0 <= xBefore) {
       
  8469 						windLeft += winding;
       
  8470 					} else if (x0 >= xAfter) {
       
  8471 						windRight += winding;
       
  8472 					}
       
  8473 				}
       
  8474 			}
       
  8475 		}
       
  8476 		return Math.max(abs(windLeft), abs(windRight));
       
  8477 	}
       
  8478 
       
  8479 	function tracePaths(segments, operator, selfOp) {
       
  8480 		operator = operator || function() {
       
  8481 			return true;
       
  8482 		};
       
  8483 		var paths = [],
       
  8484 			ZERO = 1e-3,
       
  8485 			ONE = 1 - 1e-3;
       
  8486 		for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) {
       
  8487 			seg = startSeg = segments[i];
       
  8488 			if (seg._visited || !operator(seg._winding))
       
  8489 				continue;
       
  8490 			var path = new Path(Item.NO_INSERT),
       
  8491 				inter = seg._intersection,
       
  8492 				startInterSeg = inter && inter._segment,
       
  8493 				added = false,
       
  8494 				dir = 1;
       
  8495 			do {
       
  8496 				var handleIn = dir > 0 ? seg._handleIn : seg._handleOut,
       
  8497 					handleOut = dir > 0 ? seg._handleOut : seg._handleIn,
       
  8498 					interSeg;
       
  8499 				if (added && (!operator(seg._winding) || selfOp)
       
  8500 						&& (inter = seg._intersection)
       
  8501 						&& (interSeg = inter._segment)
       
  8502 						&& interSeg !== startSeg) {
       
  8503 					if (selfOp) {
       
  8504 						seg._visited = interSeg._visited;
       
  8505 						seg = interSeg;
       
  8506 						dir = 1;
       
  8507 					} else {
       
  8508 						var c1 = seg.getCurve();
       
  8509 						if (dir > 0)
       
  8510 							c1 = c1.getPrevious();
       
  8511 						var t1 = c1.getTangentAt(dir < 1 ? ZERO : ONE, true),
       
  8512 							c4 = interSeg.getCurve(),
       
  8513 							c3 = c4.getPrevious(),
       
  8514 							t3 = c3.getTangentAt(ONE, true),
       
  8515 							t4 = c4.getTangentAt(ZERO, true),
       
  8516 							w3 = t1.cross(t3),
       
  8517 							w4 = t1.cross(t4);
       
  8518 						if (w3 * w4 !== 0) {
       
  8519 							var curve = w3 < w4 ? c3 : c4,
       
  8520 								nextCurve = operator(curve._segment1._winding)
       
  8521 									? curve
       
  8522 									: w3 < w4 ? c4 : c3,
       
  8523 								nextSeg = nextCurve._segment1;
       
  8524 							dir = nextCurve === c3 ? -1 : 1;
       
  8525 							if (nextSeg._visited && seg._path !== nextSeg._path
       
  8526 										|| !operator(nextSeg._winding)) {
       
  8527 								dir = 1;
       
  8528 							} else {
       
  8529 								seg._visited = interSeg._visited;
       
  8530 								seg = interSeg;
       
  8531 								if (nextSeg._visited)
       
  8532 									dir = 1;
       
  8533 							}
       
  8534 						} else {
       
  8535 							dir = 1;
       
  8536 						}
       
  8537 					}
       
  8538 					handleOut = dir > 0 ? seg._handleOut : seg._handleIn;
       
  8539 				}
       
  8540 				path.add(new Segment(seg._point, added && handleIn, handleOut));
       
  8541 				added = true;
       
  8542 				seg._visited = true;
       
  8543 				seg = dir > 0 ? seg.getNext() : seg. getPrevious();
       
  8544 			} while (seg && !seg._visited
       
  8545 					&& seg !== startSeg && seg !== startInterSeg
       
  8546 					&& (seg._intersection || operator(seg._winding)));
       
  8547 			if (seg && (seg === startSeg || seg === startInterSeg)) {
       
  8548 				path.firstSegment.setHandleIn((seg === startInterSeg
       
  8549 						? startInterSeg : seg)._handleIn);
       
  8550 				path.setClosed(true);
       
  8551 			} else {
       
  8552 				path.lastSegment._handleOut.set(0, 0);
       
  8553 			}
       
  8554 			if (path._segments.length >
       
  8555 					(path._closed ? path.isPolygon() ? 2 : 0 : 1))
       
  8556 				paths.push(path);
       
  8557 		}
       
  8558 		return paths;
       
  8559 	}
       
  8560 
       
  8561 	return {
       
  8562 		_getWinding: function(point, horizontal, testContains) {
       
  8563 			return getWinding(point, this._getMonoCurves(),
       
  8564 					horizontal, testContains);
       
  8565 		},
       
  8566 
       
  8567 		unite: function(path) {
       
  8568 			return computeBoolean(this, path, function(w) {
       
  8569 				return w === 1 || w === 0;
       
  8570 			}, false);
       
  8571 		},
       
  8572 
       
  8573 		intersect: function(path) {
       
  8574 			return computeBoolean(this, path, function(w) {
       
  8575 				return w === 2;
       
  8576 			}, false);
       
  8577 		},
       
  8578 
       
  8579 		subtract: function(path) {
       
  8580 			return computeBoolean(this, path, function(w) {
       
  8581 				return w === 1;
       
  8582 			}, true);
       
  8583 		},
       
  8584 
       
  8585 		exclude: function(path) {
       
  8586 			return new Group([this.subtract(path), path.subtract(this)]);
       
  8587 		},
       
  8588 
       
  8589 		divide: function(path) {
       
  8590 			return new Group([this.subtract(path), this.intersect(path)]);
       
  8591 		}
       
  8592 	};
       
  8593 });
       
  8594 
       
  8595 Path.inject({
       
  8596 	_getMonoCurves: function() {
       
  8597 		var monoCurves = this._monoCurves,
       
  8598 			prevCurve;
       
  8599 
       
  8600 		function insertCurve(v) {
       
  8601 			var y0 = v[1],
       
  8602 				y1 = v[7],
       
  8603 				curve = {
       
  8604 					values: v,
       
  8605 					winding: y0 === y1
       
  8606 						? 0
       
  8607 						: y0 > y1
       
  8608 							? -1
       
  8609 							: 1,
       
  8610 					previous: prevCurve,
       
  8611 					next: null
       
  8612 				};
       
  8613 			if (prevCurve)
       
  8614 				prevCurve.next = curve;
       
  8615 			monoCurves.push(curve);
       
  8616 			prevCurve = curve;
       
  8617 		}
       
  8618 
       
  8619 		function handleCurve(v) {
       
  8620 			if (Curve.getLength(v) === 0)
       
  8621 				return;
       
  8622 			var y0 = v[1],
       
  8623 				y1 = v[3],
       
  8624 				y2 = v[5],
       
  8625 				y3 = v[7];
       
  8626 			if (Curve.isLinear(v)) {
       
  8627 				insertCurve(v);
       
  8628 			} else {
       
  8629 				var a = 3 * (y1 - y2) - y0 + y3,
       
  8630 					b = 2 * (y0 + y2) - 4 * y1,
       
  8631 					c = y1 - y0,
       
  8632 					TOLERANCE = 0.00001,
       
  8633 					roots = [];
       
  8634 				var count = Numerical.solveQuadratic(a, b, c, roots, TOLERANCE,
       
  8635 						1 - TOLERANCE);
       
  8636 				if (count === 0) {
       
  8637 					insertCurve(v);
       
  8638 				} else {
       
  8639 					roots.sort();
       
  8640 					var t = roots[0],
       
  8641 						parts = Curve.subdivide(v, t);
       
  8642 					insertCurve(parts[0]);
       
  8643 					if (count > 1) {
       
  8644 						t = (roots[1] - t) / (1 - t);
       
  8645 						parts = Curve.subdivide(parts[1], t);
       
  8646 						insertCurve(parts[0]);
       
  8647 					}
       
  8648 					insertCurve(parts[1]);
       
  8649 				}
       
  8650 			}
       
  8651 		}
       
  8652 
       
  8653 		if (!monoCurves) {
       
  8654 			monoCurves = this._monoCurves = [];
       
  8655 			var curves = this.getCurves(),
       
  8656 				segments = this._segments;
       
  8657 			for (var i = 0, l = curves.length; i < l; i++)
       
  8658 				handleCurve(curves[i].getValues());
       
  8659 			if (!this._closed && segments.length > 1) {
       
  8660 				var p1 = segments[segments.length - 1]._point,
       
  8661 					p2 = segments[0]._point,
       
  8662 					p1x = p1._x, p1y = p1._y,
       
  8663 					p2x = p2._x, p2y = p2._y;
       
  8664 				handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
       
  8665 			}
       
  8666 			if (monoCurves.length > 0) {
       
  8667 				var first = monoCurves[0],
       
  8668 					last = monoCurves[monoCurves.length - 1];
       
  8669 				first.previous = last;
       
  8670 				last.next = first;
       
  8671 			}
       
  8672 		}
       
  8673 		return monoCurves;
       
  8674 	},
       
  8675 
       
  8676 	getInteriorPoint: function() {
       
  8677 		var bounds = this.getBounds(),
       
  8678 			point = bounds.getCenter(true);
       
  8679 		if (!this.contains(point)) {
       
  8680 			var curves = this._getMonoCurves(),
       
  8681 				roots = [],
       
  8682 				y = point.y,
       
  8683 				xIntercepts = [];
       
  8684 			for (var i = 0, l = curves.length; i < l; i++) {
       
  8685 				var values = curves[i].values;
       
  8686 				if ((curves[i].winding === 1
       
  8687 						&& y >= values[1] && y <= values[7]
       
  8688 						|| y >= values[7] && y <= values[1])
       
  8689 						&& Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
       
  8690 					for (var j = roots.length - 1; j >= 0; j--)
       
  8691 						xIntercepts.push(Curve.evaluate(values, roots[j], 0).x);
       
  8692 				}
       
  8693 				if (xIntercepts.length > 1)
       
  8694 					break;
       
  8695 			}
       
  8696 			point.x = (xIntercepts[0] + xIntercepts[1]) / 2;
       
  8697 		}
       
  8698 		return point;
       
  8699 	},
       
  8700 
       
  8701 	reorient: function() {
       
  8702 		this.setClockwise(true);
       
  8703 		return this;
       
  8704 	}
       
  8705 });
       
  8706 
       
  8707 CompoundPath.inject({
       
  8708 	_getMonoCurves: function() {
       
  8709 		var children = this._children,
       
  8710 			monoCurves = [];
       
  8711 		for (var i = 0, l = children.length; i < l; i++)
       
  8712 			monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
       
  8713 		return monoCurves;
       
  8714 	},
       
  8715 
       
  8716 	reorient: function() {
       
  8717 		var children = this.removeChildren().sort(function(a, b) {
       
  8718 			return b.getBounds().getArea() - a.getBounds().getArea();
       
  8719 		});
       
  8720 		this.addChildren(children);
       
  8721 		var clockwise = children[0].isClockwise();
       
  8722 		for (var i = 1, l = children.length; i < l; i++) {
       
  8723 			var point = children[i].getInteriorPoint(),
       
  8724 				counters = 0;
       
  8725 			for (var j = i - 1; j >= 0; j--) {
       
  8726 				if (children[j].contains(point))
       
  8727 					counters++;
       
  8728 			}
       
  8729 			children[i].setClockwise(counters % 2 === 0 && clockwise);
       
  8730 		}
       
  8731 		return this;
       
  8732 	}
       
  8733 });
       
  8734 
       
  8735 var PathFlattener = Base.extend({
       
  8736 	initialize: function(path, matrix) {
       
  8737 		this.curves = [];
       
  8738 		this.parts = [];
       
  8739 		this.length = 0;
       
  8740 		this.index = 0;
       
  8741 
       
  8742 		var segments = path._segments,
       
  8743 			segment1 = segments[0],
       
  8744 			segment2,
       
  8745 			that = this;
       
  8746 
       
  8747 		function addCurve(segment1, segment2) {
       
  8748 			var curve = Curve.getValues(segment1, segment2, matrix);
       
  8749 			that.curves.push(curve);
       
  8750 			that._computeParts(curve, segment1._index, 0, 1);
       
  8751 		}
       
  8752 
       
  8753 		for (var i = 1, l = segments.length; i < l; i++) {
       
  8754 			segment2 = segments[i];
       
  8755 			addCurve(segment1, segment2);
       
  8756 			segment1 = segment2;
       
  8757 		}
       
  8758 		if (path._closed)
       
  8759 			addCurve(segment2, segments[0]);
       
  8760 	},
       
  8761 
       
  8762 	_computeParts: function(curve, index, minT, maxT) {
       
  8763 		if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough(curve, 0.25)) {
       
  8764 			var curves = Curve.subdivide(curve);
       
  8765 			var halfT = (minT + maxT) / 2;
       
  8766 			this._computeParts(curves[0], index, minT, halfT);
       
  8767 			this._computeParts(curves[1], index, halfT, maxT);
       
  8768 		} else {
       
  8769 			var x = curve[6] - curve[0],
       
  8770 				y = curve[7] - curve[1],
       
  8771 				dist = Math.sqrt(x * x + y * y);
       
  8772 			if (dist > 0.00001) {
       
  8773 				this.length += dist;
       
  8774 				this.parts.push({
       
  8775 					offset: this.length,
       
  8776 					value: maxT,
       
  8777 					index: index
       
  8778 				});
       
  8779 			}
       
  8780 		}
       
  8781 	},
       
  8782 
       
  8783 	getParameterAt: function(offset) {
       
  8784 		var i, j = this.index;
       
  8785 		for (;;) {
       
  8786 			i = j;
       
  8787 			if (j == 0 || this.parts[--j].offset < offset)
       
  8788 				break;
       
  8789 		}
       
  8790 		for (var l = this.parts.length; i < l; i++) {
       
  8791 			var part = this.parts[i];
       
  8792 			if (part.offset >= offset) {
       
  8793 				this.index = i;
       
  8794 				var prev = this.parts[i - 1];
       
  8795 				var prevVal = prev && prev.index == part.index ? prev.value : 0,
       
  8796 					prevLen = prev ? prev.offset : 0;
       
  8797 				return {
       
  8798 					value: prevVal + (part.value - prevVal)
       
  8799 						* (offset - prevLen) / (part.offset - prevLen),
       
  8800 					index: part.index
       
  8801 				};
       
  8802 			}
       
  8803 		}
       
  8804 		var part = this.parts[this.parts.length - 1];
       
  8805 		return {
       
  8806 			value: 1,
       
  8807 			index: part.index
       
  8808 		};
       
  8809 	},
       
  8810 
       
  8811 	evaluate: function(offset, type) {
       
  8812 		var param = this.getParameterAt(offset);
       
  8813 		return Curve.evaluate(this.curves[param.index], param.value, type);
       
  8814 	},
       
  8815 
       
  8816 	drawPart: function(ctx, from, to) {
       
  8817 		from = this.getParameterAt(from);
       
  8818 		to = this.getParameterAt(to);
       
  8819 		for (var i = from.index; i <= to.index; i++) {
       
  8820 			var curve = Curve.getPart(this.curves[i],
       
  8821 					i == from.index ? from.value : 0,
       
  8822 					i == to.index ? to.value : 1);
       
  8823 			if (i == from.index)
       
  8824 				ctx.moveTo(curve[0], curve[1]);
       
  8825 			ctx.bezierCurveTo.apply(ctx, curve.slice(2));
       
  8826 		}
       
  8827 	}
       
  8828 });
       
  8829 
       
  8830 var PathFitter = Base.extend({
       
  8831 	initialize: function(path, error) {
       
  8832 		this.points = [];
       
  8833 		var segments = path._segments,
       
  8834 			prev;
       
  8835 		for (var i = 0, l = segments.length; i < l; i++) {
       
  8836 			var point = segments[i].point.clone();
       
  8837 			if (!prev || !prev.equals(point)) {
       
  8838 				this.points.push(point);
       
  8839 				prev = point;
       
  8840 			}
       
  8841 		}
       
  8842 		this.error = error;
       
  8843 	},
       
  8844 
       
  8845 	fit: function() {
       
  8846 		var points = this.points,
       
  8847 			length = points.length;
       
  8848 		this.segments = length > 0 ? [new Segment(points[0])] : [];
       
  8849 		if (length > 1)
       
  8850 			this.fitCubic(0, length - 1,
       
  8851 				points[1].subtract(points[0]).normalize(),
       
  8852 				points[length - 2].subtract(points[length - 1]).normalize());
       
  8853 		return this.segments;
       
  8854 	},
       
  8855 
       
  8856 	fitCubic: function(first, last, tan1, tan2) {
       
  8857 		if (last - first == 1) {
       
  8858 			var pt1 = this.points[first],
       
  8859 				pt2 = this.points[last],
       
  8860 				dist = pt1.getDistance(pt2) / 3;
       
  8861 			this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
       
  8862 					pt2.add(tan2.normalize(dist)), pt2]);
       
  8863 			return;
       
  8864 		}
       
  8865 		var uPrime = this.chordLengthParameterize(first, last),
       
  8866 			maxError = Math.max(this.error, this.error * this.error),
       
  8867 			split;
       
  8868 		for (var i = 0; i <= 4; i++) {
       
  8869 			var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
       
  8870 			var max = this.findMaxError(first, last, curve, uPrime);
       
  8871 			if (max.error < this.error) {
       
  8872 				this.addCurve(curve);
       
  8873 				return;
       
  8874 			}
       
  8875 			split = max.index;
       
  8876 			if (max.error >= maxError)
       
  8877 				break;
       
  8878 			this.reparameterize(first, last, uPrime, curve);
       
  8879 			maxError = max.error;
       
  8880 		}
       
  8881 		var V1 = this.points[split - 1].subtract(this.points[split]),
       
  8882 			V2 = this.points[split].subtract(this.points[split + 1]),
       
  8883 			tanCenter = V1.add(V2).divide(2).normalize();
       
  8884 		this.fitCubic(first, split, tan1, tanCenter);
       
  8885 		this.fitCubic(split, last, tanCenter.negate(), tan2);
       
  8886 	},
       
  8887 
       
  8888 	addCurve: function(curve) {
       
  8889 		var prev = this.segments[this.segments.length - 1];
       
  8890 		prev.setHandleOut(curve[1].subtract(curve[0]));
       
  8891 		this.segments.push(
       
  8892 				new Segment(curve[3], curve[2].subtract(curve[3])));
       
  8893 	},
       
  8894 
       
  8895 	generateBezier: function(first, last, uPrime, tan1, tan2) {
       
  8896 		var epsilon = 1e-11,
       
  8897 			pt1 = this.points[first],
       
  8898 			pt2 = this.points[last],
       
  8899 			C = [[0, 0], [0, 0]],
       
  8900 			X = [0, 0];
       
  8901 
       
  8902 		for (var i = 0, l = last - first + 1; i < l; i++) {
       
  8903 			var u = uPrime[i],
       
  8904 				t = 1 - u,
       
  8905 				b = 3 * u * t,
       
  8906 				b0 = t * t * t,
       
  8907 				b1 = b * t,
       
  8908 				b2 = b * u,
       
  8909 				b3 = u * u * u,
       
  8910 				a1 = tan1.normalize(b1),
       
  8911 				a2 = tan2.normalize(b2),
       
  8912 				tmp = this.points[first + i]
       
  8913 					.subtract(pt1.multiply(b0 + b1))
       
  8914 					.subtract(pt2.multiply(b2 + b3));
       
  8915 			C[0][0] += a1.dot(a1);
       
  8916 			C[0][1] += a1.dot(a2);
       
  8917 			C[1][0] = C[0][1];
       
  8918 			C[1][1] += a2.dot(a2);
       
  8919 			X[0] += a1.dot(tmp);
       
  8920 			X[1] += a2.dot(tmp);
       
  8921 		}
       
  8922 
       
  8923 		var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
       
  8924 			alpha1, alpha2;
       
  8925 		if (Math.abs(detC0C1) > epsilon) {
       
  8926 			var detC0X	= C[0][0] * X[1]	- C[1][0] * X[0],
       
  8927 				detXC1	= X[0]	  * C[1][1] - X[1]	  * C[0][1];
       
  8928 			alpha1 = detXC1 / detC0C1;
       
  8929 			alpha2 = detC0X / detC0C1;
       
  8930 		} else {
       
  8931 			var c0 = C[0][0] + C[0][1],
       
  8932 				c1 = C[1][0] + C[1][1];
       
  8933 			if (Math.abs(c0) > epsilon) {
       
  8934 				alpha1 = alpha2 = X[0] / c0;
       
  8935 			} else if (Math.abs(c1) > epsilon) {
       
  8936 				alpha1 = alpha2 = X[1] / c1;
       
  8937 			} else {
       
  8938 				alpha1 = alpha2 = 0;
       
  8939 			}
       
  8940 		}
       
  8941 
       
  8942 		var segLength = pt2.getDistance(pt1);
       
  8943 		epsilon *= segLength;
       
  8944 		if (alpha1 < epsilon || alpha2 < epsilon) {
       
  8945 			alpha1 = alpha2 = segLength / 3;
       
  8946 		}
       
  8947 
       
  8948 		return [pt1, pt1.add(tan1.normalize(alpha1)),
       
  8949 				pt2.add(tan2.normalize(alpha2)), pt2];
       
  8950 	},
       
  8951 
       
  8952 	reparameterize: function(first, last, u, curve) {
       
  8953 		for (var i = first; i <= last; i++) {
       
  8954 			u[i - first] = this.findRoot(curve, this.points[i], u[i - first]);
       
  8955 		}
       
  8956 	},
       
  8957 
       
  8958 	findRoot: function(curve, point, u) {
       
  8959 		var curve1 = [],
       
  8960 			curve2 = [];
       
  8961 		for (var i = 0; i <= 2; i++) {
       
  8962 			curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3);
       
  8963 		}
       
  8964 		for (var i = 0; i <= 1; i++) {
       
  8965 			curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2);
       
  8966 		}
       
  8967 		var pt = this.evaluate(3, curve, u),
       
  8968 			pt1 = this.evaluate(2, curve1, u),
       
  8969 			pt2 = this.evaluate(1, curve2, u),
       
  8970 			diff = pt.subtract(point),
       
  8971 			df = pt1.dot(pt1) + diff.dot(pt2);
       
  8972 		if (Math.abs(df) < 0.00001)
       
  8973 			return u;
       
  8974 		return u - diff.dot(pt1) / df;
       
  8975 	},
       
  8976 
       
  8977 	evaluate: function(degree, curve, t) {
       
  8978 		var tmp = curve.slice();
       
  8979 		for (var i = 1; i <= degree; i++) {
       
  8980 			for (var j = 0; j <= degree - i; j++) {
       
  8981 				tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t));
       
  8982 			}
       
  8983 		}
       
  8984 		return tmp[0];
       
  8985 	},
       
  8986 
       
  8987 	chordLengthParameterize: function(first, last) {
       
  8988 		var u = [0];
       
  8989 		for (var i = first + 1; i <= last; i++) {
       
  8990 			u[i - first] = u[i - first - 1]
       
  8991 					+ this.points[i].getDistance(this.points[i - 1]);
       
  8992 		}
       
  8993 		for (var i = 1, m = last - first; i <= m; i++) {
       
  8994 			u[i] /= u[m];
       
  8995 		}
       
  8996 		return u;
       
  8997 	},
       
  8998 
       
  8999 	findMaxError: function(first, last, curve, u) {
       
  9000 		var index = Math.floor((last - first + 1) / 2),
       
  9001 			maxDist = 0;
       
  9002 		for (var i = first + 1; i < last; i++) {
       
  9003 			var P = this.evaluate(3, curve, u[i - first]);
       
  9004 			var v = P.subtract(this.points[i]);
       
  9005 			var dist = v.x * v.x + v.y * v.y;
       
  9006 			if (dist >= maxDist) {
       
  9007 				maxDist = dist;
       
  9008 				index = i;
       
  9009 			}
       
  9010 		}
       
  9011 		return {
       
  9012 			error: maxDist,
       
  9013 			index: index
       
  9014 		};
       
  9015 	}
       
  9016 });
       
  9017 
       
  9018 var TextItem = Item.extend({
       
  9019 	_class: 'TextItem',
       
  9020 	_boundsSelected: true,
       
  9021 	_applyMatrix: false,
       
  9022 	_canApplyMatrix: false,
       
  9023 	_serializeFields: {
       
  9024 		content: null
       
  9025 	},
       
  9026 	_boundsGetter: 'getBounds',
       
  9027 
       
  9028 	initialize: function TextItem(arg) {
       
  9029 		this._content = '';
       
  9030 		this._lines = [];
       
  9031 		var hasProps = arg && Base.isPlainObject(arg)
       
  9032 				&& arg.x === undefined && arg.y === undefined;
       
  9033 		this._initialize(hasProps && arg, !hasProps && Point.read(arguments));
       
  9034 	},
       
  9035 
       
  9036 	_equals: function(item) {
       
  9037 		return this._content === item._content;
       
  9038 	},
       
  9039 
       
  9040 	_clone: function _clone(copy) {
       
  9041 		copy.setContent(this._content);
       
  9042 		return _clone.base.call(this, copy);
       
  9043 	},
       
  9044 
       
  9045 	getContent: function() {
       
  9046 		return this._content;
       
  9047 	},
       
  9048 
       
  9049 	setContent: function(content) {
       
  9050 		this._content = '' + content;
       
  9051 		this._lines = this._content.split(/\r\n|\n|\r/mg);
       
  9052 		this._changed(265);
       
  9053 	},
       
  9054 
       
  9055 	isEmpty: function() {
       
  9056 		return !this._content;
       
  9057 	},
       
  9058 
       
  9059 	getCharacterStyle: '#getStyle',
       
  9060 	setCharacterStyle: '#setStyle',
       
  9061 
       
  9062 	getParagraphStyle: '#getStyle',
       
  9063 	setParagraphStyle: '#setStyle'
       
  9064 });
       
  9065 
       
  9066 var PointText = TextItem.extend({
       
  9067 	_class: 'PointText',
       
  9068 
       
  9069 	initialize: function PointText() {
       
  9070 		TextItem.apply(this, arguments);
       
  9071 	},
       
  9072 
       
  9073 	clone: function(insert) {
       
  9074 		return this._clone(new PointText(Item.NO_INSERT), insert);
       
  9075 	},
       
  9076 
       
  9077 	getPoint: function() {
       
  9078 		var point = this._matrix.getTranslation();
       
  9079 		return new LinkedPoint(point.x, point.y, this, 'setPoint');
       
  9080 	},
       
  9081 
       
  9082 	setPoint: function() {
       
  9083 		var point = Point.read(arguments);
       
  9084 		this.translate(point.subtract(this._matrix.getTranslation()));
       
  9085 	},
       
  9086 
       
  9087 	_draw: function(ctx) {
       
  9088 		if (!this._content)
       
  9089 			return;
       
  9090 		this._setStyles(ctx);
       
  9091 		var style = this._style,
       
  9092 			lines = this._lines,
       
  9093 			leading = style.getLeading(),
       
  9094 			shadowColor = ctx.shadowColor;
       
  9095 		ctx.font = style.getFontStyle();
       
  9096 		ctx.textAlign = style.getJustification();
       
  9097 		for (var i = 0, l = lines.length; i < l; i++) {
       
  9098 			ctx.shadowColor = shadowColor;
       
  9099 			var line = lines[i];
       
  9100 			if (style.hasFill()) {
       
  9101 				ctx.fillText(line, 0, 0);
       
  9102 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  9103 			}
       
  9104 			if (style.hasStroke())
       
  9105 				ctx.strokeText(line, 0, 0);
       
  9106 			ctx.translate(0, leading);
       
  9107 		}
       
  9108 	},
       
  9109 
       
  9110 	_getBounds: function(getter, matrix) {
       
  9111 		var style = this._style,
       
  9112 			lines = this._lines,
       
  9113 			numLines = lines.length,
       
  9114 			justification = style.getJustification(),
       
  9115 			leading = style.getLeading(),
       
  9116 			width = this.getView().getTextWidth(style.getFontStyle(), lines),
       
  9117 			x = 0;
       
  9118 		if (justification !== 'left')
       
  9119 			x -= width / (justification === 'center' ? 2: 1);
       
  9120 		var bounds = new Rectangle(x,
       
  9121 					numLines ? - 0.75 * leading : 0,
       
  9122 					width, numLines * leading);
       
  9123 		return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
       
  9124 	}
       
  9125 });
       
  9126 
       
  9127 var Color = Base.extend(new function() {
       
  9128 	var types = {
       
  9129 		gray: ['gray'],
       
  9130 		rgb: ['red', 'green', 'blue'],
       
  9131 		hsb: ['hue', 'saturation', 'brightness'],
       
  9132 		hsl: ['hue', 'saturation', 'lightness'],
       
  9133 		gradient: ['gradient', 'origin', 'destination', 'highlight']
       
  9134 	};
       
  9135 
       
  9136 	var componentParsers = {},
       
  9137 		colorCache = {},
       
  9138 		colorCtx;
       
  9139 
       
  9140 	function fromCSS(string) {
       
  9141 		var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
       
  9142 			components;
       
  9143 		if (match) {
       
  9144 			components = [0, 0, 0];
       
  9145 			for (var i = 0; i < 3; i++) {
       
  9146 				var value = match[i + 1];
       
  9147 				components[i] = parseInt(value.length == 1
       
  9148 						? value + value : value, 16) / 255;
       
  9149 			}
       
  9150 		} else if (match = string.match(/^rgba?\((.*)\)$/)) {
       
  9151 			components = match[1].split(',');
       
  9152 			for (var i = 0, l = components.length; i < l; i++) {
       
  9153 				var value = +components[i];
       
  9154 				components[i] = i < 3 ? value / 255 : value;
       
  9155 			}
       
  9156 		} else {
       
  9157 			var cached = colorCache[string];
       
  9158 			if (!cached) {
       
  9159 				if (!colorCtx) {
       
  9160 					colorCtx = CanvasProvider.getContext(1, 1);
       
  9161 					colorCtx.globalCompositeOperation = 'copy';
       
  9162 				}
       
  9163 				colorCtx.fillStyle = 'rgba(0,0,0,0)';
       
  9164 				colorCtx.fillStyle = string;
       
  9165 				colorCtx.fillRect(0, 0, 1, 1);
       
  9166 				var data = colorCtx.getImageData(0, 0, 1, 1).data;
       
  9167 				cached = colorCache[string] = [
       
  9168 					data[0] / 255,
       
  9169 					data[1] / 255,
       
  9170 					data[2] / 255
       
  9171 				];
       
  9172 			}
       
  9173 			components = cached.slice();
       
  9174 		}
       
  9175 		return components;
       
  9176 	}
       
  9177 
       
  9178 	var hsbIndices = [
       
  9179 		[0, 3, 1],
       
  9180 		[2, 0, 1],
       
  9181 		[1, 0, 3],
       
  9182 		[1, 2, 0],
       
  9183 		[3, 1, 0],
       
  9184 		[0, 1, 2]
       
  9185 	];
       
  9186 
       
  9187 	var converters = {
       
  9188 		'rgb-hsb': function(r, g, b) {
       
  9189 			var max = Math.max(r, g, b),
       
  9190 				min = Math.min(r, g, b),
       
  9191 				delta = max - min,
       
  9192 				h = delta === 0 ? 0
       
  9193 					:	( max == r ? (g - b) / delta + (g < b ? 6 : 0)
       
  9194 						: max == g ? (b - r) / delta + 2
       
  9195 						:			 (r - g) / delta + 4) * 60;
       
  9196 			return [h, max === 0 ? 0 : delta / max, max];
       
  9197 		},
       
  9198 
       
  9199 		'hsb-rgb': function(h, s, b) {
       
  9200 			h = (((h / 60) % 6) + 6) % 6;
       
  9201 			var i = Math.floor(h),
       
  9202 				f = h - i,
       
  9203 				i = hsbIndices[i],
       
  9204 				v = [
       
  9205 					b,
       
  9206 					b * (1 - s),
       
  9207 					b * (1 - s * f),
       
  9208 					b * (1 - s * (1 - f))
       
  9209 				];
       
  9210 			return [v[i[0]], v[i[1]], v[i[2]]];
       
  9211 		},
       
  9212 
       
  9213 		'rgb-hsl': function(r, g, b) {
       
  9214 			var max = Math.max(r, g, b),
       
  9215 				min = Math.min(r, g, b),
       
  9216 				delta = max - min,
       
  9217 				achromatic = delta === 0,
       
  9218 				h = achromatic ? 0
       
  9219 					:	( max == r ? (g - b) / delta + (g < b ? 6 : 0)
       
  9220 						: max == g ? (b - r) / delta + 2
       
  9221 						:			 (r - g) / delta + 4) * 60,
       
  9222 				l = (max + min) / 2,
       
  9223 				s = achromatic ? 0 : l < 0.5
       
  9224 						? delta / (max + min)
       
  9225 						: delta / (2 - max - min);
       
  9226 			return [h, s, l];
       
  9227 		},
       
  9228 
       
  9229 		'hsl-rgb': function(h, s, l) {
       
  9230 			h = (((h / 360) % 1) + 1) % 1;
       
  9231 			if (s === 0)
       
  9232 				return [l, l, l];
       
  9233 			var t3s = [ h + 1 / 3, h, h - 1 / 3 ],
       
  9234 				t2 = l < 0.5 ? l * (1 + s) : l + s - l * s,
       
  9235 				t1 = 2 * l - t2,
       
  9236 				c = [];
       
  9237 			for (var i = 0; i < 3; i++) {
       
  9238 				var t3 = t3s[i];
       
  9239 				if (t3 < 0) t3 += 1;
       
  9240 				if (t3 > 1) t3 -= 1;
       
  9241 				c[i] = 6 * t3 < 1
       
  9242 					? t1 + (t2 - t1) * 6 * t3
       
  9243 					: 2 * t3 < 1
       
  9244 						? t2
       
  9245 						: 3 * t3 < 2
       
  9246 							? t1 + (t2 - t1) * ((2 / 3) - t3) * 6
       
  9247 							: t1;
       
  9248 			}
       
  9249 			return c;
       
  9250 		},
       
  9251 
       
  9252 		'rgb-gray': function(r, g, b) {
       
  9253 			return [r * 0.2989 + g * 0.587 + b * 0.114];
       
  9254 		},
       
  9255 
       
  9256 		'gray-rgb': function(g) {
       
  9257 			return [g, g, g];
       
  9258 		},
       
  9259 
       
  9260 		'gray-hsb': function(g) {
       
  9261 			return [0, 0, g];
       
  9262 		},
       
  9263 
       
  9264 		'gray-hsl': function(g) {
       
  9265 			return [0, 0, g];
       
  9266 		},
       
  9267 
       
  9268 		'gradient-rgb': function() {
       
  9269 			return [];
       
  9270 		},
       
  9271 
       
  9272 		'rgb-gradient': function() {
       
  9273 			return [];
       
  9274 		}
       
  9275 
       
  9276 	};
       
  9277 
       
  9278 	return Base.each(types, function(properties, type) {
       
  9279 		componentParsers[type] = [];
       
  9280 		Base.each(properties, function(name, index) {
       
  9281 			var part = Base.capitalize(name),
       
  9282 				hasOverlap = /^(hue|saturation)$/.test(name),
       
  9283 				parser = componentParsers[type][index] = name === 'gradient'
       
  9284 					? function(value) {
       
  9285 						var current = this._components[0];
       
  9286 						value = Gradient.read(Array.isArray(value) ? value
       
  9287 								: arguments, 0, { readNull: true });
       
  9288 						if (current !== value) {
       
  9289 							if (current)
       
  9290 								current._removeOwner(this);
       
  9291 							if (value)
       
  9292 								value._addOwner(this);
       
  9293 						}
       
  9294 						return value;
       
  9295 					}
       
  9296 					: type === 'gradient'
       
  9297 						? function() {
       
  9298 							return Point.read(arguments, 0, {
       
  9299 									readNull: name === 'highlight',
       
  9300 									clone: true
       
  9301 							});
       
  9302 						}
       
  9303 						: function(value) {
       
  9304 							return value == null || isNaN(value) ? 0 : value;
       
  9305 						};
       
  9306 
       
  9307 			this['get' + part] = function() {
       
  9308 				return this._type === type
       
  9309 					|| hasOverlap && /^hs[bl]$/.test(this._type)
       
  9310 						? this._components[index]
       
  9311 						: this._convert(type)[index];
       
  9312 			};
       
  9313 
       
  9314 			this['set' + part] = function(value) {
       
  9315 				if (this._type !== type
       
  9316 						&& !(hasOverlap && /^hs[bl]$/.test(this._type))) {
       
  9317 					this._components = this._convert(type);
       
  9318 					this._properties = types[type];
       
  9319 					this._type = type;
       
  9320 				}
       
  9321 				value = parser.call(this, value);
       
  9322 				if (value != null) {
       
  9323 					this._components[index] = value;
       
  9324 					this._changed();
       
  9325 				}
       
  9326 			};
       
  9327 		}, this);
       
  9328 	}, {
       
  9329 		_class: 'Color',
       
  9330 		_readIndex: true,
       
  9331 
       
  9332 		initialize: function Color(arg) {
       
  9333 			var slice = Array.prototype.slice,
       
  9334 				args = arguments,
       
  9335 				read = 0,
       
  9336 				type,
       
  9337 				components,
       
  9338 				alpha,
       
  9339 				values;
       
  9340 			if (Array.isArray(arg)) {
       
  9341 				args = arg;
       
  9342 				arg = args[0];
       
  9343 			}
       
  9344 			var argType = arg != null && typeof arg;
       
  9345 			if (argType === 'string' && arg in types) {
       
  9346 				type = arg;
       
  9347 				arg = args[1];
       
  9348 				if (Array.isArray(arg)) {
       
  9349 					components = arg;
       
  9350 					alpha = args[2];
       
  9351 				} else {
       
  9352 					if (this.__read)
       
  9353 						read = 1;
       
  9354 					args = slice.call(args, 1);
       
  9355 					argType = typeof arg;
       
  9356 				}
       
  9357 			}
       
  9358 			if (!components) {
       
  9359 				values = argType === 'number'
       
  9360 						? args
       
  9361 						: argType === 'object' && arg.length != null
       
  9362 							? arg
       
  9363 							: null;
       
  9364 				if (values) {
       
  9365 					if (!type)
       
  9366 						type = values.length >= 3
       
  9367 								? 'rgb'
       
  9368 								: 'gray';
       
  9369 					var length = types[type].length;
       
  9370 					alpha = values[length];
       
  9371 					if (this.__read)
       
  9372 						read += values === arguments
       
  9373 							? length + (alpha != null ? 1 : 0)
       
  9374 							: 1;
       
  9375 					if (values.length > length)
       
  9376 						values = slice.call(values, 0, length);
       
  9377 				} else if (argType === 'string') {
       
  9378 					type = 'rgb';
       
  9379 					components = fromCSS(arg);
       
  9380 					if (components.length === 4) {
       
  9381 						alpha = components[3];
       
  9382 						components.length--;
       
  9383 					}
       
  9384 				} else if (argType === 'object') {
       
  9385 					if (arg.constructor === Color) {
       
  9386 						type = arg._type;
       
  9387 						components = arg._components.slice();
       
  9388 						alpha = arg._alpha;
       
  9389 						if (type === 'gradient') {
       
  9390 							for (var i = 1, l = components.length; i < l; i++) {
       
  9391 								var point = components[i];
       
  9392 								if (point)
       
  9393 									components[i] = point.clone();
       
  9394 							}
       
  9395 						}
       
  9396 					} else if (arg.constructor === Gradient) {
       
  9397 						type = 'gradient';
       
  9398 						values = args;
       
  9399 					} else {
       
  9400 						type = 'hue' in arg
       
  9401 							? 'lightness' in arg
       
  9402 								? 'hsl'
       
  9403 								: 'hsb'
       
  9404 							: 'gradient' in arg || 'stops' in arg
       
  9405 									|| 'radial' in arg
       
  9406 								? 'gradient'
       
  9407 								: 'gray' in arg
       
  9408 									? 'gray'
       
  9409 									: 'rgb';
       
  9410 						var properties = types[type];
       
  9411 							parsers = componentParsers[type];
       
  9412 						this._components = components = [];
       
  9413 						for (var i = 0, l = properties.length; i < l; i++) {
       
  9414 							var value = arg[properties[i]];
       
  9415 							if (value == null && i === 0 && type === 'gradient'
       
  9416 									&& 'stops' in arg) {
       
  9417 								value = {
       
  9418 									stops: arg.stops,
       
  9419 									radial: arg.radial
       
  9420 								};
       
  9421 							}
       
  9422 							value = parsers[i].call(this, value);
       
  9423 							if (value != null)
       
  9424 								components[i] = value;
       
  9425 						}
       
  9426 						alpha = arg.alpha;
       
  9427 					}
       
  9428 				}
       
  9429 				if (this.__read && type)
       
  9430 					read = 1;
       
  9431 			}
       
  9432 			this._type = type || 'rgb';
       
  9433 			if (type === 'gradient')
       
  9434 				this._id = Color._id = (Color._id || 0) + 1;
       
  9435 			if (!components) {
       
  9436 				this._components = components = [];
       
  9437 				var parsers = componentParsers[this._type];
       
  9438 				for (var i = 0, l = parsers.length; i < l; i++) {
       
  9439 					var value = parsers[i].call(this, values && values[i]);
       
  9440 					if (value != null)
       
  9441 						components[i] = value;
       
  9442 				}
       
  9443 			}
       
  9444 			this._components = components;
       
  9445 			this._properties = types[this._type];
       
  9446 			this._alpha = alpha;
       
  9447 			if (this.__read)
       
  9448 				this.__read = read;
       
  9449 		},
       
  9450 
       
  9451 		_serialize: function(options, dictionary) {
       
  9452 			var components = this.getComponents();
       
  9453 			return Base.serialize(
       
  9454 					/^(gray|rgb)$/.test(this._type)
       
  9455 						? components
       
  9456 						: [this._type].concat(components),
       
  9457 					options, true, dictionary);
       
  9458 		},
       
  9459 
       
  9460 		_changed: function() {
       
  9461 			this._canvasStyle = null;
       
  9462 			if (this._owner)
       
  9463 				this._owner._changed(65);
       
  9464 		},
       
  9465 
       
  9466 		_convert: function(type) {
       
  9467 			var converter;
       
  9468 			return this._type === type
       
  9469 					? this._components.slice()
       
  9470 					: (converter = converters[this._type + '-' + type])
       
  9471 						? converter.apply(this, this._components)
       
  9472 						: converters['rgb-' + type].apply(this,
       
  9473 							converters[this._type + '-rgb'].apply(this,
       
  9474 								this._components));
       
  9475 		},
       
  9476 
       
  9477 		convert: function(type) {
       
  9478 			return new Color(type, this._convert(type), this._alpha);
       
  9479 		},
       
  9480 
       
  9481 		getType: function() {
       
  9482 			return this._type;
       
  9483 		},
       
  9484 
       
  9485 		setType: function(type) {
       
  9486 			this._components = this._convert(type);
       
  9487 			this._properties = types[type];
       
  9488 			this._type = type;
       
  9489 		},
       
  9490 
       
  9491 		getComponents: function() {
       
  9492 			var components = this._components.slice();
       
  9493 			if (this._alpha != null)
       
  9494 				components.push(this._alpha);
       
  9495 			return components;
       
  9496 		},
       
  9497 
       
  9498 		getAlpha: function() {
       
  9499 			return this._alpha != null ? this._alpha : 1;
       
  9500 		},
       
  9501 
       
  9502 		setAlpha: function(alpha) {
       
  9503 			this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1);
       
  9504 			this._changed();
       
  9505 		},
       
  9506 
       
  9507 		hasAlpha: function() {
       
  9508 			return this._alpha != null;
       
  9509 		},
       
  9510 
       
  9511 		equals: function(color) {
       
  9512 			var col = Base.isPlainValue(color, true)
       
  9513 					? Color.read(arguments)
       
  9514 					: color;
       
  9515 			return col === this || col && this._class === col._class
       
  9516 					&& this._type === col._type
       
  9517 					&& this._alpha === col._alpha
       
  9518 					&& Base.equals(this._components, col._components)
       
  9519 					|| false;
       
  9520 		},
       
  9521 
       
  9522 		toString: function() {
       
  9523 			var properties = this._properties,
       
  9524 				parts = [],
       
  9525 				isGradient = this._type === 'gradient',
       
  9526 				f = Formatter.instance;
       
  9527 			for (var i = 0, l = properties.length; i < l; i++) {
       
  9528 				var value = this._components[i];
       
  9529 				if (value != null)
       
  9530 					parts.push(properties[i] + ': '
       
  9531 							+ (isGradient ? value : f.number(value)));
       
  9532 			}
       
  9533 			if (this._alpha != null)
       
  9534 				parts.push('alpha: ' + f.number(this._alpha));
       
  9535 			return '{ ' + parts.join(', ') + ' }';
       
  9536 		},
       
  9537 
       
  9538 		toCSS: function(hex) {
       
  9539 			var components = this._convert('rgb'),
       
  9540 				alpha = hex || this._alpha == null ? 1 : this._alpha;
       
  9541 			function convert(val) {
       
  9542 				return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
       
  9543 			}
       
  9544 			components = [
       
  9545 				convert(components[0]),
       
  9546 				convert(components[1]),
       
  9547 				convert(components[2])
       
  9548 			];
       
  9549 			if (alpha < 1)
       
  9550 				components.push(alpha < 0 ? 0 : alpha);
       
  9551 			return hex
       
  9552 					? '#' + ((1 << 24) + (components[0] << 16)
       
  9553 						+ (components[1] << 8)
       
  9554 						+ components[2]).toString(16).slice(1)
       
  9555 					: (components.length == 4 ? 'rgba(' : 'rgb(')
       
  9556 						+ components.join(',') + ')';
       
  9557 		},
       
  9558 
       
  9559 		toCanvasStyle: function(ctx) {
       
  9560 			if (this._canvasStyle)
       
  9561 				return this._canvasStyle;
       
  9562 			if (this._type !== 'gradient')
       
  9563 				return this._canvasStyle = this.toCSS();
       
  9564 			var components = this._components,
       
  9565 				gradient = components[0],
       
  9566 				stops = gradient._stops,
       
  9567 				origin = components[1],
       
  9568 				destination = components[2],
       
  9569 				canvasGradient;
       
  9570 			if (gradient._radial) {
       
  9571 				var radius = destination.getDistance(origin),
       
  9572 					highlight = components[3];
       
  9573 				if (highlight) {
       
  9574 					var vector = highlight.subtract(origin);
       
  9575 					if (vector.getLength() > radius)
       
  9576 						highlight = origin.add(vector.normalize(radius - 0.1));
       
  9577 				}
       
  9578 				var start = highlight || origin;
       
  9579 				canvasGradient = ctx.createRadialGradient(start.x, start.y,
       
  9580 						0, origin.x, origin.y, radius);
       
  9581 			} else {
       
  9582 				canvasGradient = ctx.createLinearGradient(origin.x, origin.y,
       
  9583 						destination.x, destination.y);
       
  9584 			}
       
  9585 			for (var i = 0, l = stops.length; i < l; i++) {
       
  9586 				var stop = stops[i];
       
  9587 				canvasGradient.addColorStop(stop._rampPoint,
       
  9588 						stop._color.toCanvasStyle());
       
  9589 			}
       
  9590 			return this._canvasStyle = canvasGradient;
       
  9591 		},
       
  9592 
       
  9593 		transform: function(matrix) {
       
  9594 			if (this._type === 'gradient') {
       
  9595 				var components = this._components;
       
  9596 				for (var i = 1, l = components.length; i < l; i++) {
       
  9597 					var point = components[i];
       
  9598 					matrix._transformPoint(point, point, true);
       
  9599 				}
       
  9600 				this._changed();
       
  9601 			}
       
  9602 		},
       
  9603 
       
  9604 		statics: {
       
  9605 			_types: types,
       
  9606 
       
  9607 			random: function() {
       
  9608 				var random = Math.random;
       
  9609 				return new Color(random(), random(), random());
       
  9610 			}
       
  9611 		}
       
  9612 	});
       
  9613 }, new function() {
       
  9614 	var operators = {
       
  9615 		add: function(a, b) {
       
  9616 			return a + b;
       
  9617 		},
       
  9618 
       
  9619 		subtract: function(a, b) {
       
  9620 			return a - b;
       
  9621 		},
       
  9622 
       
  9623 		multiply: function(a, b) {
       
  9624 			return a * b;
       
  9625 		},
       
  9626 
       
  9627 		divide: function(a, b) {
       
  9628 			return a / b;
       
  9629 		}
       
  9630 	};
       
  9631 
       
  9632 	return Base.each(operators, function(operator, name) {
       
  9633 		this[name] = function(color) {
       
  9634 			color = Color.read(arguments);
       
  9635 			var type = this._type,
       
  9636 				components1 = this._components,
       
  9637 				components2 = color._convert(type);
       
  9638 			for (var i = 0, l = components1.length; i < l; i++)
       
  9639 				components2[i] = operator(components1[i], components2[i]);
       
  9640 			return new Color(type, components2,
       
  9641 					this._alpha != null
       
  9642 							? operator(this._alpha, color.getAlpha())
       
  9643 							: null);
       
  9644 		};
       
  9645 	}, {
       
  9646 	});
       
  9647 });
       
  9648 
       
  9649 Base.each(Color._types, function(properties, type) {
       
  9650 	var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) {
       
  9651 			var argType = arg != null && typeof arg,
       
  9652 				components = argType === 'object' && arg.length != null
       
  9653 					? arg
       
  9654 					: argType === 'string'
       
  9655 						? null
       
  9656 						: arguments;
       
  9657 			return components
       
  9658 					? new Color(type, components)
       
  9659 					: new Color(arg);
       
  9660 		};
       
  9661 	if (type.length == 3) {
       
  9662 		var acronym = type.toUpperCase();
       
  9663 		Color[acronym] = this[acronym + 'Color'] = ctor;
       
  9664 	}
       
  9665 }, Base.exports);
       
  9666 
       
  9667 var Gradient = Base.extend({
       
  9668 	_class: 'Gradient',
       
  9669 
       
  9670 	initialize: function Gradient(stops, radial) {
       
  9671 		this._id = Gradient._id = (Gradient._id || 0) + 1;
       
  9672 		if (stops && this._set(stops))
       
  9673 			stops = radial = null;
       
  9674 		if (!this._stops)
       
  9675 			this.setStops(stops || ['white', 'black']);
       
  9676 		if (this._radial == null)
       
  9677 			this.setRadial(typeof radial === 'string' && radial === 'radial'
       
  9678 					|| radial || false);
       
  9679 	},
       
  9680 
       
  9681 	_serialize: function(options, dictionary) {
       
  9682 		return dictionary.add(this, function() {
       
  9683 			return Base.serialize([this._stops, this._radial],
       
  9684 					options, true, dictionary);
       
  9685 		});
       
  9686 	},
       
  9687 
       
  9688 	_changed: function() {
       
  9689 		for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
       
  9690 			this._owners[i]._changed();
       
  9691 	},
       
  9692 
       
  9693 	_addOwner: function(color) {
       
  9694 		if (!this._owners)
       
  9695 			this._owners = [];
       
  9696 		this._owners.push(color);
       
  9697 	},
       
  9698 
       
  9699 	_removeOwner: function(color) {
       
  9700 		var index = this._owners ? this._owners.indexOf(color) : -1;
       
  9701 		if (index != -1) {
       
  9702 			this._owners.splice(index, 1);
       
  9703 			if (this._owners.length === 0)
       
  9704 				this._owners = undefined;
       
  9705 		}
       
  9706 	},
       
  9707 
       
  9708 	clone: function() {
       
  9709 		var stops = [];
       
  9710 		for (var i = 0, l = this._stops.length; i < l; i++)
       
  9711 			stops[i] = this._stops[i].clone();
       
  9712 		return new Gradient(stops);
       
  9713 	},
       
  9714 
       
  9715 	getStops: function() {
       
  9716 		return this._stops;
       
  9717 	},
       
  9718 
       
  9719 	setStops: function(stops) {
       
  9720 		if (this.stops) {
       
  9721 			for (var i = 0, l = this._stops.length; i < l; i++)
       
  9722 				this._stops[i]._owner = undefined;
       
  9723 		}
       
  9724 		if (stops.length < 2)
       
  9725 			throw new Error(
       
  9726 					'Gradient stop list needs to contain at least two stops.');
       
  9727 		this._stops = GradientStop.readAll(stops, 0, { clone: true });
       
  9728 		for (var i = 0, l = this._stops.length; i < l; i++) {
       
  9729 			var stop = this._stops[i];
       
  9730 			stop._owner = this;
       
  9731 			if (stop._defaultRamp)
       
  9732 				stop.setRampPoint(i / (l - 1));
       
  9733 		}
       
  9734 		this._changed();
       
  9735 	},
       
  9736 
       
  9737 	getRadial: function() {
       
  9738 		return this._radial;
       
  9739 	},
       
  9740 
       
  9741 	setRadial: function(radial) {
       
  9742 		this._radial = radial;
       
  9743 		this._changed();
       
  9744 	},
       
  9745 
       
  9746 	equals: function(gradient) {
       
  9747 		if (gradient === this)
       
  9748 			return true;
       
  9749 		if (gradient && this._class === gradient._class
       
  9750 				&& this._stops.length === gradient._stops.length) {
       
  9751 			for (var i = 0, l = this._stops.length; i < l; i++) {
       
  9752 				if (!this._stops[i].equals(gradient._stops[i]))
       
  9753 					return false;
       
  9754 			}
       
  9755 			return true;
       
  9756 		}
       
  9757 		return false;
       
  9758 	}
       
  9759 });
       
  9760 
       
  9761 var GradientStop = Base.extend({
       
  9762 	_class: 'GradientStop',
       
  9763 
       
  9764 	initialize: function GradientStop(arg0, arg1) {
       
  9765 		if (arg0) {
       
  9766 			var color, rampPoint;
       
  9767 			if (arg1 === undefined && Array.isArray(arg0)) {
       
  9768 				color = arg0[0];
       
  9769 				rampPoint = arg0[1];
       
  9770 			} else if (arg0.color) {
       
  9771 				color = arg0.color;
       
  9772 				rampPoint = arg0.rampPoint;
       
  9773 			} else {
       
  9774 				color = arg0;
       
  9775 				rampPoint = arg1;
       
  9776 			}
       
  9777 			this.setColor(color);
       
  9778 			this.setRampPoint(rampPoint);
       
  9779 		}
       
  9780 	},
       
  9781 
       
  9782 	clone: function() {
       
  9783 		return new GradientStop(this._color.clone(), this._rampPoint);
       
  9784 	},
       
  9785 
       
  9786 	_serialize: function(options, dictionary) {
       
  9787 		return Base.serialize([this._color, this._rampPoint], options, true,
       
  9788 				dictionary);
       
  9789 	},
       
  9790 
       
  9791 	_changed: function() {
       
  9792 		if (this._owner)
       
  9793 			this._owner._changed(65);
       
  9794 	},
       
  9795 
       
  9796 	getRampPoint: function() {
       
  9797 		return this._rampPoint;
       
  9798 	},
       
  9799 
       
  9800 	setRampPoint: function(rampPoint) {
       
  9801 		this._defaultRamp = rampPoint == null;
       
  9802 		this._rampPoint = rampPoint || 0;
       
  9803 		this._changed();
       
  9804 	},
       
  9805 
       
  9806 	getColor: function() {
       
  9807 		return this._color;
       
  9808 	},
       
  9809 
       
  9810 	setColor: function(color) {
       
  9811 		this._color = Color.read(arguments);
       
  9812 		if (this._color === color)
       
  9813 			this._color = color.clone();
       
  9814 		this._color._owner = this;
       
  9815 		this._changed();
       
  9816 	},
       
  9817 
       
  9818 	equals: function(stop) {
       
  9819 		return stop === this || stop && this._class === stop._class
       
  9820 				&& this._color.equals(stop._color)
       
  9821 				&& this._rampPoint == stop._rampPoint
       
  9822 				|| false;
       
  9823 	}
       
  9824 });
       
  9825 
       
  9826 var Style = Base.extend(new function() {
       
  9827 	var defaults = {
       
  9828 		fillColor: undefined,
       
  9829 		strokeColor: undefined,
       
  9830 		strokeWidth: 1,
       
  9831 		strokeCap: 'butt',
       
  9832 		strokeJoin: 'miter',
       
  9833 		strokeScaling: true,
       
  9834 		miterLimit: 10,
       
  9835 		dashOffset: 0,
       
  9836 		dashArray: [],
       
  9837 		windingRule: 'nonzero',
       
  9838 		shadowColor: undefined,
       
  9839 		shadowBlur: 0,
       
  9840 		shadowOffset: new Point(),
       
  9841 		selectedColor: undefined,
       
  9842 		fontFamily: 'sans-serif',
       
  9843 		fontWeight: 'normal',
       
  9844 		fontSize: 12,
       
  9845 		font: 'sans-serif',
       
  9846 		leading: null,
       
  9847 		justification: 'left'
       
  9848 	};
       
  9849 
       
  9850 	var flags = {
       
  9851 		strokeWidth: 97,
       
  9852 		strokeCap: 97,
       
  9853 		strokeJoin: 97,
       
  9854 		strokeScaling: 105,
       
  9855 		miterLimit: 97,
       
  9856 		fontFamily: 9,
       
  9857 		fontWeight: 9,
       
  9858 		fontSize: 9,
       
  9859 		font: 9,
       
  9860 		leading: 9,
       
  9861 		justification: 9
       
  9862 	};
       
  9863 
       
  9864 	var item = { beans: true },
       
  9865 		fields = {
       
  9866 			_defaults: defaults,
       
  9867 			_textDefaults: new Base(defaults, {
       
  9868 				fillColor: new Color()
       
  9869 			}),
       
  9870 			beans: true
       
  9871 		};
       
  9872 
       
  9873 	Base.each(defaults, function(value, key) {
       
  9874 		var isColor = /Color$/.test(key),
       
  9875 			part = Base.capitalize(key),
       
  9876 			flag = flags[key],
       
  9877 			set = 'set' + part,
       
  9878 			get = 'get' + part;
       
  9879 
       
  9880 		fields[set] = function(value) {
       
  9881 			var owner = this._owner,
       
  9882 				children = owner && owner._children;
       
  9883 			if (children && children.length > 0
       
  9884 					&& !(owner instanceof CompoundPath)) {
       
  9885 				for (var i = 0, l = children.length; i < l; i++)
       
  9886 					children[i]._style[set](value);
       
  9887 			} else {
       
  9888 				var old = this._values[key];
       
  9889 				if (old != value) {
       
  9890 					if (isColor) {
       
  9891 						if (old)
       
  9892 							old._owner = undefined;
       
  9893 						if (value && value.constructor === Color) {
       
  9894 							if (value._owner)
       
  9895 								value = value.clone();
       
  9896 							value._owner = owner;
       
  9897 						}
       
  9898 					}
       
  9899 					this._values[key] = value;
       
  9900 					if (owner)
       
  9901 						owner._changed(flag || 65);
       
  9902 				}
       
  9903 			}
       
  9904 		};
       
  9905 
       
  9906 		fields[get] = function(_dontMerge) {
       
  9907 			var owner = this._owner,
       
  9908 				children = owner && owner._children,
       
  9909 				value;
       
  9910 			if (!children || children.length === 0 || _dontMerge
       
  9911 					|| owner instanceof CompoundPath) {
       
  9912 				var value = this._values[key];
       
  9913 				if (value === undefined) {
       
  9914 					value = this._defaults[key];
       
  9915 					if (value && value.clone)
       
  9916 						value = value.clone();
       
  9917 					this._values[key] = value;
       
  9918 				} else if (isColor && !(value && value.constructor === Color)) {
       
  9919 					this._values[key] = value = Color.read([value], 0,
       
  9920 							{ readNull: true, clone: true });
       
  9921 					if (value)
       
  9922 						value._owner = owner;
       
  9923 				}
       
  9924 				return value;
       
  9925 			}
       
  9926 			for (var i = 0, l = children.length; i < l; i++) {
       
  9927 				var childValue = children[i]._style[get]();
       
  9928 				if (i === 0) {
       
  9929 					value = childValue;
       
  9930 				} else if (!Base.equals(value, childValue)) {
       
  9931 					return undefined;
       
  9932 				}
       
  9933 			}
       
  9934 			return value;
       
  9935 		};
       
  9936 
       
  9937 		item[get] = function(_dontMerge) {
       
  9938 			return this._style[get](_dontMerge);
       
  9939 		};
       
  9940 
       
  9941 		item[set] = function(value) {
       
  9942 			this._style[set](value);
       
  9943 		};
       
  9944 	});
       
  9945 
       
  9946 	Item.inject(item);
       
  9947 	return fields;
       
  9948 }, {
       
  9949 	_class: 'Style',
       
  9950 
       
  9951 	initialize: function Style(style, _owner, _project) {
       
  9952 		this._values = {};
       
  9953 		this._owner = _owner;
       
  9954 		this._project = _owner && _owner._project || _project || paper.project;
       
  9955 		if (_owner instanceof TextItem)
       
  9956 			this._defaults = this._textDefaults;
       
  9957 		if (style)
       
  9958 			this.set(style);
       
  9959 	},
       
  9960 
       
  9961 	set: function(style) {
       
  9962 		var isStyle = style instanceof Style,
       
  9963 			values = isStyle ? style._values : style;
       
  9964 		if (values) {
       
  9965 			for (var key in values) {
       
  9966 				if (key in this._defaults) {
       
  9967 					var value = values[key];
       
  9968 					this[key] = value && isStyle && value.clone
       
  9969 							? value.clone() : value;
       
  9970 				}
       
  9971 			}
       
  9972 		}
       
  9973 	},
       
  9974 
       
  9975 	equals: function(style) {
       
  9976 		return style === this || style && this._class === style._class
       
  9977 				&& Base.equals(this._values, style._values)
       
  9978 				|| false;
       
  9979 	},
       
  9980 
       
  9981 	hasFill: function() {
       
  9982 		return !!this.getFillColor();
       
  9983 	},
       
  9984 
       
  9985 	hasStroke: function() {
       
  9986 		return !!this.getStrokeColor() && this.getStrokeWidth() > 0;
       
  9987 	},
       
  9988 
       
  9989 	hasShadow: function() {
       
  9990 		return !!this.getShadowColor() && this.getShadowBlur() > 0;
       
  9991 	},
       
  9992 
       
  9993 	getView: function() {
       
  9994 		return this._project.getView();
       
  9995 	},
       
  9996 
       
  9997 	getFontStyle: function() {
       
  9998 		var fontSize = this.getFontSize();
       
  9999 		return this.getFontWeight()
       
 10000 				+ ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')
       
 10001 				+ this.getFontFamily();
       
 10002 	},
       
 10003 
       
 10004 	getFont: '#getFontFamily',
       
 10005 	setFont: '#setFontFamily',
       
 10006 
       
 10007 	getLeading: function getLeading() {
       
 10008 		var leading = getLeading.base.call(this),
       
 10009 			fontSize = this.getFontSize();
       
 10010 		if (/pt|em|%|px/.test(fontSize))
       
 10011 			fontSize = this.getView().getPixelSize(fontSize);
       
 10012 		return leading != null ? leading : fontSize * 1.2;
       
 10013 	}
       
 10014 
       
 10015 });
       
 10016 
       
 10017 var DomElement = new function() {
       
 10018 
       
 10019 	var special = /^(checked|value|selected|disabled)$/i,
       
 10020 		translated = { text: 'textContent', html: 'innerHTML' },
       
 10021 		unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1 };
       
 10022 
       
 10023 	function create(nodes, parent) {
       
 10024 		var res = [];
       
 10025 		for (var i =  0, l = nodes && nodes.length; i < l;) {
       
 10026 			var el = nodes[i++];
       
 10027 			if (typeof el === 'string') {
       
 10028 				el = document.createElement(el);
       
 10029 			} else if (!el || !el.nodeType) {
       
 10030 				continue;
       
 10031 			}
       
 10032 			if (Base.isPlainObject(nodes[i]))
       
 10033 				DomElement.set(el, nodes[i++]);
       
 10034 			if (Array.isArray(nodes[i]))
       
 10035 				create(nodes[i++], el);
       
 10036 			if (parent)
       
 10037 				parent.appendChild(el);
       
 10038 			res.push(el);
       
 10039 		}
       
 10040 		return res;
       
 10041 	}
       
 10042 
       
 10043 	function handlePrefix(el, name, set, value) {
       
 10044 		var prefixes = ['', 'webkit', 'moz', 'Moz', 'ms', 'o'],
       
 10045 			suffix = name[0].toUpperCase() + name.substring(1);
       
 10046 		for (var i = 0; i < 6; i++) {
       
 10047 			var prefix = prefixes[i],
       
 10048 				key = prefix ? prefix + suffix : name;
       
 10049 			if (key in el) {
       
 10050 				if (set) {
       
 10051 					el[key] = value;
       
 10052 				} else {
       
 10053 					return el[key];
       
 10054 				}
       
 10055 				break;
       
 10056 			}
       
 10057 		}
       
 10058 	}
       
 10059 
       
 10060 	return {
       
 10061 		create: function(nodes, parent) {
       
 10062 			var isArray = Array.isArray(nodes),
       
 10063 				res = create(isArray ? nodes : arguments, isArray ? parent : null);
       
 10064 			return res.length == 1 ? res[0] : res;
       
 10065 		},
       
 10066 
       
 10067 		find: function(selector, root) {
       
 10068 			return (root || document).querySelector(selector);
       
 10069 		},
       
 10070 
       
 10071 		findAll: function(selector, root) {
       
 10072 			return (root || document).querySelectorAll(selector);
       
 10073 		},
       
 10074 
       
 10075 		get: function(el, key) {
       
 10076 			return el
       
 10077 				? special.test(key)
       
 10078 					? key === 'value' || typeof el[key] !== 'string'
       
 10079 						? el[key]
       
 10080 						: true
       
 10081 					: key in translated
       
 10082 						? el[translated[key]]
       
 10083 						: el.getAttribute(key)
       
 10084 				: null;
       
 10085 		},
       
 10086 
       
 10087 		set: function(el, key, value) {
       
 10088 			if (typeof key !== 'string') {
       
 10089 				for (var name in key)
       
 10090 					if (key.hasOwnProperty(name))
       
 10091 						this.set(el, name, key[name]);
       
 10092 			} else if (!el || value === undefined) {
       
 10093 				return el;
       
 10094 			} else if (special.test(key)) {
       
 10095 				el[key] = value;
       
 10096 			} else if (key in translated) {
       
 10097 				el[translated[key]] = value;
       
 10098 			} else if (key === 'style') {
       
 10099 				this.setStyle(el, value);
       
 10100 			} else if (key === 'events') {
       
 10101 				DomEvent.add(el, value);
       
 10102 			} else {
       
 10103 				el.setAttribute(key, value);
       
 10104 			}
       
 10105 			return el;
       
 10106 		},
       
 10107 
       
 10108 		getStyles: function(el) {
       
 10109 			var doc = el && el.nodeType !== 9 ? el.ownerDocument : el,
       
 10110 				view = doc && doc.defaultView;
       
 10111 			return view && view.getComputedStyle(el, '');
       
 10112 		},
       
 10113 
       
 10114 		getStyle: function(el, key) {
       
 10115 			return el && el.style[key] || this.getStyles(el)[key] || null;
       
 10116 		},
       
 10117 
       
 10118 		setStyle: function(el, key, value) {
       
 10119 			if (typeof key !== 'string') {
       
 10120 				for (var name in key)
       
 10121 					if (key.hasOwnProperty(name))
       
 10122 						this.setStyle(el, name, key[name]);
       
 10123 			} else {
       
 10124 				if (/^-?[\d\.]+$/.test(value) && !(key in unitless))
       
 10125 					value += 'px';
       
 10126 				el.style[key] = value;
       
 10127 			}
       
 10128 			return el;
       
 10129 		},
       
 10130 
       
 10131 		hasClass: function(el, cls) {
       
 10132 			return new RegExp('\\s*' + cls + '\\s*').test(el.className);
       
 10133 		},
       
 10134 
       
 10135 		addClass: function(el, cls) {
       
 10136 			el.className = (el.className + ' ' + cls).trim();
       
 10137 		},
       
 10138 
       
 10139 		removeClass: function(el, cls) {
       
 10140 			el.className = el.className.replace(
       
 10141 				new RegExp('\\s*' + cls + '\\s*'), ' ').trim();
       
 10142 		},
       
 10143 
       
 10144 		remove: function(el) {
       
 10145 			if (el.parentNode)
       
 10146 				el.parentNode.removeChild(el);
       
 10147 		},
       
 10148 
       
 10149 		removeChildren: function(el) {
       
 10150 			while (el.firstChild)
       
 10151 				el.removeChild(el.firstChild);
       
 10152 		},
       
 10153 
       
 10154 		getBounds: function(el, viewport) {
       
 10155 			var doc = el.ownerDocument,
       
 10156 				body = doc.body,
       
 10157 				html = doc.documentElement,
       
 10158 				rect;
       
 10159 			try {
       
 10160 				rect = el.getBoundingClientRect();
       
 10161 			} catch (e) {
       
 10162 				rect = { left: 0, top: 0, width: 0, height: 0 };
       
 10163 			}
       
 10164 			var x = rect.left - (html.clientLeft || body.clientLeft || 0),
       
 10165 				y = rect.top - (html.clientTop || body.clientTop || 0);
       
 10166 			if (!viewport) {
       
 10167 				var view = doc.defaultView;
       
 10168 				x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
       
 10169 				y += view.pageYOffset || html.scrollTop || body.scrollTop;
       
 10170 			}
       
 10171 			return new Rectangle(x, y, rect.width, rect.height);
       
 10172 		},
       
 10173 
       
 10174 		getViewportBounds: function(el) {
       
 10175 			var doc = el.ownerDocument,
       
 10176 				view = doc.defaultView,
       
 10177 				html = doc.documentElement;
       
 10178 			return new Rectangle(0, 0,
       
 10179 				view.innerWidth || html.clientWidth,
       
 10180 				view.innerHeight || html.clientHeight
       
 10181 			);
       
 10182 		},
       
 10183 
       
 10184 		getOffset: function(el, viewport) {
       
 10185 			return this.getBounds(el, viewport).getPoint();
       
 10186 		},
       
 10187 
       
 10188 		getSize: function(el) {
       
 10189 			return this.getBounds(el, true).getSize();
       
 10190 		},
       
 10191 
       
 10192 		isInvisible: function(el) {
       
 10193 			return this.getSize(el).equals(new Size(0, 0));
       
 10194 		},
       
 10195 
       
 10196 		isInView: function(el) {
       
 10197 			return !this.isInvisible(el) && this.getViewportBounds(el).intersects(
       
 10198 					this.getBounds(el, true));
       
 10199 		},
       
 10200 
       
 10201 		getPrefixed: function(el, name) {
       
 10202 			return handlePrefix(el, name);
       
 10203 		},
       
 10204 
       
 10205 		setPrefixed: function(el, name, value) {
       
 10206 			if (typeof name === 'object') {
       
 10207 				for (var key in name)
       
 10208 					handlePrefix(el, key, true, name[key]);
       
 10209 			} else {
       
 10210 				handlePrefix(el, name, true, value);
       
 10211 			}
       
 10212 		}
       
 10213 	};
       
 10214 };
       
 10215 
       
 10216 var DomEvent = {
       
 10217 	add: function(el, events) {
       
 10218 		for (var type in events) {
       
 10219 			var func = events[type],
       
 10220 				parts = type.split(/[\s,]+/g);
       
 10221 			for (var i = 0, l = parts.length; i < l; i++)
       
 10222 				el.addEventListener(parts[i], func, false);
       
 10223 		}
       
 10224 	},
       
 10225 
       
 10226 	remove: function(el, events) {
       
 10227 		for (var type in events) {
       
 10228 			var func = events[type],
       
 10229 				parts = type.split(/[\s,]+/g);
       
 10230 			for (var i = 0, l = parts.length; i < l; i++)
       
 10231 				el.removeEventListener(parts[i], func, false);
       
 10232 		}
       
 10233 	},
       
 10234 
       
 10235 	getPoint: function(event) {
       
 10236 		var pos = event.targetTouches
       
 10237 				? event.targetTouches.length
       
 10238 					? event.targetTouches[0]
       
 10239 					: event.changedTouches[0]
       
 10240 				: event;
       
 10241 		return new Point(
       
 10242 			pos.pageX || pos.clientX + document.documentElement.scrollLeft,
       
 10243 			pos.pageY || pos.clientY + document.documentElement.scrollTop
       
 10244 		);
       
 10245 	},
       
 10246 
       
 10247 	getTarget: function(event) {
       
 10248 		return event.target || event.srcElement;
       
 10249 	},
       
 10250 
       
 10251 	getRelatedTarget: function(event) {
       
 10252 		return event.relatedTarget || event.toElement;
       
 10253 	},
       
 10254 
       
 10255 	getOffset: function(event, target) {
       
 10256 		return DomEvent.getPoint(event).subtract(DomElement.getOffset(
       
 10257 				target || DomEvent.getTarget(event)));
       
 10258 	},
       
 10259 
       
 10260 	stop: function(event) {
       
 10261 		event.stopPropagation();
       
 10262 		event.preventDefault();
       
 10263 	}
       
 10264 };
       
 10265 
       
 10266 DomEvent.requestAnimationFrame = new function() {
       
 10267 	var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
       
 10268 		requested = false,
       
 10269 		callbacks = [],
       
 10270 		focused = true,
       
 10271 		timer;
       
 10272 
       
 10273 	DomEvent.add(window, {
       
 10274 		focus: function() {
       
 10275 			focused = true;
       
 10276 		},
       
 10277 		blur: function() {
       
 10278 			focused = false;
       
 10279 		}
       
 10280 	});
       
 10281 
       
 10282 	function handleCallbacks() {
       
 10283 		for (var i = callbacks.length - 1; i >= 0; i--) {
       
 10284 			var entry = callbacks[i],
       
 10285 				func = entry[0],
       
 10286 				el = entry[1];
       
 10287 			if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
       
 10288 					|| focused) && DomElement.isInView(el)) {
       
 10289 				callbacks.splice(i, 1);
       
 10290 				func();
       
 10291 			}
       
 10292 		}
       
 10293 		if (nativeRequest) {
       
 10294 			if (callbacks.length) {
       
 10295 				nativeRequest(handleCallbacks);
       
 10296 			} else {
       
 10297 				requested = false;
       
 10298 			}
       
 10299 		}
       
 10300 	}
       
 10301 
       
 10302 	return function(callback, element) {
       
 10303 		callbacks.push([callback, element]);
       
 10304 		if (nativeRequest) {
       
 10305 			if (!requested) {
       
 10306 				nativeRequest(handleCallbacks);
       
 10307 				requested = true;
       
 10308 			}
       
 10309 		} else if (!timer) {
       
 10310 			timer = setInterval(handleCallbacks, 1000 / 60);
       
 10311 		}
       
 10312 	};
       
 10313 };
       
 10314 
       
 10315 var View = Base.extend(Callback, {
       
 10316 	_class: 'View',
       
 10317 
       
 10318 	initialize: function View(project, element) {
       
 10319 		this._project = project;
       
 10320 		this._scope = project._scope;
       
 10321 		this._element = element;
       
 10322 		var size;
       
 10323 		if (!this._pixelRatio)
       
 10324 			this._pixelRatio = window.devicePixelRatio || 1;
       
 10325 		this._id = element.getAttribute('id');
       
 10326 		if (this._id == null)
       
 10327 			element.setAttribute('id', this._id = 'view-' + View._id++);
       
 10328 		DomEvent.add(element, this._viewEvents);
       
 10329 		var none = 'none';
       
 10330 		DomElement.setPrefixed(element.style, {
       
 10331 			userSelect: none,
       
 10332 			touchAction: none,
       
 10333 			touchCallout: none,
       
 10334 			contentZooming: none,
       
 10335 			userDrag: none,
       
 10336 			tapHighlightColor: 'rgba(0,0,0,0)'
       
 10337 		});
       
 10338 		if (PaperScope.hasAttribute(element, 'resize')) {
       
 10339 			var offset = DomElement.getOffset(element, true),
       
 10340 				that = this;
       
 10341 			size = DomElement.getViewportBounds(element)
       
 10342 					.getSize().subtract(offset);
       
 10343 			this._windowEvents = {
       
 10344 				resize: function() {
       
 10345 					if (!DomElement.isInvisible(element))
       
 10346 						offset = DomElement.getOffset(element, true);
       
 10347 					that.setViewSize(DomElement.getViewportBounds(element)
       
 10348 							.getSize().subtract(offset));
       
 10349 				}
       
 10350 			};
       
 10351 			DomEvent.add(window, this._windowEvents);
       
 10352 		} else {
       
 10353 			size = DomElement.getSize(element);
       
 10354 			if (size.isNaN() || size.isZero()) {
       
 10355 				var getSize = function(name) {
       
 10356 					return element[name]
       
 10357 							|| parseInt(element.getAttribute(name), 10);
       
 10358 				};
       
 10359 				size = new Size(getSize('width'), getSize('height'));
       
 10360 			}
       
 10361 		}
       
 10362 		this._setViewSize(size);
       
 10363 		if (PaperScope.hasAttribute(element, 'stats')
       
 10364 				&& typeof Stats !== 'undefined') {
       
 10365 			this._stats = new Stats();
       
 10366 			var stats = this._stats.domElement,
       
 10367 				style = stats.style,
       
 10368 				offset = DomElement.getOffset(element);
       
 10369 			style.position = 'absolute';
       
 10370 			style.left = offset.x + 'px';
       
 10371 			style.top = offset.y + 'px';
       
 10372 			document.body.appendChild(stats);
       
 10373 		}
       
 10374 		View._views.push(this);
       
 10375 		View._viewsById[this._id] = this;
       
 10376 		this._viewSize = size;
       
 10377 		(this._matrix = new Matrix())._owner = this;
       
 10378 		this._zoom = 1;
       
 10379 		if (!View._focused)
       
 10380 			View._focused = this;
       
 10381 		this._frameItems = {};
       
 10382 		this._frameItemCount = 0;
       
 10383 	},
       
 10384 
       
 10385 	remove: function() {
       
 10386 		if (!this._project)
       
 10387 			return false;
       
 10388 		if (View._focused === this)
       
 10389 			View._focused = null;
       
 10390 		View._views.splice(View._views.indexOf(this), 1);
       
 10391 		delete View._viewsById[this._id];
       
 10392 		if (this._project._view === this)
       
 10393 			this._project._view = null;
       
 10394 		DomEvent.remove(this._element, this._viewEvents);
       
 10395 		DomEvent.remove(window, this._windowEvents);
       
 10396 		this._element = this._project = null;
       
 10397 		this.detach('frame');
       
 10398 		this._animate = false;
       
 10399 		this._frameItems = {};
       
 10400 		return true;
       
 10401 	},
       
 10402 
       
 10403 	_events: {
       
 10404 		onFrame: {
       
 10405 			install: function() {
       
 10406 				this.play();
       
 10407 			},
       
 10408 
       
 10409 			uninstall: function() {
       
 10410 				this.pause();
       
 10411 			}
       
 10412 		},
       
 10413 
       
 10414 		onResize: {}
       
 10415 	},
       
 10416 
       
 10417 	_animate: false,
       
 10418 	_time: 0,
       
 10419 	_count: 0,
       
 10420 
       
 10421 	_requestFrame: function() {
       
 10422 		var that = this;
       
 10423 		DomEvent.requestAnimationFrame(function() {
       
 10424 			that._requested = false;
       
 10425 			if (!that._animate)
       
 10426 				return;
       
 10427 			that._requestFrame();
       
 10428 			that._handleFrame();
       
 10429 		}, this._element);
       
 10430 		this._requested = true;
       
 10431 	},
       
 10432 
       
 10433 	_handleFrame: function() {
       
 10434 		paper = this._scope;
       
 10435 		var now = Date.now() / 1000,
       
 10436 			delta = this._before ? now - this._before : 0;
       
 10437 		this._before = now;
       
 10438 		this._handlingFrame = true;
       
 10439 		this.fire('frame', new Base({
       
 10440 			delta: delta,
       
 10441 			time: this._time += delta,
       
 10442 			count: this._count++
       
 10443 		}));
       
 10444 		if (this._stats)
       
 10445 			this._stats.update();
       
 10446 		this._handlingFrame = false;
       
 10447 		this.update();
       
 10448 	},
       
 10449 
       
 10450 	_animateItem: function(item, animate) {
       
 10451 		var items = this._frameItems;
       
 10452 		if (animate) {
       
 10453 			items[item._id] = {
       
 10454 				item: item,
       
 10455 				time: 0,
       
 10456 				count: 0
       
 10457 			};
       
 10458 			if (++this._frameItemCount === 1)
       
 10459 				this.attach('frame', this._handleFrameItems);
       
 10460 		} else {
       
 10461 			delete items[item._id];
       
 10462 			if (--this._frameItemCount === 0) {
       
 10463 				this.detach('frame', this._handleFrameItems);
       
 10464 			}
       
 10465 		}
       
 10466 	},
       
 10467 
       
 10468 	_handleFrameItems: function(event) {
       
 10469 		for (var i in this._frameItems) {
       
 10470 			var entry = this._frameItems[i];
       
 10471 			entry.item.fire('frame', new Base(event, {
       
 10472 				time: entry.time += event.delta,
       
 10473 				count: entry.count++
       
 10474 			}));
       
 10475 		}
       
 10476 	},
       
 10477 
       
 10478 	_update: function() {
       
 10479 		this._project._needsUpdate = true;
       
 10480 		if (this._handlingFrame)
       
 10481 			return;
       
 10482 		if (this._animate) {
       
 10483 			this._handleFrame();
       
 10484 		} else {
       
 10485 			this.update();
       
 10486 		}
       
 10487 	},
       
 10488 
       
 10489 	_changed: function(flags) {
       
 10490 		if (flags & 1)
       
 10491 			this._project._needsUpdate = true;
       
 10492 	},
       
 10493 
       
 10494 	_transform: function(matrix) {
       
 10495 		this._matrix.concatenate(matrix);
       
 10496 		this._bounds = null;
       
 10497 		this._update();
       
 10498 	},
       
 10499 
       
 10500 	getElement: function() {
       
 10501 		return this._element;
       
 10502 	},
       
 10503 
       
 10504 	getPixelRatio: function() {
       
 10505 		return this._pixelRatio;
       
 10506 	},
       
 10507 
       
 10508 	getResolution: function() {
       
 10509 		return this._pixelRatio * 72;
       
 10510 	},
       
 10511 
       
 10512 	getViewSize: function() {
       
 10513 		var size = this._viewSize;
       
 10514 		return new LinkedSize(size.width, size.height, this, 'setViewSize');
       
 10515 	},
       
 10516 
       
 10517 	setViewSize: function() {
       
 10518 		var size = Size.read(arguments),
       
 10519 			delta = size.subtract(this._viewSize);
       
 10520 		if (delta.isZero())
       
 10521 			return;
       
 10522 		this._viewSize.set(size.width, size.height);
       
 10523 		this._setViewSize(size);
       
 10524 		this._bounds = null;
       
 10525 		this.fire('resize', {
       
 10526 			size: size,
       
 10527 			delta: delta
       
 10528 		});
       
 10529 		this._update();
       
 10530 	},
       
 10531 
       
 10532 	_setViewSize: function(size) {
       
 10533 		var element = this._element;
       
 10534 		element.width = size.width;
       
 10535 		element.height = size.height;
       
 10536 	},
       
 10537 
       
 10538 	getBounds: function() {
       
 10539 		if (!this._bounds)
       
 10540 			this._bounds = this._matrix.inverted()._transformBounds(
       
 10541 					new Rectangle(new Point(), this._viewSize));
       
 10542 		return this._bounds;
       
 10543 	},
       
 10544 
       
 10545 	getSize: function() {
       
 10546 		return this.getBounds().getSize();
       
 10547 	},
       
 10548 
       
 10549 	getCenter: function() {
       
 10550 		return this.getBounds().getCenter();
       
 10551 	},
       
 10552 
       
 10553 	setCenter: function() {
       
 10554 		var center = Point.read(arguments);
       
 10555 		this.scrollBy(center.subtract(this.getCenter()));
       
 10556 	},
       
 10557 
       
 10558 	getZoom: function() {
       
 10559 		return this._zoom;
       
 10560 	},
       
 10561 
       
 10562 	setZoom: function(zoom) {
       
 10563 		this._transform(new Matrix().scale(zoom / this._zoom,
       
 10564 			this.getCenter()));
       
 10565 		this._zoom = zoom;
       
 10566 	},
       
 10567 
       
 10568 	isVisible: function() {
       
 10569 		return DomElement.isInView(this._element);
       
 10570 	},
       
 10571 
       
 10572 	scrollBy: function() {
       
 10573 		this._transform(new Matrix().translate(Point.read(arguments).negate()));
       
 10574 	},
       
 10575 
       
 10576 	play: function() {
       
 10577 		this._animate = true;
       
 10578 		if (!this._requested)
       
 10579 			this._requestFrame();
       
 10580 	},
       
 10581 
       
 10582 	pause: function() {
       
 10583 		this._animate = false;
       
 10584 	},
       
 10585 
       
 10586 	draw: function() {
       
 10587 		this.update();
       
 10588 	},
       
 10589 
       
 10590 	projectToView: function() {
       
 10591 		return this._matrix._transformPoint(Point.read(arguments));
       
 10592 	},
       
 10593 
       
 10594 	viewToProject: function() {
       
 10595 		return this._matrix._inverseTransform(Point.read(arguments));
       
 10596 	}
       
 10597 
       
 10598 }, {
       
 10599 	statics: {
       
 10600 		_views: [],
       
 10601 		_viewsById: {},
       
 10602 		_id: 0,
       
 10603 
       
 10604 		create: function(project, element) {
       
 10605 			if (typeof element === 'string')
       
 10606 				element = document.getElementById(element);
       
 10607 			return new CanvasView(project, element);
       
 10608 		}
       
 10609 	}
       
 10610 }, new function() {
       
 10611 	var tool,
       
 10612 		prevFocus,
       
 10613 		tempFocus,
       
 10614 		dragging = false;
       
 10615 
       
 10616 	function getView(event) {
       
 10617 		var target = DomEvent.getTarget(event);
       
 10618 		return target.getAttribute && View._viewsById[target.getAttribute('id')];
       
 10619 	}
       
 10620 
       
 10621 	function viewToProject(view, event) {
       
 10622 		return view.viewToProject(DomEvent.getOffset(event, view._element));
       
 10623 	}
       
 10624 
       
 10625 	function updateFocus() {
       
 10626 		if (!View._focused || !View._focused.isVisible()) {
       
 10627 			for (var i = 0, l = View._views.length; i < l; i++) {
       
 10628 				var view = View._views[i];
       
 10629 				if (view && view.isVisible()) {
       
 10630 					View._focused = tempFocus = view;
       
 10631 					break;
       
 10632 				}
       
 10633 			}
       
 10634 		}
       
 10635 	}
       
 10636 
       
 10637 	function handleMouseMove(view, point, event) {
       
 10638 		view._handleEvent('mousemove', point, event);
       
 10639 		var tool = view._scope.tool;
       
 10640 		if (tool) {
       
 10641 			tool._handleEvent(dragging && tool.responds('mousedrag')
       
 10642 					? 'mousedrag' : 'mousemove', point, event);
       
 10643 		}
       
 10644 		view.update();
       
 10645 		return tool;
       
 10646 	}
       
 10647 
       
 10648 	var navigator = window.navigator,
       
 10649 		mousedown, mousemove, mouseup;
       
 10650 	if (navigator.pointerEnabled || navigator.msPointerEnabled) {
       
 10651 		mousedown = 'pointerdown MSPointerDown';
       
 10652 		mousemove = 'pointermove MSPointerMove';
       
 10653 		mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
       
 10654 	} else {
       
 10655 		mousedown = 'touchstart';
       
 10656 		mousemove = 'touchmove';
       
 10657 		mouseup = 'touchend touchcancel';
       
 10658 		if (!('ontouchstart' in window && navigator.userAgent.match(
       
 10659 				/mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
       
 10660 			mousedown += ' mousedown';
       
 10661 			mousemove += ' mousemove';
       
 10662 			mouseup += ' mouseup';
       
 10663 		}
       
 10664 	}
       
 10665 
       
 10666 	var viewEvents = {
       
 10667 		'selectstart dragstart': function(event) {
       
 10668 			if (dragging)
       
 10669 				event.preventDefault();
       
 10670 		}
       
 10671 	};
       
 10672 
       
 10673 	var docEvents = {
       
 10674 		mouseout: function(event) {
       
 10675 			var view = View._focused,
       
 10676 				target = DomEvent.getRelatedTarget(event);
       
 10677 			if (view && (!target || target.nodeName === 'HTML'))
       
 10678 				handleMouseMove(view, viewToProject(view, event), event);
       
 10679 		},
       
 10680 
       
 10681 		scroll: updateFocus
       
 10682 	};
       
 10683 
       
 10684 	viewEvents[mousedown] = function(event) {
       
 10685 		var view = View._focused = getView(event),
       
 10686 			point = viewToProject(view, event);
       
 10687 		dragging = true;
       
 10688 		view._handleEvent('mousedown', point, event);
       
 10689 		if (tool = view._scope.tool)
       
 10690 			tool._handleEvent('mousedown', point, event);
       
 10691 		view.update();
       
 10692 	};
       
 10693 
       
 10694 	docEvents[mousemove] = function(event) {
       
 10695 		var view = View._focused;
       
 10696 		if (!dragging) {
       
 10697 			var target = getView(event);
       
 10698 			if (target) {
       
 10699 				if (view !== target)
       
 10700 					handleMouseMove(view, viewToProject(view, event), event);
       
 10701 				prevFocus = view;
       
 10702 				view = View._focused = tempFocus = target;
       
 10703 			} else if (tempFocus && tempFocus === view) {
       
 10704 				view = View._focused = prevFocus;
       
 10705 				updateFocus();
       
 10706 			}
       
 10707 		}
       
 10708 		if (view) {
       
 10709 			var point = viewToProject(view, event);
       
 10710 			if (dragging || view.getBounds().contains(point))
       
 10711 				tool = handleMouseMove(view, point, event);
       
 10712 		}
       
 10713 	};
       
 10714 
       
 10715 	docEvents[mouseup] = function(event) {
       
 10716 		var view = View._focused;
       
 10717 		if (!view || !dragging)
       
 10718 			return;
       
 10719 		var point = viewToProject(view, event);
       
 10720 		dragging = false;
       
 10721 		view._handleEvent('mouseup', point, event);
       
 10722 		if (tool)
       
 10723 			tool._handleEvent('mouseup', point, event);
       
 10724 		view.update();
       
 10725 	};
       
 10726 
       
 10727 	DomEvent.add(document, docEvents);
       
 10728 
       
 10729 	DomEvent.add(window, {
       
 10730 		load: updateFocus
       
 10731 	});
       
 10732 
       
 10733 	return {
       
 10734 		_viewEvents: viewEvents,
       
 10735 
       
 10736 		_handleEvent: function() {},
       
 10737 
       
 10738 		statics: {
       
 10739 			updateFocus: updateFocus
       
 10740 		}
       
 10741 	};
       
 10742 });
       
 10743 
       
 10744 var CanvasView = View.extend({
       
 10745 	_class: 'CanvasView',
       
 10746 
       
 10747 	initialize: function CanvasView(project, canvas) {
       
 10748 		if (!(canvas instanceof HTMLCanvasElement)) {
       
 10749 			var size = Size.read(arguments);
       
 10750 			if (size.isZero())
       
 10751 				throw new Error(
       
 10752 						'Cannot create CanvasView with the provided argument: '
       
 10753 						+ [].slice.call(arguments, 1));
       
 10754 			canvas = CanvasProvider.getCanvas(size);
       
 10755 		}
       
 10756 		this._context = canvas.getContext('2d');
       
 10757 		this._eventCounters = {};
       
 10758 		this._pixelRatio = 1;
       
 10759 		if (PaperScope.getAttribute(canvas, 'hidpi') !== 'off') {
       
 10760 			var deviceRatio = window.devicePixelRatio || 1,
       
 10761 				backingStoreRatio = DomElement.getPrefixed(this._context,
       
 10762 						'backingStorePixelRatio') || 1;
       
 10763 			this._pixelRatio = deviceRatio / backingStoreRatio;
       
 10764 		}
       
 10765 		View.call(this, project, canvas);
       
 10766 	},
       
 10767 
       
 10768 	_setViewSize: function(size) {
       
 10769 		var width = size.width,
       
 10770 			height = size.height,
       
 10771 			pixelRatio = this._pixelRatio,
       
 10772 			element = this._element,
       
 10773 			style = element.style;
       
 10774 		element.width = width * pixelRatio;
       
 10775 		element.height = height * pixelRatio;
       
 10776 		if (pixelRatio !== 1) {
       
 10777 			style.width = width + 'px';
       
 10778 			style.height = height + 'px';
       
 10779 			this._context.scale(pixelRatio, pixelRatio);
       
 10780 		}
       
 10781 	},
       
 10782 
       
 10783 	getPixelSize: function(size) {
       
 10784 		var ctx = this._context,
       
 10785 			prevFont = ctx.font;
       
 10786 		ctx.font = size + ' serif';
       
 10787 		size = parseFloat(ctx.font);
       
 10788 		ctx.font = prevFont;
       
 10789 		return size;
       
 10790 	},
       
 10791 
       
 10792 	getTextWidth: function(font, lines) {
       
 10793 		var ctx = this._context,
       
 10794 			prevFont = ctx.font,
       
 10795 			width = 0;
       
 10796 		ctx.font = font;
       
 10797 		for (var i = 0, l = lines.length; i < l; i++)
       
 10798 			width = Math.max(width, ctx.measureText(lines[i]).width);
       
 10799 		ctx.font = prevFont;
       
 10800 		return width;
       
 10801 	},
       
 10802 
       
 10803 	update: function() {
       
 10804 		var project = this._project;
       
 10805 		if (!project || !project._needsUpdate)
       
 10806 			return false;
       
 10807 		var ctx = this._context,
       
 10808 			size = this._viewSize;
       
 10809 		ctx.clearRect(0, 0, size.width + 1, size.height + 1);
       
 10810 		project.draw(ctx, this._matrix, this._pixelRatio);
       
 10811 		project._needsUpdate = false;
       
 10812 		return true;
       
 10813 	}
       
 10814 }, new function() {
       
 10815 
       
 10816 	var downPoint,
       
 10817 		lastPoint,
       
 10818 		overPoint,
       
 10819 		downItem,
       
 10820 		lastItem,
       
 10821 		overItem,
       
 10822 		dragItem,
       
 10823 		dblClick,
       
 10824 		clickTime;
       
 10825 
       
 10826 	function callEvent(view, type, event, point, target, lastPoint) {
       
 10827 		var item = target,
       
 10828 			mouseEvent;
       
 10829 
       
 10830 		function call(obj) {
       
 10831 			if (obj.responds(type)) {
       
 10832 				if (!mouseEvent) {
       
 10833 					mouseEvent = new MouseEvent(type, event, point, target,
       
 10834 							lastPoint ? point.subtract(lastPoint) : null);
       
 10835 				}
       
 10836 				if (obj.fire(type, mouseEvent) && mouseEvent.isStopped) {
       
 10837 					event.preventDefault();
       
 10838 					return true;
       
 10839 				}
       
 10840 			}
       
 10841 		}
       
 10842 
       
 10843 		while (item) {
       
 10844 			if (call(item))
       
 10845 				return true;
       
 10846 			item = item.getParent();
       
 10847 		}
       
 10848 		if (call(view))
       
 10849 			return true;
       
 10850 		return false;
       
 10851 	}
       
 10852 
       
 10853 	return {
       
 10854 		_handleEvent: function(type, point, event) {
       
 10855 			if (!this._eventCounters[type])
       
 10856 				return;
       
 10857 			var project = this._project,
       
 10858 				hit = project.hitTest(point, {
       
 10859 					tolerance: 0,
       
 10860 					fill: true,
       
 10861 					stroke: true
       
 10862 				}),
       
 10863 				item = hit && hit.item,
       
 10864 				stopped = false;
       
 10865 			switch (type) {
       
 10866 			case 'mousedown':
       
 10867 				stopped = callEvent(this, type, event, point, item);
       
 10868 				dblClick = lastItem == item && (Date.now() - clickTime < 300);
       
 10869 				downItem = lastItem = item;
       
 10870 				downPoint = lastPoint = overPoint = point;
       
 10871 				dragItem = !stopped && item;
       
 10872 				while (dragItem && !dragItem.responds('mousedrag'))
       
 10873 					dragItem = dragItem._parent;
       
 10874 				break;
       
 10875 			case 'mouseup':
       
 10876 				stopped = callEvent(this, type, event, point, item, downPoint);
       
 10877 				if (dragItem) {
       
 10878 					if (lastPoint && !lastPoint.equals(point))
       
 10879 						callEvent(this, 'mousedrag', event, point, dragItem,
       
 10880 								lastPoint);
       
 10881 					if (item !== dragItem) {
       
 10882 						overPoint = point;
       
 10883 						callEvent(this, 'mousemove', event, point, item,
       
 10884 								overPoint);
       
 10885 					}
       
 10886 				}
       
 10887 				if (!stopped && item && item === downItem) {
       
 10888 					clickTime = Date.now();
       
 10889 					callEvent(this, dblClick && downItem.responds('doubleclick')
       
 10890 							? 'doubleclick' : 'click', event, downPoint, item);
       
 10891 					dblClick = false;
       
 10892 				}
       
 10893 				downItem = dragItem = null;
       
 10894 				break;
       
 10895 			case 'mousemove':
       
 10896 				if (dragItem)
       
 10897 					stopped = callEvent(this, 'mousedrag', event, point,
       
 10898 							dragItem, lastPoint);
       
 10899 				if (!stopped) {
       
 10900 					if (item !== overItem)
       
 10901 						overPoint = point;
       
 10902 					stopped = callEvent(this, type, event, point, item,
       
 10903 							overPoint);
       
 10904 				}
       
 10905 				lastPoint = overPoint = point;
       
 10906 				if (item !== overItem) {
       
 10907 					callEvent(this, 'mouseleave', event, point, overItem);
       
 10908 					overItem = item;
       
 10909 					callEvent(this, 'mouseenter', event, point, item);
       
 10910 				}
       
 10911 				break;
       
 10912 			}
       
 10913 			return stopped;
       
 10914 		}
       
 10915 	};
       
 10916 });
       
 10917 
       
 10918 var Event = Base.extend({
       
 10919 	_class: 'Event',
       
 10920 
       
 10921 	initialize: function Event(event) {
       
 10922 		this.event = event;
       
 10923 	},
       
 10924 
       
 10925 	isPrevented: false,
       
 10926 	isStopped: false,
       
 10927 
       
 10928 	preventDefault: function() {
       
 10929 		this.isPrevented = true;
       
 10930 		this.event.preventDefault();
       
 10931 	},
       
 10932 
       
 10933 	stopPropagation: function() {
       
 10934 		this.isStopped = true;
       
 10935 		this.event.stopPropagation();
       
 10936 	},
       
 10937 
       
 10938 	stop: function() {
       
 10939 		this.stopPropagation();
       
 10940 		this.preventDefault();
       
 10941 	},
       
 10942 
       
 10943 	getModifiers: function() {
       
 10944 		return Key.modifiers;
       
 10945 	}
       
 10946 });
       
 10947 
       
 10948 var KeyEvent = Event.extend({
       
 10949 	_class: 'KeyEvent',
       
 10950 
       
 10951 	initialize: function KeyEvent(down, key, character, event) {
       
 10952 		Event.call(this, event);
       
 10953 		this.type = down ? 'keydown' : 'keyup';
       
 10954 		this.key = key;
       
 10955 		this.character = character;
       
 10956 	},
       
 10957 
       
 10958 	toString: function() {
       
 10959 		return "{ type: '" + this.type
       
 10960 				+ "', key: '" + this.key
       
 10961 				+ "', character: '" + this.character
       
 10962 				+ "', modifiers: " + this.getModifiers()
       
 10963 				+ " }";
       
 10964 	}
       
 10965 });
       
 10966 
       
 10967 var Key = new function() {
       
 10968 
       
 10969 	var specialKeys = {
       
 10970 		8: 'backspace',
       
 10971 		9: 'tab',
       
 10972 		13: 'enter',
       
 10973 		16: 'shift',
       
 10974 		17: 'control',
       
 10975 		18: 'option',
       
 10976 		19: 'pause',
       
 10977 		20: 'caps-lock',
       
 10978 		27: 'escape',
       
 10979 		32: 'space',
       
 10980 		35: 'end',
       
 10981 		36: 'home',
       
 10982 		37: 'left',
       
 10983 		38: 'up',
       
 10984 		39: 'right',
       
 10985 		40: 'down',
       
 10986 		46: 'delete',
       
 10987 		91: 'command',
       
 10988 		93: 'command',
       
 10989 		224: 'command'
       
 10990 	},
       
 10991 
       
 10992 	specialChars = {
       
 10993 		9: true,
       
 10994 		13: true,
       
 10995 		32: true
       
 10996 	},
       
 10997 
       
 10998 	modifiers = new Base({
       
 10999 		shift: false,
       
 11000 		control: false,
       
 11001 		option: false,
       
 11002 		command: false,
       
 11003 		capsLock: false,
       
 11004 		space: false
       
 11005 	}),
       
 11006 
       
 11007 	charCodeMap = {},
       
 11008 	keyMap = {},
       
 11009 	downCode;
       
 11010 
       
 11011 	function handleKey(down, keyCode, charCode, event) {
       
 11012 		var character = charCode ? String.fromCharCode(charCode) : '',
       
 11013 			specialKey = specialKeys[keyCode],
       
 11014 			key = specialKey || character.toLowerCase(),
       
 11015 			type = down ? 'keydown' : 'keyup',
       
 11016 			view = View._focused,
       
 11017 			scope = view && view.isVisible() && view._scope,
       
 11018 			tool = scope && scope.tool,
       
 11019 			name;
       
 11020 		keyMap[key] = down;
       
 11021 		if (specialKey && (name = Base.camelize(specialKey)) in modifiers)
       
 11022 			modifiers[name] = down;
       
 11023 		if (down) {
       
 11024 			charCodeMap[keyCode] = charCode;
       
 11025 		} else {
       
 11026 			delete charCodeMap[keyCode];
       
 11027 		}
       
 11028 		if (tool && tool.responds(type)) {
       
 11029 			paper = scope;
       
 11030 			tool.fire(type, new KeyEvent(down, key, character, event));
       
 11031 			if (view)
       
 11032 				view.update();
       
 11033 		}
       
 11034 	}
       
 11035 
       
 11036 	DomEvent.add(document, {
       
 11037 		keydown: function(event) {
       
 11038 			var code = event.which || event.keyCode;
       
 11039 			if (code in specialKeys || modifiers.command) {
       
 11040 				handleKey(true, code,
       
 11041 						code in specialChars || modifiers.command ? code : 0,
       
 11042 						event);
       
 11043 			} else {
       
 11044 				downCode = code;
       
 11045 			}
       
 11046 		},
       
 11047 
       
 11048 		keypress: function(event) {
       
 11049 			if (downCode != null) {
       
 11050 				handleKey(true, downCode, event.which || event.keyCode, event);
       
 11051 				downCode = null;
       
 11052 			}
       
 11053 		},
       
 11054 
       
 11055 		keyup: function(event) {
       
 11056 			var code = event.which || event.keyCode;
       
 11057 			if (code in charCodeMap)
       
 11058 				handleKey(false, code, charCodeMap[code], event);
       
 11059 		}
       
 11060 	});
       
 11061 
       
 11062 	DomEvent.add(window, {
       
 11063 		blur: function(event) {
       
 11064 			for (var code in charCodeMap)
       
 11065 				handleKey(false, code, charCodeMap[code], event);
       
 11066 		}
       
 11067 	});
       
 11068 
       
 11069 	return {
       
 11070 		modifiers: modifiers,
       
 11071 
       
 11072 		isDown: function(key) {
       
 11073 			return !!keyMap[key];
       
 11074 		}
       
 11075 	};
       
 11076 };
       
 11077 
       
 11078 var MouseEvent = Event.extend({
       
 11079 	_class: 'MouseEvent',
       
 11080 
       
 11081 	initialize: function MouseEvent(type, event, point, target, delta) {
       
 11082 		Event.call(this, event);
       
 11083 		this.type = type;
       
 11084 		this.point = point;
       
 11085 		this.target = target;
       
 11086 		this.delta = delta;
       
 11087 	},
       
 11088 
       
 11089 	toString: function() {
       
 11090 		return "{ type: '" + this.type
       
 11091 				+ "', point: " + this.point
       
 11092 				+ ', target: ' + this.target
       
 11093 				+ (this.delta ? ', delta: ' + this.delta : '')
       
 11094 				+ ', modifiers: ' + this.getModifiers()
       
 11095 				+ ' }';
       
 11096 	}
       
 11097 });
       
 11098 
       
 11099  Base.extend(Callback, {
       
 11100 	_class: 'Palette',
       
 11101 	_events: [ 'onChange' ],
       
 11102 
       
 11103 	initialize: function Palette(title, components, values) {
       
 11104 		var parent = DomElement.find('.palettejs-panel')
       
 11105 			|| DomElement.find('body').appendChild(
       
 11106 				DomElement.create('div', { 'class': 'palettejs-panel' }));
       
 11107 		this._element = parent.appendChild(
       
 11108 			DomElement.create('table', { 'class': 'palettejs-pane' }));
       
 11109 		this._title = title;
       
 11110 		if (!values)
       
 11111 			values = {};
       
 11112 		for (var name in (this.components = components)) {
       
 11113 			var component = components[name];
       
 11114 			if (!(component instanceof Component)) {
       
 11115 				if (component.value == null)
       
 11116 					component.value = values[name];
       
 11117 				component.name = name;
       
 11118 				component = components[name] = new Component(component);
       
 11119 			}
       
 11120 			this._element.appendChild(component._element);
       
 11121 			component._palette = this;
       
 11122 			if (values[name] === undefined)
       
 11123 				values[name] = component.value;
       
 11124 		}
       
 11125 		this.values = Base.each(values, function(value, name) {
       
 11126 			var component = components[name];
       
 11127 			if (component) {
       
 11128 				Base.define(values, name, {
       
 11129 					enumerable: true,
       
 11130 					configurable: true,
       
 11131 					get: function() {
       
 11132 						return component._value;
       
 11133 					},
       
 11134 					set: function(val) {
       
 11135 						component.setValue(val);
       
 11136 					}
       
 11137 				});
       
 11138 			}
       
 11139 		});
       
 11140 		if (window.paper)
       
 11141 			paper.palettes.push(this);
       
 11142 	},
       
 11143 
       
 11144 	reset: function() {
       
 11145 		for (var i in this.components)
       
 11146 			this.components[i].reset();
       
 11147 	},
       
 11148 
       
 11149 	remove: function() {
       
 11150 		DomElement.remove(this._element);
       
 11151 	}
       
 11152 });
       
 11153 
       
 11154 var Component = Base.extend(Callback, {
       
 11155 	_class: 'Component',
       
 11156 	_events: [ 'onChange', 'onClick' ],
       
 11157 
       
 11158 	_types: {
       
 11159 		'boolean': {
       
 11160 			type: 'checkbox',
       
 11161 			value: 'checked'
       
 11162 		},
       
 11163 
       
 11164 		string: {
       
 11165 			type: 'text'
       
 11166 		},
       
 11167 
       
 11168 		number: {
       
 11169 			type: 'number',
       
 11170 			number: true
       
 11171 		},
       
 11172 
       
 11173 		button: {
       
 11174 			type: 'button'
       
 11175 		},
       
 11176 
       
 11177 		text: {
       
 11178 			tag: 'div',
       
 11179 			value: 'text'
       
 11180 		},
       
 11181 
       
 11182 		slider: {
       
 11183 			type: 'range',
       
 11184 			number: true
       
 11185 		},
       
 11186 
       
 11187 		list: {
       
 11188 			tag: 'select',
       
 11189 
       
 11190 			setOptions: function() {
       
 11191 				DomElement.removeChildren(this._input);
       
 11192 				DomElement.create(Base.each(this._options, function(option) {
       
 11193 					this.push('option', { value: option, text: option });
       
 11194 				}, []), this._input);
       
 11195 			}
       
 11196 		},
       
 11197 
       
 11198 		color: {
       
 11199 			type: 'color',
       
 11200 
       
 11201 			getValue: function(value) {
       
 11202 				return new Color(value);
       
 11203 			},
       
 11204 
       
 11205 			setValue: function(value) {
       
 11206 				return new Color(value).toCSS(
       
 11207 						DomElement.get(this._input, 'type') === 'color');
       
 11208 			}
       
 11209 		}
       
 11210 	},
       
 11211 
       
 11212 	initialize: function Component(obj) {
       
 11213 		this._id = Component._id = (Component._id || 0) + 1;
       
 11214 		this._type = obj.type in this._types
       
 11215 			? obj.type
       
 11216 			: 'options' in obj
       
 11217 				? 'list'
       
 11218 				: 'onClick' in obj
       
 11219 					? 'button'
       
 11220 					: typeof obj.value;
       
 11221 		this._meta = this._types[this._type] || { type: this._type };
       
 11222 		var that = this,
       
 11223 			id = 'component-' + this._id;
       
 11224 		this._dontFire = true;
       
 11225 		this._input = DomElement.create(this._meta.tag || 'input', {
       
 11226 			id: id,
       
 11227 			type: this._meta.type,
       
 11228 			events: {
       
 11229 				change: function() {
       
 11230 					that.setValue(
       
 11231 						DomElement.get(this, that._meta.value || 'value'));
       
 11232 				},
       
 11233 				click: function() {
       
 11234 					that.fire('click');
       
 11235 				}
       
 11236 			}
       
 11237 		});
       
 11238 		this.attach('change', function(value) {
       
 11239 			if (!this._dontFire)
       
 11240 				this._palette.fire('change', this, this.name, value);
       
 11241 		});
       
 11242 		this._element = DomElement.create('tr', [
       
 11243 			'td', [this._label = DomElement.create('label', { 'for': id })],
       
 11244 			'td', [this._input]
       
 11245 		]);
       
 11246 		Base.each(obj, function(value, key) {
       
 11247 			this[key] = value;
       
 11248 		}, this);
       
 11249 		this._defaultValue = this._value;
       
 11250 		this._dontFire = false;
       
 11251 	},
       
 11252 
       
 11253 	getType: function() {
       
 11254 		return this._type;
       
 11255 	},
       
 11256 
       
 11257 	getLabel: function() {
       
 11258 		return this.__label;
       
 11259 	},
       
 11260 
       
 11261 	setLabel: function(label) {
       
 11262 		this.__label = label;
       
 11263 		DomElement.set(this._label, 'text', label + ':');
       
 11264 	},
       
 11265 
       
 11266 	getOptions: function() {
       
 11267 		return this._options;
       
 11268 	},
       
 11269 
       
 11270 	setOptions: function(options) {
       
 11271 		this._options = options;
       
 11272 		var setOptions = this._meta.setOptions;
       
 11273 		if (setOptions)
       
 11274 			setOptions.call(this);
       
 11275 	},
       
 11276 
       
 11277 	getValue: function() {
       
 11278 		var value = this._value,
       
 11279 			getValue = this._meta.getValue;
       
 11280 		return getValue ? getValue.call(this, value) : value;
       
 11281 	},
       
 11282 
       
 11283 	setValue: function(value) {
       
 11284 		var key = this._meta.value || 'value',
       
 11285 			setValue = this._meta.setValue;
       
 11286 		if (setValue)
       
 11287 			value = setValue.call(this, value);
       
 11288 		DomElement.set(this._input, key, value);
       
 11289 		value = DomElement.get(this._input, key);
       
 11290 		if (this._meta.number)
       
 11291 			value = parseFloat(value, 10);
       
 11292 		if (this._value !== value) {
       
 11293 			this._value = value;
       
 11294 			if (!this._dontFire)
       
 11295 				this.fire('change', this.getValue());
       
 11296 		}
       
 11297 	},
       
 11298 
       
 11299 	getRange: function() {
       
 11300 		return [parseFloat(DomElement.get(this._input, 'min')),
       
 11301 				parseFloat(DomElement.get(this._input, 'max'))];
       
 11302 	},
       
 11303 
       
 11304 	setRange: function(min, max) {
       
 11305 		var range = Array.isArray(min) ? min : [min, max];
       
 11306 		DomElement.set(this._input, { min: range[0], max: range[1] });
       
 11307 	},
       
 11308 
       
 11309 	getMin: function() {
       
 11310 		return this.getRange()[0];
       
 11311 	},
       
 11312 
       
 11313 	setMin: function(min) {
       
 11314 		this.setRange(min, this.getMax());
       
 11315 	},
       
 11316 
       
 11317 	getMax: function() {
       
 11318 		return this.getRange()[1];
       
 11319 	},
       
 11320 
       
 11321 	setMax: function(max) {
       
 11322 		this.setRange(this.getMin(), max);
       
 11323 	},
       
 11324 
       
 11325 	getStep: function() {
       
 11326 		return parseFloat(DomElement.get(this._input, 'step'));
       
 11327 	},
       
 11328 
       
 11329 	setStep: function(step) {
       
 11330 		DomElement.set(this._input, 'step', step);
       
 11331 	},
       
 11332 
       
 11333 	reset: function() {
       
 11334 		this.setValue(this._defaultValue);
       
 11335 	}
       
 11336 });
       
 11337 
       
 11338 var ToolEvent = Event.extend({
       
 11339 	_class: 'ToolEvent',
       
 11340 	_item: null,
       
 11341 
       
 11342 	initialize: function ToolEvent(tool, type, event) {
       
 11343 		this.tool = tool;
       
 11344 		this.type = type;
       
 11345 		this.event = event;
       
 11346 	},
       
 11347 
       
 11348 	_choosePoint: function(point, toolPoint) {
       
 11349 		return point ? point : toolPoint ? toolPoint.clone() : null;
       
 11350 	},
       
 11351 
       
 11352 	getPoint: function() {
       
 11353 		return this._choosePoint(this._point, this.tool._point);
       
 11354 	},
       
 11355 
       
 11356 	setPoint: function(point) {
       
 11357 		this._point = point;
       
 11358 	},
       
 11359 
       
 11360 	getLastPoint: function() {
       
 11361 		return this._choosePoint(this._lastPoint, this.tool._lastPoint);
       
 11362 	},
       
 11363 
       
 11364 	setLastPoint: function(lastPoint) {
       
 11365 		this._lastPoint = lastPoint;
       
 11366 	},
       
 11367 
       
 11368 	getDownPoint: function() {
       
 11369 		return this._choosePoint(this._downPoint, this.tool._downPoint);
       
 11370 	},
       
 11371 
       
 11372 	setDownPoint: function(downPoint) {
       
 11373 		this._downPoint = downPoint;
       
 11374 	},
       
 11375 
       
 11376 	getMiddlePoint: function() {
       
 11377 		if (!this._middlePoint && this.tool._lastPoint) {
       
 11378 			return this.tool._point.add(this.tool._lastPoint).divide(2);
       
 11379 		}
       
 11380 		return this._middlePoint;
       
 11381 	},
       
 11382 
       
 11383 	setMiddlePoint: function(middlePoint) {
       
 11384 		this._middlePoint = middlePoint;
       
 11385 	},
       
 11386 
       
 11387 	getDelta: function() {
       
 11388 		return !this._delta && this.tool._lastPoint
       
 11389 				? this.tool._point.subtract(this.tool._lastPoint)
       
 11390 				: this._delta;
       
 11391 	},
       
 11392 
       
 11393 	setDelta: function(delta) {
       
 11394 		this._delta = delta;
       
 11395 	},
       
 11396 
       
 11397 	getCount: function() {
       
 11398 		return /^mouse(down|up)$/.test(this.type)
       
 11399 				? this.tool._downCount
       
 11400 				: this.tool._count;
       
 11401 	},
       
 11402 
       
 11403 	setCount: function(count) {
       
 11404 		this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
       
 11405 			= count;
       
 11406 	},
       
 11407 
       
 11408 	getItem: function() {
       
 11409 		if (!this._item) {
       
 11410 			var result = this.tool._scope.project.hitTest(this.getPoint());
       
 11411 			if (result) {
       
 11412 				var item = result.item,
       
 11413 					parent = item._parent;
       
 11414 				while (/^(Group|CompoundPath)$/.test(parent._class)) {
       
 11415 					item = parent;
       
 11416 					parent = parent._parent;
       
 11417 				}
       
 11418 				this._item = item;
       
 11419 			}
       
 11420 		}
       
 11421 		return this._item;
       
 11422 	},
       
 11423 
       
 11424 	setItem: function(item) {
       
 11425 		this._item = item;
       
 11426 	},
       
 11427 
       
 11428 	toString: function() {
       
 11429 		return '{ type: ' + this.type
       
 11430 				+ ', point: ' + this.getPoint()
       
 11431 				+ ', count: ' + this.getCount()
       
 11432 				+ ', modifiers: ' + this.getModifiers()
       
 11433 				+ ' }';
       
 11434 	}
       
 11435 });
       
 11436 
       
 11437 var Tool = PaperScopeItem.extend({
       
 11438 	_class: 'Tool',
       
 11439 	_list: 'tools',
       
 11440 	_reference: 'tool',
       
 11441 	_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
       
 11442 			'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
       
 11443 			'onKeyDown', 'onKeyUp' ],
       
 11444 
       
 11445 	initialize: function Tool(props) {
       
 11446 		PaperScopeItem.call(this);
       
 11447 		this._firstMove = true;
       
 11448 		this._count = 0;
       
 11449 		this._downCount = 0;
       
 11450 		this._set(props);
       
 11451 	},
       
 11452 
       
 11453 	getMinDistance: function() {
       
 11454 		return this._minDistance;
       
 11455 	},
       
 11456 
       
 11457 	setMinDistance: function(minDistance) {
       
 11458 		this._minDistance = minDistance;
       
 11459 		if (this._minDistance != null && this._maxDistance != null
       
 11460 				&& this._minDistance > this._maxDistance) {
       
 11461 			this._maxDistance = this._minDistance;
       
 11462 		}
       
 11463 	},
       
 11464 
       
 11465 	getMaxDistance: function() {
       
 11466 		return this._maxDistance;
       
 11467 	},
       
 11468 
       
 11469 	setMaxDistance: function(maxDistance) {
       
 11470 		this._maxDistance = maxDistance;
       
 11471 		if (this._minDistance != null && this._maxDistance != null
       
 11472 				&& this._maxDistance < this._minDistance) {
       
 11473 			this._minDistance = maxDistance;
       
 11474 		}
       
 11475 	},
       
 11476 
       
 11477 	getFixedDistance: function() {
       
 11478 		return this._minDistance == this._maxDistance
       
 11479 			? this._minDistance : null;
       
 11480 	},
       
 11481 
       
 11482 	setFixedDistance: function(distance) {
       
 11483 		this._minDistance = distance;
       
 11484 		this._maxDistance = distance;
       
 11485 	},
       
 11486 
       
 11487 	_updateEvent: function(type, point, minDistance, maxDistance, start,
       
 11488 			needsChange, matchMaxDistance) {
       
 11489 		if (!start) {
       
 11490 			if (minDistance != null || maxDistance != null) {
       
 11491 				var minDist = minDistance != null ? minDistance : 0,
       
 11492 					vector = point.subtract(this._point),
       
 11493 					distance = vector.getLength();
       
 11494 				if (distance < minDist)
       
 11495 					return false;
       
 11496 				var maxDist = maxDistance != null ? maxDistance : 0;
       
 11497 				if (maxDist != 0) {
       
 11498 					if (distance > maxDist) {
       
 11499 						point = this._point.add(vector.normalize(maxDist));
       
 11500 					} else if (matchMaxDistance) {
       
 11501 						return false;
       
 11502 					}
       
 11503 				}
       
 11504 			}
       
 11505 			if (needsChange && point.equals(this._point))
       
 11506 				return false;
       
 11507 		}
       
 11508 		this._lastPoint = start && type == 'mousemove' ? point : this._point;
       
 11509 		this._point = point;
       
 11510 		switch (type) {
       
 11511 		case 'mousedown':
       
 11512 			this._lastPoint = this._downPoint;
       
 11513 			this._downPoint = this._point;
       
 11514 			this._downCount++;
       
 11515 			break;
       
 11516 		case 'mouseup':
       
 11517 			this._lastPoint = this._downPoint;
       
 11518 			break;
       
 11519 		}
       
 11520 		this._count = start ? 0 : this._count + 1;
       
 11521 		return true;
       
 11522 	},
       
 11523 
       
 11524 	_fireEvent: function(type, event) {
       
 11525 		var sets = paper.project._removeSets;
       
 11526 		if (sets) {
       
 11527 			if (type === 'mouseup')
       
 11528 				sets.mousedrag = null;
       
 11529 			var set = sets[type];
       
 11530 			if (set) {
       
 11531 				for (var id in set) {
       
 11532 					var item = set[id];
       
 11533 					for (var key in sets) {
       
 11534 						var other = sets[key];
       
 11535 						if (other && other != set)
       
 11536 							delete other[item._id];
       
 11537 					}
       
 11538 					item.remove();
       
 11539 				}
       
 11540 				sets[type] = null;
       
 11541 			}
       
 11542 		}
       
 11543 		return this.responds(type)
       
 11544 				&& this.fire(type, new ToolEvent(this, type, event));
       
 11545 	},
       
 11546 
       
 11547 	_handleEvent: function(type, point, event) {
       
 11548 		paper = this._scope;
       
 11549 		var called = false;
       
 11550 		switch (type) {
       
 11551 		case 'mousedown':
       
 11552 			this._updateEvent(type, point, null, null, true, false, false);
       
 11553 			called = this._fireEvent(type, event);
       
 11554 			break;
       
 11555 		case 'mousedrag':
       
 11556 			var needsChange = false,
       
 11557 				matchMaxDistance = false;
       
 11558 			while (this._updateEvent(type, point, this.minDistance,
       
 11559 					this.maxDistance, false, needsChange, matchMaxDistance)) {
       
 11560 				called = this._fireEvent(type, event) || called;
       
 11561 				needsChange = true;
       
 11562 				matchMaxDistance = true;
       
 11563 			}
       
 11564 			break;
       
 11565 		case 'mouseup':
       
 11566 			if (!point.equals(this._point)
       
 11567 					&& this._updateEvent('mousedrag', point, this.minDistance,
       
 11568 							this.maxDistance, false, false, false)) {
       
 11569 				called = this._fireEvent('mousedrag', event);
       
 11570 			}
       
 11571 			this._updateEvent(type, point, null, this.maxDistance, false,
       
 11572 					false, false);
       
 11573 			called = this._fireEvent(type, event) || called;
       
 11574 			this._updateEvent(type, point, null, null, true, false, false);
       
 11575 			this._firstMove = true;
       
 11576 			break;
       
 11577 		case 'mousemove':
       
 11578 			while (this._updateEvent(type, point, this.minDistance,
       
 11579 					this.maxDistance, this._firstMove, true, false)) {
       
 11580 				called = this._fireEvent(type, event) || called;
       
 11581 				this._firstMove = false;
       
 11582 			}
       
 11583 			break;
       
 11584 		}
       
 11585 		if (called)
       
 11586 			event.preventDefault();
       
 11587 		return called;
       
 11588 	}
       
 11589 
       
 11590 });
       
 11591 
       
 11592 var Http = {
       
 11593 	request: function(method, url, callback) {
       
 11594 		var xhr = new (window.ActiveXObject || XMLHttpRequest)(
       
 11595 					'Microsoft.XMLHTTP');
       
 11596 		xhr.open(method.toUpperCase(), url, true);
       
 11597 		if ('overrideMimeType' in xhr)
       
 11598 			xhr.overrideMimeType('text/plain');
       
 11599 		xhr.onreadystatechange = function() {
       
 11600 			if (xhr.readyState === 4) {
       
 11601 				var status = xhr.status;
       
 11602 				if (status === 0 || status === 200) {
       
 11603 					callback.call(xhr, xhr.responseText);
       
 11604 				} else {
       
 11605 					throw new Error('Could not load ' + url + ' (Error '
       
 11606 							+ status + ')');
       
 11607 				}
       
 11608 			}
       
 11609 		};
       
 11610 		return xhr.send(null);
       
 11611 	}
       
 11612 };
       
 11613 
       
 11614 var CanvasProvider = {
       
 11615 	canvases: [],
       
 11616 
       
 11617 	getCanvas: function(width, height) {
       
 11618 		var canvas,
       
 11619 			clear = true;
       
 11620 		if (typeof width === 'object') {
       
 11621 			height = width.height;
       
 11622 			width = width.width;
       
 11623 		}
       
 11624 		if (this.canvases.length) {
       
 11625 			canvas = this.canvases.pop();
       
 11626 		} else {
       
 11627 			canvas = document.createElement('canvas');
       
 11628 		}
       
 11629 		var ctx = canvas.getContext('2d');
       
 11630 		if (canvas.width === width && canvas.height === height) {
       
 11631 			if (clear)
       
 11632 				ctx.clearRect(0, 0, width + 1, height + 1);
       
 11633 		} else {
       
 11634 			canvas.width = width;
       
 11635 			canvas.height = height;
       
 11636 		}
       
 11637 		ctx.save();
       
 11638 		return canvas;
       
 11639 	},
       
 11640 
       
 11641 	getContext: function(width, height) {
       
 11642 		return this.getCanvas(width, height).getContext('2d');
       
 11643 	},
       
 11644 
       
 11645 	release: function(obj) {
       
 11646 		var canvas = obj.canvas ? obj.canvas : obj;
       
 11647 		canvas.getContext('2d').restore();
       
 11648 		this.canvases.push(canvas);
       
 11649 	}
       
 11650 };
       
 11651 
       
 11652 var BlendMode = new function() {
       
 11653 	var min = Math.min,
       
 11654 		max = Math.max,
       
 11655 		abs = Math.abs,
       
 11656 		sr, sg, sb, sa,
       
 11657 		br, bg, bb, ba,
       
 11658 		dr, dg, db;
       
 11659 
       
 11660 	function getLum(r, g, b) {
       
 11661 		return 0.2989 * r + 0.587 * g + 0.114 * b;
       
 11662 	}
       
 11663 
       
 11664 	function setLum(r, g, b, l) {
       
 11665 		var d = l - getLum(r, g, b);
       
 11666 		dr = r + d;
       
 11667 		dg = g + d;
       
 11668 		db = b + d;
       
 11669 		var l = getLum(dr, dg, db),
       
 11670 			mn = min(dr, dg, db),
       
 11671 			mx = max(dr, dg, db);
       
 11672 		if (mn < 0) {
       
 11673 			var lmn = l - mn;
       
 11674 			dr = l + (dr - l) * l / lmn;
       
 11675 			dg = l + (dg - l) * l / lmn;
       
 11676 			db = l + (db - l) * l / lmn;
       
 11677 		}
       
 11678 		if (mx > 255) {
       
 11679 			var ln = 255 - l,
       
 11680 				mxl = mx - l;
       
 11681 			dr = l + (dr - l) * ln / mxl;
       
 11682 			dg = l + (dg - l) * ln / mxl;
       
 11683 			db = l + (db - l) * ln / mxl;
       
 11684 		}
       
 11685 	}
       
 11686 
       
 11687 	function getSat(r, g, b) {
       
 11688 		return max(r, g, b) - min(r, g, b);
       
 11689 	}
       
 11690 
       
 11691 	function setSat(r, g, b, s) {
       
 11692 		var col = [r, g, b],
       
 11693 			mx = max(r, g, b),
       
 11694 			mn = min(r, g, b),
       
 11695 			md;
       
 11696 		mn = mn === r ? 0 : mn === g ? 1 : 2;
       
 11697 		mx = mx === r ? 0 : mx === g ? 1 : 2;
       
 11698 		md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;
       
 11699 		if (col[mx] > col[mn]) {
       
 11700 			col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]);
       
 11701 			col[mx] = s;
       
 11702 		} else {
       
 11703 			col[md] = col[mx] = 0;
       
 11704 		}
       
 11705 		col[mn] = 0;
       
 11706 		dr = col[0];
       
 11707 		dg = col[1];
       
 11708 		db = col[2];
       
 11709 	}
       
 11710 
       
 11711 	var modes = {
       
 11712 		multiply: function() {
       
 11713 			dr = br * sr / 255;
       
 11714 			dg = bg * sg / 255;
       
 11715 			db = bb * sb / 255;
       
 11716 		},
       
 11717 
       
 11718 		screen: function() {
       
 11719 			dr = br + sr - (br * sr / 255);
       
 11720 			dg = bg + sg - (bg * sg / 255);
       
 11721 			db = bb + sb - (bb * sb / 255);
       
 11722 		},
       
 11723 
       
 11724 		overlay: function() {
       
 11725 			dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255;
       
 11726 			dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255;
       
 11727 			db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255;
       
 11728 		},
       
 11729 
       
 11730 		'soft-light': function() {
       
 11731 			var t = sr * br / 255;
       
 11732 			dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255;
       
 11733 			t = sg * bg / 255;
       
 11734 			dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255;
       
 11735 			t = sb * bb / 255;
       
 11736 			db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255;
       
 11737 		},
       
 11738 
       
 11739 		'hard-light': function() {
       
 11740 			dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255;
       
 11741 			dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255;
       
 11742 			db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255;
       
 11743 		},
       
 11744 
       
 11745 		'color-dodge': function() {
       
 11746 			dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr));
       
 11747 			dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg));
       
 11748 			db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb));
       
 11749 		},
       
 11750 
       
 11751 		'color-burn': function() {
       
 11752 			dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr);
       
 11753 			dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg);
       
 11754 			db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb);
       
 11755 		},
       
 11756 
       
 11757 		darken: function() {
       
 11758 			dr = br < sr ? br : sr;
       
 11759 			dg = bg < sg ? bg : sg;
       
 11760 			db = bb < sb ? bb : sb;
       
 11761 		},
       
 11762 
       
 11763 		lighten: function() {
       
 11764 			dr = br > sr ? br : sr;
       
 11765 			dg = bg > sg ? bg : sg;
       
 11766 			db = bb > sb ? bb : sb;
       
 11767 		},
       
 11768 
       
 11769 		difference: function() {
       
 11770 			dr = br - sr;
       
 11771 			if (dr < 0)
       
 11772 				dr = -dr;
       
 11773 			dg = bg - sg;
       
 11774 			if (dg < 0)
       
 11775 				dg = -dg;
       
 11776 			db = bb - sb;
       
 11777 			if (db < 0)
       
 11778 				db = -db;
       
 11779 		},
       
 11780 
       
 11781 		exclusion: function() {
       
 11782 			dr = br + sr * (255 - br - br) / 255;
       
 11783 			dg = bg + sg * (255 - bg - bg) / 255;
       
 11784 			db = bb + sb * (255 - bb - bb) / 255;
       
 11785 		},
       
 11786 
       
 11787 		hue: function() {
       
 11788 			setSat(sr, sg, sb, getSat(br, bg, bb));
       
 11789 			setLum(dr, dg, db, getLum(br, bg, bb));
       
 11790 		},
       
 11791 
       
 11792 		saturation: function() {
       
 11793 			setSat(br, bg, bb, getSat(sr, sg, sb));
       
 11794 			setLum(dr, dg, db, getLum(br, bg, bb));
       
 11795 		},
       
 11796 
       
 11797 		luminosity: function() {
       
 11798 			setLum(br, bg, bb, getLum(sr, sg, sb));
       
 11799 		},
       
 11800 
       
 11801 		color: function() {
       
 11802 			setLum(sr, sg, sb, getLum(br, bg, bb));
       
 11803 		},
       
 11804 
       
 11805 		add: function() {
       
 11806 			dr = min(br + sr, 255);
       
 11807 			dg = min(bg + sg, 255);
       
 11808 			db = min(bb + sb, 255);
       
 11809 		},
       
 11810 
       
 11811 		subtract: function() {
       
 11812 			dr = max(br - sr, 0);
       
 11813 			dg = max(bg - sg, 0);
       
 11814 			db = max(bb - sb, 0);
       
 11815 		},
       
 11816 
       
 11817 		average: function() {
       
 11818 			dr = (br + sr) / 2;
       
 11819 			dg = (bg + sg) / 2;
       
 11820 			db = (bb + sb) / 2;
       
 11821 		},
       
 11822 
       
 11823 		negation: function() {
       
 11824 			dr = 255 - abs(255 - sr - br);
       
 11825 			dg = 255 - abs(255 - sg - bg);
       
 11826 			db = 255 - abs(255 - sb - bb);
       
 11827 		}
       
 11828 	};
       
 11829 
       
 11830 	var nativeModes = this.nativeModes = Base.each([
       
 11831 		'source-over', 'source-in', 'source-out', 'source-atop',
       
 11832 		'destination-over', 'destination-in', 'destination-out',
       
 11833 		'destination-atop', 'lighter', 'darker', 'copy', 'xor'
       
 11834 	], function(mode) {
       
 11835 		this[mode] = true;
       
 11836 	}, {});
       
 11837 
       
 11838 	var ctx = CanvasProvider.getContext(1, 1);
       
 11839 	Base.each(modes, function(func, mode) {
       
 11840 		var darken = mode === 'darken',
       
 11841 			ok = false;
       
 11842 		ctx.save();
       
 11843 		try {
       
 11844 			ctx.fillStyle = darken ? '#300' : '#a00';
       
 11845 			ctx.fillRect(0, 0, 1, 1);
       
 11846 			ctx.globalCompositeOperation = mode;
       
 11847 			if (ctx.globalCompositeOperation === mode) {
       
 11848 				ctx.fillStyle = darken ? '#a00' : '#300';
       
 11849 				ctx.fillRect(0, 0, 1, 1);
       
 11850 				ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51;
       
 11851 			}
       
 11852 		} catch (e) {}
       
 11853 		ctx.restore();
       
 11854 		nativeModes[mode] = ok;
       
 11855 	});
       
 11856 	CanvasProvider.release(ctx);
       
 11857 
       
 11858 	this.process = function(mode, srcContext, dstContext, alpha, offset) {
       
 11859 		var srcCanvas = srcContext.canvas,
       
 11860 			normal = mode === 'normal';
       
 11861 		if (normal || nativeModes[mode]) {
       
 11862 			dstContext.save();
       
 11863 			dstContext.setTransform(1, 0, 0, 1, 0, 0);
       
 11864 			dstContext.globalAlpha = alpha;
       
 11865 			if (!normal)
       
 11866 				dstContext.globalCompositeOperation = mode;
       
 11867 			dstContext.drawImage(srcCanvas, offset.x, offset.y);
       
 11868 			dstContext.restore();
       
 11869 		} else {
       
 11870 			var process = modes[mode];
       
 11871 			if (!process)
       
 11872 				return;
       
 11873 			var dstData = dstContext.getImageData(offset.x, offset.y,
       
 11874 					srcCanvas.width, srcCanvas.height),
       
 11875 				dst = dstData.data,
       
 11876 				src = srcContext.getImageData(0, 0,
       
 11877 					srcCanvas.width, srcCanvas.height).data;
       
 11878 			for (var i = 0, l = dst.length; i < l; i += 4) {
       
 11879 				sr = src[i];
       
 11880 				br = dst[i];
       
 11881 				sg = src[i + 1];
       
 11882 				bg = dst[i + 1];
       
 11883 				sb = src[i + 2];
       
 11884 				bb = dst[i + 2];
       
 11885 				sa = src[i + 3];
       
 11886 				ba = dst[i + 3];
       
 11887 				process();
       
 11888 				var a1 = sa * alpha / 255,
       
 11889 					a2 = 1 - a1;
       
 11890 				dst[i] = a1 * dr + a2 * br;
       
 11891 				dst[i + 1] = a1 * dg + a2 * bg;
       
 11892 				dst[i + 2] = a1 * db + a2 * bb;
       
 11893 				dst[i + 3] = sa * alpha + a2 * ba;
       
 11894 			}
       
 11895 			dstContext.putImageData(dstData, offset.x, offset.y);
       
 11896 		}
       
 11897 	};
       
 11898 };
       
 11899 
       
 11900 var SVGStyles = Base.each({
       
 11901 	fillColor: ['fill', 'color'],
       
 11902 	strokeColor: ['stroke', 'color'],
       
 11903 	strokeWidth: ['stroke-width', 'number'],
       
 11904 	strokeCap: ['stroke-linecap', 'string'],
       
 11905 	strokeJoin: ['stroke-linejoin', 'string'],
       
 11906 	strokeScaling: ['vector-effect', 'lookup', {
       
 11907 		true: 'none',
       
 11908 		false: 'non-scaling-stroke'
       
 11909 	}, function(item, value) {
       
 11910 		return !value
       
 11911 				&& (item instanceof PathItem
       
 11912 					|| item instanceof Shape
       
 11913 					|| item instanceof TextItem);
       
 11914 	}],
       
 11915 	miterLimit: ['stroke-miterlimit', 'number'],
       
 11916 	dashArray: ['stroke-dasharray', 'array'],
       
 11917 	dashOffset: ['stroke-dashoffset', 'number'],
       
 11918 	fontFamily: ['font-family', 'string'],
       
 11919 	fontWeight: ['font-weight', 'string'],
       
 11920 	fontSize: ['font-size', 'number'],
       
 11921 	justification: ['text-anchor', 'lookup', {
       
 11922 		left: 'start',
       
 11923 		center: 'middle',
       
 11924 		right: 'end'
       
 11925 	}],
       
 11926 	opacity: ['opacity', 'number'],
       
 11927 	blendMode: ['mix-blend-mode', 'string']
       
 11928 }, function(entry, key) {
       
 11929 	var part = Base.capitalize(key),
       
 11930 		lookup = entry[2];
       
 11931 	this[key] = {
       
 11932 		type: entry[1],
       
 11933 		property: key,
       
 11934 		attribute: entry[0],
       
 11935 		toSVG: lookup,
       
 11936 		fromSVG: lookup && Base.each(lookup, function(value, name) {
       
 11937 			this[value] = name;
       
 11938 		}, {}),
       
 11939 		exportFilter: entry[3],
       
 11940 		get: 'get' + part,
       
 11941 		set: 'set' + part
       
 11942 	};
       
 11943 }, {});
       
 11944 
       
 11945 var SVGNamespaces = {
       
 11946 	href: 'http://www.w3.org/1999/xlink',
       
 11947 	xlink: 'http://www.w3.org/2000/xmlns'
       
 11948 };
       
 11949 
       
 11950 new function() {
       
 11951 	var formatter;
       
 11952 
       
 11953 	function setAttributes(node, attrs) {
       
 11954 		for (var key in attrs) {
       
 11955 			var val = attrs[key],
       
 11956 				namespace = SVGNamespaces[key];
       
 11957 			if (typeof val === 'number')
       
 11958 				val = formatter.number(val);
       
 11959 			if (namespace) {
       
 11960 				node.setAttributeNS(namespace, key, val);
       
 11961 			} else {
       
 11962 				node.setAttribute(key, val);
       
 11963 			}
       
 11964 		}
       
 11965 		return node;
       
 11966 	}
       
 11967 
       
 11968 	function createElement(tag, attrs) {
       
 11969 		return setAttributes(
       
 11970 			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
       
 11971 	}
       
 11972 
       
 11973 	function getTransform(matrix, coordinates, center) {
       
 11974 		var attrs = new Base(),
       
 11975 			trans = matrix.getTranslation();
       
 11976 		if (coordinates) {
       
 11977 			matrix = matrix.shiftless();
       
 11978 			var point = matrix._inverseTransform(trans);
       
 11979 			attrs[center ? 'cx' : 'x'] = point.x;
       
 11980 			attrs[center ? 'cy' : 'y'] = point.y;
       
 11981 			trans = null;
       
 11982 		}
       
 11983 		if (!matrix.isIdentity()) {
       
 11984 			var decomposed = matrix.decompose();
       
 11985 			if (decomposed && !decomposed.shearing) {
       
 11986 				var parts = [],
       
 11987 					angle = decomposed.rotation,
       
 11988 					scale = decomposed.scaling;
       
 11989 				if (trans && !trans.isZero())
       
 11990 					parts.push('translate(' + formatter.point(trans) + ')');
       
 11991 				if (angle)
       
 11992 					parts.push('rotate(' + formatter.number(angle) + ')');
       
 11993 				if (!Numerical.isZero(scale.x - 1)
       
 11994 						|| !Numerical.isZero(scale.y - 1))
       
 11995 					parts.push('scale(' + formatter.point(scale) +')');
       
 11996 				attrs.transform = parts.join(' ');
       
 11997 			} else {
       
 11998 				attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
       
 11999 			}
       
 12000 		}
       
 12001 		return attrs;
       
 12002 	}
       
 12003 
       
 12004 	function exportGroup(item, options) {
       
 12005 		var attrs = getTransform(item._matrix),
       
 12006 			children = item._children;
       
 12007 		var node = createElement('g', attrs);
       
 12008 		for (var i = 0, l = children.length; i < l; i++) {
       
 12009 			var child = children[i];
       
 12010 			var childNode = exportSVG(child, options);
       
 12011 			if (childNode) {
       
 12012 				if (child.isClipMask()) {
       
 12013 					var clip = createElement('clipPath');
       
 12014 					clip.appendChild(childNode);
       
 12015 					setDefinition(child, clip, 'clip');
       
 12016 					setAttributes(node, {
       
 12017 						'clip-path': 'url(#' + clip.id + ')'
       
 12018 					});
       
 12019 				} else {
       
 12020 					node.appendChild(childNode);
       
 12021 				}
       
 12022 			}
       
 12023 		}
       
 12024 		return node;
       
 12025 	}
       
 12026 
       
 12027 	function exportRaster(item) {
       
 12028 		var attrs = getTransform(item._matrix, true),
       
 12029 			size = item.getSize();
       
 12030 		attrs.x -= size.width / 2;
       
 12031 		attrs.y -= size.height / 2;
       
 12032 		attrs.width = size.width;
       
 12033 		attrs.height = size.height;
       
 12034 		attrs.href = item.toDataURL();
       
 12035 		return createElement('image', attrs);
       
 12036 	}
       
 12037 
       
 12038 	function exportPath(item, options) {
       
 12039 		if (options.matchShapes) {
       
 12040 			var shape = item.toShape(false);
       
 12041 			if (shape)
       
 12042 				return exportShape(shape, options);
       
 12043 		}
       
 12044 		var segments = item._segments,
       
 12045 			type,
       
 12046 			attrs = getTransform(item._matrix);
       
 12047 		if (segments.length === 0)
       
 12048 			return null;
       
 12049 		if (item.isPolygon()) {
       
 12050 			if (segments.length >= 3) {
       
 12051 				type = item._closed ? 'polygon' : 'polyline';
       
 12052 				var parts = [];
       
 12053 				for(i = 0, l = segments.length; i < l; i++)
       
 12054 					parts.push(formatter.point(segments[i]._point));
       
 12055 				attrs.points = parts.join(' ');
       
 12056 			} else {
       
 12057 				type = 'line';
       
 12058 				var first = segments[0]._point,
       
 12059 					last = segments[segments.length - 1]._point;
       
 12060 				attrs.set({
       
 12061 					x1: first.x,
       
 12062 					y1: first.y,
       
 12063 					x2: last.x,
       
 12064 					y2: last.y
       
 12065 				});
       
 12066 			}
       
 12067 		} else {
       
 12068 			type = 'path';
       
 12069 			attrs.d = item.getPathData(null, options.precision);
       
 12070 		}
       
 12071 		return createElement(type, attrs);
       
 12072 	}
       
 12073 
       
 12074 	function exportShape(item) {
       
 12075 		var type = item._type,
       
 12076 			radius = item._radius,
       
 12077 			attrs = getTransform(item._matrix, true, type !== 'rectangle');
       
 12078 		if (type === 'rectangle') {
       
 12079 			type = 'rect';
       
 12080 			var size = item._size,
       
 12081 				width = size.width,
       
 12082 				height = size.height;
       
 12083 			attrs.x -= width / 2;
       
 12084 			attrs.y -= height / 2;
       
 12085 			attrs.width = width;
       
 12086 			attrs.height = height;
       
 12087 			if (radius.isZero())
       
 12088 				radius = null;
       
 12089 		}
       
 12090 		if (radius) {
       
 12091 			if (type === 'circle') {
       
 12092 				attrs.r = radius;
       
 12093 			} else {
       
 12094 				attrs.rx = radius.width;
       
 12095 				attrs.ry = radius.height;
       
 12096 			}
       
 12097 		}
       
 12098 		return createElement(type, attrs);
       
 12099 	}
       
 12100 
       
 12101 	function exportCompoundPath(item, options) {
       
 12102 		var attrs = getTransform(item._matrix);
       
 12103 		var data = item.getPathData(null, options.precision);
       
 12104 		if (data)
       
 12105 			attrs.d = data;
       
 12106 		return createElement('path', attrs);
       
 12107 	}
       
 12108 
       
 12109 	function exportPlacedSymbol(item, options) {
       
 12110 		var attrs = getTransform(item._matrix, true),
       
 12111 			symbol = item.getSymbol(),
       
 12112 			symbolNode = getDefinition(symbol, 'symbol'),
       
 12113 			definition = symbol.getDefinition(),
       
 12114 			bounds = definition.getBounds();
       
 12115 		if (!symbolNode) {
       
 12116 			symbolNode = createElement('symbol', {
       
 12117 				viewBox: formatter.rectangle(bounds)
       
 12118 			});
       
 12119 			symbolNode.appendChild(exportSVG(definition, options));
       
 12120 			setDefinition(symbol, symbolNode, 'symbol');
       
 12121 		}
       
 12122 		attrs.href = '#' + symbolNode.id;
       
 12123 		attrs.x += bounds.x;
       
 12124 		attrs.y += bounds.y;
       
 12125 		attrs.width = formatter.number(bounds.width);
       
 12126 		attrs.height = formatter.number(bounds.height);
       
 12127 		return createElement('use', attrs);
       
 12128 	}
       
 12129 
       
 12130 	function exportGradient(color) {
       
 12131 		var gradientNode = getDefinition(color, 'color');
       
 12132 		if (!gradientNode) {
       
 12133 			var gradient = color.getGradient(),
       
 12134 				radial = gradient._radial,
       
 12135 				origin = color.getOrigin().transform(),
       
 12136 				destination = color.getDestination().transform(),
       
 12137 				attrs;
       
 12138 			if (radial) {
       
 12139 				attrs = {
       
 12140 					cx: origin.x,
       
 12141 					cy: origin.y,
       
 12142 					r: origin.getDistance(destination)
       
 12143 				};
       
 12144 				var highlight = color.getHighlight();
       
 12145 				if (highlight) {
       
 12146 					highlight = highlight.transform();
       
 12147 					attrs.fx = highlight.x;
       
 12148 					attrs.fy = highlight.y;
       
 12149 				}
       
 12150 			} else {
       
 12151 				attrs = {
       
 12152 					x1: origin.x,
       
 12153 					y1: origin.y,
       
 12154 					x2: destination.x,
       
 12155 					y2: destination.y
       
 12156 				};
       
 12157 			}
       
 12158 			attrs.gradientUnits = 'userSpaceOnUse';
       
 12159 			gradientNode = createElement(
       
 12160 					(radial ? 'radial' : 'linear') + 'Gradient', attrs);
       
 12161 			var stops = gradient._stops;
       
 12162 			for (var i = 0, l = stops.length; i < l; i++) {
       
 12163 				var stop = stops[i],
       
 12164 					stopColor = stop._color,
       
 12165 					alpha = stopColor.getAlpha();
       
 12166 				attrs = {
       
 12167 					offset: stop._rampPoint,
       
 12168 					'stop-color': stopColor.toCSS(true)
       
 12169 				};
       
 12170 				if (alpha < 1)
       
 12171 					attrs['stop-opacity'] = alpha;
       
 12172 				gradientNode.appendChild(createElement('stop', attrs));
       
 12173 			}
       
 12174 			setDefinition(color, gradientNode, 'color');
       
 12175 		}
       
 12176 		return 'url(#' + gradientNode.id + ')';
       
 12177 	}
       
 12178 
       
 12179 	function exportText(item) {
       
 12180 		var node = createElement('text', getTransform(item._matrix, true));
       
 12181 		node.textContent = item._content;
       
 12182 		return node;
       
 12183 	}
       
 12184 
       
 12185 	var exporters = {
       
 12186 		Group: exportGroup,
       
 12187 		Layer: exportGroup,
       
 12188 		Raster: exportRaster,
       
 12189 		Path: exportPath,
       
 12190 		Shape: exportShape,
       
 12191 		CompoundPath: exportCompoundPath,
       
 12192 		PlacedSymbol: exportPlacedSymbol,
       
 12193 		PointText: exportText
       
 12194 	};
       
 12195 
       
 12196 	function applyStyle(item, node, isRoot) {
       
 12197 		var attrs = {},
       
 12198 			parent = !isRoot && item.getParent();
       
 12199 
       
 12200 		if (item._name != null)
       
 12201 			attrs.id = item._name;
       
 12202 
       
 12203 		Base.each(SVGStyles, function(entry) {
       
 12204 			var get = entry.get,
       
 12205 				type = entry.type,
       
 12206 				value = item[get]();
       
 12207 			if (entry.exportFilter
       
 12208 					? entry.exportFilter(item, value)
       
 12209 					: !parent || !Base.equals(parent[get](), value)) {
       
 12210 				if (type === 'color' && value != null) {
       
 12211 					var alpha = value.getAlpha();
       
 12212 					if (alpha < 1)
       
 12213 						attrs[entry.attribute + '-opacity'] = alpha;
       
 12214 				}
       
 12215 				attrs[entry.attribute] = value == null
       
 12216 					? 'none'
       
 12217 					: type === 'number'
       
 12218 						? formatter.number(value)
       
 12219 						: type === 'color'
       
 12220 							? value.gradient
       
 12221 								? exportGradient(value, item)
       
 12222 								: value.toCSS(true)
       
 12223 							: type === 'array'
       
 12224 								? value.join(',')
       
 12225 								: type === 'lookup'
       
 12226 									? entry.toSVG[value]
       
 12227 									: value;
       
 12228 			}
       
 12229 		});
       
 12230 
       
 12231 		if (attrs.opacity === 1)
       
 12232 			delete attrs.opacity;
       
 12233 
       
 12234 		if (!item._visible)
       
 12235 			attrs.visibility = 'hidden';
       
 12236 
       
 12237 		return setAttributes(node, attrs);
       
 12238 	}
       
 12239 
       
 12240 	var definitions;
       
 12241 	function getDefinition(item, type) {
       
 12242 		if (!definitions)
       
 12243 			definitions = { ids: {}, svgs: {} };
       
 12244 		return item && definitions.svgs[type + '-' + item._id];
       
 12245 	}
       
 12246 
       
 12247 	function setDefinition(item, node, type) {
       
 12248 		if (!definitions)
       
 12249 			getDefinition();
       
 12250 		var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
       
 12251 		node.id = type + '-' + id;
       
 12252 		definitions.svgs[type + '-' + item._id] = node;
       
 12253 	}
       
 12254 
       
 12255 	function exportDefinitions(node, options) {
       
 12256 		var svg = node,
       
 12257 			defs = null;
       
 12258 		if (definitions) {
       
 12259 			svg = node.nodeName.toLowerCase() === 'svg' && node;
       
 12260 			for (var i in definitions.svgs) {
       
 12261 				if (!defs) {
       
 12262 					if (!svg) {
       
 12263 						svg = createElement('svg');
       
 12264 						svg.appendChild(node);
       
 12265 					}
       
 12266 					defs = svg.insertBefore(createElement('defs'),
       
 12267 							svg.firstChild);
       
 12268 				}
       
 12269 				defs.appendChild(definitions.svgs[i]);
       
 12270 			}
       
 12271 			definitions = null;
       
 12272 		}
       
 12273 		return options.asString
       
 12274 				? new XMLSerializer().serializeToString(svg)
       
 12275 				: svg;
       
 12276 	}
       
 12277 
       
 12278 	function exportSVG(item, options, isRoot) {
       
 12279 		var exporter = exporters[item._class],
       
 12280 			node = exporter && exporter(item, options);
       
 12281 		if (node) {
       
 12282 			var onExport = options.onExport;
       
 12283 			if (onExport)
       
 12284 				node = onExport(item, node, options) || node;
       
 12285 			var data = JSON.stringify(item._data);
       
 12286 			if (data && data  !== '{}')
       
 12287 				node.setAttribute('data-paper-data', data);
       
 12288 		}
       
 12289 		return node && applyStyle(item, node, isRoot);
       
 12290 	}
       
 12291 
       
 12292 	function setOptions(options) {
       
 12293 		if (!options)
       
 12294 			options = {};
       
 12295 		formatter = new Formatter(options.precision);
       
 12296 		return options;
       
 12297 	}
       
 12298 
       
 12299 	Item.inject({
       
 12300 		exportSVG: function(options) {
       
 12301 			options = setOptions(options);
       
 12302 			return exportDefinitions(exportSVG(this, options, true), options);
       
 12303 		}
       
 12304 	});
       
 12305 
       
 12306 	Project.inject({
       
 12307 		exportSVG: function(options) {
       
 12308 			options = setOptions(options);
       
 12309 			var layers = this.layers,
       
 12310 				view = this.getView(),
       
 12311 				size = view.getViewSize(),
       
 12312 				node = createElement('svg', {
       
 12313 					x: 0,
       
 12314 					y: 0,
       
 12315 					width: size.width,
       
 12316 					height: size.height,
       
 12317 					version: '1.1',
       
 12318 					xmlns: 'http://www.w3.org/2000/svg',
       
 12319 					'xmlns:xlink': 'http://www.w3.org/1999/xlink'
       
 12320 				}),
       
 12321 				parent = node,
       
 12322 				matrix = view._matrix;
       
 12323 			if (!matrix.isIdentity())
       
 12324 				parent = node.appendChild(
       
 12325 						createElement('g', getTransform(matrix)));
       
 12326 			for (var i = 0, l = layers.length; i < l; i++)
       
 12327 				parent.appendChild(exportSVG(layers[i], options, true));
       
 12328 			return exportDefinitions(node, options);
       
 12329 		}
       
 12330 	});
       
 12331 };
       
 12332 
       
 12333 new function() {
       
 12334 
       
 12335 	function getValue(node, name, isString, allowNull) {
       
 12336 		var namespace = SVGNamespaces[name],
       
 12337 			value = namespace
       
 12338 				? node.getAttributeNS(namespace, name)
       
 12339 				: node.getAttribute(name);
       
 12340 		if (value === 'null')
       
 12341 			value = null;
       
 12342 		return value == null
       
 12343 				? allowNull
       
 12344 					? null
       
 12345 					: isString
       
 12346 						? ''
       
 12347 						: 0
       
 12348 				: isString
       
 12349 					? value
       
 12350 					: parseFloat(value);
       
 12351 	}
       
 12352 
       
 12353 	function getPoint(node, x, y, allowNull) {
       
 12354 		x = getValue(node, x, false, allowNull);
       
 12355 		y = getValue(node, y, false, allowNull);
       
 12356 		return allowNull && (x == null || y == null) ? null
       
 12357 				: new Point(x, y);
       
 12358 	}
       
 12359 
       
 12360 	function getSize(node, w, h, allowNull) {
       
 12361 		w = getValue(node, w, false, allowNull);
       
 12362 		h = getValue(node, h, false, allowNull);
       
 12363 		return allowNull && (w == null || h == null) ? null
       
 12364 				: new Size(w, h);
       
 12365 	}
       
 12366 
       
 12367 	function convertValue(value, type, lookup) {
       
 12368 		return value === 'none'
       
 12369 				? null
       
 12370 				: type === 'number'
       
 12371 					? parseFloat(value)
       
 12372 					: type === 'array'
       
 12373 						? value ? value.split(/[\s,]+/g).map(parseFloat) : []
       
 12374 						: type === 'color'
       
 12375 							? getDefinition(value) || value
       
 12376 							: type === 'lookup'
       
 12377 								? lookup[value]
       
 12378 								: value;
       
 12379 	}
       
 12380 
       
 12381 	function importGroup(node, type, options, isRoot) {
       
 12382 		var nodes = node.childNodes,
       
 12383 			isClip = type === 'clippath',
       
 12384 			item = new Group(),
       
 12385 			project = item._project,
       
 12386 			currentStyle = project._currentStyle,
       
 12387 			children = [];
       
 12388 		if (!isClip) {
       
 12389 			item = applyAttributes(item, node, isRoot);
       
 12390 			project._currentStyle = item._style.clone();
       
 12391 		}
       
 12392 		for (var i = 0, l = nodes.length; i < l; i++) {
       
 12393 			var childNode = nodes[i],
       
 12394 				child;
       
 12395 			if (childNode.nodeType === 1
       
 12396 					&& (child = importSVG(childNode, options, false))
       
 12397 					&& !(child instanceof Symbol))
       
 12398 				children.push(child);
       
 12399 		}
       
 12400 		item.addChildren(children);
       
 12401 		if (isClip)
       
 12402 			item = applyAttributes(item.reduce(), node, isRoot);
       
 12403 		project._currentStyle = currentStyle;
       
 12404 		if (isClip || type === 'defs') {
       
 12405 			item.remove();
       
 12406 			item = null;
       
 12407 		}
       
 12408 		return item;
       
 12409 	}
       
 12410 
       
 12411 	function importPoly(node, type) {
       
 12412 		var coords = node.getAttribute('points').match(
       
 12413 					/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g),
       
 12414 			points = [];
       
 12415 		for (var i = 0, l = coords.length; i < l; i += 2)
       
 12416 			points.push(new Point(
       
 12417 					parseFloat(coords[i]),
       
 12418 					parseFloat(coords[i + 1])));
       
 12419 		var path = new Path(points);
       
 12420 		if (type === 'polygon')
       
 12421 			path.closePath();
       
 12422 		return path;
       
 12423 	}
       
 12424 
       
 12425 	function importPath(node) {
       
 12426 		var data = node.getAttribute('d'),
       
 12427 			param = { pathData: data };
       
 12428 		return data.match(/m/gi).length > 1 || /z\S+/i.test(data)
       
 12429 				? new CompoundPath(param)
       
 12430 				: new Path(param);
       
 12431 	}
       
 12432 
       
 12433 	function importGradient(node, type) {
       
 12434 		var id = (getValue(node, 'href', true) || '').substring(1),
       
 12435 			isRadial = type === 'radialgradient',
       
 12436 			gradient;
       
 12437 		if (id) {
       
 12438 			gradient = definitions[id].getGradient();
       
 12439 		} else {
       
 12440 			var nodes = node.childNodes,
       
 12441 				stops = [];
       
 12442 			for (var i = 0, l = nodes.length; i < l; i++) {
       
 12443 				var child = nodes[i];
       
 12444 				if (child.nodeType === 1)
       
 12445 					stops.push(applyAttributes(new GradientStop(), child));
       
 12446 			}
       
 12447 			gradient = new Gradient(stops, isRadial);
       
 12448 		}
       
 12449 		var origin, destination, highlight;
       
 12450 		if (isRadial) {
       
 12451 			origin = getPoint(node, 'cx', 'cy');
       
 12452 			destination = origin.add(getValue(node, 'r'), 0);
       
 12453 			highlight = getPoint(node, 'fx', 'fy', true);
       
 12454 		} else {
       
 12455 			origin = getPoint(node, 'x1', 'y1');
       
 12456 			destination = getPoint(node, 'x2', 'y2');
       
 12457 		}
       
 12458 		applyAttributes(
       
 12459 			new Color(gradient, origin, destination, highlight), node);
       
 12460 		return null;
       
 12461 	}
       
 12462 
       
 12463 	var importers = {
       
 12464 		'#document': function (node, type, options, isRoot) {
       
 12465 			var nodes = node.childNodes;
       
 12466 			for (var i = 0, l = nodes.length; i < l; i++) {
       
 12467 				var child = nodes[i];
       
 12468 				if (child.nodeType === 1) {
       
 12469 					var next = child.nextSibling;
       
 12470 					document.body.appendChild(child);
       
 12471 					var item = importSVG(child, options, isRoot);
       
 12472 					if (next) {
       
 12473 						node.insertBefore(child, next);
       
 12474 					} else {
       
 12475 						node.appendChild(child);
       
 12476 					}
       
 12477 					return item;
       
 12478 				}
       
 12479 			}
       
 12480 		},
       
 12481 		g: importGroup,
       
 12482 		svg: importGroup,
       
 12483 		clippath: importGroup,
       
 12484 		polygon: importPoly,
       
 12485 		polyline: importPoly,
       
 12486 		path: importPath,
       
 12487 		lineargradient: importGradient,
       
 12488 		radialgradient: importGradient,
       
 12489 
       
 12490 		image: function (node) {
       
 12491 			var raster = new Raster(getValue(node, 'href', true));
       
 12492 			raster.attach('load', function() {
       
 12493 				var size = getSize(node, 'width', 'height');
       
 12494 				this.setSize(size);
       
 12495 				var center = this._matrix._transformPoint(
       
 12496 						getPoint(node, 'x', 'y').add(size.divide(2)));
       
 12497 				this.translate(center);
       
 12498 			});
       
 12499 			return raster;
       
 12500 		},
       
 12501 
       
 12502 		symbol: function(node, type, options, isRoot) {
       
 12503 			return new Symbol(importGroup(node, type, options, isRoot), true);
       
 12504 		},
       
 12505 
       
 12506 		defs: importGroup,
       
 12507 
       
 12508 		use: function(node) {
       
 12509 			var id = (getValue(node, 'href', true) || '').substring(1),
       
 12510 				definition = definitions[id],
       
 12511 				point = getPoint(node, 'x', 'y');
       
 12512 			return definition
       
 12513 					? definition instanceof Symbol
       
 12514 						? definition.place(point)
       
 12515 						: definition.clone().translate(point)
       
 12516 					: null;
       
 12517 		},
       
 12518 
       
 12519 		circle: function(node) {
       
 12520 			return new Shape.Circle(getPoint(node, 'cx', 'cy'),
       
 12521 					getValue(node, 'r'));
       
 12522 		},
       
 12523 
       
 12524 		ellipse: function(node) {
       
 12525 			return new Shape.Ellipse({
       
 12526 				center: getPoint(node, 'cx', 'cy'),
       
 12527 				radius: getSize(node, 'rx', 'ry')
       
 12528 			});
       
 12529 		},
       
 12530 
       
 12531 		rect: function(node) {
       
 12532 			var point = getPoint(node, 'x', 'y'),
       
 12533 				size = getSize(node, 'width', 'height'),
       
 12534 				radius = getSize(node, 'rx', 'ry');
       
 12535 			return new Shape.Rectangle(new Rectangle(point, size), radius);
       
 12536 		},
       
 12537 
       
 12538 		line: function(node) {
       
 12539 			return new Path.Line(getPoint(node, 'x1', 'y1'),
       
 12540 					getPoint(node, 'x2', 'y2'));
       
 12541 		},
       
 12542 
       
 12543 		text: function(node) {
       
 12544 			var text = new PointText(getPoint(node, 'x', 'y')
       
 12545 					.add(getPoint(node, 'dx', 'dy')));
       
 12546 			text.setContent(node.textContent.trim() || '');
       
 12547 			return text;
       
 12548 		}
       
 12549 	};
       
 12550 
       
 12551 	function applyTransform(item, value, name, node) {
       
 12552 		var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
       
 12553 			matrix = new Matrix();
       
 12554 		for (var i = 0, l = transforms.length; i < l; i++) {
       
 12555 			var transform = transforms[i];
       
 12556 			if (!transform)
       
 12557 				break;
       
 12558 			var parts = transform.split('('),
       
 12559 				command = parts[0],
       
 12560 				v = parts[1].split(/[\s,]+/g);
       
 12561 			for (var j = 0, m = v.length; j < m; j++)
       
 12562 				v[j] = parseFloat(v[j]);
       
 12563 			switch (command) {
       
 12564 			case 'matrix':
       
 12565 				matrix.concatenate(
       
 12566 						new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
       
 12567 				break;
       
 12568 			case 'rotate':
       
 12569 				matrix.rotate(v[0], v[1], v[2]);
       
 12570 				break;
       
 12571 			case 'translate':
       
 12572 				matrix.translate(v[0], v[1]);
       
 12573 				break;
       
 12574 			case 'scale':
       
 12575 				matrix.scale(v);
       
 12576 				break;
       
 12577 			case 'skewX':
       
 12578 				matrix.skew(v[0], 0);
       
 12579 				break;
       
 12580 			case 'skewY':
       
 12581 				matrix.skew(0, v[0]);
       
 12582 				break;
       
 12583 			}
       
 12584 		}
       
 12585 		item.transform(matrix);
       
 12586 	}
       
 12587 
       
 12588 	function applyOpacity(item, value, name) {
       
 12589 		var color = item[name === 'fill-opacity' ? 'getFillColor'
       
 12590 				: 'getStrokeColor']();
       
 12591 		if (color)
       
 12592 			color.setAlpha(parseFloat(value));
       
 12593 	}
       
 12594 
       
 12595 	var attributes = Base.each(SVGStyles, function(entry) {
       
 12596 		this[entry.attribute] = function(item, value) {
       
 12597 			item[entry.set](convertValue(value, entry.type, entry.fromSVG));
       
 12598 			if (entry.type === 'color' && item instanceof Shape) {
       
 12599 				var color = item[entry.get]();
       
 12600 				if (color)
       
 12601 					color.transform(new Matrix().translate(
       
 12602 							item.getPosition(true).negate()));
       
 12603 			}
       
 12604 		};
       
 12605 	}, {
       
 12606 		id: function(item, value) {
       
 12607 			definitions[value] = item;
       
 12608 			if (item.setName)
       
 12609 				item.setName(value);
       
 12610 		},
       
 12611 
       
 12612 		'clip-path': function(item, value) {
       
 12613 			var clip = getDefinition(value);
       
 12614 			if (clip) {
       
 12615 				clip = clip.clone();
       
 12616 				clip.setClipMask(true);
       
 12617 				if (item instanceof Group) {
       
 12618 					item.insertChild(0, clip);
       
 12619 				} else {
       
 12620 					return new Group(clip, item);
       
 12621 				}
       
 12622 			}
       
 12623 		},
       
 12624 
       
 12625 		gradientTransform: applyTransform,
       
 12626 		transform: applyTransform,
       
 12627 
       
 12628 		'fill-opacity': applyOpacity,
       
 12629 		'stroke-opacity': applyOpacity,
       
 12630 
       
 12631 		visibility: function(item, value) {
       
 12632 			item.setVisible(value === 'visible');
       
 12633 		},
       
 12634 
       
 12635 		display: function(item, value) {
       
 12636 			item.setVisible(value !== null);
       
 12637 		},
       
 12638 
       
 12639 		'stop-color': function(item, value) {
       
 12640 			if (item.setColor)
       
 12641 				item.setColor(value);
       
 12642 		},
       
 12643 
       
 12644 		'stop-opacity': function(item, value) {
       
 12645 			if (item._color)
       
 12646 				item._color.setAlpha(parseFloat(value));
       
 12647 		},
       
 12648 
       
 12649 		offset: function(item, value) {
       
 12650 			var percentage = value.match(/(.*)%$/);
       
 12651 			item.setRampPoint(percentage
       
 12652 					? percentage[1] / 100
       
 12653 					: parseFloat(value));
       
 12654 		},
       
 12655 
       
 12656 		viewBox: function(item, value, name, node, styles) {
       
 12657 			var rect = new Rectangle(convertValue(value, 'array')),
       
 12658 				size = getSize(node, 'width', 'height', true);
       
 12659 			if (item instanceof Group) {
       
 12660 				var scale = size ? rect.getSize().divide(size) : 1,
       
 12661 					matrix = new Matrix().translate(rect.getPoint()).scale(scale);
       
 12662 				item.transform(matrix.inverted());
       
 12663 			} else if (item instanceof Symbol) {
       
 12664 				if (size)
       
 12665 					rect.setSize(size);
       
 12666 				var clip = getAttribute(node, 'overflow', styles) != 'visible',
       
 12667 					group = item._definition;
       
 12668 				if (clip && !rect.contains(group.getBounds())) {
       
 12669 					clip = new Shape.Rectangle(rect).transform(group._matrix);
       
 12670 					clip.setClipMask(true);
       
 12671 					group.addChild(clip);
       
 12672 				}
       
 12673 			}
       
 12674 		}
       
 12675 	});
       
 12676 
       
 12677 	function getAttribute(node, name, styles) {
       
 12678 	    ppp = node;
       
 12679 		var attr = node.attributes[name],
       
 12680 			value = attr && attr.value;
       
 12681 		if (!value) {
       
 12682 			var style = Base.camelize(name);
       
 12683 			value = node.style[style];
       
 12684 			if (!value && styles.node[style] !== styles.parent[style])
       
 12685 				value = styles.node[style];
       
 12686 		}
       
 12687 		return !value
       
 12688 				? undefined
       
 12689 				: value === 'none'
       
 12690 					? null
       
 12691 					: value;
       
 12692 	}
       
 12693 
       
 12694 	function applyAttributes(item, node, isRoot) {
       
 12695 		var styles = {
       
 12696 			node: DomElement.getStyles(node) || {},
       
 12697 			parent: !isRoot && DomElement.getStyles(node.parentNode) || {}
       
 12698 		};
       
 12699 		Base.each(attributes, function(apply, name) {
       
 12700 			var value = getAttribute(node, name, styles);
       
 12701 			if (value !== undefined)
       
 12702 				item = Base.pick(apply(item, value, name, node, styles), item);
       
 12703 		});
       
 12704 		return item;
       
 12705 	}
       
 12706 
       
 12707 	var definitions = {};
       
 12708 	function getDefinition(value) {
       
 12709 		var match = value && value.match(/\((?:#|)([^)']+)/);
       
 12710 		return match && definitions[match[1]];
       
 12711 	}
       
 12712 
       
 12713 	function importSVG(source, options, isRoot) {
       
 12714 		if (!source)
       
 12715 			return null;
       
 12716 		if (!options) {
       
 12717 			options = {};
       
 12718 		} else if (typeof options === 'function') {
       
 12719 			options = { onLoad: options };
       
 12720 		}
       
 12721 
       
 12722 		var node = source,
       
 12723 			scope = paper;
       
 12724 
       
 12725 		function onLoadCallback(svg) {
       
 12726 			paper = scope;
       
 12727 			var item = importSVG(svg, options, isRoot),
       
 12728 				onLoad = options.onLoad,
       
 12729 				view = scope.project && scope.getView();
       
 12730 			if (onLoad)
       
 12731 				onLoad.call(this, item);
       
 12732 			view.update();
       
 12733 		}
       
 12734 
       
 12735 		if (isRoot) {
       
 12736 			if (typeof source === 'string' && !/^.*</.test(source)) {
       
 12737 				node = document.getElementById(source);
       
 12738 				if (node) {
       
 12739 					source = null;
       
 12740 				} else {
       
 12741 					return Http.request('get', source, onLoadCallback);
       
 12742 				}
       
 12743 			} else if (typeof File !== 'undefined' && source instanceof File) {
       
 12744 				var reader = new FileReader();
       
 12745 				reader.onload = function() {
       
 12746 					onLoadCallback(reader.result);
       
 12747 				};
       
 12748 				return reader.readAsText(source);
       
 12749 			}
       
 12750 		}
       
 12751 
       
 12752 		if (typeof source === 'string')
       
 12753 			node = new DOMParser().parseFromString(source, 'image/svg+xml');
       
 12754 		if (!node.nodeName)
       
 12755 			throw new Error('Unsupported SVG source: ' + source);
       
 12756 		var type = node.nodeName.toLowerCase(),
       
 12757 			importer = importers[type],
       
 12758 			item,
       
 12759 			data = node.getAttribute && node.getAttribute('data-paper-data'),
       
 12760 			settings = scope.settings,
       
 12761 			prevApplyMatrix = settings.applyMatrix;
       
 12762 		settings.applyMatrix = false;
       
 12763 		item = importer && importer(node, type, options, isRoot) || null;
       
 12764 		settings.applyMatrix = prevApplyMatrix;
       
 12765 		if (item) {
       
 12766 			if (type !== '#document' && !(item instanceof Group))
       
 12767 				item = applyAttributes(item, node, isRoot);
       
 12768 			var onImport = options.onImport;
       
 12769 			if (onImport)
       
 12770 				item = onImport(node, item, options) || item;
       
 12771 			if (options.expandShapes && item instanceof Shape) {
       
 12772 				item.remove();
       
 12773 				item = item.toPath();
       
 12774 			}
       
 12775 			if (data)
       
 12776 				item._data = JSON.parse(data);
       
 12777 		}
       
 12778 		if (isRoot)
       
 12779 			definitions = {};
       
 12780 		return item;
       
 12781 	}
       
 12782 
       
 12783 	Item.inject({
       
 12784 		importSVG: function(node, options) {
       
 12785 			return this.addChild(importSVG(node, options, true));
       
 12786 		}
       
 12787 	});
       
 12788 
       
 12789 	Project.inject({
       
 12790 		importSVG: function(node, options) {
       
 12791 			this.activate();
       
 12792 			return importSVG(node, options, true);
       
 12793 		}
       
 12794 	});
       
 12795 };
       
 12796 
       
 12797 Base.exports.PaperScript = (function() {
       
 12798 	var exports, define,
       
 12799 		scope = this;
       
 12800 !function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in hr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=hr[r]);mr=fr.sourceFile||null}function t(e,r){var t=vr(pr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=pr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(pr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,pr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=pr.charCodeAt(br+=2);dr>br&&10!==t&&13!==t&&8232!==t&&8329!==t;)++br,t=pr.charCodeAt(br);fr.onComment&&fr.onComment(!1,pr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;dr>br;){var e=pr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=pr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e)++br,++Ar,Sr=br;else if(14>e&&e>8)++br;else if(47===e){var r=pr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=pr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=pr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function p(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Ft,1)}function d(e){var r=pr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Vt,1)}function m(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Tt,1)}function h(e){var r=pr.charCodeAt(br+1);return r===e?x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=pr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===pr.charCodeAt(br+2)?3:2,61===pr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):(61===r&&(t=61===pr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=pr.charCodeAt(br+1);return 61===r?x(qt,61===pr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(ht);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(pt);case 123:return++br,i(dt);case 125:return++br,i(mt);case 58:return++br,i(gt);case 63:return++br,i(kt);case 48:var r=pr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return p();case 124:case 38:return d(e);case 94:return m();case 43:case 45:return h(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=dr)return i(Br);var r=pr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=pr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=dr&&t(a,"Unterminated regular expression");var o=pr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=pr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=pr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===pr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===pr.charCodeAt(br)&&(++br,w(10),n=!0);var o=pr.charCodeAt(br);(69===o||101===o)&&(o=pr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=pr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Vr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=dr&&t(yr,"Unterminated string constant");var n=pr.charCodeAt(br);if(n===e)return++br,i(Fr,r);if(92===n){n=pr.charCodeAt(++br);var a=/^[0-7]+/.exec(pr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Vr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+="	";break;case 98:r+="\b";break;case 118:r+="";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===pr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8329===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=pr.charCodeAt(br);if(Yt(a))Bt&&(e+=pr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=pr.slice(n,br)),Bt=!0,117!=pr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:pr.slice(n,br)}function L(){var e=I(),r=Dr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Vr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){for(Vr=e,br=Lr;Sr>br;)Sr=pr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function T(){this.type=null,this.start=yr,this.end=null}function V(){this.start=xr,this.end=null,null!==mr&&(this.source=mr)}function q(){var e=new T;return fr.locations&&(e.loc=new V),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new T;return r.start=e.start,fr.locations&&(r.loc=new V,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function F(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function D(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===mt||Gt.test(pr.slice(Lr,yr)))}function M(){D(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Vr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Vr=null,Tr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&F(n)&&R(!0),t=!1}return j(r,"Program")}function J(){wr===wt&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;D(yt)||B()?r.label=null:wr!==Dr?X():(r.label=lr(),M());for(var a=0;a<Tr.length;++a){var o=Tr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Tr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Tr.push(Zt),r.body=J(),Tr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Tr.push(Zt),z(ht),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),1===i.declarations.length&&D(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return D(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=D(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),D(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(dt),Tr.push(en);for(var s,c;wr!=mt;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Tr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(pr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(ht),l.param=lr(),Vr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=D($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),r=G(r),M(),r;case tt:return U(),r.test=P(),Tr.push(Zt),r.body=J(),Tr.pop(),j(r,"WhileStatement");case nt:return Vr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case dt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,p=K();if(e===Dr&&"Identifier"===p.type&&D(gt)){for(var a=0;a<Tr.length;++a)Tr[a].name===f&&t(p.start,"Label '"+f+"' is already declared");var d=wr.isLoop?"loop":wr===Yr?"switch":null;return Tr.push({name:f,kind:d}),r.body=J(),Tr.pop(),r.label=p,j(r,"LabeledStatement")}return r.expression=p,M(),j(r,"ExpressionStatement")}}function P(){z(ht);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(dt);!D(mt);){var o=J();t.body.push(o),n&&e&&F(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Vr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=D(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!D(bt))break}return j(e,"VariableDeclaration")}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];D(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(D(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var a=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(a,r,t)}return e}function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,U(),e.argument=rr(),r?N(e.argument):Vr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(D(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(D(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(pt),nr(j(t,"MemberExpression"),r)}if(!r&&D(ht)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Dr:return lr();case Or:case Fr:case jr:var e=q();return e.value=Cr,e.raw=pr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case ht:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(pt,!0,!0),j(e,"ArrayExpression");case dt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=D(ht)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!D(mt);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&D(mt))break;var a,o={key:sr()},i=!1;if(D(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==ht&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Vr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Vr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Fr?ar():lr(!0)}function cr(e,r){wr===Dr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(ht);!D(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Tr;if(Rr=!0,Tr=[],e.body=H(!0),Rr=a,Tr=o,Vr||e.body.body.length&&F(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!D(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&D(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Dr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),U(),j(r,"Identifier")}e.version="0.3.2";var fr,pr,dr,mr;e.parse=function(e,t){return pr=String(e),dr=pr.length,r(t),o(),W(fr.program)};var hr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}pr=String(e),dr=pr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(pr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Tr,Vr,qr=[],Or={type:"num"},jr={type:"regexp"},Fr={type:"string"},Dr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"},tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},pt={type:"]"},dt={type:"{",beforeExpr:!0},mt={type:"}"},ht={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Tt={binop:4,beforeExpr:!0},Vt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Ft={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:pt,braceL:dt,braceR:mt,parenL:ht,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Dr,eof:Br,num:Or,regexp:jr,string:Fr};for(var Dt in lt)e.tokTypes["_"+Dt]=lt[Dt];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc",Ht="\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f",$t=new RegExp("["+Pt+"]"),_t=new RegExp("["+Pt+Ht+"]"),Gt=/[\n\r\u2028\u2029]/,Kt=/\r\n|[\n\r\u2028\u2029]/g,Qt=e.isIdentifierStart=function(e){return 65>e?36===e:91>e?!0:97>e?95===e:123>e?!0:e>=170&&$t.test(String.fromCharCode(e))},Yt=e.isIdentifierChar=function(e){return 48>e?36===e:58>e?!0:65>e?!1:91>e?!0:97>e?95===e:123>e?!0:e>=170&&_t.test(String.fromCharCode(e))},Zt={kind:"loop"},en={kind:"switch"}});
       
 12801 
       
 12802 	var ua = navigator.userAgent,
       
 12803 		browser = {};
       
 12804 	ua.toLowerCase().replace(
       
 12805 		/(opera|chrome|safari|webkit|firefox|msie|trident)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g,
       
 12806 		function(all, n, v1, v2, rv) {
       
 12807 			if (!browser.chrome) {
       
 12808 				var v = n === 'opera' ? v2 : v1;
       
 12809 				if (n === 'trident') {
       
 12810 					v = rv;
       
 12811 					n = 'msie';
       
 12812 				}
       
 12813 				browser.version = parseFloat(v);
       
 12814 				browser.name = n;
       
 12815 				browser[n] = true;
       
 12816 				if (browser.chrome)
       
 12817 					delete browser.webkit;
       
 12818 			}
       
 12819 		}
       
 12820 	);
       
 12821 
       
 12822 	var binaryOperators = {
       
 12823 		'+': '__add',
       
 12824 		'-': '__subtract',
       
 12825 		'*': '__multiply',
       
 12826 		'/': '__divide',
       
 12827 		'%': '__modulo',
       
 12828 		'==': 'equals',
       
 12829 		'!=': 'equals'
       
 12830 	};
       
 12831 
       
 12832 	var unaryOperators = {
       
 12833 		'-': '__negate',
       
 12834 		'+': null
       
 12835 	};
       
 12836 
       
 12837 	var fields = Base.each(
       
 12838 		['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
       
 12839 		function(name) {
       
 12840 			this['__' + name] = '#' + name;
       
 12841 		},
       
 12842 		{}
       
 12843 	);
       
 12844 	Point.inject(fields);
       
 12845 	Size.inject(fields);
       
 12846 	Color.inject(fields);
       
 12847 
       
 12848 	function __$__(left, operator, right) {
       
 12849 		var handler = binaryOperators[operator];
       
 12850 		if (left && left[handler]) {
       
 12851 			var res = left[handler](right);
       
 12852 			return operator === '!=' ? !res : res;
       
 12853 		}
       
 12854 		switch (operator) {
       
 12855 		case '+': return left + right;
       
 12856 		case '-': return left - right;
       
 12857 		case '*': return left * right;
       
 12858 		case '/': return left / right;
       
 12859 		case '%': return left % right;
       
 12860 		case '==': return left == right;
       
 12861 		case '!=': return left != right;
       
 12862 		}
       
 12863 	}
       
 12864 
       
 12865 	function $__(operator, value) {
       
 12866 		var handler = unaryOperators[operator];
       
 12867 		if (handler && value && value[handler])
       
 12868 			return value[handler]();
       
 12869 		switch (operator) {
       
 12870 		case '+': return +value;
       
 12871 		case '-': return -value;
       
 12872 		}
       
 12873 	}
       
 12874 
       
 12875 	function parse(code, options) {
       
 12876 		return scope.acorn.parse(code, options);
       
 12877 	}
       
 12878 
       
 12879 	function compile(code, url, options) {
       
 12880 		if (!code)
       
 12881 			return '';
       
 12882 		options = options || {};
       
 12883 		url = url || '';
       
 12884 
       
 12885 		var insertions = [];
       
 12886 
       
 12887 		function getOffset(offset) {
       
 12888 			for (var i = 0, l = insertions.length; i < l; i++) {
       
 12889 				var insertion = insertions[i];
       
 12890 				if (insertion[0] >= offset)
       
 12891 					break;
       
 12892 				offset += insertion[1];
       
 12893 			}
       
 12894 			return offset;
       
 12895 		}
       
 12896 
       
 12897 		function getCode(node) {
       
 12898 			return code.substring(getOffset(node.range[0]),
       
 12899 					getOffset(node.range[1]));
       
 12900 		}
       
 12901 
       
 12902 		function replaceCode(node, str) {
       
 12903 			var start = getOffset(node.range[0]),
       
 12904 				end = getOffset(node.range[1]),
       
 12905 				insert = 0;
       
 12906 			for (var i = insertions.length - 1; i >= 0; i--) {
       
 12907 				if (start > insertions[i][0]) {
       
 12908 					insert = i + 1;
       
 12909 					break;
       
 12910 				}
       
 12911 			}
       
 12912 			insertions.splice(insert, 0, [start, str.length - end + start]);
       
 12913 			code = code.substring(0, start) + str + code.substring(end);
       
 12914 		}
       
 12915 
       
 12916 		function walkAST(node, parent) {
       
 12917 			if (!node)
       
 12918 				return;
       
 12919 			for (var key in node) {
       
 12920 				if (key === 'range' || key === 'loc')
       
 12921 					continue;
       
 12922 				var value = node[key];
       
 12923 				if (Array.isArray(value)) {
       
 12924 					for (var i = 0, l = value.length; i < l; i++)
       
 12925 						walkAST(value[i], node);
       
 12926 				} else if (value && typeof value === 'object') {
       
 12927 					walkAST(value, node);
       
 12928 				}
       
 12929 			}
       
 12930 			switch (node.type) {
       
 12931 			case 'UnaryExpression':
       
 12932 				if (node.operator in unaryOperators
       
 12933 						&& node.argument.type !== 'Literal') {
       
 12934 					var arg = getCode(node.argument);
       
 12935 					replaceCode(node, '$__("' + node.operator + '", '
       
 12936 							+ arg + ')');
       
 12937 				}
       
 12938 				break;
       
 12939 			case 'BinaryExpression':
       
 12940 				if (node.operator in binaryOperators
       
 12941 						&& node.left.type !== 'Literal') {
       
 12942 					var left = getCode(node.left),
       
 12943 						right = getCode(node.right);
       
 12944 					replaceCode(node, '__$__(' + left + ', "' + node.operator
       
 12945 							+ '", ' + right + ')');
       
 12946 				}
       
 12947 				break;
       
 12948 			case 'UpdateExpression':
       
 12949 			case 'AssignmentExpression':
       
 12950 				var parentType = parent && parent.type;
       
 12951 				if (!(
       
 12952 						parentType === 'ForStatement'
       
 12953 						|| parentType === 'BinaryExpression'
       
 12954 							&& /^[=!<>]/.test(parent.operator)
       
 12955 						|| parentType === 'MemberExpression' && parent.computed
       
 12956 				)) {
       
 12957 					if (node.type === 'UpdateExpression') {
       
 12958 						var arg = getCode(node.argument);
       
 12959 						var str = arg + ' = __$__(' + arg
       
 12960 								+ ', "' + node.operator[0] + '", 1)';
       
 12961 						if (!node.prefix
       
 12962 								&& (parentType === 'AssignmentExpression'
       
 12963 									|| parentType === 'VariableDeclarator'))
       
 12964 							str = arg + '; ' + str;
       
 12965 						replaceCode(node, str);
       
 12966 					} else {
       
 12967 						if (/^.=$/.test(node.operator)
       
 12968 								&& node.left.type !== 'Literal') {
       
 12969 							var left = getCode(node.left),
       
 12970 								right = getCode(node.right);
       
 12971 							replaceCode(node, left + ' = __$__(' + left + ', "'
       
 12972 									+ node.operator[0] + '", ' + right + ')');
       
 12973 						}
       
 12974 					}
       
 12975 				}
       
 12976 				break;
       
 12977 			}
       
 12978 		}
       
 12979 		var sourceMap = null,
       
 12980 			version = browser.version,
       
 12981 			lineBreaks = /\r\n|\n|\r/mg;
       
 12982 		if (browser.chrome && version >= 30
       
 12983 				|| browser.webkit && version >= 537.76
       
 12984 				|| browser.firefox && version >= 23) {
       
 12985 			var offset = 0;
       
 12986 			if (window.location.href.indexOf(url) === 0) {
       
 12987 				var html = document.getElementsByTagName('html')[0].innerHTML;
       
 12988 				offset = html.substr(0, html.indexOf(code) + 1).match(
       
 12989 						lineBreaks).length + 1;
       
 12990 			}
       
 12991 			var mappings = ['AAAA'];
       
 12992 			mappings.length = (code.match(lineBreaks) || []).length + 1 + offset;
       
 12993 			sourceMap = {
       
 12994 				version: 3,
       
 12995 				file: url,
       
 12996 				names:[],
       
 12997 				mappings: mappings.join(';AACA'),
       
 12998 				sourceRoot: '',
       
 12999 				sources: [url]
       
 13000 			};
       
 13001 			var source = options.source || !url && code;
       
 13002 			if (source)
       
 13003 				sourceMap.sourcesContent = [source];
       
 13004 		}
       
 13005 		walkAST(parse(code, { ranges: true }));
       
 13006 		if (sourceMap) {
       
 13007 			code = new Array(offset + 1).join('\n') + code
       
 13008 					+ "\n//# sourceMappingURL=data:application/json;base64,"
       
 13009 					+ (btoa(unescape(encodeURIComponent(
       
 13010 						JSON.stringify(sourceMap)))))
       
 13011 					+ "\n//# sourceURL=" + (url || 'paperscript');
       
 13012 		}
       
 13013 		return code;
       
 13014 	}
       
 13015 
       
 13016 	function execute(code, scope, url, options) {
       
 13017 		paper = scope;
       
 13018 		var view = scope.getView(),
       
 13019 			tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code)
       
 13020 					? new Tool()
       
 13021 					: null,
       
 13022 			toolHandlers = tool ? tool._events : [],
       
 13023 			handlers = ['onFrame', 'onResize'].concat(toolHandlers),
       
 13024 			params = [],
       
 13025 			args = [],
       
 13026 			func;
       
 13027 		code = compile(code, url, options);
       
 13028 		function expose(scope, hidden) {
       
 13029 			for (var key in scope) {
       
 13030 				if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)'
       
 13031 						+ key.replace(/\$/g, '\\$') + '\\b').test(code)) {
       
 13032 					params.push(key);
       
 13033 					args.push(scope[key]);
       
 13034 				}
       
 13035 			}
       
 13036 		}
       
 13037 		expose({ __$__: __$__, $__: $__, paper: scope, view: view, tool: tool },
       
 13038 				true);
       
 13039 		expose(scope);
       
 13040 		handlers = Base.each(handlers, function(key) {
       
 13041 			if (new RegExp('\\s+' + key + '\\b').test(code)) {
       
 13042 				params.push(key);
       
 13043 				this.push(key + ': ' + key);
       
 13044 			}
       
 13045 		}, []).join(', ');
       
 13046 		if (handlers)
       
 13047 			code += '\nreturn { ' + handlers + ' };';
       
 13048 		if (browser.chrome || browser.firefox) {
       
 13049 			var script = document.createElement('script'),
       
 13050 				head = document.head;
       
 13051 			if (browser.firefox)
       
 13052 				code = '\n' + code;
       
 13053 			script.appendChild(document.createTextNode(
       
 13054 				'paper._execute = function(' + params + ') {' + code + '\n}'
       
 13055 			));
       
 13056 			head.appendChild(script);
       
 13057 			func = paper._execute;
       
 13058 			delete paper._execute;
       
 13059 			head.removeChild(script);
       
 13060 		} else {
       
 13061 			func = Function(params, code);
       
 13062 		}
       
 13063 		var res = func.apply(scope, args) || {};
       
 13064 		Base.each(toolHandlers, function(key) {
       
 13065 			var value = res[key];
       
 13066 			if (value)
       
 13067 				tool[key] = value;
       
 13068 		});
       
 13069 		if (view) {
       
 13070 			if (res.onResize)
       
 13071 				view.setOnResize(res.onResize);
       
 13072 			view.fire('resize', {
       
 13073 				size: view.size,
       
 13074 				delta: new Point()
       
 13075 			});
       
 13076 			if (res.onFrame)
       
 13077 				view.setOnFrame(res.onFrame);
       
 13078 			view.update();
       
 13079 		}
       
 13080 	}
       
 13081 
       
 13082 	function loadScript(script) {
       
 13083 		if (/^text\/(?:x-|)paperscript$/.test(script.type)
       
 13084 				&& PaperScope.getAttribute(script, 'ignore') !== 'true') {
       
 13085 			var canvasId = PaperScope.getAttribute(script, 'canvas'),
       
 13086 				canvas = document.getElementById(canvasId),
       
 13087 				src = script.src,
       
 13088 				scopeAttribute = 'data-paper-scope';
       
 13089 			if (!canvas)
       
 13090 				throw new Error('Unable to find canvas with id "'
       
 13091 						+ canvasId + '"');
       
 13092 			var scope = PaperScope.get(canvas.getAttribute(scopeAttribute))
       
 13093 						|| new PaperScope().setup(canvas);
       
 13094 			canvas.setAttribute(scopeAttribute, scope._id);
       
 13095 			if (src) {
       
 13096 				Http.request('get', src, function(code) {
       
 13097 					execute(code, scope, src);
       
 13098 				});
       
 13099 			} else {
       
 13100 				execute(script.innerHTML, scope, script.baseURI);
       
 13101 			}
       
 13102 			script.setAttribute('data-paper-ignore', 'true');
       
 13103 			return scope;
       
 13104 		}
       
 13105 	}
       
 13106 
       
 13107 	function loadAll() {
       
 13108 		Base.each(document.getElementsByTagName('script'), loadScript);
       
 13109 	}
       
 13110 
       
 13111 	function load(script) {
       
 13112 		return script ? loadScript(script) : loadAll();
       
 13113 	}
       
 13114 
       
 13115 	if (document.readyState === 'complete') {
       
 13116 		setTimeout(loadAll);
       
 13117 	} else {
       
 13118 		DomEvent.add(window, { load: loadAll });
       
 13119 	}
       
 13120 
       
 13121 	return {
       
 13122 		compile: compile,
       
 13123 		execute: execute,
       
 13124 		load: load,
       
 13125 		parse: parse
       
 13126 	};
       
 13127 
       
 13128 }).call(this);
       
 13129 
       
 13130 paper = new (PaperScope.inject(Base.exports, {
       
 13131 	enumerable: true,
       
 13132 	Base: Base,
       
 13133 	Numerical: Numerical,
       
 13134 	DomElement: DomElement,
       
 13135 	DomEvent: DomEvent,
       
 13136 	Http: Http,
       
 13137 	Key: Key
       
 13138 }))();
       
 13139 
       
 13140 if (typeof define === 'function' && define.amd) {
       
 13141 	define('paper', paper);
       
 13142 } else if (typeof module === 'object' && module
       
 13143 		&& typeof module.exports === 'object') {
       
 13144 	module.exports = paper;
       
 13145 }
       
 13146 
       
 13147 return paper;
       
 13148 };