src/js/libs/paper.js
changeset 1057 3f20f286d43e
parent 938 991f897f1cb1
equal deleted inserted replaced
1056:3d19cbe56fcb 1057:3f20f286d43e
     1 (binary file text/javascript, hash: f3fa6aaffd0aeb1f4baa2eef0e11d62d6f0adc9d)
     1 /*!
       
     2  * Paper.js v0.9.24 - The Swiss Army Knife of Vector Graphics Scripting.
       
     3  * http://paperjs.org/
       
     4  *
       
     5  * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
       
     6  * http://scratchdisk.com/ & http://jonathanpuckey.com/
       
     7  *
       
     8  * Distributed under the MIT license. See LICENSE file for details.
       
     9  *
       
    10  * All rights reserved.
       
    11  *
       
    12  * Date: Fri Aug 21 16:39:41 2015 +0200
       
    13  *
       
    14  ***
       
    15  *
       
    16  * Straps.js - Class inheritance library with support for bean-style accessors
       
    17  *
       
    18  * Copyright (c) 2006 - 2013 Juerg Lehni
       
    19  * http://scratchdisk.com/
       
    20  *
       
    21  * Distributed under the MIT license.
       
    22  *
       
    23  ***
       
    24  *
       
    25  * Acorn.js
       
    26  * http://marijnhaverbeke.nl/acorn/
       
    27  *
       
    28  * Acorn is a tiny, fast JavaScript parser written in JavaScript,
       
    29  * created by Marijn Haverbeke and released under an MIT license.
       
    30  *
       
    31  */
       
    32 
       
    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 && !val.base
       
    92 						? (val && val.get ? name in dest : dest[name])
       
    93 						: null,
       
    94 				bean;
       
    95 			if (!preserve || !prev) {
       
    96 				if (isFunc && prev)
       
    97 					val.base = prev;
       
    98 				if (isFunc && beans !== false
       
    99 						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
       
   100 					beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
       
   101 				if (!res || isFunc || !res.get || typeof res.get !== 'function'
       
   102 						|| !Base.isPlainObject(res))
       
   103 					res = { value: res, writable: true };
       
   104 				if ((describe(dest, name)
       
   105 						|| { configurable: true }).configurable) {
       
   106 					res.configurable = true;
       
   107 					res.enumerable = enumerable;
       
   108 				}
       
   109 				define(dest, name, res);
       
   110 			}
       
   111 		}
       
   112 		if (src) {
       
   113 			for (var name in src) {
       
   114 				if (src.hasOwnProperty(name) && !hidden.test(name))
       
   115 					field(name);
       
   116 			}
       
   117 			for (var name in beansNames) {
       
   118 				var part = beansNames[name],
       
   119 					set = dest['set' + part],
       
   120 					get = dest['get' + part] || set && dest['is' + part];
       
   121 				if (get && (beans === true || get.length === 0))
       
   122 					field(name, { get: get, set: set });
       
   123 			}
       
   124 		}
       
   125 		return dest;
       
   126 	}
       
   127 
       
   128 	function each(obj, iter, bind) {
       
   129 		if (obj)
       
   130 			('length' in obj && !obj.getLength
       
   131 					&& typeof obj.length === 'number'
       
   132 				? forEach
       
   133 				: forIn).call(obj, iter, bind = bind || obj);
       
   134 		return bind;
       
   135 	}
       
   136 
       
   137 	function set(obj, props, exclude) {
       
   138 		for (var key in props)
       
   139 			if (props.hasOwnProperty(key) && !(exclude && exclude[key]))
       
   140 				obj[key] = props[key];
       
   141 		return obj;
       
   142 	}
       
   143 
       
   144 	return inject(function Base() {
       
   145 		for (var i = 0, l = arguments.length; i < l; i++)
       
   146 			set(this, arguments[i]);
       
   147 	}, {
       
   148 		inject: function(src) {
       
   149 			if (src) {
       
   150 				var statics = src.statics === true ? src : src.statics,
       
   151 					beans = src.beans,
       
   152 					preserve = src.preserve;
       
   153 				if (statics !== src)
       
   154 					inject(this.prototype, src, src.enumerable, beans, preserve);
       
   155 				inject(this, statics, true, beans, preserve);
       
   156 			}
       
   157 			for (var i = 1, l = arguments.length; i < l; i++)
       
   158 				this.inject(arguments[i]);
       
   159 			return this;
       
   160 		},
       
   161 
       
   162 		extend: function() {
       
   163 			var base = this,
       
   164 				ctor,
       
   165 				proto;
       
   166 			for (var i = 0, l = arguments.length; i < l; i++)
       
   167 				if (ctor = arguments[i].initialize)
       
   168 					break;
       
   169 			ctor = ctor || function() {
       
   170 				base.apply(this, arguments);
       
   171 			};
       
   172 			proto = ctor.prototype = create(this.prototype);
       
   173 			define(proto, 'constructor',
       
   174 					{ value: ctor, writable: true, configurable: true });
       
   175 			inject(ctor, this, true);
       
   176 			if (arguments.length)
       
   177 				this.inject.apply(ctor, arguments);
       
   178 			ctor.base = base;
       
   179 			return ctor;
       
   180 		}
       
   181 	}, true).inject({
       
   182 		inject: function() {
       
   183 			for (var i = 0, l = arguments.length; i < l; i++) {
       
   184 				var src = arguments[i];
       
   185 				if (src)
       
   186 					inject(this, src, src.enumerable, src.beans, src.preserve);
       
   187 			}
       
   188 			return this;
       
   189 		},
       
   190 
       
   191 		extend: function() {
       
   192 			var res = create(this);
       
   193 			return res.inject.apply(res, arguments);
       
   194 		},
       
   195 
       
   196 		each: function(iter, bind) {
       
   197 			return each(this, iter, bind);
       
   198 		},
       
   199 
       
   200 		set: function(props) {
       
   201 			return set(this, props);
       
   202 		},
       
   203 
       
   204 		clone: function() {
       
   205 			return new this.constructor(this);
       
   206 		},
       
   207 
       
   208 		statics: {
       
   209 			each: each,
       
   210 			create: create,
       
   211 			define: define,
       
   212 			describe: describe,
       
   213 			set: set,
       
   214 
       
   215 			clone: function(obj) {
       
   216 				return set(new obj.constructor(), obj);
       
   217 			},
       
   218 
       
   219 			isPlainObject: function(obj) {
       
   220 				var ctor = obj != null && obj.constructor;
       
   221 				return ctor && (ctor === Object || ctor === Base
       
   222 						|| ctor.name === 'Object');
       
   223 			},
       
   224 
       
   225 			pick: function(a, b) {
       
   226 				return a !== undefined ? a : b;
       
   227 			}
       
   228 		}
       
   229 	});
       
   230 };
       
   231 
       
   232 if (typeof module !== 'undefined')
       
   233 	module.exports = Base;
       
   234 
       
   235 Base.inject({
       
   236 	toString: function() {
       
   237 		return this._id != null
       
   238 			?  (this._class || 'Object') + (this._name
       
   239 				? " '" + this._name + "'"
       
   240 				: ' @' + this._id)
       
   241 			: '{ ' + Base.each(this, function(value, key) {
       
   242 				if (!/^_/.test(key)) {
       
   243 					var type = typeof value;
       
   244 					this.push(key + ': ' + (type === 'number'
       
   245 							? Formatter.instance.number(value)
       
   246 							: type === 'string' ? "'" + value + "'" : value));
       
   247 				}
       
   248 			}, []).join(', ') + ' }';
       
   249 	},
       
   250 
       
   251 	getClassName: function() {
       
   252 		return this._class || '';
       
   253 	},
       
   254 
       
   255 	exportJSON: function(options) {
       
   256 		return Base.exportJSON(this, options);
       
   257 	},
       
   258 
       
   259 	toJSON: function() {
       
   260 		return Base.serialize(this);
       
   261 	},
       
   262 
       
   263 	_set: function(props, exclude, dontCheck) {
       
   264 		if (props && (dontCheck || Base.isPlainObject(props))) {
       
   265 			var keys = Object.keys(props._filtering || props);
       
   266 			for (var i = 0, l = keys.length; i < l; i++) {
       
   267 				var key = keys[i];
       
   268 				if (!(exclude && exclude[key])) {
       
   269 					var value = props[key];
       
   270 					if (value !== undefined)
       
   271 						this[key] = value;
       
   272 				}
       
   273 			}
       
   274 			return true;
       
   275 		}
       
   276 	},
       
   277 
       
   278 	statics: {
       
   279 
       
   280 		exports: {
       
   281 			enumerable: true
       
   282 		},
       
   283 
       
   284 		extend: function extend() {
       
   285 			var res = extend.base.apply(this, arguments),
       
   286 				name = res.prototype._class;
       
   287 			if (name && !Base.exports[name])
       
   288 				Base.exports[name] = res;
       
   289 			return res;
       
   290 		},
       
   291 
       
   292 		equals: function(obj1, obj2) {
       
   293 			if (obj1 === obj2)
       
   294 				return true;
       
   295 			if (obj1 && obj1.equals)
       
   296 				return obj1.equals(obj2);
       
   297 			if (obj2 && obj2.equals)
       
   298 				return obj2.equals(obj1);
       
   299 			if (obj1 && obj2
       
   300 					&& typeof obj1 === 'object' && typeof obj2 === 'object') {
       
   301 				if (Array.isArray(obj1) && Array.isArray(obj2)) {
       
   302 					var length = obj1.length;
       
   303 					if (length !== obj2.length)
       
   304 						return false;
       
   305 					while (length--) {
       
   306 						if (!Base.equals(obj1[length], obj2[length]))
       
   307 							return false;
       
   308 					}
       
   309 				} else {
       
   310 					var keys = Object.keys(obj1),
       
   311 						length = keys.length;
       
   312 					if (length !== Object.keys(obj2).length)
       
   313 						return false;
       
   314 					while (length--) {
       
   315 						var key = keys[length];
       
   316 						if (!(obj2.hasOwnProperty(key)
       
   317 								&& Base.equals(obj1[key], obj2[key])))
       
   318 							return false;
       
   319 					}
       
   320 				}
       
   321 				return true;
       
   322 			}
       
   323 			return false;
       
   324 		},
       
   325 
       
   326 		read: function(list, start, options, length) {
       
   327 			if (this === Base) {
       
   328 				var value = this.peek(list, start);
       
   329 				list.__index++;
       
   330 				return value;
       
   331 			}
       
   332 			var proto = this.prototype,
       
   333 				readIndex = proto._readIndex,
       
   334 				index = start || readIndex && list.__index || 0;
       
   335 			if (!length)
       
   336 				length = list.length - index;
       
   337 			var obj = list[index];
       
   338 			if (obj instanceof this
       
   339 				|| options && options.readNull && obj == null && length <= 1) {
       
   340 				if (readIndex)
       
   341 					list.__index = index + 1;
       
   342 				return obj && options && options.clone ? obj.clone() : obj;
       
   343 			}
       
   344 			obj = Base.create(this.prototype);
       
   345 			if (readIndex)
       
   346 				obj.__read = true;
       
   347 			obj = obj.initialize.apply(obj, index > 0 || length < list.length
       
   348 				? Array.prototype.slice.call(list, index, index + length)
       
   349 				: list) || obj;
       
   350 			if (readIndex) {
       
   351 				list.__index = index + obj.__read;
       
   352 				obj.__read = undefined;
       
   353 			}
       
   354 			return obj;
       
   355 		},
       
   356 
       
   357 		peek: function(list, start) {
       
   358 			return list[list.__index = start || list.__index || 0];
       
   359 		},
       
   360 
       
   361 		remain: function(list) {
       
   362 			return list.length - (list.__index || 0);
       
   363 		},
       
   364 
       
   365 		readAll: function(list, start, options) {
       
   366 			var res = [],
       
   367 				entry;
       
   368 			for (var i = start || 0, l = list.length; i < l; i++) {
       
   369 				res.push(Array.isArray(entry = list[i])
       
   370 						? this.read(entry, 0, options)
       
   371 						: this.read(list, i, options, 1));
       
   372 			}
       
   373 			return res;
       
   374 		},
       
   375 
       
   376 		readNamed: function(list, name, start, options, length) {
       
   377 			var value = this.getNamed(list, name),
       
   378 				hasObject = value !== undefined;
       
   379 			if (hasObject) {
       
   380 				var filtered = list._filtered;
       
   381 				if (!filtered) {
       
   382 					filtered = list._filtered = Base.create(list[0]);
       
   383 					filtered._filtering = list[0];
       
   384 				}
       
   385 				filtered[name] = undefined;
       
   386 			}
       
   387 			return this.read(hasObject ? [value] : list, start, options, length);
       
   388 		},
       
   389 
       
   390 		getNamed: function(list, name) {
       
   391 			var arg = list[0];
       
   392 			if (list._hasObject === undefined)
       
   393 				list._hasObject = list.length === 1 && Base.isPlainObject(arg);
       
   394 			if (list._hasObject)
       
   395 				return name ? arg[name] : list._filtered || arg;
       
   396 		},
       
   397 
       
   398 		hasNamed: function(list, name) {
       
   399 			return !!this.getNamed(list, name);
       
   400 		},
       
   401 
       
   402 		isPlainValue: function(obj, asString) {
       
   403 			return this.isPlainObject(obj) || Array.isArray(obj)
       
   404 					|| asString && typeof obj === 'string';
       
   405 		},
       
   406 
       
   407 		serialize: function(obj, options, compact, dictionary) {
       
   408 			options = options || {};
       
   409 
       
   410 			var root = !dictionary,
       
   411 				res;
       
   412 			if (root) {
       
   413 				options.formatter = new Formatter(options.precision);
       
   414 				dictionary = {
       
   415 					length: 0,
       
   416 					definitions: {},
       
   417 					references: {},
       
   418 					add: function(item, create) {
       
   419 						var id = '#' + item._id,
       
   420 							ref = this.references[id];
       
   421 						if (!ref) {
       
   422 							this.length++;
       
   423 							var res = create.call(item),
       
   424 								name = item._class;
       
   425 							if (name && res[0] !== name)
       
   426 								res.unshift(name);
       
   427 							this.definitions[id] = res;
       
   428 							ref = this.references[id] = [id];
       
   429 						}
       
   430 						return ref;
       
   431 					}
       
   432 				};
       
   433 			}
       
   434 			if (obj && obj._serialize) {
       
   435 				res = obj._serialize(options, dictionary);
       
   436 				var name = obj._class;
       
   437 				if (name && !compact && !res._compact && res[0] !== name)
       
   438 					res.unshift(name);
       
   439 			} else if (Array.isArray(obj)) {
       
   440 				res = [];
       
   441 				for (var i = 0, l = obj.length; i < l; i++)
       
   442 					res[i] = Base.serialize(obj[i], options, compact,
       
   443 							dictionary);
       
   444 				if (compact)
       
   445 					res._compact = true;
       
   446 			} else if (Base.isPlainObject(obj)) {
       
   447 				res = {};
       
   448 				var keys = Object.keys(obj);
       
   449 				for (var i = 0, l = keys.length; i < l; i++) {
       
   450 					var key = keys[i];
       
   451 					res[key] = Base.serialize(obj[key], options, compact,
       
   452 							dictionary);
       
   453 				}
       
   454 			} else if (typeof obj === 'number') {
       
   455 				res = options.formatter.number(obj, options.precision);
       
   456 			} else {
       
   457 				res = obj;
       
   458 			}
       
   459 			return root && dictionary.length > 0
       
   460 					? [['dictionary', dictionary.definitions], res]
       
   461 					: res;
       
   462 		},
       
   463 
       
   464 		deserialize: function(json, create, _data, _isDictionary) {
       
   465 			var res = json,
       
   466 				isRoot = !_data;
       
   467 			_data = _data || {};
       
   468 			if (Array.isArray(json)) {
       
   469 				var type = json[0],
       
   470 					isDictionary = type === 'dictionary';
       
   471 				if (json.length == 1 && /^#/.test(type))
       
   472 					return _data.dictionary[type];
       
   473 				type = Base.exports[type];
       
   474 				res = [];
       
   475 				if (_isDictionary)
       
   476 					_data.dictionary = res;
       
   477 				for (var i = type ? 1 : 0, l = json.length; i < l; i++)
       
   478 					res.push(Base.deserialize(json[i], create, _data,
       
   479 							isDictionary));
       
   480 				if (type) {
       
   481 					var args = res;
       
   482 					if (create) {
       
   483 						res = create(type, args);
       
   484 					} else {
       
   485 						res = Base.create(type.prototype);
       
   486 						type.apply(res, args);
       
   487 					}
       
   488 				}
       
   489 			} else if (Base.isPlainObject(json)) {
       
   490 				res = {};
       
   491 				if (_isDictionary)
       
   492 					_data.dictionary = res;
       
   493 				for (var key in json)
       
   494 					res[key] = Base.deserialize(json[key], create, _data);
       
   495 			}
       
   496 			return isRoot && json && json.length && json[0][0] === 'dictionary'
       
   497 					? res[1]
       
   498 					: res;
       
   499 		},
       
   500 
       
   501 		exportJSON: function(obj, options) {
       
   502 			var json = Base.serialize(obj, options);
       
   503 			return options && options.asString === false
       
   504 					? json
       
   505 					: JSON.stringify(json);
       
   506 		},
       
   507 
       
   508 		importJSON: function(json, target) {
       
   509 			return Base.deserialize(
       
   510 					typeof json === 'string' ? JSON.parse(json) : json,
       
   511 					function(type, args) {
       
   512 						var obj = target && target.constructor === type
       
   513 								? target
       
   514 								: Base.create(type.prototype),
       
   515 							isTarget = obj === target;
       
   516 						if (args.length === 1 && obj instanceof Item
       
   517 								&& (isTarget || !(obj instanceof Layer))) {
       
   518 							var arg = args[0];
       
   519 							if (Base.isPlainObject(arg))
       
   520 								arg.insert = false;
       
   521 						}
       
   522 						type.apply(obj, args);
       
   523 						if (isTarget)
       
   524 							target = null;
       
   525 						return obj;
       
   526 					});
       
   527 		},
       
   528 
       
   529 		splice: function(list, items, index, remove) {
       
   530 			var amount = items && items.length,
       
   531 				append = index === undefined;
       
   532 			index = append ? list.length : index;
       
   533 			if (index > list.length)
       
   534 				index = list.length;
       
   535 			for (var i = 0; i < amount; i++)
       
   536 				items[i]._index = index + i;
       
   537 			if (append) {
       
   538 				list.push.apply(list, items);
       
   539 				return [];
       
   540 			} else {
       
   541 				var args = [index, remove];
       
   542 				if (items)
       
   543 					args.push.apply(args, items);
       
   544 				var removed = list.splice.apply(list, args);
       
   545 				for (var i = 0, l = removed.length; i < l; i++)
       
   546 					removed[i]._index = undefined;
       
   547 				for (var i = index + amount, l = list.length; i < l; i++)
       
   548 					list[i]._index = i;
       
   549 				return removed;
       
   550 			}
       
   551 		},
       
   552 
       
   553 		capitalize: function(str) {
       
   554 			return str.replace(/\b[a-z]/g, function(match) {
       
   555 				return match.toUpperCase();
       
   556 			});
       
   557 		},
       
   558 
       
   559 		camelize: function(str) {
       
   560 			return str.replace(/-(.)/g, function(all, chr) {
       
   561 				return chr.toUpperCase();
       
   562 			});
       
   563 		},
       
   564 
       
   565 		hyphenate: function(str) {
       
   566 			return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
       
   567 		}
       
   568 	}
       
   569 });
       
   570 
       
   571 var Emitter = {
       
   572 	on: function(type, func) {
       
   573 		if (typeof type !== 'string') {
       
   574 			Base.each(type, function(value, key) {
       
   575 				this.on(key, value);
       
   576 			}, this);
       
   577 		} else {
       
   578 			var types = this._eventTypes,
       
   579 				entry = types && types[type],
       
   580 				handlers = this._callbacks = this._callbacks || {};
       
   581 			handlers = handlers[type] = handlers[type] || [];
       
   582 			if (handlers.indexOf(func) === -1) {
       
   583 				handlers.push(func);
       
   584 				if (entry && entry.install && handlers.length == 1)
       
   585 					entry.install.call(this, type);
       
   586 			}
       
   587 		}
       
   588 		return this;
       
   589 	},
       
   590 
       
   591 	off: function(type, func) {
       
   592 		if (typeof type !== 'string') {
       
   593 			Base.each(type, function(value, key) {
       
   594 				this.off(key, value);
       
   595 			}, this);
       
   596 			return;
       
   597 		}
       
   598 		var types = this._eventTypes,
       
   599 			entry = types && types[type],
       
   600 			handlers = this._callbacks && this._callbacks[type],
       
   601 			index;
       
   602 		if (handlers) {
       
   603 			if (!func || (index = handlers.indexOf(func)) !== -1
       
   604 					&& handlers.length === 1) {
       
   605 				if (entry && entry.uninstall)
       
   606 					entry.uninstall.call(this, type);
       
   607 				delete this._callbacks[type];
       
   608 			} else if (index !== -1) {
       
   609 				handlers.splice(index, 1);
       
   610 			}
       
   611 		}
       
   612 		return this;
       
   613 	},
       
   614 
       
   615 	once: function(type, func) {
       
   616 		return this.on(type, function() {
       
   617 			func.apply(this, arguments);
       
   618 			this.off(type, func);
       
   619 		});
       
   620 	},
       
   621 
       
   622 	emit: function(type, event) {
       
   623 		var handlers = this._callbacks && this._callbacks[type];
       
   624 		if (!handlers)
       
   625 			return false;
       
   626 		var args = [].slice.call(arguments, 1);
       
   627 		handlers = handlers.slice();
       
   628 		for (var i = 0, l = handlers.length; i < l; i++) {
       
   629 			if (handlers[i].apply(this, args) === false) {
       
   630 				if (event && event.stop)
       
   631 					event.stop();
       
   632 				break;
       
   633 			}
       
   634 		}
       
   635 		return true;
       
   636 	},
       
   637 
       
   638 	responds: function(type) {
       
   639 		return !!(this._callbacks && this._callbacks[type]);
       
   640 	},
       
   641 
       
   642 	attach: '#on',
       
   643 	detach: '#off',
       
   644 	fire: '#emit',
       
   645 
       
   646 	_installEvents: function(install) {
       
   647 		var handlers = this._callbacks,
       
   648 			key = install ? 'install' : 'uninstall';
       
   649 		for (var type in handlers) {
       
   650 			if (handlers[type].length > 0) {
       
   651 				var types = this._eventTypes,
       
   652 					entry = types && types[type],
       
   653 					func = entry && entry[key];
       
   654 				if (func)
       
   655 					func.call(this, type);
       
   656 			}
       
   657 		}
       
   658 	},
       
   659 
       
   660 	statics: {
       
   661 		inject: function inject(src) {
       
   662 			var events = src._events;
       
   663 			if (events) {
       
   664 				var types = {};
       
   665 				Base.each(events, function(entry, key) {
       
   666 					var isString = typeof entry === 'string',
       
   667 						name = isString ? entry : key,
       
   668 						part = Base.capitalize(name),
       
   669 						type = name.substring(2).toLowerCase();
       
   670 					types[type] = isString ? {} : entry;
       
   671 					name = '_' + name;
       
   672 					src['get' + part] = function() {
       
   673 						return this[name];
       
   674 					};
       
   675 					src['set' + part] = function(func) {
       
   676 						var prev = this[name];
       
   677 						if (prev)
       
   678 							this.off(type, prev);
       
   679 						if (func)
       
   680 							this.on(type, func);
       
   681 						this[name] = func;
       
   682 					};
       
   683 				});
       
   684 				src._eventTypes = types;
       
   685 			}
       
   686 			return inject.base.apply(this, arguments);
       
   687 		}
       
   688 	}
       
   689 };
       
   690 
       
   691 var PaperScope = Base.extend({
       
   692 	_class: 'PaperScope',
       
   693 
       
   694 	initialize: function PaperScope() {
       
   695 		paper = this;
       
   696 		this.settings = new Base({
       
   697 			applyMatrix: true,
       
   698 			handleSize: 4,
       
   699 			hitTolerance: 0
       
   700 		});
       
   701 		this.project = null;
       
   702 		this.projects = [];
       
   703 		this.tools = [];
       
   704 		this.palettes = [];
       
   705 		this._id = PaperScope._id++;
       
   706 		PaperScope._scopes[this._id] = this;
       
   707 		var proto = PaperScope.prototype;
       
   708 		if (!this.support) {
       
   709 			var ctx = CanvasProvider.getContext(1, 1);
       
   710 			proto.support = {
       
   711 				nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
       
   712 				nativeBlendModes: BlendMode.nativeModes
       
   713 			};
       
   714 			CanvasProvider.release(ctx);
       
   715 		}
       
   716 
       
   717 		if (!this.browser) {
       
   718 			var agent = navigator.userAgent.toLowerCase(),
       
   719 				platform = (/(win)/.exec(agent)
       
   720 						|| /(mac)/.exec(agent)
       
   721 						|| /(linux)/.exec(agent)
       
   722 						|| [])[0],
       
   723 				browser = proto.browser = { platform: platform };
       
   724 			if (platform)
       
   725 				browser[platform] = true;
       
   726 			agent.replace(
       
   727 				/(opera|chrome|safari|webkit|firefox|msie|trident|atom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g,
       
   728 				function(all, n, v1, v2, rv) {
       
   729 					if (!browser.chrome) {
       
   730 						var v = n === 'opera' ? v2 : v1;
       
   731 						if (n === 'trident') {
       
   732 							v = rv;
       
   733 							n = 'msie';
       
   734 						}
       
   735 						browser.version = v;
       
   736 						browser.versionNumber = parseFloat(v);
       
   737 						browser.name = n;
       
   738 						browser[n] = true;
       
   739 					}
       
   740 				}
       
   741 			);
       
   742 			if (browser.chrome)
       
   743 				delete browser.webkit;
       
   744 			if (browser.atom)
       
   745 				delete browser.chrome;
       
   746 		}
       
   747 	},
       
   748 
       
   749 	version: '0.9.24',
       
   750 
       
   751 	getView: function() {
       
   752 		return this.project && this.project.getView();
       
   753 	},
       
   754 
       
   755 	getPaper: function() {
       
   756 		return this;
       
   757 	},
       
   758 
       
   759 	execute: function(code, url, options) {
       
   760 		paper.PaperScript.execute(code, this, url, options);
       
   761 		View.updateFocus();
       
   762 	},
       
   763 
       
   764 	install: function(scope) {
       
   765 		var that = this;
       
   766 		Base.each(['project', 'view', 'tool'], function(key) {
       
   767 			Base.define(scope, key, {
       
   768 				configurable: true,
       
   769 				get: function() {
       
   770 					return that[key];
       
   771 				}
       
   772 			});
       
   773 		});
       
   774 		for (var key in this)
       
   775 			if (!/^_/.test(key) && this[key])
       
   776 				scope[key] = this[key];
       
   777 	},
       
   778 
       
   779 	setup: function(element) {
       
   780 		paper = this;
       
   781 		this.project = new Project(element);
       
   782 		return this;
       
   783 	},
       
   784 
       
   785 	activate: function() {
       
   786 		paper = this;
       
   787 	},
       
   788 
       
   789 	clear: function() {
       
   790 		for (var i = this.projects.length - 1; i >= 0; i--)
       
   791 			this.projects[i].remove();
       
   792 		for (var i = this.tools.length - 1; i >= 0; i--)
       
   793 			this.tools[i].remove();
       
   794 		for (var i = this.palettes.length - 1; i >= 0; i--)
       
   795 			this.palettes[i].remove();
       
   796 	},
       
   797 
       
   798 	remove: function() {
       
   799 		this.clear();
       
   800 		delete PaperScope._scopes[this._id];
       
   801 	},
       
   802 
       
   803 	statics: new function() {
       
   804 		function handleAttribute(name) {
       
   805 			name += 'Attribute';
       
   806 			return function(el, attr) {
       
   807 				return el[name](attr) || el[name]('data-paper-' + attr);
       
   808 			};
       
   809 		}
       
   810 
       
   811 		return {
       
   812 			_scopes: {},
       
   813 			_id: 0,
       
   814 
       
   815 			get: function(id) {
       
   816 				return this._scopes[id] || null;
       
   817 			},
       
   818 
       
   819 			getAttribute: handleAttribute('get'),
       
   820 			hasAttribute: handleAttribute('has')
       
   821 		};
       
   822 	}
       
   823 });
       
   824 
       
   825 var PaperScopeItem = Base.extend(Emitter, {
       
   826 
       
   827 	initialize: function(activate) {
       
   828 		this._scope = paper;
       
   829 		this._index = this._scope[this._list].push(this) - 1;
       
   830 		if (activate || !this._scope[this._reference])
       
   831 			this.activate();
       
   832 	},
       
   833 
       
   834 	activate: function() {
       
   835 		if (!this._scope)
       
   836 			return false;
       
   837 		var prev = this._scope[this._reference];
       
   838 		if (prev && prev !== this)
       
   839 			prev.emit('deactivate');
       
   840 		this._scope[this._reference] = this;
       
   841 		this.emit('activate', prev);
       
   842 		return true;
       
   843 	},
       
   844 
       
   845 	isActive: function() {
       
   846 		return this._scope[this._reference] === this;
       
   847 	},
       
   848 
       
   849 	remove: function() {
       
   850 		if (this._index == null)
       
   851 			return false;
       
   852 		Base.splice(this._scope[this._list], null, this._index, 1);
       
   853 		if (this._scope[this._reference] == this)
       
   854 			this._scope[this._reference] = null;
       
   855 		this._scope = null;
       
   856 		return true;
       
   857 	}
       
   858 });
       
   859 
       
   860 var Formatter = Base.extend({
       
   861 	initialize: function(precision) {
       
   862 		this.precision = precision || 5;
       
   863 		this.multiplier = Math.pow(10, this.precision);
       
   864 	},
       
   865 
       
   866 	number: function(val) {
       
   867 		return Math.round(val * this.multiplier) / this.multiplier;
       
   868 	},
       
   869 
       
   870 	pair: function(val1, val2, separator) {
       
   871 		return this.number(val1) + (separator || ',') + this.number(val2);
       
   872 	},
       
   873 
       
   874 	point: function(val, separator) {
       
   875 		return this.number(val.x) + (separator || ',') + this.number(val.y);
       
   876 	},
       
   877 
       
   878 	size: function(val, separator) {
       
   879 		return this.number(val.width) + (separator || ',')
       
   880 				+ this.number(val.height);
       
   881 	},
       
   882 
       
   883 	rectangle: function(val, separator) {
       
   884 		return this.point(val, separator) + (separator || ',')
       
   885 				+ this.size(val, separator);
       
   886 	}
       
   887 });
       
   888 
       
   889 Formatter.instance = new Formatter();
       
   890 
       
   891 var Numerical = new function() {
       
   892 
       
   893 	var abscissas = [
       
   894 		[  0.5773502691896257645091488],
       
   895 		[0,0.7745966692414833770358531],
       
   896 		[  0.3399810435848562648026658,0.8611363115940525752239465],
       
   897 		[0,0.5384693101056830910363144,0.9061798459386639927976269],
       
   898 		[  0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016],
       
   899 		[0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897],
       
   900 		[  0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609],
       
   901 		[0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762],
       
   902 		[  0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640],
       
   903 		[0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380],
       
   904 		[  0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491],
       
   905 		[0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294],
       
   906 		[  0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973],
       
   907 		[0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657],
       
   908 		[  0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542]
       
   909 	];
       
   910 
       
   911 	var weights = [
       
   912 		[1],
       
   913 		[0.8888888888888888888888889,0.5555555555555555555555556],
       
   914 		[0.6521451548625461426269361,0.3478548451374538573730639],
       
   915 		[0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640],
       
   916 		[0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],
       
   917 		[0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114],
       
   918 		[0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314],
       
   919 		[0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922],
       
   920 		[0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688],
       
   921 		[0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537],
       
   922 		[0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160],
       
   923 		[0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216],
       
   924 		[0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329],
       
   925 		[0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284],
       
   926 		[0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806]
       
   927 	];
       
   928 
       
   929 	var abs = Math.abs,
       
   930 		sqrt = Math.sqrt,
       
   931 		pow = Math.pow,
       
   932 		TOLERANCE = 1e-6,
       
   933 		EPSILON = 1e-12,
       
   934 		MACHINE_EPSILON = 1.12e-16;
       
   935 
       
   936 	return {
       
   937 		TOLERANCE: TOLERANCE,
       
   938 		EPSILON: EPSILON,
       
   939 		MACHINE_EPSILON: MACHINE_EPSILON,
       
   940 		KAPPA: 4 * (sqrt(2) - 1) / 3,
       
   941 
       
   942 		isZero: function(val) {
       
   943 			return abs(val) <= EPSILON;
       
   944 		},
       
   945 
       
   946 		integrate: function(f, a, b, n) {
       
   947 			var x = abscissas[n - 2],
       
   948 				w = weights[n - 2],
       
   949 				A = (b - a) * 0.5,
       
   950 				B = A + a,
       
   951 				i = 0,
       
   952 				m = (n + 1) >> 1,
       
   953 				sum = n & 1 ? w[i++] * f(B) : 0;
       
   954 			while (i < m) {
       
   955 				var Ax = A * x[i];
       
   956 				sum += w[i++] * (f(B + Ax) + f(B - Ax));
       
   957 			}
       
   958 			return A * sum;
       
   959 		},
       
   960 
       
   961 		findRoot: function(f, df, x, a, b, n, tolerance) {
       
   962 			for (var i = 0; i < n; i++) {
       
   963 				var fx = f(x),
       
   964 					dx = fx / df(x),
       
   965 					nx = x - dx;
       
   966 				if (abs(dx) < tolerance)
       
   967 					return nx;
       
   968 				if (fx > 0) {
       
   969 					b = x;
       
   970 					x = nx <= a ? (a + b) * 0.5 : nx;
       
   971 				} else {
       
   972 					a = x;
       
   973 					x = nx >= b ? (a + b) * 0.5 : nx;
       
   974 				}
       
   975 			}
       
   976 			return x;
       
   977 		},
       
   978 
       
   979 		solveQuadratic: function(a, b, c, roots, min, max) {
       
   980 			var count = 0,
       
   981 				x1, x2 = Infinity,
       
   982 				B = b,
       
   983 				D;
       
   984 			b /= 2;
       
   985 			D = b * b - a * c;
       
   986 			if (D !== 0 && abs(D) < MACHINE_EPSILON) {
       
   987 				var gmC = pow(abs(a * b * c), 1 / 3);
       
   988 				if (gmC < 1e-8) {
       
   989 					var mult = pow(10, abs(
       
   990 						Math.floor(Math.log(gmC) * Math.LOG10E)));
       
   991 					if (!isFinite(mult))
       
   992 						mult = 0;
       
   993 					a *= mult;
       
   994 					b *= mult;
       
   995 					c *= mult;
       
   996 					D = b * b - a * c;
       
   997 				}
       
   998 			}
       
   999 			if (abs(a) < EPSILON) {
       
  1000 				if (abs(B) < EPSILON)
       
  1001 					return abs(c) < EPSILON ? -1 : 0;
       
  1002 				x1 = -c / B;
       
  1003 			} else {
       
  1004 				if (D >= -MACHINE_EPSILON) {
       
  1005 					D = D < 0 ? 0 : D;
       
  1006 					var R = sqrt(D);
       
  1007 					if (b >= MACHINE_EPSILON && b <= MACHINE_EPSILON) {
       
  1008 						x1 = abs(a) >= abs(c) ? R / a : -c / R;
       
  1009 						x2 = -x1;
       
  1010 					} else {
       
  1011 						var q = -(b + (b < 0 ? -1 : 1) * R);
       
  1012 						x1 = q / a;
       
  1013 						x2 = c / q;
       
  1014 					}
       
  1015 				}
       
  1016 			}
       
  1017 			if (isFinite(x1) && (min == null || x1 >= min && x1 <= max))
       
  1018 				roots[count++] = x1;
       
  1019 			if (x2 !== x1
       
  1020 					&& isFinite(x2) && (min == null || x2 >= min && x2 <= max))
       
  1021 				roots[count++] = x2;
       
  1022 			return count;
       
  1023 		},
       
  1024 
       
  1025 		solveCubic: function(a, b, c, d, roots, min, max) {
       
  1026 			var count = 0,
       
  1027 				x, b1, c2;
       
  1028 			if (abs(a) < EPSILON) {
       
  1029 				a = b;
       
  1030 				b1 = c;
       
  1031 				c2 = d;
       
  1032 				x = Infinity;
       
  1033 			} else if (abs(d) < EPSILON) {
       
  1034 				b1 = b;
       
  1035 				c2 = c;
       
  1036 				x = 0;
       
  1037 			} else {
       
  1038 				var ec = 1 + MACHINE_EPSILON,
       
  1039 					x0, q, qd, t, r, s, tmp;
       
  1040 				x = -(b / a) / 3;
       
  1041 				tmp = a * x,
       
  1042 				b1 = tmp + b,
       
  1043 				c2 = b1 * x + c,
       
  1044 				qd = (tmp + b1) * x + c2,
       
  1045 				q = c2 * x + d;
       
  1046 				t = q /a;
       
  1047 				r = pow(abs(t), 1/3);
       
  1048 				s = t < 0 ? -1 : 1;
       
  1049 				t = -qd / a;
       
  1050 				r = t > 0 ? 1.3247179572 * Math.max(r, sqrt(t)) : r;
       
  1051 				x0 = x - s * r;
       
  1052 				if (x0 !== x) {
       
  1053 					do {
       
  1054 						x = x0;
       
  1055 						tmp = a * x,
       
  1056 						b1 = tmp + b,
       
  1057 						c2 = b1 * x + c,
       
  1058 						qd = (tmp + b1) * x + c2,
       
  1059 						q = c2 * x + d;
       
  1060 						x0 = qd === 0 ? x : x - q / qd / ec;
       
  1061 						if (x0 === x) {
       
  1062 							x = x0;
       
  1063 							break;
       
  1064 						}
       
  1065 					} while (s * x0 > s * x);
       
  1066 					if (abs(a) * x * x > abs(d / x)) {
       
  1067 						c2 = -d / x;
       
  1068 						b1 = (c2 - c) / x;
       
  1069 					}
       
  1070 				}
       
  1071 			}
       
  1072 			var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max);
       
  1073 			if (isFinite(x) && (count === 0 || x !== roots[count - 1])
       
  1074 					&& (min == null || x >= min && x <= max))
       
  1075 				roots[count++] = x;
       
  1076 			return count;
       
  1077 		}
       
  1078 	};
       
  1079 };
       
  1080 
       
  1081 var UID = {
       
  1082 	_id: 1,
       
  1083 	_pools: {},
       
  1084 
       
  1085 	get: function(ctor) {
       
  1086 		if (ctor) {
       
  1087 			var name = ctor._class,
       
  1088 				pool = this._pools[name];
       
  1089 			if (!pool)
       
  1090 				pool = this._pools[name] = { _id: 1 };
       
  1091 			return pool._id++;
       
  1092 		} else {
       
  1093 			return this._id++;
       
  1094 		}
       
  1095 	}
       
  1096 };
       
  1097 
       
  1098 var Point = Base.extend({
       
  1099 	_class: 'Point',
       
  1100 	_readIndex: true,
       
  1101 
       
  1102 	initialize: function Point(arg0, arg1) {
       
  1103 		var type = typeof arg0;
       
  1104 		if (type === 'number') {
       
  1105 			var hasY = typeof arg1 === 'number';
       
  1106 			this.x = arg0;
       
  1107 			this.y = hasY ? arg1 : arg0;
       
  1108 			if (this.__read)
       
  1109 				this.__read = hasY ? 2 : 1;
       
  1110 		} else if (type === 'undefined' || arg0 === null) {
       
  1111 			this.x = this.y = 0;
       
  1112 			if (this.__read)
       
  1113 				this.__read = arg0 === null ? 1 : 0;
       
  1114 		} else {
       
  1115 			if (Array.isArray(arg0)) {
       
  1116 				this.x = arg0[0];
       
  1117 				this.y = arg0.length > 1 ? arg0[1] : arg0[0];
       
  1118 			} else if (arg0.x != null) {
       
  1119 				this.x = arg0.x;
       
  1120 				this.y = arg0.y;
       
  1121 			} else if (arg0.width != null) {
       
  1122 				this.x = arg0.width;
       
  1123 				this.y = arg0.height;
       
  1124 			} else if (arg0.angle != null) {
       
  1125 				this.x = arg0.length;
       
  1126 				this.y = 0;
       
  1127 				this.setAngle(arg0.angle);
       
  1128 			} else {
       
  1129 				this.x = this.y = 0;
       
  1130 				if (this.__read)
       
  1131 					this.__read = 0;
       
  1132 			}
       
  1133 			if (this.__read)
       
  1134 				this.__read = 1;
       
  1135 		}
       
  1136 	},
       
  1137 
       
  1138 	set: function(x, y) {
       
  1139 		this.x = x;
       
  1140 		this.y = y;
       
  1141 		return this;
       
  1142 	},
       
  1143 
       
  1144 	equals: function(point) {
       
  1145 		return this === point || point
       
  1146 				&& (this.x === point.x && this.y === point.y
       
  1147 					|| Array.isArray(point)
       
  1148 						&& this.x === point[0] && this.y === point[1])
       
  1149 				|| false;
       
  1150 	},
       
  1151 
       
  1152 	clone: function() {
       
  1153 		return new Point(this.x, this.y);
       
  1154 	},
       
  1155 
       
  1156 	toString: function() {
       
  1157 		var f = Formatter.instance;
       
  1158 		return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';
       
  1159 	},
       
  1160 
       
  1161 	_serialize: function(options) {
       
  1162 		var f = options.formatter;
       
  1163 		return [f.number(this.x), f.number(this.y)];
       
  1164 	},
       
  1165 
       
  1166 	getLength: function() {
       
  1167 		return Math.sqrt(this.x * this.x + this.y * this.y);
       
  1168 	},
       
  1169 
       
  1170 	setLength: function(length) {
       
  1171 		if (this.isZero()) {
       
  1172 			var angle = this._angle || 0;
       
  1173 			this.set(
       
  1174 				Math.cos(angle) * length,
       
  1175 				Math.sin(angle) * length
       
  1176 			);
       
  1177 		} else {
       
  1178 			var scale = length / this.getLength();
       
  1179 			if (Numerical.isZero(scale))
       
  1180 				this.getAngle();
       
  1181 			this.set(
       
  1182 				this.x * scale,
       
  1183 				this.y * scale
       
  1184 			);
       
  1185 		}
       
  1186 	},
       
  1187 	getAngle: function() {
       
  1188 		return this.getAngleInRadians.apply(this, arguments) * 180 / Math.PI;
       
  1189 	},
       
  1190 
       
  1191 	setAngle: function(angle) {
       
  1192 		this.setAngleInRadians.call(this, angle * Math.PI / 180);
       
  1193 	},
       
  1194 
       
  1195 	getAngleInDegrees: '#getAngle',
       
  1196 	setAngleInDegrees: '#setAngle',
       
  1197 
       
  1198 	getAngleInRadians: function() {
       
  1199 		if (!arguments.length) {
       
  1200 			return this.isZero()
       
  1201 					? this._angle || 0
       
  1202 					: this._angle = Math.atan2(this.y, this.x);
       
  1203 		} else {
       
  1204 			var point = Point.read(arguments),
       
  1205 				div = this.getLength() * point.getLength();
       
  1206 			if (Numerical.isZero(div)) {
       
  1207 				return NaN;
       
  1208 			} else {
       
  1209 				var a = this.dot(point) / div;
       
  1210 				return Math.acos(a < -1 ? -1 : a > 1 ? 1 : a);
       
  1211 			}
       
  1212 		}
       
  1213 	},
       
  1214 
       
  1215 	setAngleInRadians: function(angle) {
       
  1216 		this._angle = angle;
       
  1217 		if (!this.isZero()) {
       
  1218 			var length = this.getLength();
       
  1219 			this.set(
       
  1220 				Math.cos(angle) * length,
       
  1221 				Math.sin(angle) * length
       
  1222 			);
       
  1223 		}
       
  1224 	},
       
  1225 
       
  1226 	getQuadrant: function() {
       
  1227 		return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;
       
  1228 	}
       
  1229 }, {
       
  1230 	beans: false,
       
  1231 
       
  1232 	getDirectedAngle: function() {
       
  1233 		var point = Point.read(arguments);
       
  1234 		return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;
       
  1235 	},
       
  1236 
       
  1237 	getDistance: function() {
       
  1238 		var point = Point.read(arguments),
       
  1239 			x = point.x - this.x,
       
  1240 			y = point.y - this.y,
       
  1241 			d = x * x + y * y,
       
  1242 			squared = Base.read(arguments);
       
  1243 		return squared ? d : Math.sqrt(d);
       
  1244 	},
       
  1245 
       
  1246 	normalize: function(length) {
       
  1247 		if (length === undefined)
       
  1248 			length = 1;
       
  1249 		var current = this.getLength(),
       
  1250 			scale = current !== 0 ? length / current : 0,
       
  1251 			point = new Point(this.x * scale, this.y * scale);
       
  1252 		if (scale >= 0)
       
  1253 			point._angle = this._angle;
       
  1254 		return point;
       
  1255 	},
       
  1256 
       
  1257 	rotate: function(angle, center) {
       
  1258 		if (angle === 0)
       
  1259 			return this.clone();
       
  1260 		angle = angle * Math.PI / 180;
       
  1261 		var point = center ? this.subtract(center) : this,
       
  1262 			s = Math.sin(angle),
       
  1263 			c = Math.cos(angle);
       
  1264 		point = new Point(
       
  1265 			point.x * c - point.y * s,
       
  1266 			point.x * s + point.y * c
       
  1267 		);
       
  1268 		return center ? point.add(center) : point;
       
  1269 	},
       
  1270 
       
  1271 	transform: function(matrix) {
       
  1272 		return matrix ? matrix._transformPoint(this) : this;
       
  1273 	},
       
  1274 
       
  1275 	add: function() {
       
  1276 		var point = Point.read(arguments);
       
  1277 		return new Point(this.x + point.x, this.y + point.y);
       
  1278 	},
       
  1279 
       
  1280 	subtract: function() {
       
  1281 		var point = Point.read(arguments);
       
  1282 		return new Point(this.x - point.x, this.y - point.y);
       
  1283 	},
       
  1284 
       
  1285 	multiply: function() {
       
  1286 		var point = Point.read(arguments);
       
  1287 		return new Point(this.x * point.x, this.y * point.y);
       
  1288 	},
       
  1289 
       
  1290 	divide: function() {
       
  1291 		var point = Point.read(arguments);
       
  1292 		return new Point(this.x / point.x, this.y / point.y);
       
  1293 	},
       
  1294 
       
  1295 	modulo: function() {
       
  1296 		var point = Point.read(arguments);
       
  1297 		return new Point(this.x % point.x, this.y % point.y);
       
  1298 	},
       
  1299 
       
  1300 	negate: function() {
       
  1301 		return new Point(-this.x, -this.y);
       
  1302 	},
       
  1303 
       
  1304 	isInside: function() {
       
  1305 		return Rectangle.read(arguments).contains(this);
       
  1306 	},
       
  1307 
       
  1308 	isClose: function(point, tolerance) {
       
  1309 		return this.getDistance(point) < tolerance;
       
  1310 	},
       
  1311 
       
  1312 	isCollinear: function(point) {
       
  1313 		return Math.abs(this.cross(point)) < 0.000001;
       
  1314 	},
       
  1315 
       
  1316 	isColinear: '#isCollinear',
       
  1317 
       
  1318 	isOrthogonal: function(point) {
       
  1319 		return Math.abs(this.dot(point)) < 0.000001;
       
  1320 	},
       
  1321 
       
  1322 	isZero: function() {
       
  1323 		return Numerical.isZero(this.x) && Numerical.isZero(this.y);
       
  1324 	},
       
  1325 
       
  1326 	isNaN: function() {
       
  1327 		return isNaN(this.x) || isNaN(this.y);
       
  1328 	},
       
  1329 
       
  1330 	dot: function() {
       
  1331 		var point = Point.read(arguments);
       
  1332 		return this.x * point.x + this.y * point.y;
       
  1333 	},
       
  1334 
       
  1335 	cross: function() {
       
  1336 		var point = Point.read(arguments);
       
  1337 		return this.x * point.y - this.y * point.x;
       
  1338 	},
       
  1339 
       
  1340 	project: function() {
       
  1341 		var point = Point.read(arguments);
       
  1342 		if (point.isZero()) {
       
  1343 			return new Point(0, 0);
       
  1344 		} else {
       
  1345 			var scale = this.dot(point) / point.dot(point);
       
  1346 			return new Point(
       
  1347 				point.x * scale,
       
  1348 				point.y * scale
       
  1349 			);
       
  1350 		}
       
  1351 	},
       
  1352 
       
  1353 	statics: {
       
  1354 		min: function() {
       
  1355 			var point1 = Point.read(arguments),
       
  1356 				point2 = Point.read(arguments);
       
  1357 			return new Point(
       
  1358 				Math.min(point1.x, point2.x),
       
  1359 				Math.min(point1.y, point2.y)
       
  1360 			);
       
  1361 		},
       
  1362 
       
  1363 		max: function() {
       
  1364 			var point1 = Point.read(arguments),
       
  1365 				point2 = Point.read(arguments);
       
  1366 			return new Point(
       
  1367 				Math.max(point1.x, point2.x),
       
  1368 				Math.max(point1.y, point2.y)
       
  1369 			);
       
  1370 		},
       
  1371 
       
  1372 		random: function() {
       
  1373 			return new Point(Math.random(), Math.random());
       
  1374 		}
       
  1375 	}
       
  1376 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
       
  1377 	var op = Math[name];
       
  1378 	this[name] = function() {
       
  1379 		return new Point(op(this.x), op(this.y));
       
  1380 	};
       
  1381 }, {}));
       
  1382 
       
  1383 var LinkedPoint = Point.extend({
       
  1384 	initialize: function Point(x, y, owner, setter) {
       
  1385 		this._x = x;
       
  1386 		this._y = y;
       
  1387 		this._owner = owner;
       
  1388 		this._setter = setter;
       
  1389 	},
       
  1390 
       
  1391 	set: function(x, y, _dontNotify) {
       
  1392 		this._x = x;
       
  1393 		this._y = y;
       
  1394 		if (!_dontNotify)
       
  1395 			this._owner[this._setter](this);
       
  1396 		return this;
       
  1397 	},
       
  1398 
       
  1399 	getX: function() {
       
  1400 		return this._x;
       
  1401 	},
       
  1402 
       
  1403 	setX: function(x) {
       
  1404 		this._x = x;
       
  1405 		this._owner[this._setter](this);
       
  1406 	},
       
  1407 
       
  1408 	getY: function() {
       
  1409 		return this._y;
       
  1410 	},
       
  1411 
       
  1412 	setY: function(y) {
       
  1413 		this._y = y;
       
  1414 		this._owner[this._setter](this);
       
  1415 	}
       
  1416 });
       
  1417 
       
  1418 var Size = Base.extend({
       
  1419 	_class: 'Size',
       
  1420 	_readIndex: true,
       
  1421 
       
  1422 	initialize: function Size(arg0, arg1) {
       
  1423 		var type = typeof arg0;
       
  1424 		if (type === 'number') {
       
  1425 			var hasHeight = typeof arg1 === 'number';
       
  1426 			this.width = arg0;
       
  1427 			this.height = hasHeight ? arg1 : arg0;
       
  1428 			if (this.__read)
       
  1429 				this.__read = hasHeight ? 2 : 1;
       
  1430 		} else if (type === 'undefined' || arg0 === null) {
       
  1431 			this.width = this.height = 0;
       
  1432 			if (this.__read)
       
  1433 				this.__read = arg0 === null ? 1 : 0;
       
  1434 		} else {
       
  1435 			if (Array.isArray(arg0)) {
       
  1436 				this.width = arg0[0];
       
  1437 				this.height = arg0.length > 1 ? arg0[1] : arg0[0];
       
  1438 			} else if (arg0.width != null) {
       
  1439 				this.width = arg0.width;
       
  1440 				this.height = arg0.height;
       
  1441 			} else if (arg0.x != null) {
       
  1442 				this.width = arg0.x;
       
  1443 				this.height = arg0.y;
       
  1444 			} else {
       
  1445 				this.width = this.height = 0;
       
  1446 				if (this.__read)
       
  1447 					this.__read = 0;
       
  1448 			}
       
  1449 			if (this.__read)
       
  1450 				this.__read = 1;
       
  1451 		}
       
  1452 	},
       
  1453 
       
  1454 	set: function(width, height) {
       
  1455 		this.width = width;
       
  1456 		this.height = height;
       
  1457 		return this;
       
  1458 	},
       
  1459 
       
  1460 	equals: function(size) {
       
  1461 		return size === this || size && (this.width === size.width
       
  1462 				&& this.height === size.height
       
  1463 				|| Array.isArray(size) && this.width === size[0]
       
  1464 					&& this.height === size[1]) || false;
       
  1465 	},
       
  1466 
       
  1467 	clone: function() {
       
  1468 		return new Size(this.width, this.height);
       
  1469 	},
       
  1470 
       
  1471 	toString: function() {
       
  1472 		var f = Formatter.instance;
       
  1473 		return '{ width: ' + f.number(this.width)
       
  1474 				+ ', height: ' + f.number(this.height) + ' }';
       
  1475 	},
       
  1476 
       
  1477 	_serialize: function(options) {
       
  1478 		var f = options.formatter;
       
  1479 		return [f.number(this.width),
       
  1480 				f.number(this.height)];
       
  1481 	},
       
  1482 
       
  1483 	add: function() {
       
  1484 		var size = Size.read(arguments);
       
  1485 		return new Size(this.width + size.width, this.height + size.height);
       
  1486 	},
       
  1487 
       
  1488 	subtract: function() {
       
  1489 		var size = Size.read(arguments);
       
  1490 		return new Size(this.width - size.width, this.height - size.height);
       
  1491 	},
       
  1492 
       
  1493 	multiply: function() {
       
  1494 		var size = Size.read(arguments);
       
  1495 		return new Size(this.width * size.width, this.height * size.height);
       
  1496 	},
       
  1497 
       
  1498 	divide: function() {
       
  1499 		var size = Size.read(arguments);
       
  1500 		return new Size(this.width / size.width, this.height / size.height);
       
  1501 	},
       
  1502 
       
  1503 	modulo: function() {
       
  1504 		var size = Size.read(arguments);
       
  1505 		return new Size(this.width % size.width, this.height % size.height);
       
  1506 	},
       
  1507 
       
  1508 	negate: function() {
       
  1509 		return new Size(-this.width, -this.height);
       
  1510 	},
       
  1511 
       
  1512 	isZero: function() {
       
  1513 		return Numerical.isZero(this.width) && Numerical.isZero(this.height);
       
  1514 	},
       
  1515 
       
  1516 	isNaN: function() {
       
  1517 		return isNaN(this.width) || isNaN(this.height);
       
  1518 	},
       
  1519 
       
  1520 	statics: {
       
  1521 		min: function(size1, size2) {
       
  1522 			return new Size(
       
  1523 				Math.min(size1.width, size2.width),
       
  1524 				Math.min(size1.height, size2.height));
       
  1525 		},
       
  1526 
       
  1527 		max: function(size1, size2) {
       
  1528 			return new Size(
       
  1529 				Math.max(size1.width, size2.width),
       
  1530 				Math.max(size1.height, size2.height));
       
  1531 		},
       
  1532 
       
  1533 		random: function() {
       
  1534 			return new Size(Math.random(), Math.random());
       
  1535 		}
       
  1536 	}
       
  1537 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
       
  1538 	var op = Math[name];
       
  1539 	this[name] = function() {
       
  1540 		return new Size(op(this.width), op(this.height));
       
  1541 	};
       
  1542 }, {}));
       
  1543 
       
  1544 var LinkedSize = Size.extend({
       
  1545 	initialize: function Size(width, height, owner, setter) {
       
  1546 		this._width = width;
       
  1547 		this._height = height;
       
  1548 		this._owner = owner;
       
  1549 		this._setter = setter;
       
  1550 	},
       
  1551 
       
  1552 	set: function(width, height, _dontNotify) {
       
  1553 		this._width = width;
       
  1554 		this._height = height;
       
  1555 		if (!_dontNotify)
       
  1556 			this._owner[this._setter](this);
       
  1557 		return this;
       
  1558 	},
       
  1559 
       
  1560 	getWidth: function() {
       
  1561 		return this._width;
       
  1562 	},
       
  1563 
       
  1564 	setWidth: function(width) {
       
  1565 		this._width = width;
       
  1566 		this._owner[this._setter](this);
       
  1567 	},
       
  1568 
       
  1569 	getHeight: function() {
       
  1570 		return this._height;
       
  1571 	},
       
  1572 
       
  1573 	setHeight: function(height) {
       
  1574 		this._height = height;
       
  1575 		this._owner[this._setter](this);
       
  1576 	}
       
  1577 });
       
  1578 
       
  1579 var Rectangle = Base.extend({
       
  1580 	_class: 'Rectangle',
       
  1581 	_readIndex: true,
       
  1582 	beans: true,
       
  1583 
       
  1584 	initialize: function Rectangle(arg0, arg1, arg2, arg3) {
       
  1585 		var type = typeof arg0,
       
  1586 			read = 0;
       
  1587 		if (type === 'number') {
       
  1588 			this.x = arg0;
       
  1589 			this.y = arg1;
       
  1590 			this.width = arg2;
       
  1591 			this.height = arg3;
       
  1592 			read = 4;
       
  1593 		} else if (type === 'undefined' || arg0 === null) {
       
  1594 			this.x = this.y = this.width = this.height = 0;
       
  1595 			read = arg0 === null ? 1 : 0;
       
  1596 		} else if (arguments.length === 1) {
       
  1597 			if (Array.isArray(arg0)) {
       
  1598 				this.x = arg0[0];
       
  1599 				this.y = arg0[1];
       
  1600 				this.width = arg0[2];
       
  1601 				this.height = arg0[3];
       
  1602 				read = 1;
       
  1603 			} else if (arg0.x !== undefined || arg0.width !== undefined) {
       
  1604 				this.x = arg0.x || 0;
       
  1605 				this.y = arg0.y || 0;
       
  1606 				this.width = arg0.width || 0;
       
  1607 				this.height = arg0.height || 0;
       
  1608 				read = 1;
       
  1609 			} else if (arg0.from === undefined && arg0.to === undefined) {
       
  1610 				this.x = this.y = this.width = this.height = 0;
       
  1611 				this._set(arg0);
       
  1612 				read = 1;
       
  1613 			}
       
  1614 		}
       
  1615 		if (!read) {
       
  1616 			var point = Point.readNamed(arguments, 'from'),
       
  1617 				next = Base.peek(arguments);
       
  1618 			this.x = point.x;
       
  1619 			this.y = point.y;
       
  1620 			if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
       
  1621 				var to = Point.readNamed(arguments, 'to');
       
  1622 				this.width = to.x - point.x;
       
  1623 				this.height = to.y - point.y;
       
  1624 				if (this.width < 0) {
       
  1625 					this.x = to.x;
       
  1626 					this.width = -this.width;
       
  1627 				}
       
  1628 				if (this.height < 0) {
       
  1629 					this.y = to.y;
       
  1630 					this.height = -this.height;
       
  1631 				}
       
  1632 			} else {
       
  1633 				var size = Size.read(arguments);
       
  1634 				this.width = size.width;
       
  1635 				this.height = size.height;
       
  1636 			}
       
  1637 			read = arguments.__index;
       
  1638 		}
       
  1639 		if (this.__read)
       
  1640 			this.__read = read;
       
  1641 	},
       
  1642 
       
  1643 	set: function(x, y, width, height) {
       
  1644 		this.x = x;
       
  1645 		this.y = y;
       
  1646 		this.width = width;
       
  1647 		this.height = height;
       
  1648 		return this;
       
  1649 	},
       
  1650 
       
  1651 	clone: function() {
       
  1652 		return new Rectangle(this.x, this.y, this.width, this.height);
       
  1653 	},
       
  1654 
       
  1655 	equals: function(rect) {
       
  1656 		var rt = Base.isPlainValue(rect)
       
  1657 				? Rectangle.read(arguments)
       
  1658 				: rect;
       
  1659 		return rt === this
       
  1660 				|| rt && this.x === rt.x && this.y === rt.y
       
  1661 					&& this.width === rt.width && this.height === rt.height
       
  1662 				|| false;
       
  1663 	},
       
  1664 
       
  1665 	toString: function() {
       
  1666 		var f = Formatter.instance;
       
  1667 		return '{ x: ' + f.number(this.x)
       
  1668 				+ ', y: ' + f.number(this.y)
       
  1669 				+ ', width: ' + f.number(this.width)
       
  1670 				+ ', height: ' + f.number(this.height)
       
  1671 				+ ' }';
       
  1672 	},
       
  1673 
       
  1674 	_serialize: function(options) {
       
  1675 		var f = options.formatter;
       
  1676 		return [f.number(this.x),
       
  1677 				f.number(this.y),
       
  1678 				f.number(this.width),
       
  1679 				f.number(this.height)];
       
  1680 	},
       
  1681 
       
  1682 	getPoint: function(_dontLink) {
       
  1683 		var ctor = _dontLink ? Point : LinkedPoint;
       
  1684 		return new ctor(this.x, this.y, this, 'setPoint');
       
  1685 	},
       
  1686 
       
  1687 	setPoint: function() {
       
  1688 		var point = Point.read(arguments);
       
  1689 		this.x = point.x;
       
  1690 		this.y = point.y;
       
  1691 	},
       
  1692 
       
  1693 	getSize: function(_dontLink) {
       
  1694 		var ctor = _dontLink ? Size : LinkedSize;
       
  1695 		return new ctor(this.width, this.height, this, 'setSize');
       
  1696 	},
       
  1697 
       
  1698 	setSize: function() {
       
  1699 		var size = Size.read(arguments);
       
  1700 		if (this._fixX)
       
  1701 			this.x += (this.width - size.width) * this._fixX;
       
  1702 		if (this._fixY)
       
  1703 			this.y += (this.height - size.height) * this._fixY;
       
  1704 		this.width = size.width;
       
  1705 		this.height = size.height;
       
  1706 		this._fixW = 1;
       
  1707 		this._fixH = 1;
       
  1708 	},
       
  1709 
       
  1710 	getLeft: function() {
       
  1711 		return this.x;
       
  1712 	},
       
  1713 
       
  1714 	setLeft: function(left) {
       
  1715 		if (!this._fixW)
       
  1716 			this.width -= left - this.x;
       
  1717 		this.x = left;
       
  1718 		this._fixX = 0;
       
  1719 	},
       
  1720 
       
  1721 	getTop: function() {
       
  1722 		return this.y;
       
  1723 	},
       
  1724 
       
  1725 	setTop: function(top) {
       
  1726 		if (!this._fixH)
       
  1727 			this.height -= top - this.y;
       
  1728 		this.y = top;
       
  1729 		this._fixY = 0;
       
  1730 	},
       
  1731 
       
  1732 	getRight: function() {
       
  1733 		return this.x + this.width;
       
  1734 	},
       
  1735 
       
  1736 	setRight: function(right) {
       
  1737 		if (this._fixX !== undefined && this._fixX !== 1)
       
  1738 			this._fixW = 0;
       
  1739 		if (this._fixW)
       
  1740 			this.x = right - this.width;
       
  1741 		else
       
  1742 			this.width = right - this.x;
       
  1743 		this._fixX = 1;
       
  1744 	},
       
  1745 
       
  1746 	getBottom: function() {
       
  1747 		return this.y + this.height;
       
  1748 	},
       
  1749 
       
  1750 	setBottom: function(bottom) {
       
  1751 		if (this._fixY !== undefined && this._fixY !== 1)
       
  1752 			this._fixH = 0;
       
  1753 		if (this._fixH)
       
  1754 			this.y = bottom - this.height;
       
  1755 		else
       
  1756 			this.height = bottom - this.y;
       
  1757 		this._fixY = 1;
       
  1758 	},
       
  1759 
       
  1760 	getCenterX: function() {
       
  1761 		return this.x + this.width * 0.5;
       
  1762 	},
       
  1763 
       
  1764 	setCenterX: function(x) {
       
  1765 		this.x = x - this.width * 0.5;
       
  1766 		this._fixX = 0.5;
       
  1767 	},
       
  1768 
       
  1769 	getCenterY: function() {
       
  1770 		return this.y + this.height * 0.5;
       
  1771 	},
       
  1772 
       
  1773 	setCenterY: function(y) {
       
  1774 		this.y = y - this.height * 0.5;
       
  1775 		this._fixY = 0.5;
       
  1776 	},
       
  1777 
       
  1778 	getCenter: function(_dontLink) {
       
  1779 		var ctor = _dontLink ? Point : LinkedPoint;
       
  1780 		return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');
       
  1781 	},
       
  1782 
       
  1783 	setCenter: function() {
       
  1784 		var point = Point.read(arguments);
       
  1785 		this.setCenterX(point.x);
       
  1786 		this.setCenterY(point.y);
       
  1787 		return this;
       
  1788 	},
       
  1789 
       
  1790 	getArea: function() {
       
  1791 		return this.width * this.height;
       
  1792 	},
       
  1793 
       
  1794 	isEmpty: function() {
       
  1795 		return this.width === 0 || this.height === 0;
       
  1796 	},
       
  1797 
       
  1798 	contains: function(arg) {
       
  1799 		return arg && arg.width !== undefined
       
  1800 				|| (Array.isArray(arg) ? arg : arguments).length == 4
       
  1801 				? this._containsRectangle(Rectangle.read(arguments))
       
  1802 				: this._containsPoint(Point.read(arguments));
       
  1803 	},
       
  1804 
       
  1805 	_containsPoint: function(point) {
       
  1806 		var x = point.x,
       
  1807 			y = point.y;
       
  1808 		return x >= this.x && y >= this.y
       
  1809 				&& x <= this.x + this.width
       
  1810 				&& y <= this.y + this.height;
       
  1811 	},
       
  1812 
       
  1813 	_containsRectangle: function(rect) {
       
  1814 		var x = rect.x,
       
  1815 			y = rect.y;
       
  1816 		return x >= this.x && y >= this.y
       
  1817 				&& x + rect.width <= this.x + this.width
       
  1818 				&& y + rect.height <= this.y + this.height;
       
  1819 	},
       
  1820 
       
  1821 	intersects: function() {
       
  1822 		var rect = Rectangle.read(arguments);
       
  1823 		return rect.x + rect.width > this.x
       
  1824 				&& rect.y + rect.height > this.y
       
  1825 				&& rect.x < this.x + this.width
       
  1826 				&& rect.y < this.y + this.height;
       
  1827 	},
       
  1828 
       
  1829 	touches: function() {
       
  1830 		var rect = Rectangle.read(arguments);
       
  1831 		return rect.x + rect.width >= this.x
       
  1832 				&& rect.y + rect.height >= this.y
       
  1833 				&& rect.x <= this.x + this.width
       
  1834 				&& rect.y <= this.y + this.height;
       
  1835 	},
       
  1836 
       
  1837 	intersect: function() {
       
  1838 		var rect = Rectangle.read(arguments),
       
  1839 			x1 = Math.max(this.x, rect.x),
       
  1840 			y1 = Math.max(this.y, rect.y),
       
  1841 			x2 = Math.min(this.x + this.width, rect.x + rect.width),
       
  1842 			y2 = Math.min(this.y + this.height, rect.y + rect.height);
       
  1843 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1844 	},
       
  1845 
       
  1846 	unite: function() {
       
  1847 		var rect = Rectangle.read(arguments),
       
  1848 			x1 = Math.min(this.x, rect.x),
       
  1849 			y1 = Math.min(this.y, rect.y),
       
  1850 			x2 = Math.max(this.x + this.width, rect.x + rect.width),
       
  1851 			y2 = Math.max(this.y + this.height, rect.y + rect.height);
       
  1852 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1853 	},
       
  1854 
       
  1855 	include: function() {
       
  1856 		var point = Point.read(arguments);
       
  1857 		var x1 = Math.min(this.x, point.x),
       
  1858 			y1 = Math.min(this.y, point.y),
       
  1859 			x2 = Math.max(this.x + this.width, point.x),
       
  1860 			y2 = Math.max(this.y + this.height, point.y);
       
  1861 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1862 	},
       
  1863 
       
  1864 	expand: function() {
       
  1865 		var amount = Size.read(arguments),
       
  1866 			hor = amount.width,
       
  1867 			ver = amount.height;
       
  1868 		return new Rectangle(this.x - hor / 2, this.y - ver / 2,
       
  1869 				this.width + hor, this.height + ver);
       
  1870 	},
       
  1871 
       
  1872 	scale: function(hor, ver) {
       
  1873 		return this.expand(this.width * hor - this.width,
       
  1874 				this.height * (ver === undefined ? hor : ver) - this.height);
       
  1875 	}
       
  1876 }, Base.each([
       
  1877 		['Top', 'Left'], ['Top', 'Right'],
       
  1878 		['Bottom', 'Left'], ['Bottom', 'Right'],
       
  1879 		['Left', 'Center'], ['Top', 'Center'],
       
  1880 		['Right', 'Center'], ['Bottom', 'Center']
       
  1881 	],
       
  1882 	function(parts, index) {
       
  1883 		var part = parts.join('');
       
  1884 		var xFirst = /^[RL]/.test(part);
       
  1885 		if (index >= 4)
       
  1886 			parts[1] += xFirst ? 'Y' : 'X';
       
  1887 		var x = parts[xFirst ? 0 : 1],
       
  1888 			y = parts[xFirst ? 1 : 0],
       
  1889 			getX = 'get' + x,
       
  1890 			getY = 'get' + y,
       
  1891 			setX = 'set' + x,
       
  1892 			setY = 'set' + y,
       
  1893 			get = 'get' + part,
       
  1894 			set = 'set' + part;
       
  1895 		this[get] = function(_dontLink) {
       
  1896 			var ctor = _dontLink ? Point : LinkedPoint;
       
  1897 			return new ctor(this[getX](), this[getY](), this, set);
       
  1898 		};
       
  1899 		this[set] = function() {
       
  1900 			var point = Point.read(arguments);
       
  1901 			this[setX](point.x);
       
  1902 			this[setY](point.y);
       
  1903 		};
       
  1904 	}, {
       
  1905 		beans: true
       
  1906 	}
       
  1907 ));
       
  1908 
       
  1909 var LinkedRectangle = Rectangle.extend({
       
  1910 	initialize: function Rectangle(x, y, width, height, owner, setter) {
       
  1911 		this.set(x, y, width, height, true);
       
  1912 		this._owner = owner;
       
  1913 		this._setter = setter;
       
  1914 	},
       
  1915 
       
  1916 	set: function(x, y, width, height, _dontNotify) {
       
  1917 		this._x = x;
       
  1918 		this._y = y;
       
  1919 		this._width = width;
       
  1920 		this._height = height;
       
  1921 		if (!_dontNotify)
       
  1922 			this._owner[this._setter](this);
       
  1923 		return this;
       
  1924 	}
       
  1925 }, new function() {
       
  1926 	var proto = Rectangle.prototype;
       
  1927 
       
  1928 	return Base.each(['x', 'y', 'width', 'height'], function(key) {
       
  1929 		var part = Base.capitalize(key);
       
  1930 		var internal = '_' + key;
       
  1931 		this['get' + part] = function() {
       
  1932 			return this[internal];
       
  1933 		};
       
  1934 
       
  1935 		this['set' + part] = function(value) {
       
  1936 			this[internal] = value;
       
  1937 			if (!this._dontNotify)
       
  1938 				this._owner[this._setter](this);
       
  1939 		};
       
  1940 	}, Base.each(['Point', 'Size', 'Center',
       
  1941 			'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',
       
  1942 			'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
       
  1943 			'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],
       
  1944 		function(key) {
       
  1945 			var name = 'set' + key;
       
  1946 			this[name] = function() {
       
  1947 				this._dontNotify = true;
       
  1948 				proto[name].apply(this, arguments);
       
  1949 				this._dontNotify = false;
       
  1950 				this._owner[this._setter](this);
       
  1951 			};
       
  1952 		}, {
       
  1953 			isSelected: function() {
       
  1954 				return this._owner._boundsSelected;
       
  1955 			},
       
  1956 
       
  1957 			setSelected: function(selected) {
       
  1958 				var owner = this._owner;
       
  1959 				if (owner.setSelected) {
       
  1960 					owner._boundsSelected = selected;
       
  1961 					owner.setSelected(selected || owner._selectedSegmentState > 0);
       
  1962 				}
       
  1963 			}
       
  1964 		})
       
  1965 	);
       
  1966 });
       
  1967 
       
  1968 var Matrix = Base.extend({
       
  1969 	_class: 'Matrix',
       
  1970 
       
  1971 	initialize: function Matrix(arg) {
       
  1972 		var count = arguments.length,
       
  1973 			ok = true;
       
  1974 		if (count === 6) {
       
  1975 			this.set.apply(this, arguments);
       
  1976 		} else if (count === 1) {
       
  1977 			if (arg instanceof Matrix) {
       
  1978 				this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
       
  1979 			} else if (Array.isArray(arg)) {
       
  1980 				this.set.apply(this, arg);
       
  1981 			} else {
       
  1982 				ok = false;
       
  1983 			}
       
  1984 		} else if (count === 0) {
       
  1985 			this.reset();
       
  1986 		} else {
       
  1987 			ok = false;
       
  1988 		}
       
  1989 		if (!ok)
       
  1990 			throw new Error('Unsupported matrix parameters');
       
  1991 	},
       
  1992 
       
  1993 	set: function(a, c, b, d, tx, ty, _dontNotify) {
       
  1994 		this._a = a;
       
  1995 		this._c = c;
       
  1996 		this._b = b;
       
  1997 		this._d = d;
       
  1998 		this._tx = tx;
       
  1999 		this._ty = ty;
       
  2000 		if (!_dontNotify)
       
  2001 			this._changed();
       
  2002 		return this;
       
  2003 	},
       
  2004 
       
  2005 	_serialize: function(options) {
       
  2006 		return Base.serialize(this.getValues(), options);
       
  2007 	},
       
  2008 
       
  2009 	_changed: function() {
       
  2010 		var owner = this._owner;
       
  2011 		if (owner) {
       
  2012 			if (owner._applyMatrix) {
       
  2013 				owner.transform(null, true);
       
  2014 			} else {
       
  2015 				owner._changed(9);
       
  2016 			}
       
  2017 		}
       
  2018 	},
       
  2019 
       
  2020 	clone: function() {
       
  2021 		return new Matrix(this._a, this._c, this._b, this._d,
       
  2022 				this._tx, this._ty);
       
  2023 	},
       
  2024 
       
  2025 	equals: function(mx) {
       
  2026 		return mx === this || mx && this._a === mx._a && this._b === mx._b
       
  2027 				&& this._c === mx._c && this._d === mx._d
       
  2028 				&& this._tx === mx._tx && this._ty === mx._ty
       
  2029 				|| false;
       
  2030 	},
       
  2031 
       
  2032 	toString: function() {
       
  2033 		var f = Formatter.instance;
       
  2034 		return '[[' + [f.number(this._a), f.number(this._b),
       
  2035 					f.number(this._tx)].join(', ') + '], ['
       
  2036 				+ [f.number(this._c), f.number(this._d),
       
  2037 					f.number(this._ty)].join(', ') + ']]';
       
  2038 	},
       
  2039 
       
  2040 	reset: function(_dontNotify) {
       
  2041 		this._a = this._d = 1;
       
  2042 		this._c = this._b = this._tx = this._ty = 0;
       
  2043 		if (!_dontNotify)
       
  2044 			this._changed();
       
  2045 		return this;
       
  2046 	},
       
  2047 
       
  2048 	apply: function(recursively, _setApplyMatrix) {
       
  2049 		var owner = this._owner;
       
  2050 		if (owner) {
       
  2051 			owner.transform(null, true, Base.pick(recursively, true),
       
  2052 					_setApplyMatrix);
       
  2053 			return this.isIdentity();
       
  2054 		}
       
  2055 		return false;
       
  2056 	},
       
  2057 
       
  2058 	translate: function() {
       
  2059 		var point = Point.read(arguments),
       
  2060 			x = point.x,
       
  2061 			y = point.y;
       
  2062 		this._tx += x * this._a + y * this._b;
       
  2063 		this._ty += x * this._c + y * this._d;
       
  2064 		this._changed();
       
  2065 		return this;
       
  2066 	},
       
  2067 
       
  2068 	scale: function() {
       
  2069 		var scale = Point.read(arguments),
       
  2070 			center = Point.read(arguments, 0, { readNull: true });
       
  2071 		if (center)
       
  2072 			this.translate(center);
       
  2073 		this._a *= scale.x;
       
  2074 		this._c *= scale.x;
       
  2075 		this._b *= scale.y;
       
  2076 		this._d *= scale.y;
       
  2077 		if (center)
       
  2078 			this.translate(center.negate());
       
  2079 		this._changed();
       
  2080 		return this;
       
  2081 	},
       
  2082 
       
  2083 	rotate: function(angle ) {
       
  2084 		angle *= Math.PI / 180;
       
  2085 		var center = Point.read(arguments, 1),
       
  2086 			x = center.x,
       
  2087 			y = center.y,
       
  2088 			cos = Math.cos(angle),
       
  2089 			sin = Math.sin(angle),
       
  2090 			tx = x - x * cos + y * sin,
       
  2091 			ty = y - x * sin - y * cos,
       
  2092 			a = this._a,
       
  2093 			b = this._b,
       
  2094 			c = this._c,
       
  2095 			d = this._d;
       
  2096 		this._a = cos * a + sin * b;
       
  2097 		this._b = -sin * a + cos * b;
       
  2098 		this._c = cos * c + sin * d;
       
  2099 		this._d = -sin * c + cos * d;
       
  2100 		this._tx += tx * a + ty * b;
       
  2101 		this._ty += tx * c + ty * d;
       
  2102 		this._changed();
       
  2103 		return this;
       
  2104 	},
       
  2105 
       
  2106 	shear: function() {
       
  2107 		var shear = Point.read(arguments),
       
  2108 			center = Point.read(arguments, 0, { readNull: true });
       
  2109 		if (center)
       
  2110 			this.translate(center);
       
  2111 		var a = this._a,
       
  2112 			c = this._c;
       
  2113 		this._a += shear.y * this._b;
       
  2114 		this._c += shear.y * this._d;
       
  2115 		this._b += shear.x * a;
       
  2116 		this._d += shear.x * c;
       
  2117 		if (center)
       
  2118 			this.translate(center.negate());
       
  2119 		this._changed();
       
  2120 		return this;
       
  2121 	},
       
  2122 
       
  2123 	skew: function() {
       
  2124 		var skew = Point.read(arguments),
       
  2125 			center = Point.read(arguments, 0, { readNull: true }),
       
  2126 			toRadians = Math.PI / 180,
       
  2127 			shear = new Point(Math.tan(skew.x * toRadians),
       
  2128 				Math.tan(skew.y * toRadians));
       
  2129 		return this.shear(shear, center);
       
  2130 	},
       
  2131 
       
  2132 	concatenate: function(mx) {
       
  2133 		var a1 = this._a,
       
  2134 			b1 = this._b,
       
  2135 			c1 = this._c,
       
  2136 			d1 = this._d,
       
  2137 			a2 = mx._a,
       
  2138 			b2 = mx._b,
       
  2139 			c2 = mx._c,
       
  2140 			d2 = mx._d,
       
  2141 			tx2 = mx._tx,
       
  2142 			ty2 = mx._ty;
       
  2143 		this._a = a2 * a1 + c2 * b1;
       
  2144 		this._b = b2 * a1 + d2 * b1;
       
  2145 		this._c = a2 * c1 + c2 * d1;
       
  2146 		this._d = b2 * c1 + d2 * d1;
       
  2147 		this._tx += tx2 * a1 + ty2 * b1;
       
  2148 		this._ty += tx2 * c1 + ty2 * d1;
       
  2149 		this._changed();
       
  2150 		return this;
       
  2151 	},
       
  2152 
       
  2153 	preConcatenate: function(mx) {
       
  2154 		var a1 = this._a,
       
  2155 			b1 = this._b,
       
  2156 			c1 = this._c,
       
  2157 			d1 = this._d,
       
  2158 			tx1 = this._tx,
       
  2159 			ty1 = this._ty,
       
  2160 			a2 = mx._a,
       
  2161 			b2 = mx._b,
       
  2162 			c2 = mx._c,
       
  2163 			d2 = mx._d,
       
  2164 			tx2 = mx._tx,
       
  2165 			ty2 = mx._ty;
       
  2166 		this._a = a2 * a1 + b2 * c1;
       
  2167 		this._b = a2 * b1 + b2 * d1;
       
  2168 		this._c = c2 * a1 + d2 * c1;
       
  2169 		this._d = c2 * b1 + d2 * d1;
       
  2170 		this._tx = a2 * tx1 + b2 * ty1 + tx2;
       
  2171 		this._ty = c2 * tx1 + d2 * ty1 + ty2;
       
  2172 		this._changed();
       
  2173 		return this;
       
  2174 	},
       
  2175 
       
  2176 	chain: function(mx) {
       
  2177 		var a1 = this._a,
       
  2178 			b1 = this._b,
       
  2179 			c1 = this._c,
       
  2180 			d1 = this._d,
       
  2181 			tx1 = this._tx,
       
  2182 			ty1 = this._ty,
       
  2183 			a2 = mx._a,
       
  2184 			b2 = mx._b,
       
  2185 			c2 = mx._c,
       
  2186 			d2 = mx._d,
       
  2187 			tx2 = mx._tx,
       
  2188 			ty2 = mx._ty;
       
  2189 		return new Matrix(
       
  2190 				a2 * a1 + c2 * b1,
       
  2191 				a2 * c1 + c2 * d1,
       
  2192 				b2 * a1 + d2 * b1,
       
  2193 				b2 * c1 + d2 * d1,
       
  2194 				tx1 + tx2 * a1 + ty2 * b1,
       
  2195 				ty1 + tx2 * c1 + ty2 * d1);
       
  2196 	},
       
  2197 
       
  2198 	isIdentity: function() {
       
  2199 		return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
       
  2200 				&& this._tx === 0 && this._ty === 0;
       
  2201 	},
       
  2202 
       
  2203 	orNullIfIdentity: function() {
       
  2204 		return this.isIdentity() ? null : this;
       
  2205 	},
       
  2206 
       
  2207 	isInvertible: function() {
       
  2208 		return !!this._getDeterminant();
       
  2209 	},
       
  2210 
       
  2211 	isSingular: function() {
       
  2212 		return !this._getDeterminant();
       
  2213 	},
       
  2214 
       
  2215 	transform: function( src, dst, count) {
       
  2216 		return arguments.length < 3
       
  2217 			? this._transformPoint(Point.read(arguments))
       
  2218 			: this._transformCoordinates(src, dst, count);
       
  2219 	},
       
  2220 
       
  2221 	_transformPoint: function(point, dest, _dontNotify) {
       
  2222 		var x = point.x,
       
  2223 			y = point.y;
       
  2224 		if (!dest)
       
  2225 			dest = new Point();
       
  2226 		return dest.set(
       
  2227 			x * this._a + y * this._b + this._tx,
       
  2228 			x * this._c + y * this._d + this._ty,
       
  2229 			_dontNotify
       
  2230 		);
       
  2231 	},
       
  2232 
       
  2233 	_transformCoordinates: function(src, dst, count) {
       
  2234 		var i = 0,
       
  2235 			j = 0,
       
  2236 			max = 2 * count;
       
  2237 		while (i < max) {
       
  2238 			var x = src[i++],
       
  2239 				y = src[i++];
       
  2240 			dst[j++] = x * this._a + y * this._b + this._tx;
       
  2241 			dst[j++] = x * this._c + y * this._d + this._ty;
       
  2242 		}
       
  2243 		return dst;
       
  2244 	},
       
  2245 
       
  2246 	_transformCorners: function(rect) {
       
  2247 		var x1 = rect.x,
       
  2248 			y1 = rect.y,
       
  2249 			x2 = x1 + rect.width,
       
  2250 			y2 = y1 + rect.height,
       
  2251 			coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
       
  2252 		return this._transformCoordinates(coords, coords, 4);
       
  2253 	},
       
  2254 
       
  2255 	_transformBounds: function(bounds, dest, _dontNotify) {
       
  2256 		var coords = this._transformCorners(bounds),
       
  2257 			min = coords.slice(0, 2),
       
  2258 			max = coords.slice();
       
  2259 		for (var i = 2; i < 8; i++) {
       
  2260 			var val = coords[i],
       
  2261 				j = i & 1;
       
  2262 			if (val < min[j])
       
  2263 				min[j] = val;
       
  2264 			else if (val > max[j])
       
  2265 				max[j] = val;
       
  2266 		}
       
  2267 		if (!dest)
       
  2268 			dest = new Rectangle();
       
  2269 		return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
       
  2270 				_dontNotify);
       
  2271 	},
       
  2272 
       
  2273 	inverseTransform: function() {
       
  2274 		return this._inverseTransform(Point.read(arguments));
       
  2275 	},
       
  2276 
       
  2277 	_getDeterminant: function() {
       
  2278 		var det = this._a * this._d - this._b * this._c;
       
  2279 		return isFinite(det) && !Numerical.isZero(det)
       
  2280 				&& isFinite(this._tx) && isFinite(this._ty)
       
  2281 				? det : null;
       
  2282 	},
       
  2283 
       
  2284 	_inverseTransform: function(point, dest, _dontNotify) {
       
  2285 		var det = this._getDeterminant();
       
  2286 		if (!det)
       
  2287 			return null;
       
  2288 		var x = point.x - this._tx,
       
  2289 			y = point.y - this._ty;
       
  2290 		if (!dest)
       
  2291 			dest = new Point();
       
  2292 		return dest.set(
       
  2293 			(x * this._d - y * this._b) / det,
       
  2294 			(y * this._a - x * this._c) / det,
       
  2295 			_dontNotify
       
  2296 		);
       
  2297 	},
       
  2298 
       
  2299 	decompose: function() {
       
  2300 		var a = this._a, b = this._b, c = this._c, d = this._d;
       
  2301 		if (Numerical.isZero(a * d - b * c))
       
  2302 			return null;
       
  2303 
       
  2304 		var scaleX = Math.sqrt(a * a + b * b);
       
  2305 		a /= scaleX;
       
  2306 		b /= scaleX;
       
  2307 
       
  2308 		var shear = a * c + b * d;
       
  2309 		c -= a * shear;
       
  2310 		d -= b * shear;
       
  2311 
       
  2312 		var scaleY = Math.sqrt(c * c + d * d);
       
  2313 		c /= scaleY;
       
  2314 		d /= scaleY;
       
  2315 		shear /= scaleY;
       
  2316 
       
  2317 		if (a * d < b * c) {
       
  2318 			a = -a;
       
  2319 			b = -b;
       
  2320 			shear = -shear;
       
  2321 			scaleX = -scaleX;
       
  2322 		}
       
  2323 
       
  2324 		return {
       
  2325 			scaling: new Point(scaleX, scaleY),
       
  2326 			rotation: -Math.atan2(b, a) * 180 / Math.PI,
       
  2327 			shearing: shear
       
  2328 		};
       
  2329 	},
       
  2330 
       
  2331 	getValues: function() {
       
  2332 		return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
       
  2333 	},
       
  2334 
       
  2335 	getTranslation: function() {
       
  2336 		return new Point(this._tx, this._ty);
       
  2337 	},
       
  2338 
       
  2339 	getScaling: function() {
       
  2340 		return (this.decompose() || {}).scaling;
       
  2341 	},
       
  2342 
       
  2343 	getRotation: function() {
       
  2344 		return (this.decompose() || {}).rotation;
       
  2345 	},
       
  2346 
       
  2347 	inverted: function() {
       
  2348 		var det = this._getDeterminant();
       
  2349 		return det && new Matrix(
       
  2350 				this._d / det,
       
  2351 				-this._c / det,
       
  2352 				-this._b / det,
       
  2353 				this._a / det,
       
  2354 				(this._b * this._ty - this._d * this._tx) / det,
       
  2355 				(this._c * this._tx - this._a * this._ty) / det);
       
  2356 	},
       
  2357 
       
  2358 	shiftless: function() {
       
  2359 		return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
       
  2360 	},
       
  2361 
       
  2362 	applyToContext: function(ctx) {
       
  2363 		ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
       
  2364 	}
       
  2365 }, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) {
       
  2366 	var part = Base.capitalize(name),
       
  2367 		prop = '_' + name;
       
  2368 	this['get' + part] = function() {
       
  2369 		return this[prop];
       
  2370 	};
       
  2371 	this['set' + part] = function(value) {
       
  2372 		this[prop] = value;
       
  2373 		this._changed();
       
  2374 	};
       
  2375 }, {}));
       
  2376 
       
  2377 var Line = Base.extend({
       
  2378 	_class: 'Line',
       
  2379 
       
  2380 	initialize: function Line(arg0, arg1, arg2, arg3, arg4) {
       
  2381 		var asVector = false;
       
  2382 		if (arguments.length >= 4) {
       
  2383 			this._px = arg0;
       
  2384 			this._py = arg1;
       
  2385 			this._vx = arg2;
       
  2386 			this._vy = arg3;
       
  2387 			asVector = arg4;
       
  2388 		} else {
       
  2389 			this._px = arg0.x;
       
  2390 			this._py = arg0.y;
       
  2391 			this._vx = arg1.x;
       
  2392 			this._vy = arg1.y;
       
  2393 			asVector = arg2;
       
  2394 		}
       
  2395 		if (!asVector) {
       
  2396 			this._vx -= this._px;
       
  2397 			this._vy -= this._py;
       
  2398 		}
       
  2399 	},
       
  2400 
       
  2401 	getPoint: function() {
       
  2402 		return new Point(this._px, this._py);
       
  2403 	},
       
  2404 
       
  2405 	getVector: function() {
       
  2406 		return new Point(this._vx, this._vy);
       
  2407 	},
       
  2408 
       
  2409 	getLength: function() {
       
  2410 		return this.getVector().getLength();
       
  2411 	},
       
  2412 
       
  2413 	intersect: function(line, isInfinite) {
       
  2414 		return Line.intersect(
       
  2415 				this._px, this._py, this._vx, this._vy,
       
  2416 				line._px, line._py, line._vx, line._vy,
       
  2417 				true, isInfinite);
       
  2418 	},
       
  2419 
       
  2420 	getSide: function(point) {
       
  2421 		return Line.getSide(
       
  2422 				this._px, this._py, this._vx, this._vy,
       
  2423 				point.x, point.y, true);
       
  2424 	},
       
  2425 
       
  2426 	getDistance: function(point) {
       
  2427 		return Math.abs(Line.getSignedDistance(
       
  2428 				this._px, this._py, this._vx, this._vy,
       
  2429 				point.x, point.y, true));
       
  2430 	},
       
  2431 
       
  2432 	statics: {
       
  2433 		intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector,
       
  2434 				isInfinite) {
       
  2435 			if (!asVector) {
       
  2436 				avx -= apx;
       
  2437 				avy -= apy;
       
  2438 				bvx -= bpx;
       
  2439 				bvy -= bpy;
       
  2440 			}
       
  2441 			var cross = avx * bvy - avy * bvx;
       
  2442 			if (!Numerical.isZero(cross)) {
       
  2443 				var dx = apx - bpx,
       
  2444 					dy = apy - bpy,
       
  2445 					ta = (bvx * dy - bvy * dx) / cross,
       
  2446 					tb = (avx * dy - avy * dx) / cross;
       
  2447 				if (isInfinite || 0 <= ta && ta <= 1 && 0 <= tb && tb <= 1)
       
  2448 					return new Point(
       
  2449 								apx + ta * avx,
       
  2450 								apy + ta * avy);
       
  2451 			}
       
  2452 		},
       
  2453 
       
  2454 		getSide: function(px, py, vx, vy, x, y, asVector) {
       
  2455 			if (!asVector) {
       
  2456 				vx -= px;
       
  2457 				vy -= py;
       
  2458 			}
       
  2459 			var v2x = x - px,
       
  2460 				v2y = y - py,
       
  2461 				ccw = v2x * vy - v2y * vx;
       
  2462 			if (ccw === 0) {
       
  2463 				ccw = v2x * vx + v2y * vy;
       
  2464 				if (ccw > 0) {
       
  2465 					v2x -= vx;
       
  2466 					v2y -= vy;
       
  2467 					ccw = v2x * vx + v2y * vy;
       
  2468 					if (ccw < 0)
       
  2469 						ccw = 0;
       
  2470 				}
       
  2471 			}
       
  2472 			return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
       
  2473 		},
       
  2474 
       
  2475 		getSignedDistance: function(px, py, vx, vy, x, y, asVector) {
       
  2476 			if (!asVector) {
       
  2477 				vx -= px;
       
  2478 				vy -= py;
       
  2479 			}
       
  2480 			return Numerical.isZero(vx)
       
  2481 					? vy >= 0 ? px - x : x - px
       
  2482 					: Numerical.isZero(vy)
       
  2483 						? vx >= 0 ? y - py : py - y
       
  2484 						: (vx * (y - py) - vy * (x - px)) / Math.sqrt(vx * vx + vy * vy);
       
  2485 		}
       
  2486 	}
       
  2487 });
       
  2488 
       
  2489 var Project = PaperScopeItem.extend({
       
  2490 	_class: 'Project',
       
  2491 	_list: 'projects',
       
  2492 	_reference: 'project',
       
  2493 
       
  2494 	initialize: function Project(element) {
       
  2495 		PaperScopeItem.call(this, true);
       
  2496 		this.layers = [];
       
  2497 		this._activeLayer = null;
       
  2498 		this.symbols = [];
       
  2499 		this._currentStyle = new Style(null, null, this);
       
  2500 		this._view = View.create(this,
       
  2501 				element || CanvasProvider.getCanvas(1, 1));
       
  2502 		this._selectedItems = {};
       
  2503 		this._selectedItemCount = 0;
       
  2504 		this._updateVersion = 0;
       
  2505 	},
       
  2506 
       
  2507 	_serialize: function(options, dictionary) {
       
  2508 		return Base.serialize(this.layers, options, true, dictionary);
       
  2509 	},
       
  2510 
       
  2511 	clear: function() {
       
  2512 		for (var i = this.layers.length - 1; i >= 0; i--)
       
  2513 			this.layers[i].remove();
       
  2514 		this.symbols = [];
       
  2515 	},
       
  2516 
       
  2517 	isEmpty: function() {
       
  2518 		return this.layers.length === 0;
       
  2519 	},
       
  2520 
       
  2521 	remove: function remove() {
       
  2522 		if (!remove.base.call(this))
       
  2523 			return false;
       
  2524 		if (this._view)
       
  2525 			this._view.remove();
       
  2526 		return true;
       
  2527 	},
       
  2528 
       
  2529 	getView: function() {
       
  2530 		return this._view;
       
  2531 	},
       
  2532 
       
  2533 	getCurrentStyle: function() {
       
  2534 		return this._currentStyle;
       
  2535 	},
       
  2536 
       
  2537 	setCurrentStyle: function(style) {
       
  2538 		this._currentStyle.initialize(style);
       
  2539 	},
       
  2540 
       
  2541 	getIndex: function() {
       
  2542 		return this._index;
       
  2543 	},
       
  2544 
       
  2545 	getOptions: function() {
       
  2546 		return this._scope.settings;
       
  2547 	},
       
  2548 
       
  2549 	getActiveLayer: function() {
       
  2550 		return this._activeLayer || new Layer({ project: this });
       
  2551 	},
       
  2552 
       
  2553 	getSelectedItems: function() {
       
  2554 		var items = [];
       
  2555 		for (var id in this._selectedItems) {
       
  2556 			var item = this._selectedItems[id];
       
  2557 			if (item.isInserted())
       
  2558 				items.push(item);
       
  2559 		}
       
  2560 		return items;
       
  2561 	},
       
  2562 
       
  2563 	insertChild: function(index, item, _preserve) {
       
  2564 		if (item instanceof Layer) {
       
  2565 			item._remove(false, true);
       
  2566 			Base.splice(this.layers, [item], index, 0);
       
  2567 			item._setProject(this, true);
       
  2568 			if (this._changes)
       
  2569 				item._changed(5);
       
  2570 			if (!this._activeLayer)
       
  2571 				this._activeLayer = item;
       
  2572 		} else if (item instanceof Item) {
       
  2573 			(this._activeLayer
       
  2574 				|| this.insertChild(index, new Layer(Item.NO_INSERT)))
       
  2575 					.insertChild(index, item, _preserve);
       
  2576 		} else {
       
  2577 			item = null;
       
  2578 		}
       
  2579 		return item;
       
  2580 	},
       
  2581 
       
  2582 	addChild: function(item, _preserve) {
       
  2583 		return this.insertChild(undefined, item, _preserve);
       
  2584 	},
       
  2585 
       
  2586 	_updateSelection: function(item) {
       
  2587 		var id = item._id,
       
  2588 			selectedItems = this._selectedItems;
       
  2589 		if (item._selected) {
       
  2590 			if (selectedItems[id] !== item) {
       
  2591 				this._selectedItemCount++;
       
  2592 				selectedItems[id] = item;
       
  2593 			}
       
  2594 		} else if (selectedItems[id] === item) {
       
  2595 			this._selectedItemCount--;
       
  2596 			delete selectedItems[id];
       
  2597 		}
       
  2598 	},
       
  2599 
       
  2600 	selectAll: function() {
       
  2601 		var layers = this.layers;
       
  2602 		for (var i = 0, l = layers.length; i < l; i++)
       
  2603 			layers[i].setFullySelected(true);
       
  2604 	},
       
  2605 
       
  2606 	deselectAll: function() {
       
  2607 		var selectedItems = this._selectedItems;
       
  2608 		for (var i in selectedItems)
       
  2609 			selectedItems[i].setFullySelected(false);
       
  2610 	},
       
  2611 
       
  2612 	hitTest: function() {
       
  2613 		var point = Point.read(arguments),
       
  2614 			options = HitResult.getOptions(Base.read(arguments));
       
  2615 		for (var i = this.layers.length - 1; i >= 0; i--) {
       
  2616 			var res = this.layers[i]._hitTest(point, options);
       
  2617 			if (res) return res;
       
  2618 		}
       
  2619 		return null;
       
  2620 	},
       
  2621 
       
  2622 	getItems: function(match) {
       
  2623 		return Item._getItems(this.layers, match);
       
  2624 	},
       
  2625 
       
  2626 	getItem: function(match) {
       
  2627 		return Item._getItems(this.layers, match, null, null, true)[0] || null;
       
  2628 	},
       
  2629 
       
  2630 	importJSON: function(json) {
       
  2631 		this.activate();
       
  2632 		var layer = this._activeLayer;
       
  2633 		return Base.importJSON(json, layer && layer.isEmpty() && layer);
       
  2634 	},
       
  2635 
       
  2636 	draw: function(ctx, matrix, pixelRatio) {
       
  2637 		this._updateVersion++;
       
  2638 		ctx.save();
       
  2639 		matrix.applyToContext(ctx);
       
  2640 		var param = new Base({
       
  2641 			offset: new Point(0, 0),
       
  2642 			pixelRatio: pixelRatio,
       
  2643 			viewMatrix: matrix.isIdentity() ? null : matrix,
       
  2644 			matrices: [new Matrix()],
       
  2645 			updateMatrix: true
       
  2646 		});
       
  2647 		for (var i = 0, layers = this.layers, l = layers.length; i < l; i++)
       
  2648 			layers[i].draw(ctx, param);
       
  2649 		ctx.restore();
       
  2650 
       
  2651 		if (this._selectedItemCount > 0) {
       
  2652 			ctx.save();
       
  2653 			ctx.strokeWidth = 1;
       
  2654 			var items = this._selectedItems,
       
  2655 				size = this._scope.settings.handleSize,
       
  2656 				version = this._updateVersion;
       
  2657 			for (var id in items)
       
  2658 				items[id]._drawSelection(ctx, matrix, size, items, version);
       
  2659 			ctx.restore();
       
  2660 		}
       
  2661 	}
       
  2662 });
       
  2663 
       
  2664 var Symbol = Base.extend({
       
  2665 	_class: 'Symbol',
       
  2666 
       
  2667 	initialize: function Symbol(item, dontCenter) {
       
  2668 		this._id = UID.get();
       
  2669 		this.project = paper.project;
       
  2670 		this.project.symbols.push(this);
       
  2671 		if (item)
       
  2672 			this.setDefinition(item, dontCenter);
       
  2673 	},
       
  2674 
       
  2675 	_serialize: function(options, dictionary) {
       
  2676 		return dictionary.add(this, function() {
       
  2677 			return Base.serialize([this._class, this._definition],
       
  2678 					options, false, dictionary);
       
  2679 		});
       
  2680 	},
       
  2681 
       
  2682 	_changed: function(flags) {
       
  2683 		if (flags & 8) {
       
  2684 			Item._clearBoundsCache(this);
       
  2685 		}
       
  2686 		if (flags & 1) {
       
  2687 			this.project._needsUpdate = true;
       
  2688 		}
       
  2689 	},
       
  2690 
       
  2691 	getDefinition: function() {
       
  2692 		return this._definition;
       
  2693 	},
       
  2694 
       
  2695 	setDefinition: function(item, _dontCenter) {
       
  2696 		if (item._parentSymbol)
       
  2697 			item = item.clone();
       
  2698 		if (this._definition)
       
  2699 			this._definition._parentSymbol = null;
       
  2700 		this._definition = item;
       
  2701 		item.remove();
       
  2702 		item.setSelected(false);
       
  2703 		if (!_dontCenter)
       
  2704 			item.setPosition(new Point());
       
  2705 		item._parentSymbol = this;
       
  2706 		this._changed(9);
       
  2707 	},
       
  2708 
       
  2709 	place: function(position) {
       
  2710 		return new PlacedSymbol(this, position);
       
  2711 	},
       
  2712 
       
  2713 	clone: function() {
       
  2714 		return new Symbol(this._definition.clone(false));
       
  2715 	},
       
  2716 
       
  2717 	equals: function(symbol) {
       
  2718 		return symbol === this
       
  2719 				|| symbol && this.definition.equals(symbol.definition)
       
  2720 				|| false;
       
  2721 	}
       
  2722 });
       
  2723 
       
  2724 var Item = Base.extend(Emitter, {
       
  2725 	statics: {
       
  2726 		extend: function extend(src) {
       
  2727 			if (src._serializeFields)
       
  2728 				src._serializeFields = new Base(
       
  2729 						this.prototype._serializeFields, src._serializeFields);
       
  2730 			return extend.base.apply(this, arguments);
       
  2731 		},
       
  2732 
       
  2733 		NO_INSERT: { insert: false }
       
  2734 	},
       
  2735 
       
  2736 	_class: 'Item',
       
  2737 	_applyMatrix: true,
       
  2738 	_canApplyMatrix: true,
       
  2739 	_boundsSelected: false,
       
  2740 	_selectChildren: false,
       
  2741 	_serializeFields: {
       
  2742 		name: null,
       
  2743 		applyMatrix: null,
       
  2744 		matrix: new Matrix(),
       
  2745 		pivot: null,
       
  2746 		locked: false,
       
  2747 		visible: true,
       
  2748 		blendMode: 'normal',
       
  2749 		opacity: 1,
       
  2750 		guide: false,
       
  2751 		selected: false,
       
  2752 		clipMask: false,
       
  2753 		data: {}
       
  2754 	},
       
  2755 
       
  2756 	initialize: function Item() {
       
  2757 	},
       
  2758 
       
  2759 	_initialize: function(props, point) {
       
  2760 		var hasProps = props && Base.isPlainObject(props),
       
  2761 			internal = hasProps && props.internal === true,
       
  2762 			matrix = this._matrix = new Matrix(),
       
  2763 			project = hasProps && props.project || paper.project;
       
  2764 		if (!internal)
       
  2765 			this._id = UID.get();
       
  2766 		this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix;
       
  2767 		if (point)
       
  2768 			matrix.translate(point);
       
  2769 		matrix._owner = this;
       
  2770 		this._style = new Style(project._currentStyle, this, project);
       
  2771 		if (!this._project) {
       
  2772 			if (internal || hasProps && props.insert === false) {
       
  2773 				this._setProject(project);
       
  2774 			} else if (hasProps && props.parent) {
       
  2775 				this.setParent(props.parent);
       
  2776 			} else {
       
  2777 				(project._activeLayer || new Layer()).addChild(this);
       
  2778 			}
       
  2779 		}
       
  2780 		if (hasProps && props !== Item.NO_INSERT)
       
  2781 			this._set(props, { insert: true, project: true, parent: true },
       
  2782 					true);
       
  2783 		return hasProps;
       
  2784 	},
       
  2785 
       
  2786 	_events: new function() {
       
  2787 
       
  2788 		var mouseFlags = {
       
  2789 			mousedown: {
       
  2790 				mousedown: 1,
       
  2791 				mousedrag: 1,
       
  2792 				click: 1,
       
  2793 				doubleclick: 1
       
  2794 			},
       
  2795 			mouseup: {
       
  2796 				mouseup: 1,
       
  2797 				mousedrag: 1,
       
  2798 				click: 1,
       
  2799 				doubleclick: 1
       
  2800 			},
       
  2801 			mousemove: {
       
  2802 				mousedrag: 1,
       
  2803 				mousemove: 1,
       
  2804 				mouseenter: 1,
       
  2805 				mouseleave: 1
       
  2806 			}
       
  2807 		};
       
  2808 
       
  2809 		var mouseEvent = {
       
  2810 			install: function(type) {
       
  2811 				var counters = this.getView()._eventCounters;
       
  2812 				if (counters) {
       
  2813 					for (var key in mouseFlags) {
       
  2814 						counters[key] = (counters[key] || 0)
       
  2815 								+ (mouseFlags[key][type] || 0);
       
  2816 					}
       
  2817 				}
       
  2818 			},
       
  2819 			uninstall: function(type) {
       
  2820 				var counters = this.getView()._eventCounters;
       
  2821 				if (counters) {
       
  2822 					for (var key in mouseFlags)
       
  2823 						counters[key] -= mouseFlags[key][type] || 0;
       
  2824 				}
       
  2825 			}
       
  2826 		};
       
  2827 
       
  2828 		return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
       
  2829 			'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
       
  2830 			function(name) {
       
  2831 				this[name] = mouseEvent;
       
  2832 			}, {
       
  2833 				onFrame: {
       
  2834 					install: function() {
       
  2835 						this._animateItem(true);
       
  2836 					},
       
  2837 					uninstall: function() {
       
  2838 						this._animateItem(false);
       
  2839 					}
       
  2840 				},
       
  2841 
       
  2842 				onLoad: {}
       
  2843 			}
       
  2844 		);
       
  2845 	},
       
  2846 
       
  2847 	_animateItem: function(animate) {
       
  2848 		this.getView()._animateItem(this, animate);
       
  2849 	},
       
  2850 
       
  2851 	_serialize: function(options, dictionary) {
       
  2852 		var props = {},
       
  2853 			that = this;
       
  2854 
       
  2855 		function serialize(fields) {
       
  2856 			for (var key in fields) {
       
  2857 				var value = that[key];
       
  2858 				if (!Base.equals(value, key === 'leading'
       
  2859 						? fields.fontSize * 1.2 : fields[key])) {
       
  2860 					props[key] = Base.serialize(value, options,
       
  2861 							key !== 'data', dictionary);
       
  2862 				}
       
  2863 			}
       
  2864 		}
       
  2865 
       
  2866 		serialize(this._serializeFields);
       
  2867 		if (!(this instanceof Group))
       
  2868 			serialize(this._style._defaults);
       
  2869 		return [ this._class, props ];
       
  2870 	},
       
  2871 
       
  2872 	_changed: function(flags) {
       
  2873 		var symbol = this._parentSymbol,
       
  2874 			cacheParent = this._parent || symbol,
       
  2875 			project = this._project;
       
  2876 		if (flags & 8) {
       
  2877 			this._bounds = this._position = this._decomposed =
       
  2878 					this._globalMatrix = this._currentPath = undefined;
       
  2879 		}
       
  2880 		if (cacheParent
       
  2881 				&& (flags & 40)) {
       
  2882 			Item._clearBoundsCache(cacheParent);
       
  2883 		}
       
  2884 		if (flags & 2) {
       
  2885 			Item._clearBoundsCache(this);
       
  2886 		}
       
  2887 		if (project) {
       
  2888 			if (flags & 1) {
       
  2889 				project._needsUpdate = true;
       
  2890 			}
       
  2891 			if (project._changes) {
       
  2892 				var entry = project._changesById[this._id];
       
  2893 				if (entry) {
       
  2894 					entry.flags |= flags;
       
  2895 				} else {
       
  2896 					entry = { item: this, flags: flags };
       
  2897 					project._changesById[this._id] = entry;
       
  2898 					project._changes.push(entry);
       
  2899 				}
       
  2900 			}
       
  2901 		}
       
  2902 		if (symbol)
       
  2903 			symbol._changed(flags);
       
  2904 	},
       
  2905 
       
  2906 	set: function(props) {
       
  2907 		if (props)
       
  2908 			this._set(props);
       
  2909 		return this;
       
  2910 	},
       
  2911 
       
  2912 	getId: function() {
       
  2913 		return this._id;
       
  2914 	},
       
  2915 
       
  2916 	getName: function() {
       
  2917 		return this._name;
       
  2918 	},
       
  2919 
       
  2920 	setName: function(name, unique) {
       
  2921 
       
  2922 		if (this._name)
       
  2923 			this._removeNamed();
       
  2924 		if (name === (+name) + '')
       
  2925 			throw new Error(
       
  2926 					'Names consisting only of numbers are not supported.');
       
  2927 		var parent = this._parent;
       
  2928 		if (name && parent) {
       
  2929 			var children = parent._children,
       
  2930 				namedChildren = parent._namedChildren,
       
  2931 				orig = name,
       
  2932 				i = 1;
       
  2933 			while (unique && children[name])
       
  2934 				name = orig + ' ' + (i++);
       
  2935 			(namedChildren[name] = namedChildren[name] || []).push(this);
       
  2936 			children[name] = this;
       
  2937 		}
       
  2938 		this._name = name || undefined;
       
  2939 		this._changed(128);
       
  2940 	},
       
  2941 
       
  2942 	getStyle: function() {
       
  2943 		return this._style;
       
  2944 	},
       
  2945 
       
  2946 	setStyle: function(style) {
       
  2947 		this.getStyle().set(style);
       
  2948 	}
       
  2949 }, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
       
  2950 	function(name) {
       
  2951 		var part = Base.capitalize(name),
       
  2952 			name = '_' + name;
       
  2953 		this['get' + part] = function() {
       
  2954 			return this[name];
       
  2955 		};
       
  2956 		this['set' + part] = function(value) {
       
  2957 			if (value != this[name]) {
       
  2958 				this[name] = value;
       
  2959 				this._changed(name === '_locked'
       
  2960 						? 128 : 129);
       
  2961 			}
       
  2962 		};
       
  2963 	},
       
  2964 {}), {
       
  2965 	beans: true,
       
  2966 
       
  2967 	_locked: false,
       
  2968 
       
  2969 	_visible: true,
       
  2970 
       
  2971 	_blendMode: 'normal',
       
  2972 
       
  2973 	_opacity: 1,
       
  2974 
       
  2975 	_guide: false,
       
  2976 
       
  2977 	isSelected: function() {
       
  2978 		if (this._selectChildren) {
       
  2979 			var children = this._children;
       
  2980 			for (var i = 0, l = children.length; i < l; i++)
       
  2981 				if (children[i].isSelected())
       
  2982 					return true;
       
  2983 		}
       
  2984 		return this._selected;
       
  2985 	},
       
  2986 
       
  2987 	setSelected: function(selected, noChildren) {
       
  2988 		if (!noChildren && this._selectChildren) {
       
  2989 			var children = this._children;
       
  2990 			for (var i = 0, l = children.length; i < l; i++)
       
  2991 				children[i].setSelected(selected);
       
  2992 		}
       
  2993 		if ((selected = !!selected) ^ this._selected) {
       
  2994 			this._selected = selected;
       
  2995 			this._project._updateSelection(this);
       
  2996 			this._changed(129);
       
  2997 		}
       
  2998 	},
       
  2999 
       
  3000 	_selected: false,
       
  3001 
       
  3002 	isFullySelected: function() {
       
  3003 		var children = this._children;
       
  3004 		if (children && this._selected) {
       
  3005 			for (var i = 0, l = children.length; i < l; i++)
       
  3006 				if (!children[i].isFullySelected())
       
  3007 					return false;
       
  3008 			return true;
       
  3009 		}
       
  3010 		return this._selected;
       
  3011 	},
       
  3012 
       
  3013 	setFullySelected: function(selected) {
       
  3014 		var children = this._children;
       
  3015 		if (children) {
       
  3016 			for (var i = 0, l = children.length; i < l; i++)
       
  3017 				children[i].setFullySelected(selected);
       
  3018 		}
       
  3019 		this.setSelected(selected, true);
       
  3020 	},
       
  3021 
       
  3022 	isClipMask: function() {
       
  3023 		return this._clipMask;
       
  3024 	},
       
  3025 
       
  3026 	setClipMask: function(clipMask) {
       
  3027 		if (this._clipMask != (clipMask = !!clipMask)) {
       
  3028 			this._clipMask = clipMask;
       
  3029 			if (clipMask) {
       
  3030 				this.setFillColor(null);
       
  3031 				this.setStrokeColor(null);
       
  3032 			}
       
  3033 			this._changed(129);
       
  3034 			if (this._parent)
       
  3035 				this._parent._changed(1024);
       
  3036 		}
       
  3037 	},
       
  3038 
       
  3039 	_clipMask: false,
       
  3040 
       
  3041 	getData: function() {
       
  3042 		if (!this._data)
       
  3043 			this._data = {};
       
  3044 		return this._data;
       
  3045 	},
       
  3046 
       
  3047 	setData: function(data) {
       
  3048 		this._data = data;
       
  3049 	},
       
  3050 
       
  3051 	getPosition: function(_dontLink) {
       
  3052 		var position = this._position,
       
  3053 			ctor = _dontLink ? Point : LinkedPoint;
       
  3054 		if (!position) {
       
  3055 			var pivot = this._pivot;
       
  3056 			position = this._position = pivot
       
  3057 					? this._matrix._transformPoint(pivot)
       
  3058 					: this.getBounds().getCenter(true);
       
  3059 		}
       
  3060 		return new ctor(position.x, position.y, this, 'setPosition');
       
  3061 	},
       
  3062 
       
  3063 	setPosition: function() {
       
  3064 		this.translate(Point.read(arguments).subtract(this.getPosition(true)));
       
  3065 	},
       
  3066 
       
  3067 	getPivot: function(_dontLink) {
       
  3068 		var pivot = this._pivot;
       
  3069 		if (pivot) {
       
  3070 			var ctor = _dontLink ? Point : LinkedPoint;
       
  3071 			pivot = new ctor(pivot.x, pivot.y, this, 'setPivot');
       
  3072 		}
       
  3073 		return pivot;
       
  3074 	},
       
  3075 
       
  3076 	setPivot: function() {
       
  3077 		this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
       
  3078 		this._position = undefined;
       
  3079 	},
       
  3080 
       
  3081 	_pivot: null,
       
  3082 }, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds',
       
  3083 		'internalBounds', 'internalRoughBounds'],
       
  3084 	function(key) {
       
  3085 		var getter = 'get' + Base.capitalize(key),
       
  3086 			match = key.match(/^internal(.*)$/),
       
  3087 			internalGetter = match ? 'get' + match[1] : null;
       
  3088 		this[getter] = function(_matrix) {
       
  3089 			var boundsGetter = this._boundsGetter,
       
  3090 				name = !internalGetter && (typeof boundsGetter === 'string'
       
  3091 						? boundsGetter : boundsGetter && boundsGetter[getter])
       
  3092 						|| getter,
       
  3093 				bounds = this._getCachedBounds(name, _matrix, this,
       
  3094 						internalGetter);
       
  3095 			return key === 'bounds'
       
  3096 					? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
       
  3097 							bounds.height, this, 'setBounds')
       
  3098 					: bounds;
       
  3099 		};
       
  3100 	},
       
  3101 {
       
  3102 	beans: true,
       
  3103 
       
  3104 	_getBounds: function(getter, matrix, cacheItem) {
       
  3105 		var children = this._children;
       
  3106 		if (!children || children.length == 0)
       
  3107 			return new Rectangle();
       
  3108 		Item._updateBoundsCache(this, cacheItem);
       
  3109 		var x1 = Infinity,
       
  3110 			x2 = -x1,
       
  3111 			y1 = x1,
       
  3112 			y2 = x2;
       
  3113 		for (var i = 0, l = children.length; i < l; i++) {
       
  3114 			var child = children[i];
       
  3115 			if (child._visible && !child.isEmpty()) {
       
  3116 				var rect = child._getCachedBounds(getter,
       
  3117 						matrix && matrix.chain(child._matrix), cacheItem);
       
  3118 				x1 = Math.min(rect.x, x1);
       
  3119 				y1 = Math.min(rect.y, y1);
       
  3120 				x2 = Math.max(rect.x + rect.width, x2);
       
  3121 				y2 = Math.max(rect.y + rect.height, y2);
       
  3122 			}
       
  3123 		}
       
  3124 		return isFinite(x1)
       
  3125 				? new Rectangle(x1, y1, x2 - x1, y2 - y1)
       
  3126 				: new Rectangle();
       
  3127 	},
       
  3128 
       
  3129 	setBounds: function() {
       
  3130 		var rect = Rectangle.read(arguments),
       
  3131 			bounds = this.getBounds(),
       
  3132 			matrix = new Matrix(),
       
  3133 			center = rect.getCenter();
       
  3134 		matrix.translate(center);
       
  3135 		if (rect.width != bounds.width || rect.height != bounds.height) {
       
  3136 			matrix.scale(
       
  3137 					bounds.width != 0 ? rect.width / bounds.width : 1,
       
  3138 					bounds.height != 0 ? rect.height / bounds.height : 1);
       
  3139 		}
       
  3140 		center = bounds.getCenter();
       
  3141 		matrix.translate(-center.x, -center.y);
       
  3142 		this.transform(matrix);
       
  3143 	},
       
  3144 
       
  3145 	_getCachedBounds: function(getter, matrix, cacheItem, internalGetter) {
       
  3146 		matrix = matrix && matrix.orNullIfIdentity();
       
  3147 		var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(),
       
  3148 			cache = (!matrix || matrix.equals(_matrix)) && getter;
       
  3149 		Item._updateBoundsCache(this._parent || this._parentSymbol, cacheItem);
       
  3150 		if (cache && this._bounds && this._bounds[cache])
       
  3151 			return this._bounds[cache].clone();
       
  3152 		var bounds = this._getBounds(internalGetter || getter,
       
  3153 				matrix || _matrix, cacheItem);
       
  3154 		if (cache) {
       
  3155 			if (!this._bounds)
       
  3156 				this._bounds = {};
       
  3157 			var cached = this._bounds[cache] = bounds.clone();
       
  3158 			cached._internal = !!internalGetter;
       
  3159 		}
       
  3160 		return bounds;
       
  3161 	},
       
  3162 
       
  3163 	statics: {
       
  3164 		_updateBoundsCache: function(parent, item) {
       
  3165 			if (parent) {
       
  3166 				var id = item._id,
       
  3167 					ref = parent._boundsCache = parent._boundsCache || {
       
  3168 						ids: {},
       
  3169 						list: []
       
  3170 					};
       
  3171 				if (!ref.ids[id]) {
       
  3172 					ref.list.push(item);
       
  3173 					ref.ids[id] = item;
       
  3174 				}
       
  3175 			}
       
  3176 		},
       
  3177 
       
  3178 		_clearBoundsCache: function(item) {
       
  3179 			var cache = item._boundsCache;
       
  3180 			if (cache) {
       
  3181 				item._bounds = item._position = item._boundsCache = undefined;
       
  3182 				for (var i = 0, list = cache.list, l = list.length; i < l; i++){
       
  3183 					var other = list[i];
       
  3184 					if (other !== item) {
       
  3185 						other._bounds = other._position = undefined;
       
  3186 						if (other._boundsCache)
       
  3187 							Item._clearBoundsCache(other);
       
  3188 					}
       
  3189 				}
       
  3190 			}
       
  3191 		}
       
  3192 	}
       
  3193 
       
  3194 }), {
       
  3195 	beans: true,
       
  3196 
       
  3197 	_decompose: function() {
       
  3198 		return this._decomposed = this._matrix.decompose();
       
  3199 	},
       
  3200 
       
  3201 	getRotation: function() {
       
  3202 		var decomposed = this._decomposed || this._decompose();
       
  3203 		return decomposed && decomposed.rotation;
       
  3204 	},
       
  3205 
       
  3206 	setRotation: function(rotation) {
       
  3207 		var current = this.getRotation();
       
  3208 		if (current != null && rotation != null) {
       
  3209 			var decomposed = this._decomposed;
       
  3210 			this.rotate(rotation - current);
       
  3211 			decomposed.rotation = rotation;
       
  3212 			this._decomposed = decomposed;
       
  3213 		}
       
  3214 	},
       
  3215 
       
  3216 	getScaling: function(_dontLink) {
       
  3217 		var decomposed = this._decomposed || this._decompose(),
       
  3218 			scaling = decomposed && decomposed.scaling,
       
  3219 			ctor = _dontLink ? Point : LinkedPoint;
       
  3220 		return scaling && new ctor(scaling.x, scaling.y, this, 'setScaling');
       
  3221 	},
       
  3222 
       
  3223 	setScaling: function() {
       
  3224 		var current = this.getScaling();
       
  3225 		if (current) {
       
  3226 			var scaling = Point.read(arguments, 0, { clone: true }),
       
  3227 				decomposed = this._decomposed;
       
  3228 			this.scale(scaling.x / current.x, scaling.y / current.y);
       
  3229 			decomposed.scaling = scaling;
       
  3230 			this._decomposed = decomposed;
       
  3231 		}
       
  3232 	},
       
  3233 
       
  3234 	getMatrix: function() {
       
  3235 		return this._matrix;
       
  3236 	},
       
  3237 
       
  3238 	setMatrix: function() {
       
  3239 		var matrix = this._matrix;
       
  3240 		matrix.initialize.apply(matrix, arguments);
       
  3241 		if (this._applyMatrix) {
       
  3242 			this.transform(null, true);
       
  3243 		} else {
       
  3244 			this._changed(9);
       
  3245 		}
       
  3246 	},
       
  3247 
       
  3248 	getGlobalMatrix: function(_dontClone) {
       
  3249 		var matrix = this._globalMatrix,
       
  3250 			updateVersion = this._project._updateVersion;
       
  3251 		if (matrix && matrix._updateVersion !== updateVersion)
       
  3252 			matrix = null;
       
  3253 		if (!matrix) {
       
  3254 			matrix = this._globalMatrix = this._matrix.clone();
       
  3255 			var parent = this._parent;
       
  3256 			if (parent)
       
  3257 				matrix.preConcatenate(parent.getGlobalMatrix(true));
       
  3258 			matrix._updateVersion = updateVersion;
       
  3259 		}
       
  3260 		return _dontClone ? matrix : matrix.clone();
       
  3261 	},
       
  3262 
       
  3263 	getApplyMatrix: function() {
       
  3264 		return this._applyMatrix;
       
  3265 	},
       
  3266 
       
  3267 	setApplyMatrix: function(apply) {
       
  3268 		if (this._applyMatrix = this._canApplyMatrix && !!apply)
       
  3269 			this.transform(null, true);
       
  3270 	},
       
  3271 
       
  3272 	getTransformContent: '#getApplyMatrix',
       
  3273 	setTransformContent: '#setApplyMatrix',
       
  3274 }, {
       
  3275 	getProject: function() {
       
  3276 		return this._project;
       
  3277 	},
       
  3278 
       
  3279 	_setProject: function(project, installEvents) {
       
  3280 		if (this._project !== project) {
       
  3281 			if (this._project)
       
  3282 				this._installEvents(false);
       
  3283 			this._project = project;
       
  3284 			var children = this._children;
       
  3285 			for (var i = 0, l = children && children.length; i < l; i++)
       
  3286 				children[i]._setProject(project);
       
  3287 			installEvents = true;
       
  3288 		}
       
  3289 		if (installEvents)
       
  3290 			this._installEvents(true);
       
  3291 	},
       
  3292 
       
  3293 	getView: function() {
       
  3294 		return this._project.getView();
       
  3295 	},
       
  3296 
       
  3297 	_installEvents: function _installEvents(install) {
       
  3298 		_installEvents.base.call(this, install);
       
  3299 		var children = this._children;
       
  3300 		for (var i = 0, l = children && children.length; i < l; i++)
       
  3301 			children[i]._installEvents(install);
       
  3302 	},
       
  3303 
       
  3304 	getLayer: function() {
       
  3305 		var parent = this;
       
  3306 		while (parent = parent._parent) {
       
  3307 			if (parent instanceof Layer)
       
  3308 				return parent;
       
  3309 		}
       
  3310 		return null;
       
  3311 	},
       
  3312 
       
  3313 	getParent: function() {
       
  3314 		return this._parent;
       
  3315 	},
       
  3316 
       
  3317 	setParent: function(item) {
       
  3318 		return item.addChild(this);
       
  3319 	},
       
  3320 
       
  3321 	getChildren: function() {
       
  3322 		return this._children;
       
  3323 	},
       
  3324 
       
  3325 	setChildren: function(items) {
       
  3326 		this.removeChildren();
       
  3327 		this.addChildren(items);
       
  3328 	},
       
  3329 
       
  3330 	getFirstChild: function() {
       
  3331 		return this._children && this._children[0] || null;
       
  3332 	},
       
  3333 
       
  3334 	getLastChild: function() {
       
  3335 		return this._children && this._children[this._children.length - 1]
       
  3336 				|| null;
       
  3337 	},
       
  3338 
       
  3339 	getNextSibling: function() {
       
  3340 		return this._parent && this._parent._children[this._index + 1] || null;
       
  3341 	},
       
  3342 
       
  3343 	getPreviousSibling: function() {
       
  3344 		return this._parent && this._parent._children[this._index - 1] || null;
       
  3345 	},
       
  3346 
       
  3347 	getIndex: function() {
       
  3348 		return this._index;
       
  3349 	},
       
  3350 
       
  3351 	equals: function(item) {
       
  3352 		return item === this || item && this._class === item._class
       
  3353 				&& this._style.equals(item._style)
       
  3354 				&& this._matrix.equals(item._matrix)
       
  3355 				&& this._locked === item._locked
       
  3356 				&& this._visible === item._visible
       
  3357 				&& this._blendMode === item._blendMode
       
  3358 				&& this._opacity === item._opacity
       
  3359 				&& this._clipMask === item._clipMask
       
  3360 				&& this._guide === item._guide
       
  3361 				&& this._equals(item)
       
  3362 				|| false;
       
  3363 	},
       
  3364 
       
  3365 	_equals: function(item) {
       
  3366 		return Base.equals(this._children, item._children);
       
  3367 	},
       
  3368 
       
  3369 	clone: function(insert) {
       
  3370 		return this._clone(new this.constructor(Item.NO_INSERT), insert);
       
  3371 	},
       
  3372 
       
  3373 	_clone: function(copy, insert, includeMatrix) {
       
  3374 		var keys = ['_locked', '_visible', '_blendMode', '_opacity',
       
  3375 				'_clipMask', '_guide'],
       
  3376 			children = this._children;
       
  3377 		copy.setStyle(this._style);
       
  3378 		for (var i = 0, l = children && children.length; i < l; i++) {
       
  3379 			copy.addChild(children[i].clone(false), true);
       
  3380 		}
       
  3381 		for (var i = 0, l = keys.length; i < l; i++) {
       
  3382 			var key = keys[i];
       
  3383 			if (this.hasOwnProperty(key))
       
  3384 				copy[key] = this[key];
       
  3385 		}
       
  3386 		if (includeMatrix !== false)
       
  3387 			copy._matrix.initialize(this._matrix);
       
  3388 		copy.setApplyMatrix(this._applyMatrix);
       
  3389 		copy.setPivot(this._pivot);
       
  3390 		copy.setSelected(this._selected);
       
  3391 		copy._data = this._data ? Base.clone(this._data) : null;
       
  3392 		if (insert || insert === undefined)
       
  3393 			copy.insertAbove(this);
       
  3394 		if (this._name)
       
  3395 			copy.setName(this._name, true);
       
  3396 		return copy;
       
  3397 	},
       
  3398 
       
  3399 	copyTo: function(itemOrProject) {
       
  3400 		return itemOrProject.addChild(this.clone(false));
       
  3401 	},
       
  3402 
       
  3403 	rasterize: function(resolution) {
       
  3404 		var bounds = this.getStrokeBounds(),
       
  3405 			scale = (resolution || this.getView().getResolution()) / 72,
       
  3406 			topLeft = bounds.getTopLeft().floor(),
       
  3407 			bottomRight = bounds.getBottomRight().ceil(),
       
  3408 			size = new Size(bottomRight.subtract(topLeft)),
       
  3409 			canvas = CanvasProvider.getCanvas(size.multiply(scale)),
       
  3410 			ctx = canvas.getContext('2d'),
       
  3411 			matrix = new Matrix().scale(scale).translate(topLeft.negate());
       
  3412 		ctx.save();
       
  3413 		matrix.applyToContext(ctx);
       
  3414 		this.draw(ctx, new Base({ matrices: [matrix] }));
       
  3415 		ctx.restore();
       
  3416 		var raster = new Raster(Item.NO_INSERT);
       
  3417 		raster.setCanvas(canvas);
       
  3418 		raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
       
  3419 				.scale(1 / scale));
       
  3420 		raster.insertAbove(this);
       
  3421 		return raster;
       
  3422 	},
       
  3423 
       
  3424 	contains: function() {
       
  3425 		return !!this._contains(
       
  3426 				this._matrix._inverseTransform(Point.read(arguments)));
       
  3427 	},
       
  3428 
       
  3429 	_contains: function(point) {
       
  3430 		if (this._children) {
       
  3431 			for (var i = this._children.length - 1; i >= 0; i--) {
       
  3432 				if (this._children[i].contains(point))
       
  3433 					return true;
       
  3434 			}
       
  3435 			return false;
       
  3436 		}
       
  3437 		return point.isInside(this.getInternalBounds());
       
  3438 	},
       
  3439 
       
  3440 	isInside: function() {
       
  3441 		return Rectangle.read(arguments).contains(this.getBounds());
       
  3442 	},
       
  3443 
       
  3444 	_asPathItem: function() {
       
  3445 		return new Path.Rectangle({
       
  3446 			rectangle: this.getInternalBounds(),
       
  3447 			matrix: this._matrix,
       
  3448 			insert: false,
       
  3449 		});
       
  3450 	},
       
  3451 
       
  3452 	intersects: function(item, _matrix) {
       
  3453 		if (!(item instanceof Item))
       
  3454 			return false;
       
  3455 		return this._asPathItem().getIntersections(item._asPathItem(),
       
  3456 				_matrix || item._matrix).length > 0;
       
  3457 	},
       
  3458 
       
  3459 	hitTest: function() {
       
  3460 		return this._hitTest(
       
  3461 				Point.read(arguments),
       
  3462 				HitResult.getOptions(Base.read(arguments)));
       
  3463 	},
       
  3464 
       
  3465 	_hitTest: function(point, options) {
       
  3466 		if (this._locked || !this._visible || this._guide && !options.guides
       
  3467 				|| this.isEmpty())
       
  3468 			return null;
       
  3469 
       
  3470 		var matrix = this._matrix,
       
  3471 			parentTotalMatrix = options._totalMatrix,
       
  3472 			view = this.getView(),
       
  3473 			totalMatrix = options._totalMatrix = parentTotalMatrix
       
  3474 					? parentTotalMatrix.chain(matrix)
       
  3475 					: this.getGlobalMatrix().preConcatenate(view._matrix),
       
  3476 			tolerancePadding = options._tolerancePadding = new Size(
       
  3477 						Path._getPenPadding(1, totalMatrix.inverted())
       
  3478 					).multiply(
       
  3479 						Math.max(options.tolerance, 0.000001)
       
  3480 					);
       
  3481 		point = matrix._inverseTransform(point);
       
  3482 
       
  3483 		if (!this._children && !this.getInternalRoughBounds()
       
  3484 				.expand(tolerancePadding.multiply(2))._containsPoint(point))
       
  3485 			return null;
       
  3486 		var checkSelf = !(options.guides && !this._guide
       
  3487 				|| options.selected && !this._selected
       
  3488 				|| options.type && options.type !== Base.hyphenate(this._class)
       
  3489 				|| options.class && !(this instanceof options.class)),
       
  3490 			that = this,
       
  3491 			res;
       
  3492 
       
  3493 		function checkBounds(type, part) {
       
  3494 			var pt = bounds['get' + part]();
       
  3495 			if (point.subtract(pt).divide(tolerancePadding).length <= 1)
       
  3496 				return new HitResult(type, that,
       
  3497 						{ name: Base.hyphenate(part), point: pt });
       
  3498 		}
       
  3499 
       
  3500 		if (checkSelf && (options.center || options.bounds) && this._parent) {
       
  3501 			var bounds = this.getInternalBounds();
       
  3502 			if (options.center)
       
  3503 				res = checkBounds('center', 'Center');
       
  3504 			if (!res && options.bounds) {
       
  3505 				var points = [
       
  3506 					'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
       
  3507 					'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
       
  3508 				];
       
  3509 				for (var i = 0; i < 8 && !res; i++)
       
  3510 					res = checkBounds('bounds', points[i]);
       
  3511 			}
       
  3512 		}
       
  3513 
       
  3514 		var children = !res && this._children;
       
  3515 		if (children) {
       
  3516 			var opts = this._getChildHitTestOptions(options);
       
  3517 			for (var i = children.length - 1; i >= 0 && !res; i--)
       
  3518 				res = children[i]._hitTest(point, opts);
       
  3519 		}
       
  3520 		if (!res && checkSelf)
       
  3521 			res = this._hitTestSelf(point, options);
       
  3522 		if (res && res.point)
       
  3523 			res.point = matrix.transform(res.point);
       
  3524 		options._totalMatrix = parentTotalMatrix;
       
  3525 		return res;
       
  3526 	},
       
  3527 
       
  3528 	_getChildHitTestOptions: function(options) {
       
  3529 		return options;
       
  3530 	},
       
  3531 
       
  3532 	_hitTestSelf: function(point, options) {
       
  3533 		if (options.fill && this.hasFill() && this._contains(point))
       
  3534 			return new HitResult('fill', this);
       
  3535 	},
       
  3536 
       
  3537 	matches: function(name, compare) {
       
  3538 		function matchObject(obj1, obj2) {
       
  3539 			for (var i in obj1) {
       
  3540 				if (obj1.hasOwnProperty(i)) {
       
  3541 					var val1 = obj1[i],
       
  3542 						val2 = obj2[i];
       
  3543 					if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {
       
  3544 						if (!matchObject(val1, val2))
       
  3545 							return false;
       
  3546 					} else if (!Base.equals(val1, val2)) {
       
  3547 						return false;
       
  3548 					}
       
  3549 				}
       
  3550 			}
       
  3551 			return true;
       
  3552 		}
       
  3553 		var type = typeof name;
       
  3554 		if (type === 'object') {
       
  3555 			for (var key in name) {
       
  3556 				if (name.hasOwnProperty(key) && !this.matches(key, name[key]))
       
  3557 					return false;
       
  3558 			}
       
  3559 		} else if (type === 'function') {
       
  3560 			return name(this);
       
  3561 		} else {
       
  3562 			var value = /^(empty|editable)$/.test(name)
       
  3563 					? this['is' + Base.capitalize(name)]()
       
  3564 					: name === 'type'
       
  3565 						? Base.hyphenate(this._class)
       
  3566 						: this[name];
       
  3567 			if (/^(constructor|class)$/.test(name)) {
       
  3568 				if (!(this instanceof compare))
       
  3569 					return false;
       
  3570 			} else if (compare instanceof RegExp) {
       
  3571 				if (!compare.test(value))
       
  3572 					return false;
       
  3573 			} else if (typeof compare === 'function') {
       
  3574 				if (!compare(value))
       
  3575 					return false;
       
  3576 			} else if (Base.isPlainObject(compare)) {
       
  3577 				if (!matchObject(compare, value))
       
  3578 					return false;
       
  3579 			} else if (!Base.equals(value, compare)) {
       
  3580 				return false;
       
  3581 			}
       
  3582 		}
       
  3583 		return true;
       
  3584 	},
       
  3585 
       
  3586 	getItems: function(match) {
       
  3587 		return Item._getItems(this._children, match, this._matrix);
       
  3588 	},
       
  3589 
       
  3590 	getItem: function(match) {
       
  3591 		return Item._getItems(this._children, match, this._matrix, null, true)
       
  3592 				[0] || null;
       
  3593 	},
       
  3594 
       
  3595 	statics: {
       
  3596 		_getItems: function _getItems(children, match, matrix, param,
       
  3597 				firstOnly) {
       
  3598 			if (!param && typeof match === 'object') {
       
  3599 				var overlapping = match.overlapping,
       
  3600 					inside = match.inside,
       
  3601 					bounds = overlapping || inside,
       
  3602 					rect =	bounds && Rectangle.read([bounds]);
       
  3603 				param = {
       
  3604 					items: [],
       
  3605 					inside: !!inside,
       
  3606 					overlapping: !!overlapping,
       
  3607 					rect: rect,
       
  3608 					path: overlapping && new Path.Rectangle({
       
  3609 						rectangle: rect,
       
  3610 						insert: false
       
  3611 					})
       
  3612 				};
       
  3613 				if (bounds)
       
  3614 					match = Base.set({}, match,
       
  3615 							{ inside: true, overlapping: true });
       
  3616 			}
       
  3617 			var items = param && param.items,
       
  3618 				rect = param && param.rect;
       
  3619 			matrix = rect && (matrix || new Matrix());
       
  3620 			for (var i = 0, l = children && children.length; i < l; i++) {
       
  3621 				var child = children[i],
       
  3622 					childMatrix = matrix && matrix.chain(child._matrix),
       
  3623 					add = true;
       
  3624 				if (rect) {
       
  3625 					var bounds = child.getBounds(childMatrix);
       
  3626 					if (!rect.intersects(bounds))
       
  3627 						continue;
       
  3628 					if (!(param.inside && rect.contains(bounds))
       
  3629 							&& !(param.overlapping && (bounds.contains(rect)
       
  3630 								|| param.path.intersects(child, childMatrix))))
       
  3631 						add = false;
       
  3632 				}
       
  3633 				if (add && child.matches(match)) {
       
  3634 					items.push(child);
       
  3635 					if (firstOnly)
       
  3636 						break;
       
  3637 				}
       
  3638 				_getItems(child._children, match,
       
  3639 						childMatrix, param,
       
  3640 						firstOnly);
       
  3641 				if (firstOnly && items.length > 0)
       
  3642 					break;
       
  3643 			}
       
  3644 			return items;
       
  3645 		}
       
  3646 	}
       
  3647 }, {
       
  3648 
       
  3649 	importJSON: function(json) {
       
  3650 		var res = Base.importJSON(json, this);
       
  3651 		return res !== this
       
  3652 				? this.addChild(res)
       
  3653 				: res;
       
  3654 	},
       
  3655 
       
  3656 	addChild: function(item, _preserve) {
       
  3657 		return this.insertChild(undefined, item, _preserve);
       
  3658 	},
       
  3659 
       
  3660 	insertChild: function(index, item, _preserve) {
       
  3661 		var res = item ? this.insertChildren(index, [item], _preserve) : null;
       
  3662 		return res && res[0];
       
  3663 	},
       
  3664 
       
  3665 	addChildren: function(items, _preserve) {
       
  3666 		return this.insertChildren(this._children.length, items, _preserve);
       
  3667 	},
       
  3668 
       
  3669 	insertChildren: function(index, items, _preserve, _proto) {
       
  3670 		var children = this._children;
       
  3671 		if (children && items && items.length > 0) {
       
  3672 			items = Array.prototype.slice.apply(items);
       
  3673 			for (var i = items.length - 1; i >= 0; i--) {
       
  3674 				var item = items[i];
       
  3675 				if (_proto && !(item instanceof _proto)) {
       
  3676 					items.splice(i, 1);
       
  3677 				} else {
       
  3678 					var shift = item._parent === this && item._index < index;
       
  3679 					if (item._remove(false, true) && shift)
       
  3680 						index--;
       
  3681 				}
       
  3682 			}
       
  3683 			Base.splice(children, items, index, 0);
       
  3684 			var project = this._project,
       
  3685 				notifySelf = project && project._changes;
       
  3686 			for (var i = 0, l = items.length; i < l; i++) {
       
  3687 				var item = items[i];
       
  3688 				item._parent = this;
       
  3689 				item._setProject(this._project, true);
       
  3690 				if (item._name)
       
  3691 					item.setName(item._name);
       
  3692 				if (notifySelf)
       
  3693 					this._changed(5);
       
  3694 			}
       
  3695 			this._changed(11);
       
  3696 		} else {
       
  3697 			items = null;
       
  3698 		}
       
  3699 		return items;
       
  3700 	},
       
  3701 
       
  3702 	_insertSibling: function(index, item, _preserve) {
       
  3703 		return this._parent
       
  3704 				? this._parent.insertChild(index, item, _preserve)
       
  3705 				: null;
       
  3706 	},
       
  3707 
       
  3708 	insertAbove: function(item, _preserve) {
       
  3709 		return item._insertSibling(item._index + 1, this, _preserve);
       
  3710 	},
       
  3711 
       
  3712 	insertBelow: function(item, _preserve) {
       
  3713 		return item._insertSibling(item._index, this, _preserve);
       
  3714 	},
       
  3715 
       
  3716 	sendToBack: function() {
       
  3717 		return (this._parent || this instanceof Layer && this._project)
       
  3718 				.insertChild(0, this);
       
  3719 	},
       
  3720 
       
  3721 	bringToFront: function() {
       
  3722 		return (this._parent || this instanceof Layer && this._project)
       
  3723 				.addChild(this);
       
  3724 	},
       
  3725 
       
  3726 	appendTop: '#addChild',
       
  3727 
       
  3728 	appendBottom: function(item) {
       
  3729 		return this.insertChild(0, item);
       
  3730 	},
       
  3731 
       
  3732 	moveAbove: '#insertAbove',
       
  3733 
       
  3734 	moveBelow: '#insertBelow',
       
  3735 
       
  3736 	reduce: function() {
       
  3737 		if (this._children && this._children.length === 1) {
       
  3738 			var child = this._children[0].reduce();
       
  3739 			child.insertAbove(this);
       
  3740 			child.setStyle(this._style);
       
  3741 			this.remove();
       
  3742 			return child;
       
  3743 		}
       
  3744 		return this;
       
  3745 	},
       
  3746 
       
  3747 	_removeNamed: function() {
       
  3748 		var parent = this._parent;
       
  3749 		if (parent) {
       
  3750 			var children = parent._children,
       
  3751 				namedChildren = parent._namedChildren,
       
  3752 				name = this._name,
       
  3753 				namedArray = namedChildren[name],
       
  3754 				index = namedArray ? namedArray.indexOf(this) : -1;
       
  3755 			if (index !== -1) {
       
  3756 				if (children[name] == this)
       
  3757 					delete children[name];
       
  3758 				namedArray.splice(index, 1);
       
  3759 				if (namedArray.length) {
       
  3760 					children[name] = namedArray[namedArray.length - 1];
       
  3761 				} else {
       
  3762 					delete namedChildren[name];
       
  3763 				}
       
  3764 			}
       
  3765 		}
       
  3766 	},
       
  3767 
       
  3768 	_remove: function(notifySelf, notifyParent) {
       
  3769 		var parent = this._parent;
       
  3770 		if (parent) {
       
  3771 			if (this._name)
       
  3772 				this._removeNamed();
       
  3773 			if (this._index != null)
       
  3774 				Base.splice(parent._children, null, this._index, 1);
       
  3775 			this._installEvents(false);
       
  3776 			if (notifySelf) {
       
  3777 				var project = this._project;
       
  3778 				if (project && project._changes)
       
  3779 					this._changed(5);
       
  3780 			}
       
  3781 			if (notifyParent)
       
  3782 				parent._changed(11);
       
  3783 			this._parent = null;
       
  3784 			return true;
       
  3785 		}
       
  3786 		return false;
       
  3787 	},
       
  3788 
       
  3789 	remove: function() {
       
  3790 		return this._remove(true, true);
       
  3791 	},
       
  3792 
       
  3793 	replaceWith: function(item) {
       
  3794 		var ok = item && item.insertBelow(this);
       
  3795 		if (ok)
       
  3796 			this.remove();
       
  3797 		return ok;
       
  3798 	},
       
  3799 
       
  3800 	removeChildren: function(from, to) {
       
  3801 		if (!this._children)
       
  3802 			return null;
       
  3803 		from = from || 0;
       
  3804 		to = Base.pick(to, this._children.length);
       
  3805 		var removed = Base.splice(this._children, null, from, to - from);
       
  3806 		for (var i = removed.length - 1; i >= 0; i--) {
       
  3807 			removed[i]._remove(true, false);
       
  3808 		}
       
  3809 		if (removed.length > 0)
       
  3810 			this._changed(11);
       
  3811 		return removed;
       
  3812 	},
       
  3813 
       
  3814 	clear: '#removeChildren',
       
  3815 
       
  3816 	reverseChildren: function() {
       
  3817 		if (this._children) {
       
  3818 			this._children.reverse();
       
  3819 			for (var i = 0, l = this._children.length; i < l; i++)
       
  3820 				this._children[i]._index = i;
       
  3821 			this._changed(11);
       
  3822 		}
       
  3823 	},
       
  3824 
       
  3825 	isEmpty: function() {
       
  3826 		return !this._children || this._children.length === 0;
       
  3827 	},
       
  3828 
       
  3829 	isEditable: function() {
       
  3830 		var item = this;
       
  3831 		while (item) {
       
  3832 			if (!item._visible || item._locked)
       
  3833 				return false;
       
  3834 			item = item._parent;
       
  3835 		}
       
  3836 		return true;
       
  3837 	},
       
  3838 
       
  3839 	hasFill: function() {
       
  3840 		return this.getStyle().hasFill();
       
  3841 	},
       
  3842 
       
  3843 	hasStroke: function() {
       
  3844 		return this.getStyle().hasStroke();
       
  3845 	},
       
  3846 
       
  3847 	hasShadow: function() {
       
  3848 		return this.getStyle().hasShadow();
       
  3849 	},
       
  3850 
       
  3851 	_getOrder: function(item) {
       
  3852 		function getList(item) {
       
  3853 			var list = [];
       
  3854 			do {
       
  3855 				list.unshift(item);
       
  3856 			} while (item = item._parent);
       
  3857 			return list;
       
  3858 		}
       
  3859 		var list1 = getList(this),
       
  3860 			list2 = getList(item);
       
  3861 		for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {
       
  3862 			if (list1[i] != list2[i]) {
       
  3863 				return list1[i]._index < list2[i]._index ? 1 : -1;
       
  3864 			}
       
  3865 		}
       
  3866 		return 0;
       
  3867 	},
       
  3868 
       
  3869 	hasChildren: function() {
       
  3870 		return this._children && this._children.length > 0;
       
  3871 	},
       
  3872 
       
  3873 	isInserted: function() {
       
  3874 		return this._parent ? this._parent.isInserted() : false;
       
  3875 	},
       
  3876 
       
  3877 	isAbove: function(item) {
       
  3878 		return this._getOrder(item) === -1;
       
  3879 	},
       
  3880 
       
  3881 	isBelow: function(item) {
       
  3882 		return this._getOrder(item) === 1;
       
  3883 	},
       
  3884 
       
  3885 	isParent: function(item) {
       
  3886 		return this._parent === item;
       
  3887 	},
       
  3888 
       
  3889 	isChild: function(item) {
       
  3890 		return item && item._parent === this;
       
  3891 	},
       
  3892 
       
  3893 	isDescendant: function(item) {
       
  3894 		var parent = this;
       
  3895 		while (parent = parent._parent) {
       
  3896 			if (parent == item)
       
  3897 				return true;
       
  3898 		}
       
  3899 		return false;
       
  3900 	},
       
  3901 
       
  3902 	isAncestor: function(item) {
       
  3903 		return item ? item.isDescendant(this) : false;
       
  3904 	},
       
  3905 
       
  3906 	isGroupedWith: function(item) {
       
  3907 		var parent = this._parent;
       
  3908 		while (parent) {
       
  3909 			if (parent._parent
       
  3910 				&& /^(Group|Layer|CompoundPath)$/.test(parent._class)
       
  3911 				&& item.isDescendant(parent))
       
  3912 					return true;
       
  3913 			parent = parent._parent;
       
  3914 		}
       
  3915 		return false;
       
  3916 	},
       
  3917 
       
  3918 	translate: function() {
       
  3919 		var mx = new Matrix();
       
  3920 		return this.transform(mx.translate.apply(mx, arguments));
       
  3921 	},
       
  3922 
       
  3923 	rotate: function(angle ) {
       
  3924 		return this.transform(new Matrix().rotate(angle,
       
  3925 				Point.read(arguments, 1, { readNull: true })
       
  3926 					|| this.getPosition(true)));
       
  3927 	}
       
  3928 }, Base.each(['scale', 'shear', 'skew'], function(name) {
       
  3929 	this[name] = function() {
       
  3930 		var point = Point.read(arguments),
       
  3931 			center = Point.read(arguments, 0, { readNull: true });
       
  3932 		return this.transform(new Matrix()[name](point,
       
  3933 				center || this.getPosition(true)));
       
  3934 	};
       
  3935 }, {
       
  3936 
       
  3937 }), {
       
  3938 	transform: function(matrix, _applyMatrix, _applyRecursively,
       
  3939 			_setApplyMatrix) {
       
  3940 		if (matrix && matrix.isIdentity())
       
  3941 			matrix = null;
       
  3942 		var _matrix = this._matrix,
       
  3943 			applyMatrix = (_applyMatrix || this._applyMatrix)
       
  3944 					&& ((!_matrix.isIdentity() || matrix)
       
  3945 						|| _applyMatrix && _applyRecursively && this._children);
       
  3946 		if (!matrix && !applyMatrix)
       
  3947 			return this;
       
  3948 		if (matrix)
       
  3949 			_matrix.preConcatenate(matrix);
       
  3950 		if (applyMatrix = applyMatrix && this._transformContent(_matrix,
       
  3951 					_applyRecursively, _setApplyMatrix)) {
       
  3952 			var pivot = this._pivot,
       
  3953 				style = this._style,
       
  3954 				fillColor = style.getFillColor(true),
       
  3955 				strokeColor = style.getStrokeColor(true);
       
  3956 			if (pivot)
       
  3957 				_matrix._transformPoint(pivot, pivot, true);
       
  3958 			if (fillColor)
       
  3959 				fillColor.transform(_matrix);
       
  3960 			if (strokeColor)
       
  3961 				strokeColor.transform(_matrix);
       
  3962 			_matrix.reset(true);
       
  3963 			if (_setApplyMatrix && this._canApplyMatrix)
       
  3964 				this._applyMatrix = true;
       
  3965 		}
       
  3966 		var bounds = this._bounds,
       
  3967 			position = this._position;
       
  3968 		this._changed(9);
       
  3969 		var decomp = bounds && matrix && matrix.decompose();
       
  3970 		if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
       
  3971 			for (var key in bounds) {
       
  3972 				var rect = bounds[key];
       
  3973 				if (applyMatrix || !rect._internal)
       
  3974 					matrix._transformBounds(rect, rect);
       
  3975 			}
       
  3976 			var getter = this._boundsGetter,
       
  3977 				rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
       
  3978 			if (rect)
       
  3979 				this._position = rect.getCenter(true);
       
  3980 			this._bounds = bounds;
       
  3981 		} else if (matrix && position) {
       
  3982 			this._position = matrix._transformPoint(position, position);
       
  3983 		}
       
  3984 		return this;
       
  3985 	},
       
  3986 
       
  3987 	_transformContent: function(matrix, applyRecursively, setApplyMatrix) {
       
  3988 		var children = this._children;
       
  3989 		if (children) {
       
  3990 			for (var i = 0, l = children.length; i < l; i++)
       
  3991 				children[i].transform(matrix, true, applyRecursively,
       
  3992 						setApplyMatrix);
       
  3993 			return true;
       
  3994 		}
       
  3995 	},
       
  3996 
       
  3997 	globalToLocal: function() {
       
  3998 		return this.getGlobalMatrix(true)._inverseTransform(
       
  3999 				Point.read(arguments));
       
  4000 	},
       
  4001 
       
  4002 	localToGlobal: function() {
       
  4003 		return this.getGlobalMatrix(true)._transformPoint(
       
  4004 				Point.read(arguments));
       
  4005 	},
       
  4006 
       
  4007 	parentToLocal: function() {
       
  4008 		return this._matrix._inverseTransform(Point.read(arguments));
       
  4009 	},
       
  4010 
       
  4011 	localToParent: function() {
       
  4012 		return this._matrix._transformPoint(Point.read(arguments));
       
  4013 	},
       
  4014 
       
  4015 	fitBounds: function(rectangle, fill) {
       
  4016 		rectangle = Rectangle.read(arguments);
       
  4017 		var bounds = this.getBounds(),
       
  4018 			itemRatio = bounds.height / bounds.width,
       
  4019 			rectRatio = rectangle.height / rectangle.width,
       
  4020 			scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
       
  4021 					? rectangle.width / bounds.width
       
  4022 					: rectangle.height / bounds.height,
       
  4023 			newBounds = new Rectangle(new Point(),
       
  4024 					new Size(bounds.width * scale, bounds.height * scale));
       
  4025 		newBounds.setCenter(rectangle.getCenter());
       
  4026 		this.setBounds(newBounds);
       
  4027 	},
       
  4028 
       
  4029 	_setStyles: function(ctx) {
       
  4030 		var style = this._style,
       
  4031 			fillColor = style.getFillColor(),
       
  4032 			strokeColor = style.getStrokeColor(),
       
  4033 			shadowColor = style.getShadowColor();
       
  4034 		if (fillColor)
       
  4035 			ctx.fillStyle = fillColor.toCanvasStyle(ctx);
       
  4036 		if (strokeColor) {
       
  4037 			var strokeWidth = style.getStrokeWidth();
       
  4038 			if (strokeWidth > 0) {
       
  4039 				ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
       
  4040 				ctx.lineWidth = strokeWidth;
       
  4041 				var strokeJoin = style.getStrokeJoin(),
       
  4042 					strokeCap = style.getStrokeCap(),
       
  4043 					miterLimit = style.getMiterLimit();
       
  4044 				if (strokeJoin)
       
  4045 					ctx.lineJoin = strokeJoin;
       
  4046 				if (strokeCap)
       
  4047 					ctx.lineCap = strokeCap;
       
  4048 				if (miterLimit)
       
  4049 					ctx.miterLimit = miterLimit;
       
  4050 				if (paper.support.nativeDash) {
       
  4051 					var dashArray = style.getDashArray(),
       
  4052 						dashOffset = style.getDashOffset();
       
  4053 					if (dashArray && dashArray.length) {
       
  4054 						if ('setLineDash' in ctx) {
       
  4055 							ctx.setLineDash(dashArray);
       
  4056 							ctx.lineDashOffset = dashOffset;
       
  4057 						} else {
       
  4058 							ctx.mozDash = dashArray;
       
  4059 							ctx.mozDashOffset = dashOffset;
       
  4060 						}
       
  4061 					}
       
  4062 				}
       
  4063 			}
       
  4064 		}
       
  4065 		if (shadowColor) {
       
  4066 			var shadowBlur = style.getShadowBlur();
       
  4067 			if (shadowBlur > 0) {
       
  4068 				ctx.shadowColor = shadowColor.toCanvasStyle(ctx);
       
  4069 				ctx.shadowBlur = shadowBlur;
       
  4070 				var offset = this.getShadowOffset();
       
  4071 				ctx.shadowOffsetX = offset.x;
       
  4072 				ctx.shadowOffsetY = offset.y;
       
  4073 			}
       
  4074 		}
       
  4075 	},
       
  4076 
       
  4077 	draw: function(ctx, param, parentStrokeMatrix) {
       
  4078 		var updateVersion = this._updateVersion = this._project._updateVersion;
       
  4079 		if (!this._visible || this._opacity === 0)
       
  4080 			return;
       
  4081 		var matrices = param.matrices,
       
  4082 			viewMatrix = param.viewMatrix,
       
  4083 			matrix = this._matrix,
       
  4084 			globalMatrix = matrices[matrices.length - 1].chain(matrix);
       
  4085 		if (!globalMatrix.isInvertible())
       
  4086 			return;
       
  4087 
       
  4088 		function getViewMatrix(matrix) {
       
  4089 			return viewMatrix ? viewMatrix.chain(matrix) : matrix;
       
  4090 		}
       
  4091 
       
  4092 		matrices.push(globalMatrix);
       
  4093 		if (param.updateMatrix) {
       
  4094 			globalMatrix._updateVersion = updateVersion;
       
  4095 			this._globalMatrix = globalMatrix;
       
  4096 		}
       
  4097 
       
  4098 		var blendMode = this._blendMode,
       
  4099 			opacity = this._opacity,
       
  4100 			normalBlend = blendMode === 'normal',
       
  4101 			nativeBlend = BlendMode.nativeModes[blendMode],
       
  4102 			direct = normalBlend && opacity === 1
       
  4103 					|| param.dontStart
       
  4104 					|| param.clip
       
  4105 					|| (nativeBlend || normalBlend && opacity < 1)
       
  4106 						&& this._canComposite(),
       
  4107 			pixelRatio = param.pixelRatio || 1,
       
  4108 			mainCtx, itemOffset, prevOffset;
       
  4109 		if (!direct) {
       
  4110 			var bounds = this.getStrokeBounds(getViewMatrix(globalMatrix));
       
  4111 			if (!bounds.width || !bounds.height)
       
  4112 				return;
       
  4113 			prevOffset = param.offset;
       
  4114 			itemOffset = param.offset = bounds.getTopLeft().floor();
       
  4115 			mainCtx = ctx;
       
  4116 			ctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1)
       
  4117 					.multiply(pixelRatio));
       
  4118 			if (pixelRatio !== 1)
       
  4119 				ctx.scale(pixelRatio, pixelRatio);
       
  4120 		}
       
  4121 		ctx.save();
       
  4122 		var strokeMatrix = parentStrokeMatrix
       
  4123 				? parentStrokeMatrix.chain(matrix)
       
  4124 				: !this.getStrokeScaling(true) && getViewMatrix(globalMatrix),
       
  4125 			clip = !direct && param.clipItem,
       
  4126 			transform = !strokeMatrix || clip;
       
  4127 		if (direct) {
       
  4128 			ctx.globalAlpha = opacity;
       
  4129 			if (nativeBlend)
       
  4130 				ctx.globalCompositeOperation = blendMode;
       
  4131 		} else if (transform) {
       
  4132 			ctx.translate(-itemOffset.x, -itemOffset.y);
       
  4133 		}
       
  4134 		if (transform)
       
  4135 			(direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
       
  4136 		if (clip)
       
  4137 			param.clipItem.draw(ctx, param.extend({ clip: true }));
       
  4138 		if (strokeMatrix) {
       
  4139 			ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
       
  4140 			var offset = param.offset;
       
  4141 			if (offset)
       
  4142 				ctx.translate(-offset.x, -offset.y);
       
  4143 		}
       
  4144 		this._draw(ctx, param, strokeMatrix);
       
  4145 		ctx.restore();
       
  4146 		matrices.pop();
       
  4147 		if (param.clip && !param.dontFinish)
       
  4148 			ctx.clip();
       
  4149 		if (!direct) {
       
  4150 			BlendMode.process(blendMode, ctx, mainCtx, opacity,
       
  4151 					itemOffset.subtract(prevOffset).multiply(pixelRatio));
       
  4152 			CanvasProvider.release(ctx);
       
  4153 			param.offset = prevOffset;
       
  4154 		}
       
  4155 	},
       
  4156 
       
  4157 	_isUpdated: function(updateVersion) {
       
  4158 		var parent = this._parent;
       
  4159 		if (parent instanceof CompoundPath)
       
  4160 			return parent._isUpdated(updateVersion);
       
  4161 		var updated = this._updateVersion === updateVersion;
       
  4162 		if (!updated && parent && parent._visible
       
  4163 				&& parent._isUpdated(updateVersion)) {
       
  4164 			this._updateVersion = updateVersion;
       
  4165 			updated = true;
       
  4166 		}
       
  4167 		return updated;
       
  4168 	},
       
  4169 
       
  4170 	_drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
       
  4171 		if ((this._drawSelected || this._boundsSelected)
       
  4172 				&& this._isUpdated(updateVersion)) {
       
  4173 			var color = this.getSelectedColor(true)
       
  4174 					|| this.getLayer().getSelectedColor(true),
       
  4175 				mx = matrix.chain(this.getGlobalMatrix(true));
       
  4176 			ctx.strokeStyle = ctx.fillStyle = color
       
  4177 					? color.toCanvasStyle(ctx) : '#009dec';
       
  4178 			if (this._drawSelected)
       
  4179 				this._drawSelected(ctx, mx, selectedItems);
       
  4180 			if (this._boundsSelected) {
       
  4181 				var half = size / 2;
       
  4182 					coords = mx._transformCorners(this.getInternalBounds());
       
  4183 				ctx.beginPath();
       
  4184 				for (var i = 0; i < 8; i++)
       
  4185 					ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
       
  4186 				ctx.closePath();
       
  4187 				ctx.stroke();
       
  4188 				for (var i = 0; i < 8; i++)
       
  4189 					ctx.fillRect(coords[i] - half, coords[++i] - half,
       
  4190 							size, size);
       
  4191 			}
       
  4192 		}
       
  4193 	},
       
  4194 
       
  4195 	_canComposite: function() {
       
  4196 		return false;
       
  4197 	}
       
  4198 }, Base.each(['down', 'drag', 'up', 'move'], function(name) {
       
  4199 	this['removeOn' + Base.capitalize(name)] = function() {
       
  4200 		var hash = {};
       
  4201 		hash[name] = true;
       
  4202 		return this.removeOn(hash);
       
  4203 	};
       
  4204 }, {
       
  4205 
       
  4206 	removeOn: function(obj) {
       
  4207 		for (var name in obj) {
       
  4208 			if (obj[name]) {
       
  4209 				var key = 'mouse' + name,
       
  4210 					project = this._project,
       
  4211 					sets = project._removeSets = project._removeSets || {};
       
  4212 				sets[key] = sets[key] || {};
       
  4213 				sets[key][this._id] = this;
       
  4214 			}
       
  4215 		}
       
  4216 		return this;
       
  4217 	}
       
  4218 }));
       
  4219 
       
  4220 var Group = Item.extend({
       
  4221 	_class: 'Group',
       
  4222 	_selectChildren: true,
       
  4223 	_serializeFields: {
       
  4224 		children: []
       
  4225 	},
       
  4226 
       
  4227 	initialize: function Group(arg) {
       
  4228 		this._children = [];
       
  4229 		this._namedChildren = {};
       
  4230 		if (!this._initialize(arg))
       
  4231 			this.addChildren(Array.isArray(arg) ? arg : arguments);
       
  4232 	},
       
  4233 
       
  4234 	_changed: function _changed(flags) {
       
  4235 		_changed.base.call(this, flags);
       
  4236 		if (flags & 1026) {
       
  4237 			this._clipItem = undefined;
       
  4238 		}
       
  4239 	},
       
  4240 
       
  4241 	_getClipItem: function() {
       
  4242 		var clipItem = this._clipItem;
       
  4243 		if (clipItem === undefined) {
       
  4244 			clipItem = null;
       
  4245 			for (var i = 0, l = this._children.length; i < l; i++) {
       
  4246 				var child = this._children[i];
       
  4247 				if (child._clipMask) {
       
  4248 					clipItem = child;
       
  4249 					break;
       
  4250 				}
       
  4251 			}
       
  4252 			this._clipItem = clipItem;
       
  4253 		}
       
  4254 		return clipItem;
       
  4255 	},
       
  4256 
       
  4257 	isClipped: function() {
       
  4258 		return !!this._getClipItem();
       
  4259 	},
       
  4260 
       
  4261 	setClipped: function(clipped) {
       
  4262 		var child = this.getFirstChild();
       
  4263 		if (child)
       
  4264 			child.setClipMask(clipped);
       
  4265 	},
       
  4266 
       
  4267 	_draw: function(ctx, param) {
       
  4268 		var clip = param.clip,
       
  4269 			clipItem = !clip && this._getClipItem(),
       
  4270 			draw = true;
       
  4271 		param = param.extend({ clipItem: clipItem, clip: false });
       
  4272 		if (clip) {
       
  4273 			if (this._currentPath) {
       
  4274 				ctx.currentPath = this._currentPath;
       
  4275 				draw = false;
       
  4276 			} else {
       
  4277 				ctx.beginPath();
       
  4278 				param.dontStart = param.dontFinish = true;
       
  4279 			}
       
  4280 		} else if (clipItem) {
       
  4281 			clipItem.draw(ctx, param.extend({ clip: true }));
       
  4282 		}
       
  4283 		if (draw) {
       
  4284 			for (var i = 0, l = this._children.length; i < l; i++) {
       
  4285 				var item = this._children[i];
       
  4286 				if (item !== clipItem)
       
  4287 					item.draw(ctx, param);
       
  4288 			}
       
  4289 		}
       
  4290 		if (clip) {
       
  4291 			this._currentPath = ctx.currentPath;
       
  4292 		}
       
  4293 	}
       
  4294 });
       
  4295 
       
  4296 var Layer = Group.extend({
       
  4297 	_class: 'Layer',
       
  4298 
       
  4299 	initialize: function Layer(arg) {
       
  4300 		var props = Base.isPlainObject(arg)
       
  4301 				? new Base(arg)
       
  4302 				: { children: Array.isArray(arg) ? arg : arguments },
       
  4303 			insert = props.insert;
       
  4304 		props.insert = false;
       
  4305 		Group.call(this, props);
       
  4306 		if (insert || insert === undefined) {
       
  4307 			this._project.addChild(this);
       
  4308 			this.activate();
       
  4309 		}
       
  4310 	},
       
  4311 
       
  4312 	_remove: function _remove(notifySelf, notifyParent) {
       
  4313 		if (this._parent)
       
  4314 			return _remove.base.call(this, notifySelf, notifyParent);
       
  4315 		if (this._index != null) {
       
  4316 			var project = this._project;
       
  4317 			if (project._activeLayer === this)
       
  4318 				project._activeLayer = this.getNextSibling()
       
  4319 						|| this.getPreviousSibling();
       
  4320 			Base.splice(project.layers, null, this._index, 1);
       
  4321 			this._installEvents(false);
       
  4322 			if (notifySelf && project._changes)
       
  4323 				this._changed(5);
       
  4324 			if (notifyParent) {
       
  4325 				project._needsUpdate = true;
       
  4326 			}
       
  4327 			return true;
       
  4328 		}
       
  4329 		return false;
       
  4330 	},
       
  4331 
       
  4332 	getNextSibling: function getNextSibling() {
       
  4333 		return this._parent ? getNextSibling.base.call(this)
       
  4334 				: this._project.layers[this._index + 1] || null;
       
  4335 	},
       
  4336 
       
  4337 	getPreviousSibling: function getPreviousSibling() {
       
  4338 		return this._parent ? getPreviousSibling.base.call(this)
       
  4339 				: this._project.layers[this._index - 1] || null;
       
  4340 	},
       
  4341 
       
  4342 	isInserted: function isInserted() {
       
  4343 		return this._parent ? isInserted.base.call(this) : this._index != null;
       
  4344 	},
       
  4345 
       
  4346 	activate: function() {
       
  4347 		this._project._activeLayer = this;
       
  4348 	},
       
  4349 
       
  4350 	_insertSibling: function _insertSibling(index, item, _preserve) {
       
  4351 		return !this._parent
       
  4352 				? this._project.insertChild(index, item, _preserve)
       
  4353 				: _insertSibling.base.call(this, index, item, _preserve);
       
  4354 	}
       
  4355 });
       
  4356 
       
  4357 var Shape = Item.extend({
       
  4358 	_class: 'Shape',
       
  4359 	_applyMatrix: false,
       
  4360 	_canApplyMatrix: false,
       
  4361 	_boundsSelected: true,
       
  4362 	_serializeFields: {
       
  4363 		type: null,
       
  4364 		size: null,
       
  4365 		radius: null
       
  4366 	},
       
  4367 
       
  4368 	initialize: function Shape(props) {
       
  4369 		this._initialize(props);
       
  4370 	},
       
  4371 
       
  4372 	_equals: function(item) {
       
  4373 		return this._type === item._type
       
  4374 			&& this._size.equals(item._size)
       
  4375 			&& Base.equals(this._radius, item._radius);
       
  4376 	},
       
  4377 
       
  4378 	clone: function(insert) {
       
  4379 		var copy = new Shape(Item.NO_INSERT);
       
  4380 		copy.setType(this._type);
       
  4381 		copy.setSize(this._size);
       
  4382 		copy.setRadius(this._radius);
       
  4383 		return this._clone(copy, insert);
       
  4384 	},
       
  4385 
       
  4386 	getType: function() {
       
  4387 		return this._type;
       
  4388 	},
       
  4389 
       
  4390 	setType: function(type) {
       
  4391 		this._type = type;
       
  4392 	},
       
  4393 
       
  4394 	getShape: '#getType',
       
  4395 	setShape: '#setType',
       
  4396 
       
  4397 	getSize: function() {
       
  4398 		var size = this._size;
       
  4399 		return new LinkedSize(size.width, size.height, this, 'setSize');
       
  4400 	},
       
  4401 
       
  4402 	setSize: function() {
       
  4403 		var size = Size.read(arguments);
       
  4404 		if (!this._size) {
       
  4405 			this._size = size.clone();
       
  4406 		} else if (!this._size.equals(size)) {
       
  4407 			var type = this._type,
       
  4408 				width = size.width,
       
  4409 				height = size.height;
       
  4410 			if (type === 'rectangle') {
       
  4411 				var radius = Size.min(this._radius, size.divide(2));
       
  4412 				this._radius.set(radius.width, radius.height);
       
  4413 			} else if (type === 'circle') {
       
  4414 				width = height = (width + height) / 2;
       
  4415 				this._radius = width / 2;
       
  4416 			} else if (type === 'ellipse') {
       
  4417 				this._radius.set(width / 2, height / 2);
       
  4418 			}
       
  4419 			this._size.set(width, height);
       
  4420 			this._changed(9);
       
  4421 		}
       
  4422 	},
       
  4423 
       
  4424 	getRadius: function() {
       
  4425 		var rad = this._radius;
       
  4426 		return this._type === 'circle'
       
  4427 				? rad
       
  4428 				: new LinkedSize(rad.width, rad.height, this, 'setRadius');
       
  4429 	},
       
  4430 
       
  4431 	setRadius: function(radius) {
       
  4432 		var type = this._type;
       
  4433 		if (type === 'circle') {
       
  4434 			if (radius === this._radius)
       
  4435 				return;
       
  4436 			var size = radius * 2;
       
  4437 			this._radius = radius;
       
  4438 			this._size.set(size, size);
       
  4439 		} else {
       
  4440 			radius = Size.read(arguments);
       
  4441 			if (!this._radius) {
       
  4442 				this._radius = radius.clone();
       
  4443 			} else {
       
  4444 				if (this._radius.equals(radius))
       
  4445 					return;
       
  4446 				this._radius.set(radius.width, radius.height);
       
  4447 				if (type === 'rectangle') {
       
  4448 					var size = Size.max(this._size, radius.multiply(2));
       
  4449 					this._size.set(size.width, size.height);
       
  4450 				} else if (type === 'ellipse') {
       
  4451 					this._size.set(radius.width * 2, radius.height * 2);
       
  4452 				}
       
  4453 			}
       
  4454 		}
       
  4455 		this._changed(9);
       
  4456 	},
       
  4457 
       
  4458 	isEmpty: function() {
       
  4459 		return false;
       
  4460 	},
       
  4461 
       
  4462 	toPath: function(insert) {
       
  4463 		var path = this._clone(new Path[Base.capitalize(this._type)]({
       
  4464 			center: new Point(),
       
  4465 			size: this._size,
       
  4466 			radius: this._radius,
       
  4467 			insert: false
       
  4468 		}), insert);
       
  4469 		if (paper.settings.applyMatrix)
       
  4470 			path.setApplyMatrix(true);
       
  4471 		return path;
       
  4472 	},
       
  4473 
       
  4474 	_draw: function(ctx, param, strokeMatrix) {
       
  4475 		var style = this._style,
       
  4476 			hasFill = style.hasFill(),
       
  4477 			hasStroke = style.hasStroke(),
       
  4478 			dontPaint = param.dontFinish || param.clip,
       
  4479 			untransformed = !strokeMatrix;
       
  4480 		if (hasFill || hasStroke || dontPaint) {
       
  4481 			var type = this._type,
       
  4482 				radius = this._radius,
       
  4483 				isCircle = type === 'circle';
       
  4484 			if (!param.dontStart)
       
  4485 				ctx.beginPath();
       
  4486 			if (untransformed && isCircle) {
       
  4487 				ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
       
  4488 			} else {
       
  4489 				var rx = isCircle ? radius : radius.width,
       
  4490 					ry = isCircle ? radius : radius.height,
       
  4491 					size = this._size,
       
  4492 					width = size.width,
       
  4493 					height = size.height;
       
  4494 				if (untransformed && type === 'rectangle' && rx === 0 && ry === 0) {
       
  4495 					ctx.rect(-width / 2, -height / 2, width, height);
       
  4496 				} else {
       
  4497 					var x = width / 2,
       
  4498 						y = height / 2,
       
  4499 						kappa = 1 - 0.5522847498307936,
       
  4500 						cx = rx * kappa,
       
  4501 						cy = ry * kappa,
       
  4502 						c = [
       
  4503 							-x, -y + ry,
       
  4504 							-x, -y + cy,
       
  4505 							-x + cx, -y,
       
  4506 							-x + rx, -y,
       
  4507 							x - rx, -y,
       
  4508 							x - cx, -y,
       
  4509 							x, -y + cy,
       
  4510 							x, -y + ry,
       
  4511 							x, y - ry,
       
  4512 							x, y - cy,
       
  4513 							x - cx, y,
       
  4514 							x - rx, y,
       
  4515 							-x + rx, y,
       
  4516 							-x + cx, y,
       
  4517 							-x, y - cy,
       
  4518 							-x, y - ry
       
  4519 						];
       
  4520 					if (strokeMatrix)
       
  4521 						strokeMatrix.transform(c, c, 32);
       
  4522 					ctx.moveTo(c[0], c[1]);
       
  4523 					ctx.bezierCurveTo(c[2], c[3], c[4], c[5], c[6], c[7]);
       
  4524 					if (x !== rx)
       
  4525 						ctx.lineTo(c[8], c[9]);
       
  4526 					ctx.bezierCurveTo(c[10], c[11], c[12], c[13], c[14], c[15]);
       
  4527 					if (y !== ry)
       
  4528 						ctx.lineTo(c[16], c[17]);
       
  4529 					ctx.bezierCurveTo(c[18], c[19], c[20], c[21], c[22], c[23]);
       
  4530 					if (x !== rx)
       
  4531 						ctx.lineTo(c[24], c[25]);
       
  4532 					ctx.bezierCurveTo(c[26], c[27], c[28], c[29], c[30], c[31]);
       
  4533 				}
       
  4534 			}
       
  4535 			ctx.closePath();
       
  4536 		}
       
  4537 		if (!dontPaint && (hasFill || hasStroke)) {
       
  4538 			this._setStyles(ctx);
       
  4539 			if (hasFill) {
       
  4540 				ctx.fill(style.getWindingRule());
       
  4541 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  4542 			}
       
  4543 			if (hasStroke)
       
  4544 				ctx.stroke();
       
  4545 		}
       
  4546 	},
       
  4547 
       
  4548 	_canComposite: function() {
       
  4549 		return !(this.hasFill() && this.hasStroke());
       
  4550 	},
       
  4551 
       
  4552 	_getBounds: function(getter, matrix) {
       
  4553 		var rect = new Rectangle(this._size).setCenter(0, 0);
       
  4554 		if (getter !== 'getBounds' && this.hasStroke())
       
  4555 			rect = rect.expand(this.getStrokeWidth());
       
  4556 		return matrix ? matrix._transformBounds(rect) : rect;
       
  4557 	}
       
  4558 },
       
  4559 new function() {
       
  4560 
       
  4561 	function getCornerCenter(that, point, expand) {
       
  4562 		var radius = that._radius;
       
  4563 		if (!radius.isZero()) {
       
  4564 			var halfSize = that._size.divide(2);
       
  4565 			for (var i = 0; i < 4; i++) {
       
  4566 				var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1),
       
  4567 					corner = dir.multiply(halfSize),
       
  4568 					center = corner.subtract(dir.multiply(radius)),
       
  4569 					rect = new Rectangle(corner, center);
       
  4570 				if ((expand ? rect.expand(expand) : rect).contains(point))
       
  4571 					return center;
       
  4572 			}
       
  4573 		}
       
  4574 	}
       
  4575 
       
  4576 	function getEllipseRadius(point, radius) {
       
  4577 		var angle = point.getAngleInRadians(),
       
  4578 			width = radius.width * 2,
       
  4579 			height = radius.height * 2,
       
  4580 			x = width * Math.sin(angle),
       
  4581 			y = height * Math.cos(angle);
       
  4582 		return width * height / (2 * Math.sqrt(x * x + y * y));
       
  4583 	}
       
  4584 
       
  4585 	return {
       
  4586 		_contains: function _contains(point) {
       
  4587 			if (this._type === 'rectangle') {
       
  4588 				var center = getCornerCenter(this, point);
       
  4589 				return center
       
  4590 						? point.subtract(center).divide(this._radius)
       
  4591 							.getLength() <= 1
       
  4592 						: _contains.base.call(this, point);
       
  4593 			} else {
       
  4594 				return point.divide(this.size).getLength() <= 0.5;
       
  4595 			}
       
  4596 		},
       
  4597 
       
  4598 		_hitTestSelf: function _hitTestSelf(point, options) {
       
  4599 			var hit = false;
       
  4600 			if (this.hasStroke()) {
       
  4601 				var type = this._type,
       
  4602 					radius = this._radius,
       
  4603 					strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
       
  4604 				if (type === 'rectangle') {
       
  4605 					var center = getCornerCenter(this, point, strokeWidth);
       
  4606 					if (center) {
       
  4607 						var pt = point.subtract(center);
       
  4608 						hit = 2 * Math.abs(pt.getLength()
       
  4609 								- getEllipseRadius(pt, radius)) <= strokeWidth;
       
  4610 					} else {
       
  4611 						var rect = new Rectangle(this._size).setCenter(0, 0),
       
  4612 							outer = rect.expand(strokeWidth),
       
  4613 							inner = rect.expand(-strokeWidth);
       
  4614 						hit = outer._containsPoint(point)
       
  4615 								&& !inner._containsPoint(point);
       
  4616 					}
       
  4617 				} else {
       
  4618 					if (type === 'ellipse')
       
  4619 						radius = getEllipseRadius(point, radius);
       
  4620 					hit = 2 * Math.abs(point.getLength() - radius)
       
  4621 							<= strokeWidth;
       
  4622 				}
       
  4623 			}
       
  4624 			return hit
       
  4625 					? new HitResult('stroke', this)
       
  4626 					: _hitTestSelf.base.apply(this, arguments);
       
  4627 		}
       
  4628 	};
       
  4629 }, {
       
  4630 
       
  4631 statics: new function() {
       
  4632 	function createShape(type, point, size, radius, args) {
       
  4633 		var item = new Shape(Base.getNamed(args));
       
  4634 		item._type = type;
       
  4635 		item._size = size;
       
  4636 		item._radius = radius;
       
  4637 		return item.translate(point);
       
  4638 	}
       
  4639 
       
  4640 	return {
       
  4641 		Circle: function() {
       
  4642 			var center = Point.readNamed(arguments, 'center'),
       
  4643 				radius = Base.readNamed(arguments, 'radius');
       
  4644 			return createShape('circle', center, new Size(radius * 2), radius,
       
  4645 					arguments);
       
  4646 		},
       
  4647 
       
  4648 		Rectangle: function() {
       
  4649 			var rect = Rectangle.readNamed(arguments, 'rectangle'),
       
  4650 				radius = Size.min(Size.readNamed(arguments, 'radius'),
       
  4651 						rect.getSize(true).divide(2));
       
  4652 			return createShape('rectangle', rect.getCenter(true),
       
  4653 					rect.getSize(true), radius, arguments);
       
  4654 		},
       
  4655 
       
  4656 		Ellipse: function() {
       
  4657 			var ellipse = Shape._readEllipse(arguments),
       
  4658 				radius = ellipse.radius;
       
  4659 			return createShape('ellipse', ellipse.center, radius.multiply(2),
       
  4660 					radius, arguments);
       
  4661 		},
       
  4662 
       
  4663 		_readEllipse: function(args) {
       
  4664 			var center,
       
  4665 				radius;
       
  4666 			if (Base.hasNamed(args, 'radius')) {
       
  4667 				center = Point.readNamed(args, 'center');
       
  4668 				radius = Size.readNamed(args, 'radius');
       
  4669 			} else {
       
  4670 				var rect = Rectangle.readNamed(args, 'rectangle');
       
  4671 				center = rect.getCenter(true);
       
  4672 				radius = rect.getSize(true).divide(2);
       
  4673 			}
       
  4674 			return { center: center, radius: radius };
       
  4675 		}
       
  4676 	};
       
  4677 }});
       
  4678 
       
  4679 var Raster = Item.extend({
       
  4680 	_class: 'Raster',
       
  4681 	_applyMatrix: false,
       
  4682 	_canApplyMatrix: false,
       
  4683 	_boundsGetter: 'getBounds',
       
  4684 	_boundsSelected: true,
       
  4685 	_serializeFields: {
       
  4686 		crossOrigin: null,
       
  4687 		source: null
       
  4688 	},
       
  4689 
       
  4690 	initialize: function Raster(object, position) {
       
  4691 		if (!this._initialize(object,
       
  4692 				position !== undefined && Point.read(arguments, 1))) {
       
  4693 			if (typeof object === 'string') {
       
  4694 				this.setSource(object);
       
  4695 			} else {
       
  4696 				this.setImage(object);
       
  4697 			}
       
  4698 		}
       
  4699 		if (!this._size) {
       
  4700 			this._size = new Size();
       
  4701 			this._loaded = false;
       
  4702 		}
       
  4703 	},
       
  4704 
       
  4705 	_equals: function(item) {
       
  4706 		return this.getSource() === item.getSource();
       
  4707 	},
       
  4708 
       
  4709 	clone: function(insert) {
       
  4710 		var copy = new Raster(Item.NO_INSERT),
       
  4711 			image = this._image,
       
  4712 			canvas = this._canvas;
       
  4713 		if (image) {
       
  4714 			copy.setImage(image);
       
  4715 		} else if (canvas) {
       
  4716 			var copyCanvas = CanvasProvider.getCanvas(this._size);
       
  4717 			copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
       
  4718 			copy.setImage(copyCanvas);
       
  4719 		}
       
  4720 		copy._crossOrigin = this._crossOrigin;
       
  4721 		return this._clone(copy, insert);
       
  4722 	},
       
  4723 
       
  4724 	getSize: function() {
       
  4725 		var size = this._size;
       
  4726 		return new LinkedSize(size ? size.width : 0, size ? size.height : 0,
       
  4727 				this, 'setSize');
       
  4728 	},
       
  4729 
       
  4730 	setSize: function() {
       
  4731 		var size = Size.read(arguments);
       
  4732 		if (!size.equals(this._size)) {
       
  4733 			if (size.width > 0 && size.height > 0) {
       
  4734 				var element = this.getElement();
       
  4735 				this.setImage(CanvasProvider.getCanvas(size));
       
  4736 				if (element)
       
  4737 					this.getContext(true).drawImage(element, 0, 0,
       
  4738 							size.width, size.height);
       
  4739 			} else {
       
  4740 				if (this._canvas)
       
  4741 					CanvasProvider.release(this._canvas);
       
  4742 				this._size = size.clone();
       
  4743 			}
       
  4744 		}
       
  4745 	},
       
  4746 
       
  4747 	getWidth: function() {
       
  4748 		return this._size ? this._size.width : 0;
       
  4749 	},
       
  4750 
       
  4751 	setWidth: function(width) {
       
  4752 		this.setSize(width, this.getHeight());
       
  4753 	},
       
  4754 
       
  4755 	getHeight: function() {
       
  4756 		return this._size ? this._size.height : 0;
       
  4757 	},
       
  4758 
       
  4759 	setHeight: function(height) {
       
  4760 		this.setSize(this.getWidth(), height);
       
  4761 	},
       
  4762 
       
  4763 	isEmpty: function() {
       
  4764 		var size = this._size;
       
  4765 		return !size || size.width === 0 && size.height === 0;
       
  4766 	},
       
  4767 
       
  4768 	getResolution: function() {
       
  4769 		var matrix = this._matrix,
       
  4770 			orig = new Point(0, 0).transform(matrix),
       
  4771 			u = new Point(1, 0).transform(matrix).subtract(orig),
       
  4772 			v = new Point(0, 1).transform(matrix).subtract(orig);
       
  4773 		return new Size(
       
  4774 			72 / u.getLength(),
       
  4775 			72 / v.getLength()
       
  4776 		);
       
  4777 	},
       
  4778 
       
  4779 	getPpi: '#getResolution',
       
  4780 
       
  4781 	getImage: function() {
       
  4782 		return this._image;
       
  4783 	},
       
  4784 
       
  4785 	setImage: function(image) {
       
  4786 		if (this._canvas)
       
  4787 			CanvasProvider.release(this._canvas);
       
  4788 		if (image && image.getContext) {
       
  4789 			this._image = null;
       
  4790 			this._canvas = image;
       
  4791 			this._loaded = true;
       
  4792 		} else {
       
  4793 			this._image = image;
       
  4794 			this._canvas = null;
       
  4795 			this._loaded = image && image.complete;
       
  4796 		}
       
  4797 		this._size = new Size(
       
  4798 				image ? image.naturalWidth || image.width : 0,
       
  4799 				image ? image.naturalHeight || image.height : 0);
       
  4800 		this._context = null;
       
  4801 		this._changed(521);
       
  4802 	},
       
  4803 
       
  4804 	getCanvas: function() {
       
  4805 		if (!this._canvas) {
       
  4806 			var ctx = CanvasProvider.getContext(this._size);
       
  4807 			try {
       
  4808 				if (this._image)
       
  4809 					ctx.drawImage(this._image, 0, 0);
       
  4810 				this._canvas = ctx.canvas;
       
  4811 			} catch (e) {
       
  4812 				CanvasProvider.release(ctx);
       
  4813 			}
       
  4814 		}
       
  4815 		return this._canvas;
       
  4816 	},
       
  4817 
       
  4818 	setCanvas: '#setImage',
       
  4819 
       
  4820 	getContext: function(modify) {
       
  4821 		if (!this._context)
       
  4822 			this._context = this.getCanvas().getContext('2d');
       
  4823 		if (modify) {
       
  4824 			this._image = null;
       
  4825 			this._changed(513);
       
  4826 		}
       
  4827 		return this._context;
       
  4828 	},
       
  4829 
       
  4830 	setContext: function(context) {
       
  4831 		this._context = context;
       
  4832 	},
       
  4833 
       
  4834 	getSource: function() {
       
  4835 		return this._image && this._image.src || this.toDataURL();
       
  4836 	},
       
  4837 
       
  4838 	setSource: function(src) {
       
  4839 		var that = this,
       
  4840 			crossOrigin = this._crossOrigin,
       
  4841 			image;
       
  4842 
       
  4843 		function loaded() {
       
  4844 			var view = that.getView();
       
  4845 			if (view) {
       
  4846 				paper = view._scope;
       
  4847 				that.setImage(image);
       
  4848 				that.emit('load');
       
  4849 				view.update();
       
  4850 			}
       
  4851 		}
       
  4852 
       
  4853 		image = document.getElementById(src) || new Image();
       
  4854 		if (crossOrigin)
       
  4855 			image.crossOrigin = crossOrigin;
       
  4856 		if (image.naturalWidth && image.naturalHeight) {
       
  4857 			setTimeout(loaded, 0);
       
  4858 		} else {
       
  4859 			DomEvent.add(image, { load: loaded });
       
  4860 			if (!image.src)
       
  4861 				image.src = src;
       
  4862 		}
       
  4863 		this.setImage(image);
       
  4864 	},
       
  4865 
       
  4866 	getCrossOrigin: function() {
       
  4867 		return this._image && this._image.crossOrigin || this._crossOrigin || '';
       
  4868 	},
       
  4869 
       
  4870 	setCrossOrigin: function(crossOrigin) {
       
  4871 		this._crossOrigin = crossOrigin;
       
  4872 		if (this._image)
       
  4873 			this._image.crossOrigin = crossOrigin;
       
  4874 	},
       
  4875 
       
  4876 	getElement: function() {
       
  4877 		return this._canvas || this._loaded && this._image;
       
  4878 	}
       
  4879 }, {
       
  4880 	beans: false,
       
  4881 
       
  4882 	getSubCanvas: function() {
       
  4883 		var rect = Rectangle.read(arguments),
       
  4884 			ctx = CanvasProvider.getContext(rect.getSize());
       
  4885 		ctx.drawImage(this.getCanvas(), rect.x, rect.y,
       
  4886 				rect.width, rect.height, 0, 0, rect.width, rect.height);
       
  4887 		return ctx.canvas;
       
  4888 	},
       
  4889 
       
  4890 	getSubRaster: function() {
       
  4891 		var rect = Rectangle.read(arguments),
       
  4892 			raster = new Raster(Item.NO_INSERT);
       
  4893 		raster.setImage(this.getSubCanvas(rect));
       
  4894 		raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
       
  4895 		raster._matrix.preConcatenate(this._matrix);
       
  4896 		raster.insertAbove(this);
       
  4897 		return raster;
       
  4898 	},
       
  4899 
       
  4900 	toDataURL: function() {
       
  4901 		var src = this._image && this._image.src;
       
  4902 		if (/^data:/.test(src))
       
  4903 			return src;
       
  4904 		var canvas = this.getCanvas();
       
  4905 		return canvas ? canvas.toDataURL() : null;
       
  4906 	},
       
  4907 
       
  4908 	drawImage: function(image ) {
       
  4909 		var point = Point.read(arguments, 1);
       
  4910 		this.getContext(true).drawImage(image, point.x, point.y);
       
  4911 	},
       
  4912 
       
  4913 	getAverageColor: function(object) {
       
  4914 		var bounds, path;
       
  4915 		if (!object) {
       
  4916 			bounds = this.getBounds();
       
  4917 		} else if (object instanceof PathItem) {
       
  4918 			path = object;
       
  4919 			bounds = object.getBounds();
       
  4920 		} else if (object.width) {
       
  4921 			bounds = new Rectangle(object);
       
  4922 		} else if (object.x) {
       
  4923 			bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
       
  4924 		}
       
  4925 		var sampleSize = 32,
       
  4926 			width = Math.min(bounds.width, sampleSize),
       
  4927 			height = Math.min(bounds.height, sampleSize);
       
  4928 		var ctx = Raster._sampleContext;
       
  4929 		if (!ctx) {
       
  4930 			ctx = Raster._sampleContext = CanvasProvider.getContext(
       
  4931 					new Size(sampleSize));
       
  4932 		} else {
       
  4933 			ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);
       
  4934 		}
       
  4935 		ctx.save();
       
  4936 		var matrix = new Matrix()
       
  4937 				.scale(width / bounds.width, height / bounds.height)
       
  4938 				.translate(-bounds.x, -bounds.y);
       
  4939 		matrix.applyToContext(ctx);
       
  4940 		if (path)
       
  4941 			path.draw(ctx, new Base({ clip: true, matrices: [matrix] }));
       
  4942 		this._matrix.applyToContext(ctx);
       
  4943 		var element = this.getElement(),
       
  4944 			size = this._size;
       
  4945 		if (element)
       
  4946 			ctx.drawImage(element, -size.width / 2, -size.height / 2);
       
  4947 		ctx.restore();
       
  4948 		var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
       
  4949 				Math.ceil(height)).data,
       
  4950 			channels = [0, 0, 0],
       
  4951 			total = 0;
       
  4952 		for (var i = 0, l = pixels.length; i < l; i += 4) {
       
  4953 			var alpha = pixels[i + 3];
       
  4954 			total += alpha;
       
  4955 			alpha /= 255;
       
  4956 			channels[0] += pixels[i] * alpha;
       
  4957 			channels[1] += pixels[i + 1] * alpha;
       
  4958 			channels[2] += pixels[i + 2] * alpha;
       
  4959 		}
       
  4960 		for (var i = 0; i < 3; i++)
       
  4961 			channels[i] /= total;
       
  4962 		return total ? Color.read(channels) : null;
       
  4963 	},
       
  4964 
       
  4965 	getPixel: function() {
       
  4966 		var point = Point.read(arguments);
       
  4967 		var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
       
  4968 		return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
       
  4969 				data[3] / 255);
       
  4970 	},
       
  4971 
       
  4972 	setPixel: function() {
       
  4973 		var point = Point.read(arguments),
       
  4974 			color = Color.read(arguments),
       
  4975 			components = color._convert('rgb'),
       
  4976 			alpha = color._alpha,
       
  4977 			ctx = this.getContext(true),
       
  4978 			imageData = ctx.createImageData(1, 1),
       
  4979 			data = imageData.data;
       
  4980 		data[0] = components[0] * 255;
       
  4981 		data[1] = components[1] * 255;
       
  4982 		data[2] = components[2] * 255;
       
  4983 		data[3] = alpha != null ? alpha * 255 : 255;
       
  4984 		ctx.putImageData(imageData, point.x, point.y);
       
  4985 	},
       
  4986 
       
  4987 	createImageData: function() {
       
  4988 		var size = Size.read(arguments);
       
  4989 		return this.getContext().createImageData(size.width, size.height);
       
  4990 	},
       
  4991 
       
  4992 	getImageData: function() {
       
  4993 		var rect = Rectangle.read(arguments);
       
  4994 		if (rect.isEmpty())
       
  4995 			rect = new Rectangle(this._size);
       
  4996 		return this.getContext().getImageData(rect.x, rect.y,
       
  4997 				rect.width, rect.height);
       
  4998 	},
       
  4999 
       
  5000 	setImageData: function(data ) {
       
  5001 		var point = Point.read(arguments, 1);
       
  5002 		this.getContext(true).putImageData(data, point.x, point.y);
       
  5003 	},
       
  5004 
       
  5005 	_getBounds: function(getter, matrix) {
       
  5006 		var rect = new Rectangle(this._size).setCenter(0, 0);
       
  5007 		return matrix ? matrix._transformBounds(rect) : rect;
       
  5008 	},
       
  5009 
       
  5010 	_hitTestSelf: function(point) {
       
  5011 		if (this._contains(point)) {
       
  5012 			var that = this;
       
  5013 			return new HitResult('pixel', that, {
       
  5014 				offset: point.add(that._size.divide(2)).round(),
       
  5015 				color: {
       
  5016 					get: function() {
       
  5017 						return that.getPixel(this.offset);
       
  5018 					}
       
  5019 				}
       
  5020 			});
       
  5021 		}
       
  5022 	},
       
  5023 
       
  5024 	_draw: function(ctx) {
       
  5025 		var element = this.getElement();
       
  5026 		if (element) {
       
  5027 			ctx.globalAlpha = this._opacity;
       
  5028 			ctx.drawImage(element,
       
  5029 					-this._size.width / 2, -this._size.height / 2);
       
  5030 		}
       
  5031 	},
       
  5032 
       
  5033 	_canComposite: function() {
       
  5034 		return true;
       
  5035 	}
       
  5036 });
       
  5037 
       
  5038 var PlacedSymbol = Item.extend({
       
  5039 	_class: 'PlacedSymbol',
       
  5040 	_applyMatrix: false,
       
  5041 	_canApplyMatrix: false,
       
  5042 	_boundsGetter: { getBounds: 'getStrokeBounds' },
       
  5043 	_boundsSelected: true,
       
  5044 	_serializeFields: {
       
  5045 		symbol: null
       
  5046 	},
       
  5047 
       
  5048 	initialize: function PlacedSymbol(arg0, arg1) {
       
  5049 		if (!this._initialize(arg0,
       
  5050 				arg1 !== undefined && Point.read(arguments, 1)))
       
  5051 			this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
       
  5052 	},
       
  5053 
       
  5054 	_equals: function(item) {
       
  5055 		return this._symbol === item._symbol;
       
  5056 	},
       
  5057 
       
  5058 	getSymbol: function() {
       
  5059 		return this._symbol;
       
  5060 	},
       
  5061 
       
  5062 	setSymbol: function(symbol) {
       
  5063 		this._symbol = symbol;
       
  5064 		this._changed(9);
       
  5065 	},
       
  5066 
       
  5067 	clone: function(insert) {
       
  5068 		var copy = new PlacedSymbol(Item.NO_INSERT);
       
  5069 		copy.setSymbol(this._symbol);
       
  5070 		return this._clone(copy, insert);
       
  5071 	},
       
  5072 
       
  5073 	isEmpty: function() {
       
  5074 		return this._symbol._definition.isEmpty();
       
  5075 	},
       
  5076 
       
  5077 	_getBounds: function(getter, matrix, cacheItem) {
       
  5078 		var definition = this.symbol._definition;
       
  5079 		return definition._getCachedBounds(getter,
       
  5080 				matrix && matrix.chain(definition._matrix), cacheItem);
       
  5081 	},
       
  5082 
       
  5083 	_hitTestSelf: function(point, options) {
       
  5084 		var res = this._symbol._definition._hitTest(point, options);
       
  5085 		if (res)
       
  5086 			res.item = this;
       
  5087 		return res;
       
  5088 	},
       
  5089 
       
  5090 	_draw: function(ctx, param) {
       
  5091 		this.symbol._definition.draw(ctx, param);
       
  5092 	}
       
  5093 
       
  5094 });
       
  5095 
       
  5096 var HitResult = Base.extend({
       
  5097 	_class: 'HitResult',
       
  5098 
       
  5099 	initialize: function HitResult(type, item, values) {
       
  5100 		this.type = type;
       
  5101 		this.item = item;
       
  5102 		if (values) {
       
  5103 			values.enumerable = true;
       
  5104 			this.inject(values);
       
  5105 		}
       
  5106 	},
       
  5107 
       
  5108 	statics: {
       
  5109 		getOptions: function(options) {
       
  5110 			return new Base({
       
  5111 				type: null,
       
  5112 				tolerance: paper.settings.hitTolerance,
       
  5113 				fill: !options,
       
  5114 				stroke: !options,
       
  5115 				segments: !options,
       
  5116 				handles: false,
       
  5117 				ends: false,
       
  5118 				center: false,
       
  5119 				bounds: false,
       
  5120 				guides: false,
       
  5121 				selected: false
       
  5122 			}, options);
       
  5123 		}
       
  5124 	}
       
  5125 });
       
  5126 
       
  5127 var Segment = Base.extend({
       
  5128 	_class: 'Segment',
       
  5129 	beans: true,
       
  5130 
       
  5131 	initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
       
  5132 		var count = arguments.length,
       
  5133 			point, handleIn, handleOut;
       
  5134 		if (count === 0) {
       
  5135 		} else if (count === 1) {
       
  5136 			if (arg0.point) {
       
  5137 				point = arg0.point;
       
  5138 				handleIn = arg0.handleIn;
       
  5139 				handleOut = arg0.handleOut;
       
  5140 			} else {
       
  5141 				point = arg0;
       
  5142 			}
       
  5143 		} else if (count === 2 && typeof arg0 === 'number') {
       
  5144 			point = arguments;
       
  5145 		} else if (count <= 3) {
       
  5146 			point = arg0;
       
  5147 			handleIn = arg1;
       
  5148 			handleOut = arg2;
       
  5149 		} else {
       
  5150 			point = arg0 !== undefined ? [ arg0, arg1 ] : null;
       
  5151 			handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
       
  5152 			handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
       
  5153 		}
       
  5154 		new SegmentPoint(point, this, '_point');
       
  5155 		new SegmentPoint(handleIn, this, '_handleIn');
       
  5156 		new SegmentPoint(handleOut, this, '_handleOut');
       
  5157 	},
       
  5158 
       
  5159 	_serialize: function(options) {
       
  5160 		return Base.serialize(this.isStraight() ? this._point
       
  5161 				: [this._point, this._handleIn, this._handleOut],
       
  5162 				options, true);
       
  5163 	},
       
  5164 
       
  5165 	_changed: function(point) {
       
  5166 		var path = this._path;
       
  5167 		if (!path)
       
  5168 			return;
       
  5169 		var curves = path._curves,
       
  5170 			index = this._index,
       
  5171 			curve;
       
  5172 		if (curves) {
       
  5173 			if ((!point || point === this._point || point === this._handleIn)
       
  5174 					&& (curve = index > 0 ? curves[index - 1] : path._closed
       
  5175 						? curves[curves.length - 1] : null))
       
  5176 				curve._changed();
       
  5177 			if ((!point || point === this._point || point === this._handleOut)
       
  5178 					&& (curve = curves[index]))
       
  5179 				curve._changed();
       
  5180 		}
       
  5181 		path._changed(25);
       
  5182 	},
       
  5183 
       
  5184 	getPoint: function() {
       
  5185 		return this._point;
       
  5186 	},
       
  5187 
       
  5188 	setPoint: function() {
       
  5189 		var point = Point.read(arguments);
       
  5190 		this._point.set(point.x, point.y);
       
  5191 	},
       
  5192 
       
  5193 	getHandleIn: function() {
       
  5194 		return this._handleIn;
       
  5195 	},
       
  5196 
       
  5197 	setHandleIn: function() {
       
  5198 		var point = Point.read(arguments);
       
  5199 		this._handleIn.set(point.x, point.y);
       
  5200 	},
       
  5201 
       
  5202 	getHandleOut: function() {
       
  5203 		return this._handleOut;
       
  5204 	},
       
  5205 
       
  5206 	setHandleOut: function() {
       
  5207 		var point = Point.read(arguments);
       
  5208 		this._handleOut.set(point.x, point.y);
       
  5209 	},
       
  5210 
       
  5211 	hasHandles: function() {
       
  5212 		return !this.isStraight();
       
  5213 	},
       
  5214 
       
  5215 	isStraight: function() {
       
  5216 		return this._handleIn.isZero() && this._handleOut.isZero();
       
  5217 	},
       
  5218 
       
  5219 	isLinear: function() {
       
  5220 		return Segment.isLinear(this, this.getNext());
       
  5221 	},
       
  5222 
       
  5223 	isCollinear: function(segment) {
       
  5224 		return Segment.isCollinear(this, this.getNext(),
       
  5225 				segment, segment.getNext());
       
  5226 	},
       
  5227 
       
  5228 	isColinear: '#isCollinear',
       
  5229 
       
  5230 	isOrthogonal: function() {
       
  5231 		return Segment.isOrthogonal(this.getPrevious(), this, this.getNext());
       
  5232 	},
       
  5233 
       
  5234 	isOrthogonalArc: function() {
       
  5235 		return Segment.isOrthogonalArc(this, this.getNext());
       
  5236 	},
       
  5237 
       
  5238 	isArc: '#isOrthogonalArc',
       
  5239 
       
  5240 	_selectionState: 0,
       
  5241 
       
  5242 	isSelected: function(_point) {
       
  5243 		var state = this._selectionState;
       
  5244 		return !_point ? !!(state & 7)
       
  5245 			: _point === this._point ? !!(state & 4)
       
  5246 			: _point === this._handleIn ? !!(state & 1)
       
  5247 			: _point === this._handleOut ? !!(state & 2)
       
  5248 			: false;
       
  5249 	},
       
  5250 
       
  5251 	setSelected: function(selected, _point) {
       
  5252 		var path = this._path,
       
  5253 			selected = !!selected,
       
  5254 			state = this._selectionState,
       
  5255 			oldState = state,
       
  5256 			flag = !_point ? 7
       
  5257 					: _point === this._point ? 4
       
  5258 					: _point === this._handleIn ? 1
       
  5259 					: _point === this._handleOut ? 2
       
  5260 					: 0;
       
  5261 		if (selected) {
       
  5262 			state |= flag;
       
  5263 		} else {
       
  5264 			state &= ~flag;
       
  5265 		}
       
  5266 		this._selectionState = state;
       
  5267 		if (path && state !== oldState) {
       
  5268 			path._updateSelection(this, oldState, state);
       
  5269 			path._changed(129);
       
  5270 		}
       
  5271 	},
       
  5272 
       
  5273 	getIndex: function() {
       
  5274 		return this._index !== undefined ? this._index : null;
       
  5275 	},
       
  5276 
       
  5277 	getPath: function() {
       
  5278 		return this._path || null;
       
  5279 	},
       
  5280 
       
  5281 	getCurve: function() {
       
  5282 		var path = this._path,
       
  5283 			index = this._index;
       
  5284 		if (path) {
       
  5285 			if (index > 0 && !path._closed
       
  5286 					&& index === path._segments.length - 1)
       
  5287 				index--;
       
  5288 			return path.getCurves()[index] || null;
       
  5289 		}
       
  5290 		return null;
       
  5291 	},
       
  5292 
       
  5293 	getLocation: function() {
       
  5294 		var curve = this.getCurve();
       
  5295 		return curve
       
  5296 				? new CurveLocation(curve, this === curve._segment1 ? 0 : 1)
       
  5297 				: null;
       
  5298 	},
       
  5299 
       
  5300 	getNext: function() {
       
  5301 		var segments = this._path && this._path._segments;
       
  5302 		return segments && (segments[this._index + 1]
       
  5303 				|| this._path._closed && segments[0]) || null;
       
  5304 	},
       
  5305 
       
  5306 	getPrevious: function() {
       
  5307 		var segments = this._path && this._path._segments;
       
  5308 		return segments && (segments[this._index - 1]
       
  5309 				|| this._path._closed && segments[segments.length - 1]) || null;
       
  5310 	},
       
  5311 
       
  5312 	reverse: function() {
       
  5313 		return new Segment(this._point, this._handleOut, this._handleIn);
       
  5314 	},
       
  5315 
       
  5316 	remove: function() {
       
  5317 		return this._path ? !!this._path.removeSegment(this._index) : false;
       
  5318 	},
       
  5319 
       
  5320 	clone: function() {
       
  5321 		return new Segment(this._point, this._handleIn, this._handleOut);
       
  5322 	},
       
  5323 
       
  5324 	equals: function(segment) {
       
  5325 		return segment === this || segment && this._class === segment._class
       
  5326 				&& this._point.equals(segment._point)
       
  5327 				&& this._handleIn.equals(segment._handleIn)
       
  5328 				&& this._handleOut.equals(segment._handleOut)
       
  5329 				|| false;
       
  5330 	},
       
  5331 
       
  5332 	toString: function() {
       
  5333 		var parts = [ 'point: ' + this._point ];
       
  5334 		if (!this._handleIn.isZero())
       
  5335 			parts.push('handleIn: ' + this._handleIn);
       
  5336 		if (!this._handleOut.isZero())
       
  5337 			parts.push('handleOut: ' + this._handleOut);
       
  5338 		return '{ ' + parts.join(', ') + ' }';
       
  5339 	},
       
  5340 
       
  5341 	transform: function(matrix) {
       
  5342 		this._transformCoordinates(matrix, new Array(6), true);
       
  5343 		this._changed();
       
  5344 	},
       
  5345 
       
  5346 	_transformCoordinates: function(matrix, coords, change) {
       
  5347 		var point = this._point,
       
  5348 			handleIn = !change || !this._handleIn.isZero()
       
  5349 					? this._handleIn : null,
       
  5350 			handleOut = !change || !this._handleOut.isZero()
       
  5351 					? this._handleOut : null,
       
  5352 			x = point._x,
       
  5353 			y = point._y,
       
  5354 			i = 2;
       
  5355 		coords[0] = x;
       
  5356 		coords[1] = y;
       
  5357 		if (handleIn) {
       
  5358 			coords[i++] = handleIn._x + x;
       
  5359 			coords[i++] = handleIn._y + y;
       
  5360 		}
       
  5361 		if (handleOut) {
       
  5362 			coords[i++] = handleOut._x + x;
       
  5363 			coords[i++] = handleOut._y + y;
       
  5364 		}
       
  5365 		if (matrix) {
       
  5366 			matrix._transformCoordinates(coords, coords, i / 2);
       
  5367 			x = coords[0];
       
  5368 			y = coords[1];
       
  5369 			if (change) {
       
  5370 				point._x = x;
       
  5371 				point._y = y;
       
  5372 				i  = 2;
       
  5373 				if (handleIn) {
       
  5374 					handleIn._x = coords[i++] - x;
       
  5375 					handleIn._y = coords[i++] - y;
       
  5376 				}
       
  5377 				if (handleOut) {
       
  5378 					handleOut._x = coords[i++] - x;
       
  5379 					handleOut._y = coords[i++] - y;
       
  5380 				}
       
  5381 			} else {
       
  5382 				if (!handleIn) {
       
  5383 					coords[i++] = x;
       
  5384 					coords[i++] = y;
       
  5385 				}
       
  5386 				if (!handleOut) {
       
  5387 					coords[i++] = x;
       
  5388 					coords[i++] = y;
       
  5389 				}
       
  5390 			}
       
  5391 		}
       
  5392 		return coords;
       
  5393 	},
       
  5394 
       
  5395    statics: {
       
  5396 
       
  5397 		isLinear: function(seg1, seg2) {
       
  5398 			var l = seg2._point.subtract(seg1._point);
       
  5399 			return l.isCollinear(seg1._handleOut)
       
  5400 					&& l.isCollinear(seg2._handleIn);
       
  5401 		},
       
  5402 
       
  5403 		isCollinear: function(seg1, seg2, seg3, seg4) {
       
  5404 			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
       
  5405 					&& seg3._handleOut.isZero() && seg4._handleIn.isZero()
       
  5406 					&& seg2._point.subtract(seg1._point).isCollinear(
       
  5407 						seg4._point.subtract(seg3._point));
       
  5408 		},
       
  5409 
       
  5410 		isOrthogonal: function(seg1, seg2, seg3) {
       
  5411 			return seg1._handleOut.isZero() && seg2._handleIn.isZero()
       
  5412 				&& seg2._handleOut.isZero() && seg3._handleIn.isZero()
       
  5413 				&& seg2._point.subtract(seg1._point).isOrthogonal(
       
  5414 						seg3._point.subtract(seg2._point));
       
  5415 		},
       
  5416 
       
  5417 		isOrthogonalArc: function(seg1, seg2) {
       
  5418 			var handle1 = seg1._handleOut,
       
  5419 				handle2 = seg2._handleIn,
       
  5420 				kappa = 0.5522847498307936;
       
  5421 			if (handle1.isOrthogonal(handle2)) {
       
  5422 				var pt1 = seg1._point,
       
  5423 					pt2 = seg2._point,
       
  5424 					corner = new Line(pt1, handle1, true).intersect(
       
  5425 							new Line(pt2, handle2, true), true);
       
  5426 				return corner && Numerical.isZero(handle1.getLength() /
       
  5427 						corner.subtract(pt1).getLength() - kappa)
       
  5428 					&& Numerical.isZero(handle2.getLength() /
       
  5429 						corner.subtract(pt2).getLength() - kappa);
       
  5430 			}
       
  5431 			return false;
       
  5432 		},
       
  5433 	}
       
  5434 });
       
  5435 
       
  5436 var SegmentPoint = Point.extend({
       
  5437 	initialize: function SegmentPoint(point, owner, key) {
       
  5438 		var x, y, selected;
       
  5439 		if (!point) {
       
  5440 			x = y = 0;
       
  5441 		} else if ((x = point[0]) !== undefined) {
       
  5442 			y = point[1];
       
  5443 		} else {
       
  5444 			var pt = point;
       
  5445 			if ((x = pt.x) === undefined) {
       
  5446 				pt = Point.read(arguments);
       
  5447 				x = pt.x;
       
  5448 			}
       
  5449 			y = pt.y;
       
  5450 			selected = pt.selected;
       
  5451 		}
       
  5452 		this._x = x;
       
  5453 		this._y = y;
       
  5454 		this._owner = owner;
       
  5455 		owner[key] = this;
       
  5456 		if (selected)
       
  5457 			this.setSelected(true);
       
  5458 	},
       
  5459 
       
  5460 	set: function(x, y) {
       
  5461 		this._x = x;
       
  5462 		this._y = y;
       
  5463 		this._owner._changed(this);
       
  5464 		return this;
       
  5465 	},
       
  5466 
       
  5467 	_serialize: function(options) {
       
  5468 		var f = options.formatter,
       
  5469 			x = f.number(this._x),
       
  5470 			y = f.number(this._y);
       
  5471 		return this.isSelected()
       
  5472 				? { x: x, y: y, selected: true }
       
  5473 				: [x, y];
       
  5474 	},
       
  5475 
       
  5476 	getX: function() {
       
  5477 		return this._x;
       
  5478 	},
       
  5479 
       
  5480 	setX: function(x) {
       
  5481 		this._x = x;
       
  5482 		this._owner._changed(this);
       
  5483 	},
       
  5484 
       
  5485 	getY: function() {
       
  5486 		return this._y;
       
  5487 	},
       
  5488 
       
  5489 	setY: function(y) {
       
  5490 		this._y = y;
       
  5491 		this._owner._changed(this);
       
  5492 	},
       
  5493 
       
  5494 	isZero: function() {
       
  5495 		return Numerical.isZero(this._x) && Numerical.isZero(this._y);
       
  5496 	},
       
  5497 
       
  5498 	setSelected: function(selected) {
       
  5499 		this._owner.setSelected(selected, this);
       
  5500 	},
       
  5501 
       
  5502 	isSelected: function() {
       
  5503 		return this._owner.isSelected(this);
       
  5504 	}
       
  5505 });
       
  5506 
       
  5507 var Curve = Base.extend({
       
  5508 	_class: 'Curve',
       
  5509 
       
  5510 	initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
       
  5511 		var count = arguments.length;
       
  5512 		if (count === 3) {
       
  5513 			this._path = arg0;
       
  5514 			this._segment1 = arg1;
       
  5515 			this._segment2 = arg2;
       
  5516 		} else if (count === 0) {
       
  5517 			this._segment1 = new Segment();
       
  5518 			this._segment2 = new Segment();
       
  5519 		} else if (count === 1) {
       
  5520 			this._segment1 = new Segment(arg0.segment1);
       
  5521 			this._segment2 = new Segment(arg0.segment2);
       
  5522 		} else if (count === 2) {
       
  5523 			this._segment1 = new Segment(arg0);
       
  5524 			this._segment2 = new Segment(arg1);
       
  5525 		} else {
       
  5526 			var point1, handle1, handle2, point2;
       
  5527 			if (count === 4) {
       
  5528 				point1 = arg0;
       
  5529 				handle1 = arg1;
       
  5530 				handle2 = arg2;
       
  5531 				point2 = arg3;
       
  5532 			} else if (count === 8) {
       
  5533 				point1 = [arg0, arg1];
       
  5534 				point2 = [arg6, arg7];
       
  5535 				handle1 = [arg2 - arg0, arg3 - arg1];
       
  5536 				handle2 = [arg4 - arg6, arg5 - arg7];
       
  5537 			}
       
  5538 			this._segment1 = new Segment(point1, null, handle1);
       
  5539 			this._segment2 = new Segment(point2, handle2, null);
       
  5540 		}
       
  5541 	},
       
  5542 
       
  5543 	_changed: function() {
       
  5544 		this._length = this._bounds = undefined;
       
  5545 	},
       
  5546 
       
  5547 	getPoint1: function() {
       
  5548 		return this._segment1._point;
       
  5549 	},
       
  5550 
       
  5551 	setPoint1: function() {
       
  5552 		var point = Point.read(arguments);
       
  5553 		this._segment1._point.set(point.x, point.y);
       
  5554 	},
       
  5555 
       
  5556 	getPoint2: function() {
       
  5557 		return this._segment2._point;
       
  5558 	},
       
  5559 
       
  5560 	setPoint2: function() {
       
  5561 		var point = Point.read(arguments);
       
  5562 		this._segment2._point.set(point.x, point.y);
       
  5563 	},
       
  5564 
       
  5565 	getHandle1: function() {
       
  5566 		return this._segment1._handleOut;
       
  5567 	},
       
  5568 
       
  5569 	setHandle1: function() {
       
  5570 		var point = Point.read(arguments);
       
  5571 		this._segment1._handleOut.set(point.x, point.y);
       
  5572 	},
       
  5573 
       
  5574 	getHandle2: function() {
       
  5575 		return this._segment2._handleIn;
       
  5576 	},
       
  5577 
       
  5578 	setHandle2: function() {
       
  5579 		var point = Point.read(arguments);
       
  5580 		this._segment2._handleIn.set(point.x, point.y);
       
  5581 	},
       
  5582 
       
  5583 	getSegment1: function() {
       
  5584 		return this._segment1;
       
  5585 	},
       
  5586 
       
  5587 	getSegment2: function() {
       
  5588 		return this._segment2;
       
  5589 	},
       
  5590 
       
  5591 	getPath: function() {
       
  5592 		return this._path;
       
  5593 	},
       
  5594 
       
  5595 	getIndex: function() {
       
  5596 		return this._segment1._index;
       
  5597 	},
       
  5598 
       
  5599 	getNext: function() {
       
  5600 		var curves = this._path && this._path._curves;
       
  5601 		return curves && (curves[this._segment1._index + 1]
       
  5602 				|| this._path._closed && curves[0]) || null;
       
  5603 	},
       
  5604 
       
  5605 	getPrevious: function() {
       
  5606 		var curves = this._path && this._path._curves;
       
  5607 		return curves && (curves[this._segment1._index - 1]
       
  5608 				|| this._path._closed && curves[curves.length - 1]) || null;
       
  5609 	},
       
  5610 
       
  5611 	isSelected: function() {
       
  5612 		return this.getPoint1().isSelected()
       
  5613 				&& this.getHandle2().isSelected()
       
  5614 				&& this.getHandle2().isSelected()
       
  5615 				&& this.getPoint2().isSelected();
       
  5616 	},
       
  5617 
       
  5618 	setSelected: function(selected) {
       
  5619 		this.getPoint1().setSelected(selected);
       
  5620 		this.getHandle1().setSelected(selected);
       
  5621 		this.getHandle2().setSelected(selected);
       
  5622 		this.getPoint2().setSelected(selected);
       
  5623 	},
       
  5624 
       
  5625 	getValues: function(matrix) {
       
  5626 		return Curve.getValues(this._segment1, this._segment2, matrix);
       
  5627 	},
       
  5628 
       
  5629 	getPoints: function() {
       
  5630 		var coords = this.getValues(),
       
  5631 			points = [];
       
  5632 		for (var i = 0; i < 8; i += 2)
       
  5633 			points.push(new Point(coords[i], coords[i + 1]));
       
  5634 		return points;
       
  5635 	},
       
  5636 
       
  5637 	getLength: function() {
       
  5638 		if (this._length == null) {
       
  5639 			this._length = this.isLinear()
       
  5640 				? this._segment2._point.getDistance(this._segment1._point)
       
  5641 				: Curve.getLength(this.getValues(), 0, 1);
       
  5642 		}
       
  5643 		return this._length;
       
  5644 	},
       
  5645 
       
  5646 	getArea: function() {
       
  5647 		return Curve.getArea(this.getValues());
       
  5648 	},
       
  5649 
       
  5650 	getPart: function(from, to) {
       
  5651 		return new Curve(Curve.getPart(this.getValues(), from, to));
       
  5652 	},
       
  5653 
       
  5654 	getPartLength: function(from, to) {
       
  5655 		return Curve.getLength(this.getValues(), from, to);
       
  5656 	},
       
  5657 
       
  5658 	hasHandles: function() {
       
  5659 		return !this._segment1._handleOut.isZero()
       
  5660 				|| !this._segment2._handleIn.isZero();
       
  5661 	},
       
  5662 
       
  5663 	isLinear: function() {
       
  5664 		return Segment.isLinear(this._segment1, this._segment2);
       
  5665 	},
       
  5666 
       
  5667 	isCollinear: function(curve) {
       
  5668 		return Ssegment.isCollinear(this._segment1, this._segment2,
       
  5669 				curve._segment1, curve._segment2);
       
  5670 	},
       
  5671 
       
  5672 	isOrthogonalArc: function() {
       
  5673 		return Segment.isOrthogonalArc(this._segment1, this._segment2);
       
  5674 	},
       
  5675 
       
  5676 	getIntersections: function(curve) {
       
  5677 		return Curve.filterIntersections(Curve.getIntersections(
       
  5678 				this.getValues(), curve.getValues(), this, curve, []));
       
  5679 	},
       
  5680 
       
  5681 	_getParameter: function(offset, isParameter) {
       
  5682 		return isParameter
       
  5683 				? offset
       
  5684 				: offset && offset.curve === this
       
  5685 					? offset.parameter
       
  5686 					: offset === undefined && isParameter === undefined
       
  5687 						? 0.5
       
  5688 						: this.getParameterAt(offset, 0);
       
  5689 	},
       
  5690 
       
  5691 	divide: function(offset, isParameter, ignoreLinear) {
       
  5692 		var parameter = this._getParameter(offset, isParameter),
       
  5693 			tolerance = 0.000001,
       
  5694 			res = null;
       
  5695 		if (parameter > tolerance && parameter < 1 - tolerance) {
       
  5696 			var parts = Curve.subdivide(this.getValues(), parameter),
       
  5697 				isLinear = ignoreLinear ? false : this.isLinear(),
       
  5698 				left = parts[0],
       
  5699 				right = parts[1];
       
  5700 
       
  5701 			if (!isLinear) {
       
  5702 				this._segment1._handleOut.set(left[2] - left[0],
       
  5703 						left[3] - left[1]);
       
  5704 				this._segment2._handleIn.set(right[4] - right[6],
       
  5705 						right[5] - right[7]);
       
  5706 			}
       
  5707 
       
  5708 			var x = left[6], y = left[7],
       
  5709 				segment = new Segment(new Point(x, y),
       
  5710 						!isLinear && new Point(left[4] - x, left[5] - y),
       
  5711 						!isLinear && new Point(right[2] - x, right[3] - y));
       
  5712 
       
  5713 			if (this._path) {
       
  5714 				if (this._segment1._index > 0 && this._segment2._index === 0) {
       
  5715 					this._path.add(segment);
       
  5716 				} else {
       
  5717 					this._path.insert(this._segment2._index, segment);
       
  5718 				}
       
  5719 				res = this;
       
  5720 			} else {
       
  5721 				var end = this._segment2;
       
  5722 				this._segment2 = segment;
       
  5723 				res = new Curve(segment, end);
       
  5724 			}
       
  5725 		}
       
  5726 		return res;
       
  5727 	},
       
  5728 
       
  5729 	split: function(offset, isParameter) {
       
  5730 		return this._path
       
  5731 			? this._path.split(this._segment1._index,
       
  5732 					this._getParameter(offset, isParameter))
       
  5733 			: null;
       
  5734 	},
       
  5735 
       
  5736 	reverse: function() {
       
  5737 		return new Curve(this._segment2.reverse(), this._segment1.reverse());
       
  5738 	},
       
  5739 
       
  5740 	remove: function() {
       
  5741 		var removed = false;
       
  5742 		if (this._path) {
       
  5743 			var segment2 = this._segment2,
       
  5744 				handleOut = segment2._handleOut;
       
  5745 			removed = segment2.remove();
       
  5746 			if (removed)
       
  5747 				this._segment1._handleOut.set(handleOut.x, handleOut.y);
       
  5748 		}
       
  5749 		return removed;
       
  5750 	},
       
  5751 
       
  5752 	clone: function() {
       
  5753 		return new Curve(this._segment1, this._segment2);
       
  5754 	},
       
  5755 
       
  5756 	toString: function() {
       
  5757 		var parts = [ 'point1: ' + this._segment1._point ];
       
  5758 		if (!this._segment1._handleOut.isZero())
       
  5759 			parts.push('handle1: ' + this._segment1._handleOut);
       
  5760 		if (!this._segment2._handleIn.isZero())
       
  5761 			parts.push('handle2: ' + this._segment2._handleIn);
       
  5762 		parts.push('point2: ' + this._segment2._point);
       
  5763 		return '{ ' + parts.join(', ') + ' }';
       
  5764 	},
       
  5765 
       
  5766 statics: {
       
  5767 	getValues: function(segment1, segment2, matrix) {
       
  5768 		var p1 = segment1._point,
       
  5769 			h1 = segment1._handleOut,
       
  5770 			h2 = segment2._handleIn,
       
  5771 			p2 = segment2._point,
       
  5772 			values = [
       
  5773 				p1._x, p1._y,
       
  5774 				p1._x + h1._x, p1._y + h1._y,
       
  5775 				p2._x + h2._x, p2._y + h2._y,
       
  5776 				p2._x, p2._y
       
  5777 			];
       
  5778 		if (matrix)
       
  5779 			matrix._transformCoordinates(values, values, 4);
       
  5780 		return values;
       
  5781 	},
       
  5782 
       
  5783 	subdivide: function(v, t) {
       
  5784 		var p1x = v[0], p1y = v[1],
       
  5785 			c1x = v[2], c1y = v[3],
       
  5786 			c2x = v[4], c2y = v[5],
       
  5787 			p2x = v[6], p2y = v[7];
       
  5788 		if (t === undefined)
       
  5789 			t = 0.5;
       
  5790 		var u = 1 - t,
       
  5791 			p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y,
       
  5792 			p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y,
       
  5793 			p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y,
       
  5794 			p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y,
       
  5795 			p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
       
  5796 			p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
       
  5797 		return [
       
  5798 			[p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
       
  5799 			[p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
       
  5800 		];
       
  5801 	},
       
  5802 
       
  5803 	solveCubic: function (v, coord, val, roots, min, max) {
       
  5804 		var p1 = v[coord],
       
  5805 			c1 = v[coord + 2],
       
  5806 			c2 = v[coord + 4],
       
  5807 			p2 = v[coord + 6],
       
  5808 			c = 3 * (c1 - p1),
       
  5809 			b = 3 * (c2 - c1) - c,
       
  5810 			a = p2 - p1 - c - b;
       
  5811 		return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
       
  5812 	},
       
  5813 
       
  5814 	getParameterOf: function(v, x, y) {
       
  5815 		var tolerance = 0.000001;
       
  5816 		if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance)
       
  5817 			return 0;
       
  5818 		if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance)
       
  5819 			return 1;
       
  5820 		var txs = [],
       
  5821 			tys = [],
       
  5822 			sx = Curve.solveCubic(v, 0, x, txs, 0, 1),
       
  5823 			sy = Curve.solveCubic(v, 1, y, tys, 0, 1),
       
  5824 			tx, ty;
       
  5825 		for (var cx = 0;  sx === -1 || cx < sx;) {
       
  5826 			if (sx === -1 || (tx = txs[cx++]) > 0 && tx < 1) {
       
  5827 				for (var cy = 0; sy === -1 || cy < sy;) {
       
  5828 					if (sy === -1 || (ty = tys[cy++]) > 0 && ty < 1) {
       
  5829 						if (sx === -1) {
       
  5830 							tx = ty;
       
  5831 						} else if (sy === -1) {
       
  5832 							ty = tx;
       
  5833 						}
       
  5834 						if (Math.abs(tx - ty) < tolerance)
       
  5835 							return (tx + ty) * 0.5;
       
  5836 					}
       
  5837 				}
       
  5838 				if (sx === -1)
       
  5839 					break;
       
  5840 			}
       
  5841 		}
       
  5842 		return null;
       
  5843 	},
       
  5844 
       
  5845 	getPart: function(v, from, to) {
       
  5846 		if (from > 0)
       
  5847 			v = Curve.subdivide(v, from)[1];
       
  5848 		if (to < 1)
       
  5849 			v = Curve.subdivide(v, (to - from) / (1 - from))[0];
       
  5850 		return v;
       
  5851 	},
       
  5852 
       
  5853 	hasHandles: function(v) {
       
  5854 		var isZero = Numerical.isZero;
       
  5855 		return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3])
       
  5856 				&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]));
       
  5857 	},
       
  5858 
       
  5859 	isLinear: function(v) {
       
  5860 		var p1x = v[0], p1y = v[1],
       
  5861 			p2x = v[6], p2y = v[7],
       
  5862 			l = new Point(p2x - p1x, p2y - p1y);
       
  5863 		return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y))
       
  5864 				&& l.isCollinear(new Point(v[4] - p2x, v[5] - p2y));
       
  5865 	},
       
  5866 
       
  5867 	isFlatEnough: function(v, tolerance) {
       
  5868 		var p1x = v[0], p1y = v[1],
       
  5869 			c1x = v[2], c1y = v[3],
       
  5870 			c2x = v[4], c2y = v[5],
       
  5871 			p2x = v[6], p2y = v[7],
       
  5872 			ux = 3 * c1x - 2 * p1x - p2x,
       
  5873 			uy = 3 * c1y - 2 * p1y - p2y,
       
  5874 			vx = 3 * c2x - 2 * p2x - p1x,
       
  5875 			vy = 3 * c2y - 2 * p2y - p1y;
       
  5876 		return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
       
  5877 				< 10 * tolerance * tolerance;
       
  5878 	},
       
  5879 
       
  5880 	getArea: function(v) {
       
  5881 		var p1x = v[0], p1y = v[1],
       
  5882 			c1x = v[2], c1y = v[3],
       
  5883 			c2x = v[4], c2y = v[5],
       
  5884 			p2x = v[6], p2y = v[7];
       
  5885 		return (  3.0 * c1y * p1x - 1.5 * c1y * c2x
       
  5886 				- 1.5 * c1y * p2x - 3.0 * p1y * c1x
       
  5887 				- 1.5 * p1y * c2x - 0.5 * p1y * p2x
       
  5888 				+ 1.5 * c2y * p1x + 1.5 * c2y * c1x
       
  5889 				- 3.0 * c2y * p2x + 0.5 * p2y * p1x
       
  5890 				+ 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10;
       
  5891 	},
       
  5892 
       
  5893 	getEdgeSum: function(v) {
       
  5894 		return	  (v[0] - v[2]) * (v[3] + v[1])
       
  5895 				+ (v[2] - v[4]) * (v[5] + v[3])
       
  5896 				+ (v[4] - v[6]) * (v[7] + v[5]);
       
  5897 	},
       
  5898 
       
  5899 	getBounds: function(v) {
       
  5900 		var min = v.slice(0, 2),
       
  5901 			max = min.slice(),
       
  5902 			roots = [0, 0];
       
  5903 		for (var i = 0; i < 2; i++)
       
  5904 			Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],
       
  5905 					i, 0, min, max, roots);
       
  5906 		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
       
  5907 	},
       
  5908 
       
  5909 	_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {
       
  5910 		function add(value, padding) {
       
  5911 			var left = value - padding,
       
  5912 				right = value + padding;
       
  5913 			if (left < min[coord])
       
  5914 				min[coord] = left;
       
  5915 			if (right > max[coord])
       
  5916 				max[coord] = right;
       
  5917 		}
       
  5918 		var a = 3 * (v1 - v2) - v0 + v3,
       
  5919 			b = 2 * (v0 + v2) - 4 * v1,
       
  5920 			c = v1 - v0,
       
  5921 			count = Numerical.solveQuadratic(a, b, c, roots),
       
  5922 			tMin = 0.000001,
       
  5923 			tMax = 1 - tMin;
       
  5924 		add(v3, 0);
       
  5925 		for (var i = 0; i < count; i++) {
       
  5926 			var t = roots[i],
       
  5927 				u = 1 - t;
       
  5928 			if (tMin < t && t < tMax)
       
  5929 				add(u * u * u * v0
       
  5930 					+ 3 * u * u * t * v1
       
  5931 					+ 3 * u * t * t * v2
       
  5932 					+ t * t * t * v3,
       
  5933 					padding);
       
  5934 		}
       
  5935 	}
       
  5936 }}, Base.each(
       
  5937 	['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
       
  5938 	function(name) {
       
  5939 		this[name] = function() {
       
  5940 			if (!this._bounds)
       
  5941 				this._bounds = {};
       
  5942 			var bounds = this._bounds[name];
       
  5943 			if (!bounds) {
       
  5944 				bounds = this._bounds[name] = Path[name]([this._segment1,
       
  5945 						this._segment2], false, this._path.getStyle());
       
  5946 			}
       
  5947 			return bounds.clone();
       
  5948 		};
       
  5949 	},
       
  5950 {
       
  5951 
       
  5952 }), {
       
  5953 	beans: false,
       
  5954 
       
  5955 	getParameterAt: function(offset, start) {
       
  5956 		return Curve.getParameterAt(this.getValues(), offset, start);
       
  5957 	},
       
  5958 
       
  5959 	getParameterOf: function() {
       
  5960 		var point = Point.read(arguments);
       
  5961 		return Curve.getParameterOf(this.getValues(), point.x, point.y);
       
  5962 	},
       
  5963 
       
  5964 	getLocationAt: function(offset, isParameter) {
       
  5965 		var t = isParameter ? offset : this.getParameterAt(offset);
       
  5966 		return t != null && t >= 0 && t <= 1
       
  5967 				? new CurveLocation(this, t)
       
  5968 				: null;
       
  5969 	},
       
  5970 
       
  5971 	getLocationOf: function() {
       
  5972 		return this.getLocationAt(this.getParameterOf(Point.read(arguments)),
       
  5973 				true);
       
  5974 	},
       
  5975 
       
  5976 	getOffsetOf: function() {
       
  5977 		var loc = this.getLocationOf.apply(this, arguments);
       
  5978 		return loc ? loc.getOffset() : null;
       
  5979 	},
       
  5980 
       
  5981 	getNearestLocation: function() {
       
  5982 		var point = Point.read(arguments),
       
  5983 			values = this.getValues(),
       
  5984 			count = 100,
       
  5985 			minDist = Infinity,
       
  5986 			minT = 0;
       
  5987 
       
  5988 		function refine(t) {
       
  5989 			if (t >= 0 && t <= 1) {
       
  5990 				var dist = point.getDistance(Curve.getPoint(values, t), true);
       
  5991 				if (dist < minDist) {
       
  5992 					minDist = dist;
       
  5993 					minT = t;
       
  5994 					return true;
       
  5995 				}
       
  5996 			}
       
  5997 		}
       
  5998 
       
  5999 		for (var i = 0; i <= count; i++)
       
  6000 			refine(i / count);
       
  6001 
       
  6002 		var step = 1 / (count * 2);
       
  6003 		while (step > 0.000001) {
       
  6004 			if (!refine(minT - step) && !refine(minT + step))
       
  6005 				step /= 2;
       
  6006 		}
       
  6007 		var pt = Curve.getPoint(values, minT);
       
  6008 		return new CurveLocation(this, minT, pt, null, null, null,
       
  6009 				point.getDistance(pt));
       
  6010 	},
       
  6011 
       
  6012 	getNearestPoint: function() {
       
  6013 		return this.getNearestLocation.apply(this, arguments).getPoint();
       
  6014 	}
       
  6015 
       
  6016 },
       
  6017 new function() {
       
  6018 	var methods = ['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',
       
  6019 		'getWeightedNormal', 'getCurvature'];
       
  6020 	return Base.each(methods,
       
  6021 	function(name) {
       
  6022 		this[name + 'At'] = function(offset, isParameter) {
       
  6023 			var values = this.getValues();
       
  6024 			return Curve[name](values, isParameter ? offset
       
  6025 					: Curve.getParameterAt(values, offset, 0));
       
  6026 		};
       
  6027 	}, {
       
  6028 		statics: {
       
  6029 			evaluateMethods: methods
       
  6030 		}
       
  6031 	})
       
  6032 },
       
  6033 new function() {
       
  6034 
       
  6035 	function getLengthIntegrand(v) {
       
  6036 		var p1x = v[0], p1y = v[1],
       
  6037 			c1x = v[2], c1y = v[3],
       
  6038 			c2x = v[4], c2y = v[5],
       
  6039 			p2x = v[6], p2y = v[7],
       
  6040 
       
  6041 			ax = 9 * (c1x - c2x) + 3 * (p2x - p1x),
       
  6042 			bx = 6 * (p1x + c2x) - 12 * c1x,
       
  6043 			cx = 3 * (c1x - p1x),
       
  6044 
       
  6045 			ay = 9 * (c1y - c2y) + 3 * (p2y - p1y),
       
  6046 			by = 6 * (p1y + c2y) - 12 * c1y,
       
  6047 			cy = 3 * (c1y - p1y);
       
  6048 
       
  6049 		return function(t) {
       
  6050 			var dx = (ax * t + bx) * t + cx,
       
  6051 				dy = (ay * t + by) * t + cy;
       
  6052 			return Math.sqrt(dx * dx + dy * dy);
       
  6053 		};
       
  6054 	}
       
  6055 
       
  6056 	function getIterations(a, b) {
       
  6057 		return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));
       
  6058 	}
       
  6059 
       
  6060 	function evaluate(v, t, type, normalized) {
       
  6061 		if (t == null || t < 0 || t > 1)
       
  6062 			return null;
       
  6063 		var p1x = v[0], p1y = v[1],
       
  6064 			c1x = v[2], c1y = v[3],
       
  6065 			c2x = v[4], c2y = v[5],
       
  6066 			p2x = v[6], p2y = v[7],
       
  6067 			tolerance = 0.000001,
       
  6068 			x, y;
       
  6069 
       
  6070 		if (type === 0 && (t < tolerance || t > 1 - tolerance)) {
       
  6071 			var isZero = t < tolerance;
       
  6072 			x = isZero ? p1x : p2x;
       
  6073 			y = isZero ? p1y : p2y;
       
  6074 		} else {
       
  6075 			var cx = 3 * (c1x - p1x),
       
  6076 				bx = 3 * (c2x - c1x) - cx,
       
  6077 				ax = p2x - p1x - cx - bx,
       
  6078 
       
  6079 				cy = 3 * (c1y - p1y),
       
  6080 				by = 3 * (c2y - c1y) - cy,
       
  6081 				ay = p2y - p1y - cy - by;
       
  6082 			if (type === 0) {
       
  6083 				x = ((ax * t + bx) * t + cx) * t + p1x;
       
  6084 				y = ((ay * t + by) * t + cy) * t + p1y;
       
  6085 			} else {
       
  6086 				if (t < tolerance) {
       
  6087 					x = cx;
       
  6088 					y = cy;
       
  6089 				} else if (t > 1 - tolerance) {
       
  6090 					x = 3 * (p2x - c2x);
       
  6091 					y = 3 * (p2y - c2y);
       
  6092 				} else {
       
  6093 					x = (3 * ax * t + 2 * bx) * t + cx;
       
  6094 					y = (3 * ay * t + 2 * by) * t + cy;
       
  6095 				}
       
  6096 				if (normalized) {
       
  6097 					if (x === 0 && y === 0
       
  6098 							&& (t < tolerance || t > 1 - tolerance)) {
       
  6099 						x = c2x - c1x;
       
  6100 						y = c2y - c1y;
       
  6101 					}
       
  6102 					var len = Math.sqrt(x * x + y * y);
       
  6103 					x /= len;
       
  6104 					y /= len;
       
  6105 				}
       
  6106 				if (type === 3) {
       
  6107 					var x2 = 6 * ax * t + 2 * bx,
       
  6108 						y2 = 6 * ay * t + 2 * by,
       
  6109 						d = Math.pow(x * x + y * y, 3 / 2);
       
  6110 					x = d !== 0 ? (x * y2 - y * x2) / d : 0;
       
  6111 					y = 0;
       
  6112 				}
       
  6113 			}
       
  6114 		}
       
  6115 		return type === 2 ? new Point(y, -x) : new Point(x, y);
       
  6116 	}
       
  6117 
       
  6118 	return {
       
  6119 		statics: true,
       
  6120 
       
  6121 		getLength: function(v, a, b) {
       
  6122 			if (a === undefined)
       
  6123 				a = 0;
       
  6124 			if (b === undefined)
       
  6125 				b = 1;
       
  6126 			var isZero = Numerical.isZero;
       
  6127 			if (a === 0 && b === 1
       
  6128 					&& isZero(v[0] - v[2]) && isZero(v[1] - v[3])
       
  6129 					&& isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
       
  6130 				var dx = v[6] - v[0],
       
  6131 					dy = v[7] - v[1];
       
  6132 				return Math.sqrt(dx * dx + dy * dy);
       
  6133 			}
       
  6134 			var ds = getLengthIntegrand(v);
       
  6135 			return Numerical.integrate(ds, a, b, getIterations(a, b));
       
  6136 		},
       
  6137 
       
  6138 		getParameterAt: function(v, offset, start) {
       
  6139 			if (start === undefined)
       
  6140 				start = offset < 0 ? 1 : 0
       
  6141 			if (offset === 0)
       
  6142 				return start;
       
  6143 			var tolerance = 0.000001,
       
  6144 				abs = Math.abs,
       
  6145 				forward = offset > 0,
       
  6146 				a = forward ? start : 0,
       
  6147 				b = forward ? 1 : start,
       
  6148 				ds = getLengthIntegrand(v),
       
  6149 				rangeLength = Numerical.integrate(ds, a, b,
       
  6150 						getIterations(a, b));
       
  6151 			if (abs(offset - rangeLength) < tolerance) {
       
  6152 				return forward ? b : a;
       
  6153 			} else if (abs(offset) > rangeLength) {
       
  6154 				return null;
       
  6155 			}
       
  6156 			var guess = offset / rangeLength,
       
  6157 				length = 0;
       
  6158 			function f(t) {
       
  6159 				length += Numerical.integrate(ds, start, t,
       
  6160 						getIterations(start, t));
       
  6161 				start = t;
       
  6162 				return length - offset;
       
  6163 			}
       
  6164 			return Numerical.findRoot(f, ds, start + guess, a, b, 16,
       
  6165 					tolerance);
       
  6166 		},
       
  6167 
       
  6168 		getPoint: function(v, t) {
       
  6169 			return evaluate(v, t, 0, false);
       
  6170 		},
       
  6171 
       
  6172 		getTangent: function(v, t) {
       
  6173 			return evaluate(v, t, 1, true);
       
  6174 		},
       
  6175 
       
  6176 		getWeightedTangent: function(v, t) {
       
  6177 			return evaluate(v, t, 1, false);
       
  6178 		},
       
  6179 
       
  6180 		getNormal: function(v, t) {
       
  6181 			return evaluate(v, t, 2, true);
       
  6182 		},
       
  6183 
       
  6184 		getWeightedNormal: function(v, t) {
       
  6185 			return evaluate(v, t, 2, false);
       
  6186 		},
       
  6187 
       
  6188 		getCurvature: function(v, t) {
       
  6189 			return evaluate(v, t, 3, false).x;
       
  6190 		}
       
  6191 	};
       
  6192 }, new function() {
       
  6193 	function addLocation(locations, include, curve1, t1, point1, curve2, t2,
       
  6194 			point2) {
       
  6195 		var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2);
       
  6196 		if (!include || include(loc))
       
  6197 			locations.push(loc);
       
  6198 	}
       
  6199 
       
  6200 	function addCurveIntersections(v1, v2, curve1, curve2, locations, include,
       
  6201 			tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) {
       
  6202 		if (recursion > 32)
       
  6203 			return;
       
  6204 		var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
       
  6205 			tolerance = 0.000001,
       
  6206 			getSignedDistance = Line.getSignedDistance,
       
  6207 			d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0,
       
  6208 			d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0,
       
  6209 			factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
       
  6210 			dMin = factor * Math.min(0, d1, d2),
       
  6211 			dMax = factor * Math.max(0, d1, d2),
       
  6212 			dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]),
       
  6213 			dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),
       
  6214 			dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),
       
  6215 			dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),
       
  6216 			tMinNew, tMaxNew, tDiff;
       
  6217 		if (q0x === q3x && uMax - uMin < tolerance && recursion > 3) {
       
  6218 			tMaxNew = tMinNew = (tMax + tMin) / 2;
       
  6219 			tDiff = 0;
       
  6220 		} else {
       
  6221 			var hull = getConvexHull(dp0, dp1, dp2, dp3),
       
  6222 				top = hull[0],
       
  6223 				bottom = hull[1],
       
  6224 				tMinClip, tMaxClip;
       
  6225 			tMinClip = clipConvexHull(top, bottom, dMin, dMax);
       
  6226 			top.reverse();
       
  6227 			bottom.reverse();
       
  6228 			tMaxClip = clipConvexHull(top, bottom, dMin, dMax);
       
  6229 			if (tMinClip == null || tMaxClip == null)
       
  6230 				return;
       
  6231 			v1 = Curve.getPart(v1, tMinClip, tMaxClip);
       
  6232 			tDiff = tMaxClip - tMinClip;
       
  6233 			tMinNew = tMax * tMinClip + tMin * (1 - tMinClip);
       
  6234 			tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip);
       
  6235 		}
       
  6236 		if (oldTDiff > 0.5 && tDiff > 0.5) {
       
  6237 			if (tMaxNew - tMinNew > uMax - uMin) {
       
  6238 				var parts = Curve.subdivide(v1, 0.5),
       
  6239 					t = tMinNew + (tMaxNew - tMinNew) / 2;
       
  6240 				addCurveIntersections(
       
  6241 					v2, parts[0], curve2, curve1, locations, include,
       
  6242 					uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion);
       
  6243 				addCurveIntersections(
       
  6244 					v2, parts[1], curve2, curve1, locations, include,
       
  6245 					uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion);
       
  6246 			} else {
       
  6247 				var parts = Curve.subdivide(v2, 0.5),
       
  6248 					t = uMin + (uMax - uMin) / 2;
       
  6249 				addCurveIntersections(
       
  6250 					parts[0], v1, curve2, curve1, locations, include,
       
  6251 					uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
       
  6252 				addCurveIntersections(
       
  6253 					parts[1], v1, curve2, curve1, locations, include,
       
  6254 					t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion);
       
  6255 			}
       
  6256 		} else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) {
       
  6257 			var t1 = tMinNew + (tMaxNew - tMinNew) / 2,
       
  6258 				t2 = uMin + (uMax - uMin) / 2;
       
  6259 			if (reverse) {
       
  6260 				addLocation(locations, include,
       
  6261 						curve2, t2, Curve.getPoint(v2, t2),
       
  6262 						curve1, t1, Curve.getPoint(v1, t1));
       
  6263 			} else {
       
  6264 				addLocation(locations, include,
       
  6265 						curve1, t1, Curve.getPoint(v1, t1),
       
  6266 						curve2, t2, Curve.getPoint(v2, t2));
       
  6267 			}
       
  6268 		} else if (tDiff > 0) {
       
  6269 			addCurveIntersections(v2, v1, curve2, curve1, locations, include,
       
  6270 					uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
       
  6271 		}
       
  6272 	}
       
  6273 
       
  6274 	function getConvexHull(dq0, dq1, dq2, dq3) {
       
  6275 		var p0 = [ 0, dq0 ],
       
  6276 			p1 = [ 1 / 3, dq1 ],
       
  6277 			p2 = [ 2 / 3, dq2 ],
       
  6278 			p3 = [ 1, dq3 ],
       
  6279 			getSignedDistance = Line.getSignedDistance,
       
  6280 			dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
       
  6281 			dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2),
       
  6282 			flip = false,
       
  6283 			hull;
       
  6284 		if (dist1 * dist2 < 0) {
       
  6285 			hull = [[p0, p1, p3], [p0, p2, p3]];
       
  6286 			flip = dist1 < 0;
       
  6287 		} else {
       
  6288 			var pmax, cross = 0,
       
  6289 				distZero = dist1 === 0 || dist2 === 0;
       
  6290 			if (Math.abs(dist1) > Math.abs(dist2)) {
       
  6291 				pmax = p1;
       
  6292 				cross = (dq3 - dq2 - (dq3 - dq0) / 3)
       
  6293 						* (2 * (dq3 - dq2) - dq3 + dq1) / 3;
       
  6294 			} else {
       
  6295 				pmax = p2;
       
  6296 				cross = (dq1 - dq0 + (dq0 - dq3) / 3)
       
  6297 						* (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
       
  6298 			}
       
  6299 			hull = cross < 0 || distZero
       
  6300 					? [[p0, pmax, p3], [p0, p3]]
       
  6301 					: [[p0, p1, p2, p3], [p0, p3]];
       
  6302 			flip = dist1 ? dist1 < 0 : dist2 < 0;
       
  6303 		}
       
  6304 		return flip ? hull.reverse() : hull;
       
  6305 	}
       
  6306 
       
  6307 	function clipConvexHull(hullTop, hullBottom, dMin, dMax) {
       
  6308 		if (hullTop[0][1] < dMin) {
       
  6309 			return clipConvexHullPart(hullTop, true, dMin);
       
  6310 		} else if (hullBottom[0][1] > dMax) {
       
  6311 			return clipConvexHullPart(hullBottom, false, dMax);
       
  6312 		} else {
       
  6313 			return hullTop[0][0];
       
  6314 		}
       
  6315 	}
       
  6316 
       
  6317 	function clipConvexHullPart(part, top, threshold) {
       
  6318 		var px = part[0][0],
       
  6319 			py = part[0][1];
       
  6320 		for (var i = 1, l = part.length; i < l; i++) {
       
  6321 			var qx = part[i][0],
       
  6322 				qy = part[i][1];
       
  6323 			if (top ? qy >= threshold : qy <= threshold)
       
  6324 				return px + (threshold - py) * (qx - px) / (qy - py);
       
  6325 			px = qx;
       
  6326 			py = qy;
       
  6327 		}
       
  6328 		return null;
       
  6329 	}
       
  6330 
       
  6331 	function addCurveLineIntersections(v1, v2, curve1, curve2, locations,
       
  6332 			include) {
       
  6333 		var flip = Curve.isLinear(v1),
       
  6334 			vc = flip ? v2 : v1,
       
  6335 			vl = flip ? v1 : v2,
       
  6336 			lx1 = vl[0], ly1 = vl[1],
       
  6337 			lx2 = vl[6], ly2 = vl[7],
       
  6338 			ldx = lx2 - lx1,
       
  6339 			ldy = ly2 - ly1,
       
  6340 			angle = Math.atan2(-ldy, ldx),
       
  6341 			sin = Math.sin(angle),
       
  6342 			cos = Math.cos(angle),
       
  6343 			rlx2 = ldx * cos - ldy * sin,
       
  6344 			rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0],
       
  6345 			rvc = [];
       
  6346 		for(var i = 0; i < 8; i += 2) {
       
  6347 			var x = vc[i] - lx1,
       
  6348 				y = vc[i + 1] - ly1;
       
  6349 			rvc.push(
       
  6350 				x * cos - y * sin,
       
  6351 				y * cos + x * sin);
       
  6352 		}
       
  6353 		var roots = [],
       
  6354 			count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1);
       
  6355 		for (var i = 0; i < count; i++) {
       
  6356 			var tc = roots[i],
       
  6357 				x = Curve.getPoint(rvc, tc).x;
       
  6358 			if (x >= 0 && x <= rlx2) {
       
  6359 				var tl = Curve.getParameterOf(rvl, x, 0),
       
  6360 					t1 = flip ? tl : tc,
       
  6361 					t2 = flip ? tc : tl;
       
  6362 				addLocation(locations, include,
       
  6363 						curve1, t1, Curve.getPoint(v1, t1),
       
  6364 						curve2, t2, Curve.getPoint(v2, t2));
       
  6365 			}
       
  6366 		}
       
  6367 	}
       
  6368 
       
  6369 	function addLineIntersection(v1, v2, curve1, curve2, locations, include) {
       
  6370 		var point = Line.intersect(
       
  6371 				v1[0], v1[1], v1[6], v1[7],
       
  6372 				v2[0], v2[1], v2[6], v2[7]);
       
  6373 		if (point) {
       
  6374 			var x = point.x,
       
  6375 				y = point.y;
       
  6376 			addLocation(locations, include,
       
  6377 					curve1, Curve.getParameterOf(v1, x, y), point,
       
  6378 					curve2, Curve.getParameterOf(v2, x, y), point);
       
  6379 		}
       
  6380 	}
       
  6381 
       
  6382 	return { statics: {
       
  6383 		getIntersections: function(v1, v2, c1, c2, locations, include) {
       
  6384 			var linear1 = Curve.isLinear(v1),
       
  6385 				linear2 = Curve.isLinear(v2),
       
  6386 				c1p1 = c1.getPoint1(),
       
  6387 				c1p2 = c1.getPoint2(),
       
  6388 				c2p1 = c2.getPoint1(),
       
  6389 				c2p2 = c2.getPoint2(),
       
  6390 				tolerance = 0.000001;
       
  6391 			if (c1p1.isClose(c2p1, tolerance))
       
  6392 				addLocation(locations, include, c1, 0, c1p1, c2, 0, c1p1);
       
  6393 			if (c1p1.isClose(c2p2, tolerance))
       
  6394 				addLocation(locations, include, c1, 0, c1p1, c2, 1, c1p1);
       
  6395 			(linear1 && linear2
       
  6396 				? addLineIntersection
       
  6397 				: linear1 || linear2
       
  6398 					? addCurveLineIntersections
       
  6399 					: addCurveIntersections)(
       
  6400 						v1, v2, c1, c2, locations, include,
       
  6401 						0, 1, 0, 1, 0, false, 0);
       
  6402 			if (c1p2.isClose(c2p1, tolerance))
       
  6403 				addLocation(locations, include, c1, 1, c1p2, c2, 0, c1p2);
       
  6404 			if (c1p2.isClose(c2p2, tolerance))
       
  6405 				addLocation(locations, include, c1, 1, c1p2, c2, 1, c1p2);
       
  6406 			return locations;
       
  6407 		},
       
  6408 
       
  6409 		filterIntersections: function(locations, _expand) {
       
  6410 			var last = locations.length - 1,
       
  6411 				tMax = 1 - 0.000001;
       
  6412 			for (var i = last; i >= 0; i--) {
       
  6413 				var loc = locations[i],
       
  6414 					next = loc._curve.getNext(),
       
  6415 					next2 = loc._curve2.getNext();
       
  6416 				if (next && loc._parameter >= tMax) {
       
  6417 					loc._parameter = 0;
       
  6418 					loc._curve = next;
       
  6419 				}
       
  6420 				if (next2 && loc._parameter2 >= tMax) {
       
  6421 					loc._parameter2 = 0;
       
  6422 					loc._curve2 = next2;
       
  6423 				}
       
  6424 			}
       
  6425 
       
  6426 			function compare(loc1, loc2) {
       
  6427 				var path1 = loc1.getPath(),
       
  6428 					path2 = loc2.getPath();
       
  6429 				return path1 === path2
       
  6430 						? (loc1.getIndex() + loc1.getParameter())
       
  6431 								- (loc2.getIndex() + loc2.getParameter())
       
  6432 						: path1._id - path2._id;
       
  6433 			}
       
  6434 
       
  6435 			if (last > 0) {
       
  6436 				locations.sort(compare);
       
  6437 				for (var i = last; i > 0; i--) {
       
  6438 					if (locations[i].equals(locations[i - 1])) {
       
  6439 						locations.splice(i, 1);
       
  6440 						last--;
       
  6441 					}
       
  6442 				}
       
  6443 			}
       
  6444 			if (_expand) {
       
  6445 				for (var i = last; i >= 0; i--)
       
  6446 					locations.push(locations[i].getIntersection());
       
  6447 				locations.sort(compare);
       
  6448 			}
       
  6449 			return locations;
       
  6450 		}
       
  6451 	}};
       
  6452 });
       
  6453 
       
  6454 var CurveLocation = Base.extend({
       
  6455 	_class: 'CurveLocation',
       
  6456 	beans: true,
       
  6457 
       
  6458 	initialize: function CurveLocation(curve, parameter, point, _curve2,
       
  6459 			_parameter2, _point2, _distance) {
       
  6460 		this._id = UID.get(CurveLocation);
       
  6461 		var path = curve._path;
       
  6462 		this._version = path ? path._version : 0;
       
  6463 		this._curve = curve;
       
  6464 		this._parameter = parameter;
       
  6465 		this._point = point || curve.getPointAt(parameter, true);
       
  6466 		this._curve2 = _curve2;
       
  6467 		this._parameter2 = _parameter2;
       
  6468 		this._point2 = _point2;
       
  6469 		this._distance = _distance;
       
  6470 		this._segment1 = curve._segment1;
       
  6471 		this._segment2 = curve._segment2;
       
  6472 	},
       
  6473 
       
  6474 	getSegment: function(_preferFirst) {
       
  6475 		if (!this._segment) {
       
  6476 			var curve = this.getCurve(),
       
  6477 				parameter = this.getParameter();
       
  6478 			if (parameter === 1) {
       
  6479 				this._segment = curve._segment2;
       
  6480 			} else if (parameter === 0 || _preferFirst) {
       
  6481 				this._segment = curve._segment1;
       
  6482 			} else if (parameter == null) {
       
  6483 				return null;
       
  6484 			} else {
       
  6485 				this._segment = curve.getPartLength(0, parameter)
       
  6486 					< curve.getPartLength(parameter, 1)
       
  6487 						? curve._segment1
       
  6488 						: curve._segment2;
       
  6489 			}
       
  6490 		}
       
  6491 		return this._segment;
       
  6492 	},
       
  6493 
       
  6494 	getCurve: function() {
       
  6495 		var curve = this._curve,
       
  6496 			path = curve && curve._path;
       
  6497 		if (path && path._version !== this._version) {
       
  6498 			curve = null;
       
  6499 			this._parameter = null;
       
  6500 		}
       
  6501 		if (!curve) {
       
  6502 			curve = this._segment1.getCurve();
       
  6503 			if (curve.getParameterOf(this._point) == null)
       
  6504 				curve = this._segment2.getPrevious().getCurve();
       
  6505 			this._curve = curve;
       
  6506 			path = curve._path;
       
  6507 			this._version = path ? path._version : 0;
       
  6508 		}
       
  6509 		return curve;
       
  6510 	},
       
  6511 
       
  6512 	getPath: function() {
       
  6513 		var curve = this.getCurve();
       
  6514 		return curve && curve._path;
       
  6515 	},
       
  6516 
       
  6517 	getIndex: function() {
       
  6518 		var curve = this.getCurve();
       
  6519 		return curve && curve.getIndex();
       
  6520 	},
       
  6521 
       
  6522 	getParameter: function() {
       
  6523 		var curve = this.getCurve(),
       
  6524 			parameter = this._parameter;
       
  6525 		return curve && parameter == null
       
  6526 			? this._parameter = curve.getParameterOf(this._point)
       
  6527 			: parameter;
       
  6528 	},
       
  6529 
       
  6530 	getPoint: function() {
       
  6531 		return this._point;
       
  6532 	},
       
  6533 
       
  6534 	getOffset: function() {
       
  6535 		var path = this.getPath();
       
  6536 		return path ? path._getOffset(this) : this.getCurveOffset();
       
  6537 	},
       
  6538 
       
  6539 	getCurveOffset: function() {
       
  6540 		var curve = this.getCurve(),
       
  6541 			parameter = this.getParameter();
       
  6542 		return parameter != null && curve && curve.getPartLength(0, parameter);
       
  6543 	},
       
  6544 
       
  6545 	getIntersection: function() {
       
  6546 		var intersection = this._intersection;
       
  6547 		if (!intersection && this._curve2) {
       
  6548 			this._intersection = intersection = new CurveLocation(this._curve2,
       
  6549 					this._parameter2, this._point2 || this._point, this);
       
  6550 			intersection._intersection = this;
       
  6551 		}
       
  6552 		return intersection;
       
  6553 	},
       
  6554 
       
  6555 	getDistance: function() {
       
  6556 		return this._distance;
       
  6557 	},
       
  6558 
       
  6559 	divide: function() {
       
  6560 		var curve = this.getCurve();
       
  6561 		return curve && curve.divide(this.getParameter(), true);
       
  6562 	},
       
  6563 
       
  6564 	split: function() {
       
  6565 		var curve = this.getCurve();
       
  6566 		return curve && curve.split(this.getParameter(), true);
       
  6567 	},
       
  6568 
       
  6569 	equals: function(loc) {
       
  6570 		var abs = Math.abs,
       
  6571 			tolerance = 0.000001;
       
  6572 		return this === loc
       
  6573 				|| loc instanceof CurveLocation
       
  6574 					&& this.getCurve() === loc.getCurve()
       
  6575 					&& abs(this.getParameter() - loc.getParameter()) < tolerance
       
  6576 					&& this._curve2 === loc._curve2
       
  6577 					&& abs(this._parameter2 - loc._parameter2) < tolerance
       
  6578 				|| false;
       
  6579 	},
       
  6580 
       
  6581 	toString: function() {
       
  6582 		var parts = [],
       
  6583 			point = this.getPoint(),
       
  6584 			f = Formatter.instance;
       
  6585 		if (point)
       
  6586 			parts.push('point: ' + point);
       
  6587 		var index = this.getIndex();
       
  6588 		if (index != null)
       
  6589 			parts.push('index: ' + index);
       
  6590 		var parameter = this.getParameter();
       
  6591 		if (parameter != null)
       
  6592 			parts.push('parameter: ' + f.number(parameter));
       
  6593 		if (this._distance != null)
       
  6594 			parts.push('distance: ' + f.number(this._distance));
       
  6595 		return '{ ' + parts.join(', ') + ' }';
       
  6596 	}
       
  6597 }, Base.each(Curve.evaluateMethods, function(name) {
       
  6598 	if (name !== 'getPoint') {
       
  6599 		var get = name + 'At';
       
  6600 		this[name] = function() {
       
  6601 			var parameter = this.getParameter(),
       
  6602 				curve = this.getCurve();
       
  6603 			return parameter != null && curve && curve[get](parameter, true);
       
  6604 		};
       
  6605 	}
       
  6606 }, {}));
       
  6607 
       
  6608 var PathItem = Item.extend({
       
  6609 	_class: 'PathItem',
       
  6610 
       
  6611 	initialize: function PathItem() {
       
  6612 	},
       
  6613 
       
  6614 	getIntersections: function(path, _matrix, _expand) {
       
  6615 		if (this === path)
       
  6616 			path = null;
       
  6617 		var locations = [],
       
  6618 			curves1 = this.getCurves(),
       
  6619 			curves2 = path ? path.getCurves() : curves1,
       
  6620 			matrix1 = this._matrix.orNullIfIdentity(),
       
  6621 			matrix2 = path ? (_matrix || path._matrix).orNullIfIdentity()
       
  6622 				: matrix1,
       
  6623 			length1 = curves1.length,
       
  6624 			length2 = path ? curves2.length : length1,
       
  6625 			values2 = [],
       
  6626 			tMin = 0.000001,
       
  6627 			tMax = 1 - tMin;
       
  6628 		if (path && !this.getBounds(matrix1).touches(path.getBounds(matrix2)))
       
  6629 			return [];
       
  6630 		for (var i = 0; i < length2; i++)
       
  6631 			values2[i] = curves2[i].getValues(matrix2);
       
  6632 		for (var i = 0; i < length1; i++) {
       
  6633 			var curve1 = curves1[i],
       
  6634 				values1 = path ? curve1.getValues(matrix1) : values2[i];
       
  6635 			if (!path) {
       
  6636 				var seg1 = curve1.getSegment1(),
       
  6637 					seg2 = curve1.getSegment2(),
       
  6638 					h1 = seg1._handleOut,
       
  6639 					h2 = seg2._handleIn;
       
  6640 				if (new Line(seg1._point.subtract(h1), h1.multiply(2), true)
       
  6641 						.intersect(new Line(seg2._point.subtract(h2),
       
  6642 						h2.multiply(2), true), false)) {
       
  6643 					var parts = Curve.subdivide(values1);
       
  6644 					Curve.getIntersections(
       
  6645 						parts[0], parts[1], curve1, curve1, locations,
       
  6646 						function(loc) {
       
  6647 							if (loc._parameter <= tMax) {
       
  6648 								loc._parameter /= 2;
       
  6649 								loc._parameter2 = 0.5 + loc._parameter2 / 2;
       
  6650 								return true;
       
  6651 							}
       
  6652 						}
       
  6653 					);
       
  6654 				}
       
  6655 			}
       
  6656 			for (var j = path ? 0 : i + 1; j < length2; j++) {
       
  6657 				Curve.getIntersections(
       
  6658 					values1, values2[j], curve1, curves2[j], locations,
       
  6659 					!path && (j === i + 1 || j === length2 - 1 && i === 0)
       
  6660 						&& function(loc) {
       
  6661 							var t = loc._parameter;
       
  6662 							return t >= tMin && t <= tMax;
       
  6663 						}
       
  6664 				);
       
  6665 			}
       
  6666 		}
       
  6667 		return Curve.filterIntersections(locations, _expand);
       
  6668 	},
       
  6669 
       
  6670 	_asPathItem: function() {
       
  6671 		return this;
       
  6672 	},
       
  6673 
       
  6674 	setPathData: function(data) {
       
  6675 
       
  6676 		var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
       
  6677 			coords,
       
  6678 			relative = false,
       
  6679 			previous,
       
  6680 			control,
       
  6681 			current = new Point(),
       
  6682 			start = new Point();
       
  6683 
       
  6684 		function getCoord(index, coord) {
       
  6685 			var val = +coords[index];
       
  6686 			if (relative)
       
  6687 				val += current[coord];
       
  6688 			return val;
       
  6689 		}
       
  6690 
       
  6691 		function getPoint(index) {
       
  6692 			return new Point(
       
  6693 				getCoord(index, 'x'),
       
  6694 				getCoord(index + 1, 'y')
       
  6695 			);
       
  6696 		}
       
  6697 
       
  6698 		this.clear();
       
  6699 
       
  6700 		for (var i = 0, l = parts && parts.length; i < l; i++) {
       
  6701 			var part = parts[i],
       
  6702 				command = part[0],
       
  6703 				lower = command.toLowerCase();
       
  6704 			coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
       
  6705 			var length = coords && coords.length;
       
  6706 			relative = command === lower;
       
  6707 			if (previous === 'z' && !/[mz]/.test(lower))
       
  6708 				this.moveTo(current = start);
       
  6709 			switch (lower) {
       
  6710 			case 'm':
       
  6711 			case 'l':
       
  6712 				var move = lower === 'm';
       
  6713 				for (var j = 0; j < length; j += 2)
       
  6714 					this[j === 0 && move ? 'moveTo' : 'lineTo'](
       
  6715 							current = getPoint(j));
       
  6716 				control = current;
       
  6717 				if (move)
       
  6718 					start = current;
       
  6719 				break;
       
  6720 			case 'h':
       
  6721 			case 'v':
       
  6722 				var coord = lower === 'h' ? 'x' : 'y';
       
  6723 				for (var j = 0; j < length; j++) {
       
  6724 					current[coord] = getCoord(j, coord);
       
  6725 					this.lineTo(current);
       
  6726 				}
       
  6727 				control = current;
       
  6728 				break;
       
  6729 			case 'c':
       
  6730 				for (var j = 0; j < length; j += 6) {
       
  6731 					this.cubicCurveTo(
       
  6732 							getPoint(j),
       
  6733 							control = getPoint(j + 2),
       
  6734 							current = getPoint(j + 4));
       
  6735 				}
       
  6736 				break;
       
  6737 			case 's':
       
  6738 				for (var j = 0; j < length; j += 4) {
       
  6739 					this.cubicCurveTo(
       
  6740 							/[cs]/.test(previous)
       
  6741 									? current.multiply(2).subtract(control)
       
  6742 									: current,
       
  6743 							control = getPoint(j),
       
  6744 							current = getPoint(j + 2));
       
  6745 					previous = lower;
       
  6746 				}
       
  6747 				break;
       
  6748 			case 'q':
       
  6749 				for (var j = 0; j < length; j += 4) {
       
  6750 					this.quadraticCurveTo(
       
  6751 							control = getPoint(j),
       
  6752 							current = getPoint(j + 2));
       
  6753 				}
       
  6754 				break;
       
  6755 			case 't':
       
  6756 				for (var j = 0; j < length; j += 2) {
       
  6757 					this.quadraticCurveTo(
       
  6758 							control = (/[qt]/.test(previous)
       
  6759 									? current.multiply(2).subtract(control)
       
  6760 									: current),
       
  6761 							current = getPoint(j));
       
  6762 					previous = lower;
       
  6763 				}
       
  6764 				break;
       
  6765 			case 'a':
       
  6766 				for (var j = 0; j < length; j += 7) {
       
  6767 					this.arcTo(current = getPoint(j + 5),
       
  6768 							new Size(+coords[j], +coords[j + 1]),
       
  6769 							+coords[j + 2], +coords[j + 4], +coords[j + 3]);
       
  6770 				}
       
  6771 				break;
       
  6772 			case 'z':
       
  6773 				this.closePath(true);
       
  6774 				break;
       
  6775 			}
       
  6776 			previous = lower;
       
  6777 		}
       
  6778 	},
       
  6779 
       
  6780 	_canComposite: function() {
       
  6781 		return !(this.hasFill() && this.hasStroke());
       
  6782 	},
       
  6783 
       
  6784 	_contains: function(point) {
       
  6785 		var winding = this._getWinding(point, false, true);
       
  6786 		return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
       
  6787 	}
       
  6788 
       
  6789 });
       
  6790 
       
  6791 var Path = PathItem.extend({
       
  6792 	_class: 'Path',
       
  6793 	_serializeFields: {
       
  6794 		segments: [],
       
  6795 		closed: false
       
  6796 	},
       
  6797 
       
  6798 	initialize: function Path(arg) {
       
  6799 		this._closed = false;
       
  6800 		this._segments = [];
       
  6801 		this._version = 0;
       
  6802 		var segments = Array.isArray(arg)
       
  6803 			? typeof arg[0] === 'object'
       
  6804 				? arg
       
  6805 				: arguments
       
  6806 			: arg && (arg.size === undefined && (arg.x !== undefined
       
  6807 					|| arg.point !== undefined))
       
  6808 				? arguments
       
  6809 				: null;
       
  6810 		if (segments && segments.length > 0) {
       
  6811 			this.setSegments(segments);
       
  6812 		} else {
       
  6813 			this._curves = undefined;
       
  6814 			this._selectedSegmentState = 0;
       
  6815 			if (!segments && typeof arg === 'string') {
       
  6816 				this.setPathData(arg);
       
  6817 				arg = null;
       
  6818 			}
       
  6819 		}
       
  6820 		this._initialize(!segments && arg);
       
  6821 	},
       
  6822 
       
  6823 	_equals: function(item) {
       
  6824 		return this._closed === item._closed
       
  6825 				&& Base.equals(this._segments, item._segments);
       
  6826 	},
       
  6827 
       
  6828 	clone: function(insert) {
       
  6829 		var copy = new Path(Item.NO_INSERT);
       
  6830 		copy.setSegments(this._segments);
       
  6831 		copy._closed = this._closed;
       
  6832 		if (this._clockwise !== undefined)
       
  6833 			copy._clockwise = this._clockwise;
       
  6834 		return this._clone(copy, insert);
       
  6835 	},
       
  6836 
       
  6837 	_changed: function _changed(flags) {
       
  6838 		_changed.base.call(this, flags);
       
  6839 		if (flags & 8) {
       
  6840 			var parent = this._parent;
       
  6841 			if (parent)
       
  6842 				parent._currentPath = undefined;
       
  6843 			this._length = this._clockwise = undefined;
       
  6844 			if (flags & 16) {
       
  6845 				this._version++;
       
  6846 			} else if (this._curves) {
       
  6847 			   for (var i = 0, l = this._curves.length; i < l; i++)
       
  6848 					this._curves[i]._changed();
       
  6849 			}
       
  6850 			this._monoCurves = undefined;
       
  6851 		} else if (flags & 32) {
       
  6852 			this._bounds = undefined;
       
  6853 		}
       
  6854 	},
       
  6855 
       
  6856 	getStyle: function() {
       
  6857 		var parent = this._parent;
       
  6858 		return (parent instanceof CompoundPath ? parent : this)._style;
       
  6859 	},
       
  6860 
       
  6861 	getSegments: function() {
       
  6862 		return this._segments;
       
  6863 	},
       
  6864 
       
  6865 	setSegments: function(segments) {
       
  6866 		var fullySelected = this.isFullySelected();
       
  6867 		this._segments.length = 0;
       
  6868 		this._selectedSegmentState = 0;
       
  6869 		this._curves = undefined;
       
  6870 		if (segments && segments.length > 0)
       
  6871 			this._add(Segment.readAll(segments));
       
  6872 		if (fullySelected)
       
  6873 			this.setFullySelected(true);
       
  6874 	},
       
  6875 
       
  6876 	getFirstSegment: function() {
       
  6877 		return this._segments[0];
       
  6878 	},
       
  6879 
       
  6880 	getLastSegment: function() {
       
  6881 		return this._segments[this._segments.length - 1];
       
  6882 	},
       
  6883 
       
  6884 	getCurves: function() {
       
  6885 		var curves = this._curves,
       
  6886 			segments = this._segments;
       
  6887 		if (!curves) {
       
  6888 			var length = this._countCurves();
       
  6889 			curves = this._curves = new Array(length);
       
  6890 			for (var i = 0; i < length; i++)
       
  6891 				curves[i] = new Curve(this, segments[i],
       
  6892 					segments[i + 1] || segments[0]);
       
  6893 		}
       
  6894 		return curves;
       
  6895 	},
       
  6896 
       
  6897 	getFirstCurve: function() {
       
  6898 		return this.getCurves()[0];
       
  6899 	},
       
  6900 
       
  6901 	getLastCurve: function() {
       
  6902 		var curves = this.getCurves();
       
  6903 		return curves[curves.length - 1];
       
  6904 	},
       
  6905 
       
  6906 	isClosed: function() {
       
  6907 		return this._closed;
       
  6908 	},
       
  6909 
       
  6910 	setClosed: function(closed) {
       
  6911 		if (this._closed != (closed = !!closed)) {
       
  6912 			this._closed = closed;
       
  6913 			if (this._curves) {
       
  6914 				var length = this._curves.length = this._countCurves();
       
  6915 				if (closed)
       
  6916 					this._curves[length - 1] = new Curve(this,
       
  6917 						this._segments[length - 1], this._segments[0]);
       
  6918 			}
       
  6919 			this._changed(25);
       
  6920 		}
       
  6921 	}
       
  6922 }, {
       
  6923 	beans: true,
       
  6924 
       
  6925 	getPathData: function(_matrix, _precision) {
       
  6926 		var segments = this._segments,
       
  6927 			length = segments.length,
       
  6928 			f = new Formatter(_precision),
       
  6929 			coords = new Array(6),
       
  6930 			first = true,
       
  6931 			curX, curY,
       
  6932 			prevX, prevY,
       
  6933 			inX, inY,
       
  6934 			outX, outY,
       
  6935 			parts = [];
       
  6936 
       
  6937 		function addSegment(segment, skipLine) {
       
  6938 			segment._transformCoordinates(_matrix, coords, false);
       
  6939 			curX = coords[0];
       
  6940 			curY = coords[1];
       
  6941 			if (first) {
       
  6942 				parts.push('M' + f.pair(curX, curY));
       
  6943 				first = false;
       
  6944 			} else {
       
  6945 				inX = coords[2];
       
  6946 				inY = coords[3];
       
  6947 				if (inX === curX && inY === curY
       
  6948 						&& outX === prevX && outY === prevY) {
       
  6949 					if (!skipLine)
       
  6950 						parts.push('l' + f.pair(curX - prevX, curY - prevY));
       
  6951 				} else {
       
  6952 					parts.push('c' + f.pair(outX - prevX, outY - prevY)
       
  6953 							+ ' ' + f.pair(inX - prevX, inY - prevY)
       
  6954 							+ ' ' + f.pair(curX - prevX, curY - prevY));
       
  6955 				}
       
  6956 			}
       
  6957 			prevX = curX;
       
  6958 			prevY = curY;
       
  6959 			outX = coords[4];
       
  6960 			outY = coords[5];
       
  6961 		}
       
  6962 
       
  6963 		if (length === 0)
       
  6964 			return '';
       
  6965 
       
  6966 		for (var i = 0; i < length; i++)
       
  6967 			addSegment(segments[i]);
       
  6968 		if (this._closed && length > 0) {
       
  6969 			addSegment(segments[0], true);
       
  6970 			parts.push('z');
       
  6971 		}
       
  6972 		return parts.join('');
       
  6973 	}
       
  6974 }, {
       
  6975 
       
  6976 	isEmpty: function() {
       
  6977 		return this._segments.length === 0;
       
  6978 	},
       
  6979 
       
  6980 	isLinear: function() {
       
  6981 		var segments = this._segments;
       
  6982 		for (var i = 0, l = segments.length; i < l; i++) {
       
  6983 			if (!segments[i].isLinear())
       
  6984 				return false;
       
  6985 		}
       
  6986 		return true;
       
  6987 	},
       
  6988 
       
  6989 	hasHandles: function() {
       
  6990 		var segments = this._segments;
       
  6991 		for (var i = 0, l = segments.length; i < l; i++) {
       
  6992 			if (segments[i].hasHandles())
       
  6993 				return true;
       
  6994 		}
       
  6995 		return false;
       
  6996 	},
       
  6997 
       
  6998 	_transformContent: function(matrix) {
       
  6999 		var coords = new Array(6);
       
  7000 		for (var i = 0, l = this._segments.length; i < l; i++)
       
  7001 			this._segments[i]._transformCoordinates(matrix, coords, true);
       
  7002 		return true;
       
  7003 	},
       
  7004 
       
  7005 	_add: function(segs, index) {
       
  7006 		var segments = this._segments,
       
  7007 			curves = this._curves,
       
  7008 			amount = segs.length,
       
  7009 			append = index == null,
       
  7010 			index = append ? segments.length : index;
       
  7011 		for (var i = 0; i < amount; i++) {
       
  7012 			var segment = segs[i];
       
  7013 			if (segment._path)
       
  7014 				segment = segs[i] = segment.clone();
       
  7015 			segment._path = this;
       
  7016 			segment._index = index + i;
       
  7017 			if (segment._selectionState)
       
  7018 				this._updateSelection(segment, 0, segment._selectionState);
       
  7019 		}
       
  7020 		if (append) {
       
  7021 			segments.push.apply(segments, segs);
       
  7022 		} else {
       
  7023 			segments.splice.apply(segments, [index, 0].concat(segs));
       
  7024 			for (var i = index + amount, l = segments.length; i < l; i++)
       
  7025 				segments[i]._index = i;
       
  7026 		}
       
  7027 		if (curves || segs._curves) {
       
  7028 			if (!curves)
       
  7029 				curves = this._curves = [];
       
  7030 			var from = index > 0 ? index - 1 : index,
       
  7031 				start = from,
       
  7032 				to = Math.min(from + amount, this._countCurves());
       
  7033 			if (segs._curves) {
       
  7034 				curves.splice.apply(curves, [from, 0].concat(segs._curves));
       
  7035 				start += segs._curves.length;
       
  7036 			}
       
  7037 			for (var i = start; i < to; i++)
       
  7038 				curves.splice(i, 0, new Curve(this, null, null));
       
  7039 			this._adjustCurves(from, to);
       
  7040 		}
       
  7041 		this._changed(25);
       
  7042 		return segs;
       
  7043 	},
       
  7044 
       
  7045 	_adjustCurves: function(from, to) {
       
  7046 		var segments = this._segments,
       
  7047 			curves = this._curves,
       
  7048 			curve;
       
  7049 		for (var i = from; i < to; i++) {
       
  7050 			curve = curves[i];
       
  7051 			curve._path = this;
       
  7052 			curve._segment1 = segments[i];
       
  7053 			curve._segment2 = segments[i + 1] || segments[0];
       
  7054 			curve._changed();
       
  7055 		}
       
  7056 		if (curve = curves[this._closed && from === 0 ? segments.length - 1
       
  7057 				: from - 1]) {
       
  7058 			curve._segment2 = segments[from] || segments[0];
       
  7059 			curve._changed();
       
  7060 		}
       
  7061 		if (curve = curves[to]) {
       
  7062 			curve._segment1 = segments[to];
       
  7063 			curve._changed();
       
  7064 		}
       
  7065 	},
       
  7066 
       
  7067 	_countCurves: function() {
       
  7068 		var length = this._segments.length;
       
  7069 		return !this._closed && length > 0 ? length - 1 : length;
       
  7070 	},
       
  7071 
       
  7072 	add: function(segment1 ) {
       
  7073 		return arguments.length > 1 && typeof segment1 !== 'number'
       
  7074 			? this._add(Segment.readAll(arguments))
       
  7075 			: this._add([ Segment.read(arguments) ])[0];
       
  7076 	},
       
  7077 
       
  7078 	insert: function(index, segment1 ) {
       
  7079 		return arguments.length > 2 && typeof segment1 !== 'number'
       
  7080 			? this._add(Segment.readAll(arguments, 1), index)
       
  7081 			: this._add([ Segment.read(arguments, 1) ], index)[0];
       
  7082 	},
       
  7083 
       
  7084 	addSegment: function() {
       
  7085 		return this._add([ Segment.read(arguments) ])[0];
       
  7086 	},
       
  7087 
       
  7088 	insertSegment: function(index ) {
       
  7089 		return this._add([ Segment.read(arguments, 1) ], index)[0];
       
  7090 	},
       
  7091 
       
  7092 	addSegments: function(segments) {
       
  7093 		return this._add(Segment.readAll(segments));
       
  7094 	},
       
  7095 
       
  7096 	insertSegments: function(index, segments) {
       
  7097 		return this._add(Segment.readAll(segments), index);
       
  7098 	},
       
  7099 
       
  7100 	removeSegment: function(index) {
       
  7101 		return this.removeSegments(index, index + 1)[0] || null;
       
  7102 	},
       
  7103 
       
  7104 	removeSegments: function(from, to, _includeCurves) {
       
  7105 		from = from || 0;
       
  7106 		to = Base.pick(to, this._segments.length);
       
  7107 		var segments = this._segments,
       
  7108 			curves = this._curves,
       
  7109 			count = segments.length,
       
  7110 			removed = segments.splice(from, to - from),
       
  7111 			amount = removed.length;
       
  7112 		if (!amount)
       
  7113 			return removed;
       
  7114 		for (var i = 0; i < amount; i++) {
       
  7115 			var segment = removed[i];
       
  7116 			if (segment._selectionState)
       
  7117 				this._updateSelection(segment, segment._selectionState, 0);
       
  7118 			segment._index = segment._path = null;
       
  7119 		}
       
  7120 		for (var i = from, l = segments.length; i < l; i++)
       
  7121 			segments[i]._index = i;
       
  7122 		if (curves) {
       
  7123 			var index = from > 0 && to === count + (this._closed ? 1 : 0)
       
  7124 					? from - 1
       
  7125 					: from,
       
  7126 				curves = curves.splice(index, amount);
       
  7127 			if (_includeCurves)
       
  7128 				removed._curves = curves.slice(1);
       
  7129 			this._adjustCurves(index, index);
       
  7130 		}
       
  7131 		this._changed(25);
       
  7132 		return removed;
       
  7133 	},
       
  7134 
       
  7135 	clear: '#removeSegments',
       
  7136 
       
  7137 	getLength: function() {
       
  7138 		if (this._length == null) {
       
  7139 			var curves = this.getCurves();
       
  7140 			this._length = 0;
       
  7141 			for (var i = 0, l = curves.length; i < l; i++)
       
  7142 				this._length += curves[i].getLength();
       
  7143 		}
       
  7144 		return this._length;
       
  7145 	},
       
  7146 
       
  7147 	getArea: function() {
       
  7148 		var curves = this.getCurves();
       
  7149 		var area = 0;
       
  7150 		for (var i = 0, l = curves.length; i < l; i++)
       
  7151 			area += curves[i].getArea();
       
  7152 		return area;
       
  7153 	},
       
  7154 
       
  7155 	isFullySelected: function() {
       
  7156 		var length = this._segments.length;
       
  7157 		return this._selected && length > 0 && this._selectedSegmentState
       
  7158 				=== length * 7;
       
  7159 	},
       
  7160 
       
  7161 	setFullySelected: function(selected) {
       
  7162 		if (selected)
       
  7163 			this._selectSegments(true);
       
  7164 		this.setSelected(selected);
       
  7165 	},
       
  7166 
       
  7167 	setSelected: function setSelected(selected) {
       
  7168 		if (!selected)
       
  7169 			this._selectSegments(false);
       
  7170 		setSelected.base.call(this, selected);
       
  7171 	},
       
  7172 
       
  7173 	_selectSegments: function(selected) {
       
  7174 		var length = this._segments.length;
       
  7175 		this._selectedSegmentState = selected
       
  7176 				? length * 7 : 0;
       
  7177 		for (var i = 0; i < length; i++)
       
  7178 			this._segments[i]._selectionState = selected
       
  7179 					? 7 : 0;
       
  7180 	},
       
  7181 
       
  7182 	_updateSelection: function(segment, oldState, newState) {
       
  7183 		segment._selectionState = newState;
       
  7184 		var total = this._selectedSegmentState += newState - oldState;
       
  7185 		if (total > 0)
       
  7186 			this.setSelected(true);
       
  7187 	},
       
  7188 
       
  7189 	flatten: function(maxDistance) {
       
  7190 		var iterator = new PathIterator(this, 64, 0.1),
       
  7191 			pos = 0,
       
  7192 			step = iterator.length / Math.ceil(iterator.length / maxDistance),
       
  7193 			end = iterator.length + (this._closed ? -step : step) / 2;
       
  7194 		var segments = [];
       
  7195 		while (pos <= end) {
       
  7196 			segments.push(new Segment(iterator.getPointAt(pos)));
       
  7197 			pos += step;
       
  7198 		}
       
  7199 		this.setSegments(segments);
       
  7200 	},
       
  7201 
       
  7202 	reduce: function() {
       
  7203 		var curves = this.getCurves();
       
  7204 		for (var i = curves.length - 1; i >= 0; i--) {
       
  7205 			var curve = curves[i];
       
  7206 			if (curve.isLinear() && curve.getLength() === 0)
       
  7207 				curve.remove();
       
  7208 		}
       
  7209 		return this;
       
  7210 	},
       
  7211 
       
  7212 	simplify: function(tolerance) {
       
  7213 		if (this._segments.length > 2) {
       
  7214 			var fitter = new PathFitter(this, tolerance || 2.5);
       
  7215 			this.setSegments(fitter.fit());
       
  7216 		}
       
  7217 	},
       
  7218 
       
  7219 	split: function(index, parameter) {
       
  7220 		if (parameter === null)
       
  7221 			return null;
       
  7222 		if (arguments.length === 1) {
       
  7223 			var arg = index;
       
  7224 			if (typeof arg === 'number')
       
  7225 				arg = this.getLocationAt(arg);
       
  7226 			if (!arg)
       
  7227 				return null
       
  7228 			index = arg.index;
       
  7229 			parameter = arg.parameter;
       
  7230 		}
       
  7231 		var tolerance = 0.000001;
       
  7232 		if (parameter >= 1 - tolerance) {
       
  7233 			index++;
       
  7234 			parameter--;
       
  7235 		}
       
  7236 		var curves = this.getCurves();
       
  7237 		if (index >= 0 && index < curves.length) {
       
  7238 			if (parameter > tolerance) {
       
  7239 				curves[index++].divide(parameter, true);
       
  7240 			}
       
  7241 			var segs = this.removeSegments(index, this._segments.length, true),
       
  7242 				path;
       
  7243 			if (this._closed) {
       
  7244 				this.setClosed(false);
       
  7245 				path = this;
       
  7246 			} else {
       
  7247 				path = this._clone(new Path().insertAbove(this, true));
       
  7248 			}
       
  7249 			path._add(segs, 0);
       
  7250 			this.addSegment(segs[0]);
       
  7251 			return path;
       
  7252 		}
       
  7253 		return null;
       
  7254 	},
       
  7255 
       
  7256 	isClockwise: function() {
       
  7257 		if (this._clockwise !== undefined)
       
  7258 			return this._clockwise;
       
  7259 		return Path.isClockwise(this._segments);
       
  7260 	},
       
  7261 
       
  7262 	setClockwise: function(clockwise) {
       
  7263 		if (this.isClockwise() != (clockwise = !!clockwise))
       
  7264 			this.reverse();
       
  7265 		this._clockwise = clockwise;
       
  7266 	},
       
  7267 
       
  7268 	reverse: function() {
       
  7269 		this._segments.reverse();
       
  7270 		for (var i = 0, l = this._segments.length; i < l; i++) {
       
  7271 			var segment = this._segments[i];
       
  7272 			var handleIn = segment._handleIn;
       
  7273 			segment._handleIn = segment._handleOut;
       
  7274 			segment._handleOut = handleIn;
       
  7275 			segment._index = i;
       
  7276 		}
       
  7277 		this._curves = null;
       
  7278 		if (this._clockwise !== undefined)
       
  7279 			this._clockwise = !this._clockwise;
       
  7280 		this._changed(9);
       
  7281 	},
       
  7282 
       
  7283 	join: function(path) {
       
  7284 		if (path) {
       
  7285 			var segments = path._segments,
       
  7286 				last1 = this.getLastSegment(),
       
  7287 				last2 = path.getLastSegment();
       
  7288 			if (!last2)
       
  7289 				return this;
       
  7290 			if (last1 && last1._point.equals(last2._point))
       
  7291 				path.reverse();
       
  7292 			var first2 = path.getFirstSegment();
       
  7293 			if (last1 && last1._point.equals(first2._point)) {
       
  7294 				last1.setHandleOut(first2._handleOut);
       
  7295 				this._add(segments.slice(1));
       
  7296 			} else {
       
  7297 				var first1 = this.getFirstSegment();
       
  7298 				if (first1 && first1._point.equals(first2._point))
       
  7299 					path.reverse();
       
  7300 				last2 = path.getLastSegment();
       
  7301 				if (first1 && first1._point.equals(last2._point)) {
       
  7302 					first1.setHandleIn(last2._handleIn);
       
  7303 					this._add(segments.slice(0, segments.length - 1), 0);
       
  7304 				} else {
       
  7305 					this._add(segments.slice());
       
  7306 				}
       
  7307 			}
       
  7308 			if (path.closed)
       
  7309 				this._add([segments[0]]);
       
  7310 			path.remove();
       
  7311 		}
       
  7312 		var first = this.getFirstSegment(),
       
  7313 			last = this.getLastSegment();
       
  7314 		if (first !== last && first._point.equals(last._point)) {
       
  7315 			first.setHandleIn(last._handleIn);
       
  7316 			last.remove();
       
  7317 			this.setClosed(true);
       
  7318 		}
       
  7319 		return this;
       
  7320 	},
       
  7321 
       
  7322 	toShape: function(insert) {
       
  7323 		if (!this._closed)
       
  7324 			return null;
       
  7325 
       
  7326 		var segments = this._segments,
       
  7327 			type,
       
  7328 			size,
       
  7329 			radius,
       
  7330 			topCenter;
       
  7331 
       
  7332 		function isCollinear(i, j) {
       
  7333 			return segments[i].isCollinear(segments[j]);
       
  7334 		}
       
  7335 
       
  7336 		function isOrthogonal(i) {
       
  7337 			return segments[i].isOrthogonal();
       
  7338 		}
       
  7339 
       
  7340 		function isArc(i) {
       
  7341 			return segments[i].isOrthogonalArc();
       
  7342 		}
       
  7343 
       
  7344 		function getDistance(i, j) {
       
  7345 			return segments[i]._point.getDistance(segments[j]._point);
       
  7346 		}
       
  7347 
       
  7348 		if (!this.hasHandles() && segments.length === 4
       
  7349 				&& isCollinear(0, 2) && isCollinear(1, 3) && isOrthogonal(1)) {
       
  7350 			type = Shape.Rectangle;
       
  7351 			size = new Size(getDistance(0, 3), getDistance(0, 1));
       
  7352 			topCenter = segments[1]._point.add(segments[2]._point).divide(2);
       
  7353 		} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)
       
  7354 				&& isArc(6) && isCollinear(1, 5) && isCollinear(3, 7)) {
       
  7355 			type = Shape.Rectangle;
       
  7356 			size = new Size(getDistance(1, 6), getDistance(0, 3));
       
  7357 			radius = size.subtract(new Size(getDistance(0, 7),
       
  7358 					getDistance(1, 2))).divide(2);
       
  7359 			topCenter = segments[3]._point.add(segments[4]._point).divide(2);
       
  7360 		} else if (segments.length === 4
       
  7361 				&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
       
  7362 			if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {
       
  7363 				type = Shape.Circle;
       
  7364 				radius = getDistance(0, 2) / 2;
       
  7365 			} else {
       
  7366 				type = Shape.Ellipse;
       
  7367 				radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);
       
  7368 			}
       
  7369 			topCenter = segments[1]._point;
       
  7370 		}
       
  7371 
       
  7372 		if (type) {
       
  7373 			var center = this.getPosition(true),
       
  7374 				shape = this._clone(new type({
       
  7375 					center: center,
       
  7376 					size: size,
       
  7377 					radius: radius,
       
  7378 					insert: false
       
  7379 				}), insert, false);
       
  7380 			shape.rotate(topCenter.subtract(center).getAngle() + 90);
       
  7381 			return shape;
       
  7382 		}
       
  7383 		return null;
       
  7384 	},
       
  7385 
       
  7386 	_hitTestSelf: function(point, options) {
       
  7387 		var that = this,
       
  7388 			style = this.getStyle(),
       
  7389 			segments = this._segments,
       
  7390 			numSegments = segments.length,
       
  7391 			closed = this._closed,
       
  7392 			tolerancePadding = options._tolerancePadding,
       
  7393 			strokePadding = tolerancePadding,
       
  7394 			join, cap, miterLimit,
       
  7395 			area, loc, res,
       
  7396 			hitStroke = options.stroke && style.hasStroke(),
       
  7397 			hitFill = options.fill && style.hasFill(),
       
  7398 			hitCurves = options.curves,
       
  7399 			radius = hitStroke
       
  7400 					? style.getStrokeWidth() / 2
       
  7401 					: hitFill && options.tolerance > 0 || hitCurves
       
  7402 						? 0 : null;
       
  7403 		if (radius !== null) {
       
  7404 			if (radius > 0) {
       
  7405 				join = style.getStrokeJoin();
       
  7406 				cap = style.getStrokeCap();
       
  7407 				miterLimit = radius * style.getMiterLimit();
       
  7408 				strokePadding = tolerancePadding.add(new Point(radius, radius));
       
  7409 			} else {
       
  7410 				join = cap = 'round';
       
  7411 			}
       
  7412 		}
       
  7413 
       
  7414 		function isCloseEnough(pt, padding) {
       
  7415 			return point.subtract(pt).divide(padding).length <= 1;
       
  7416 		}
       
  7417 
       
  7418 		function checkSegmentPoint(seg, pt, name) {
       
  7419 			if (!options.selected || pt.isSelected()) {
       
  7420 				var anchor = seg._point;
       
  7421 				if (pt !== anchor)
       
  7422 					pt = pt.add(anchor);
       
  7423 				if (isCloseEnough(pt, strokePadding)) {
       
  7424 					return new HitResult(name, that, {
       
  7425 						segment: seg,
       
  7426 						point: pt
       
  7427 					});
       
  7428 				}
       
  7429 			}
       
  7430 		}
       
  7431 
       
  7432 		function checkSegmentPoints(seg, ends) {
       
  7433 			return (ends || options.segments)
       
  7434 				&& checkSegmentPoint(seg, seg._point, 'segment')
       
  7435 				|| (!ends && options.handles) && (
       
  7436 					checkSegmentPoint(seg, seg._handleIn, 'handle-in') ||
       
  7437 					checkSegmentPoint(seg, seg._handleOut, 'handle-out'));
       
  7438 		}
       
  7439 
       
  7440 		function addToArea(point) {
       
  7441 			area.add(point);
       
  7442 		}
       
  7443 
       
  7444 		function checkSegmentStroke(segment) {
       
  7445 			if (join !== 'round' || cap !== 'round') {
       
  7446 				area = new Path({ internal: true, closed: true });
       
  7447 				if (closed || segment._index > 0
       
  7448 						&& segment._index < numSegments - 1) {
       
  7449 					if (join !== 'round' && (segment._handleIn.isZero()
       
  7450 							|| segment._handleOut.isZero()))
       
  7451 						Path._addBevelJoin(segment, join, radius, miterLimit,
       
  7452 								addToArea, true);
       
  7453 				} else if (cap !== 'round') {
       
  7454 					Path._addSquareCap(segment, cap, radius, addToArea, true);
       
  7455 				}
       
  7456 				if (!area.isEmpty()) {
       
  7457 					var loc;
       
  7458 					return area.contains(point)
       
  7459 						|| (loc = area.getNearestLocation(point))
       
  7460 							&& isCloseEnough(loc.getPoint(), tolerancePadding);
       
  7461 				}
       
  7462 			}
       
  7463 			return isCloseEnough(segment._point, strokePadding);
       
  7464 		}
       
  7465 
       
  7466 		if (options.ends && !options.segments && !closed) {
       
  7467 			if (res = checkSegmentPoints(segments[0], true)
       
  7468 					|| checkSegmentPoints(segments[numSegments - 1], true))
       
  7469 				return res;
       
  7470 		} else if (options.segments || options.handles) {
       
  7471 			for (var i = 0; i < numSegments; i++)
       
  7472 				if (res = checkSegmentPoints(segments[i]))
       
  7473 					return res;
       
  7474 		}
       
  7475 		if (radius !== null) {
       
  7476 			loc = this.getNearestLocation(point);
       
  7477 			if (loc) {
       
  7478 				var parameter = loc.getParameter();
       
  7479 				if (parameter === 0 || parameter === 1 && numSegments > 1) {
       
  7480 					if (!checkSegmentStroke(loc.getSegment()))
       
  7481 						loc = null;
       
  7482 				} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
       
  7483 					loc = null;
       
  7484 				}
       
  7485 			}
       
  7486 			if (!loc && join === 'miter' && numSegments > 1) {
       
  7487 				for (var i = 0; i < numSegments; i++) {
       
  7488 					var segment = segments[i];
       
  7489 					if (point.getDistance(segment._point) <= miterLimit
       
  7490 							&& checkSegmentStroke(segment)) {
       
  7491 						loc = segment.getLocation();
       
  7492 						break;
       
  7493 					}
       
  7494 				}
       
  7495 			}
       
  7496 		}
       
  7497 		return !loc && hitFill && this._contains(point)
       
  7498 				|| loc && !hitStroke && !hitCurves
       
  7499 					? new HitResult('fill', this)
       
  7500 					: loc
       
  7501 						? new HitResult(hitStroke ? 'stroke' : 'curve', this, {
       
  7502 							location: loc,
       
  7503 							point: loc.getPoint()
       
  7504 						})
       
  7505 						: null;
       
  7506 	}
       
  7507 
       
  7508 }, Base.each(Curve.evaluateMethods,
       
  7509 	function(name) {
       
  7510 		this[name + 'At'] = function(offset, isParameter) {
       
  7511 			var loc = this.getLocationAt(offset, isParameter);
       
  7512 			return loc && loc[name]();
       
  7513 		};
       
  7514 	},
       
  7515 {
       
  7516 	beans: false,
       
  7517 
       
  7518 	_getOffset: function(location) {
       
  7519 		var index = location && location.getIndex();
       
  7520 		if (index != null) {
       
  7521 			var curves = this.getCurves(),
       
  7522 				offset = 0;
       
  7523 			for (var i = 0; i < index; i++)
       
  7524 				offset += curves[i].getLength();
       
  7525 			var curve = curves[index],
       
  7526 				parameter = location.getParameter();
       
  7527 			if (parameter > 0)
       
  7528 				offset += curve.getPartLength(0, parameter);
       
  7529 			return offset;
       
  7530 		}
       
  7531 		return null;
       
  7532 	},
       
  7533 
       
  7534 	getLocationOf: function() {
       
  7535 		var point = Point.read(arguments),
       
  7536 			curves = this.getCurves();
       
  7537 		for (var i = 0, l = curves.length; i < l; i++) {
       
  7538 			var loc = curves[i].getLocationOf(point);
       
  7539 			if (loc)
       
  7540 				return loc;
       
  7541 		}
       
  7542 		return null;
       
  7543 	},
       
  7544 
       
  7545 	getOffsetOf: function() {
       
  7546 		var loc = this.getLocationOf.apply(this, arguments);
       
  7547 		return loc ? loc.getOffset() : null;
       
  7548 	},
       
  7549 
       
  7550 	getLocationAt: function(offset, isParameter) {
       
  7551 		var curves = this.getCurves(),
       
  7552 			length = 0;
       
  7553 		if (isParameter) {
       
  7554 			var index = ~~offset,
       
  7555 				curve = curves[index];
       
  7556 			return curve ? curve.getLocationAt(offset - index, true) : null;
       
  7557 		}
       
  7558 		for (var i = 0, l = curves.length; i < l; i++) {
       
  7559 			var start = length,
       
  7560 				curve = curves[i];
       
  7561 			length += curve.getLength();
       
  7562 			if (length > offset) {
       
  7563 				return curve.getLocationAt(offset - start);
       
  7564 			}
       
  7565 		}
       
  7566 		if (offset <= this.getLength())
       
  7567 			return new CurveLocation(curves[curves.length - 1], 1);
       
  7568 		return null;
       
  7569 	},
       
  7570 
       
  7571 	getNearestLocation: function() {
       
  7572 		var point = Point.read(arguments),
       
  7573 			curves = this.getCurves(),
       
  7574 			minDist = Infinity,
       
  7575 			minLoc = null;
       
  7576 		for (var i = 0, l = curves.length; i < l; i++) {
       
  7577 			var loc = curves[i].getNearestLocation(point);
       
  7578 			if (loc._distance < minDist) {
       
  7579 				minDist = loc._distance;
       
  7580 				minLoc = loc;
       
  7581 			}
       
  7582 		}
       
  7583 		return minLoc;
       
  7584 	},
       
  7585 
       
  7586 	getNearestPoint: function() {
       
  7587 		return this.getNearestLocation.apply(this, arguments).getPoint();
       
  7588 	}
       
  7589 }), new function() {
       
  7590 
       
  7591 	function drawHandles(ctx, segments, matrix, size) {
       
  7592 		var half = size / 2;
       
  7593 
       
  7594 		function drawHandle(index) {
       
  7595 			var hX = coords[index],
       
  7596 				hY = coords[index + 1];
       
  7597 			if (pX != hX || pY != hY) {
       
  7598 				ctx.beginPath();
       
  7599 				ctx.moveTo(pX, pY);
       
  7600 				ctx.lineTo(hX, hY);
       
  7601 				ctx.stroke();
       
  7602 				ctx.beginPath();
       
  7603 				ctx.arc(hX, hY, half, 0, Math.PI * 2, true);
       
  7604 				ctx.fill();
       
  7605 			}
       
  7606 		}
       
  7607 
       
  7608 		var coords = new Array(6);
       
  7609 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7610 			var segment = segments[i];
       
  7611 			segment._transformCoordinates(matrix, coords, false);
       
  7612 			var state = segment._selectionState,
       
  7613 				pX = coords[0],
       
  7614 				pY = coords[1];
       
  7615 			if (state & 1)
       
  7616 				drawHandle(2);
       
  7617 			if (state & 2)
       
  7618 				drawHandle(4);
       
  7619 			ctx.fillRect(pX - half, pY - half, size, size);
       
  7620 			if (!(state & 4)) {
       
  7621 				var fillStyle = ctx.fillStyle;
       
  7622 				ctx.fillStyle = '#ffffff';
       
  7623 				ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2);
       
  7624 				ctx.fillStyle = fillStyle;
       
  7625 			}
       
  7626 		}
       
  7627 	}
       
  7628 
       
  7629 	function drawSegments(ctx, path, matrix) {
       
  7630 		var segments = path._segments,
       
  7631 			length = segments.length,
       
  7632 			coords = new Array(6),
       
  7633 			first = true,
       
  7634 			curX, curY,
       
  7635 			prevX, prevY,
       
  7636 			inX, inY,
       
  7637 			outX, outY;
       
  7638 
       
  7639 		function drawSegment(segment) {
       
  7640 			if (matrix) {
       
  7641 				segment._transformCoordinates(matrix, coords, false);
       
  7642 				curX = coords[0];
       
  7643 				curY = coords[1];
       
  7644 			} else {
       
  7645 				var point = segment._point;
       
  7646 				curX = point._x;
       
  7647 				curY = point._y;
       
  7648 			}
       
  7649 			if (first) {
       
  7650 				ctx.moveTo(curX, curY);
       
  7651 				first = false;
       
  7652 			} else {
       
  7653 				if (matrix) {
       
  7654 					inX = coords[2];
       
  7655 					inY = coords[3];
       
  7656 				} else {
       
  7657 					var handle = segment._handleIn;
       
  7658 					inX = curX + handle._x;
       
  7659 					inY = curY + handle._y;
       
  7660 				}
       
  7661 				if (inX === curX && inY === curY
       
  7662 						&& outX === prevX && outY === prevY) {
       
  7663 					ctx.lineTo(curX, curY);
       
  7664 				} else {
       
  7665 					ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
       
  7666 				}
       
  7667 			}
       
  7668 			prevX = curX;
       
  7669 			prevY = curY;
       
  7670 			if (matrix) {
       
  7671 				outX = coords[4];
       
  7672 				outY = coords[5];
       
  7673 			} else {
       
  7674 				var handle = segment._handleOut;
       
  7675 				outX = prevX + handle._x;
       
  7676 				outY = prevY + handle._y;
       
  7677 			}
       
  7678 		}
       
  7679 
       
  7680 		for (var i = 0; i < length; i++)
       
  7681 			drawSegment(segments[i]);
       
  7682 		if (path._closed && length > 0)
       
  7683 			drawSegment(segments[0]);
       
  7684 	}
       
  7685 
       
  7686 	return {
       
  7687 		_draw: function(ctx, param, strokeMatrix) {
       
  7688 			var dontStart = param.dontStart,
       
  7689 				dontPaint = param.dontFinish || param.clip,
       
  7690 				style = this.getStyle(),
       
  7691 				hasFill = style.hasFill(),
       
  7692 				hasStroke = style.hasStroke(),
       
  7693 				dashArray = style.getDashArray(),
       
  7694 				dashLength = !paper.support.nativeDash && hasStroke
       
  7695 						&& dashArray && dashArray.length;
       
  7696 
       
  7697 			if (!dontStart)
       
  7698 				ctx.beginPath();
       
  7699 
       
  7700 			if (!dontStart && this._currentPath) {
       
  7701 				ctx.currentPath = this._currentPath;
       
  7702 			} else if (hasFill || hasStroke && !dashLength || dontPaint) {
       
  7703 				drawSegments(ctx, this, strokeMatrix);
       
  7704 				if (this._closed)
       
  7705 					ctx.closePath();
       
  7706 				if (!dontStart)
       
  7707 					this._currentPath = ctx.currentPath;
       
  7708 			}
       
  7709 
       
  7710 			function getOffset(i) {
       
  7711 				return dashArray[((i % dashLength) + dashLength) % dashLength];
       
  7712 			}
       
  7713 
       
  7714 			if (!dontPaint && (hasFill || hasStroke)) {
       
  7715 				this._setStyles(ctx);
       
  7716 				if (hasFill) {
       
  7717 					ctx.fill(style.getWindingRule());
       
  7718 					ctx.shadowColor = 'rgba(0,0,0,0)';
       
  7719 				}
       
  7720 				if (hasStroke) {
       
  7721 					if (dashLength) {
       
  7722 						if (!dontStart)
       
  7723 							ctx.beginPath();
       
  7724 						var iterator = new PathIterator(this, 32, 0.25,
       
  7725 								strokeMatrix),
       
  7726 							length = iterator.length,
       
  7727 							from = -style.getDashOffset(), to,
       
  7728 							i = 0;
       
  7729 						from = from % length;
       
  7730 						while (from > 0) {
       
  7731 							from -= getOffset(i--) + getOffset(i--);
       
  7732 						}
       
  7733 						while (from < length) {
       
  7734 							to = from + getOffset(i++);
       
  7735 							if (from > 0 || to > 0)
       
  7736 								iterator.drawPart(ctx,
       
  7737 										Math.max(from, 0), Math.max(to, 0));
       
  7738 							from = to + getOffset(i++);
       
  7739 						}
       
  7740 					}
       
  7741 					ctx.stroke();
       
  7742 				}
       
  7743 			}
       
  7744 		},
       
  7745 
       
  7746 		_drawSelected: function(ctx, matrix) {
       
  7747 			ctx.beginPath();
       
  7748 			drawSegments(ctx, this, matrix);
       
  7749 			ctx.stroke();
       
  7750 			drawHandles(ctx, this._segments, matrix, paper.settings.handleSize);
       
  7751 		}
       
  7752 	};
       
  7753 }, new function() {
       
  7754 
       
  7755 	function getFirstControlPoints(rhs) {
       
  7756 		var n = rhs.length,
       
  7757 			x = [],
       
  7758 			tmp = [],
       
  7759 			b = 2;
       
  7760 		x[0] = rhs[0] / b;
       
  7761 		for (var i = 1; i < n; i++) {
       
  7762 			tmp[i] = 1 / b;
       
  7763 			b = (i < n - 1 ? 4 : 2) - tmp[i];
       
  7764 			x[i] = (rhs[i] - x[i - 1]) / b;
       
  7765 		}
       
  7766 		for (var i = 1; i < n; i++) {
       
  7767 			x[n - i - 1] -= tmp[n - i] * x[n - i];
       
  7768 		}
       
  7769 		return x;
       
  7770 	}
       
  7771 
       
  7772 	return {
       
  7773 		smooth: function() {
       
  7774 			var segments = this._segments,
       
  7775 				size = segments.length,
       
  7776 				closed = this._closed,
       
  7777 				n = size,
       
  7778 				overlap = 0;
       
  7779 			if (size <= 2)
       
  7780 				return;
       
  7781 			if (closed) {
       
  7782 				overlap = Math.min(size, 4);
       
  7783 				n += Math.min(size, overlap) * 2;
       
  7784 			}
       
  7785 			var knots = [];
       
  7786 			for (var i = 0; i < size; i++)
       
  7787 				knots[i + overlap] = segments[i]._point;
       
  7788 			if (closed) {
       
  7789 				for (var i = 0; i < overlap; i++) {
       
  7790 					knots[i] = segments[i + size - overlap]._point;
       
  7791 					knots[i + size + overlap] = segments[i]._point;
       
  7792 				}
       
  7793 			} else {
       
  7794 				n--;
       
  7795 			}
       
  7796 			var rhs = [];
       
  7797 
       
  7798 			for (var i = 1; i < n - 1; i++)
       
  7799 				rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
       
  7800 			rhs[0] = knots[0]._x + 2 * knots[1]._x;
       
  7801 			rhs[n - 1] = 3 * knots[n - 1]._x;
       
  7802 			var x = getFirstControlPoints(rhs);
       
  7803 
       
  7804 			for (var i = 1; i < n - 1; i++)
       
  7805 				rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
       
  7806 			rhs[0] = knots[0]._y + 2 * knots[1]._y;
       
  7807 			rhs[n - 1] = 3 * knots[n - 1]._y;
       
  7808 			var y = getFirstControlPoints(rhs);
       
  7809 
       
  7810 			if (closed) {
       
  7811 				for (var i = 0, j = size; i < overlap; i++, j++) {
       
  7812 					var f1 = i / overlap,
       
  7813 						f2 = 1 - f1,
       
  7814 						ie = i + overlap,
       
  7815 						je = j + overlap;
       
  7816 					x[j] = x[i] * f1 + x[j] * f2;
       
  7817 					y[j] = y[i] * f1 + y[j] * f2;
       
  7818 					x[je] = x[ie] * f2 + x[je] * f1;
       
  7819 					y[je] = y[ie] * f2 + y[je] * f1;
       
  7820 				}
       
  7821 				n--;
       
  7822 			}
       
  7823 			var handleIn = null;
       
  7824 			for (var i = overlap; i <= n - overlap; i++) {
       
  7825 				var segment = segments[i - overlap];
       
  7826 				if (handleIn)
       
  7827 					segment.setHandleIn(handleIn.subtract(segment._point));
       
  7828 				if (i < n) {
       
  7829 					segment.setHandleOut(
       
  7830 							new Point(x[i], y[i]).subtract(segment._point));
       
  7831 					handleIn = i < n - 1
       
  7832 							? new Point(
       
  7833 								2 * knots[i + 1]._x - x[i + 1],
       
  7834 								2 * knots[i + 1]._y - y[i + 1])
       
  7835 							: new Point(
       
  7836 								(knots[n]._x + x[n - 1]) / 2,
       
  7837 								(knots[n]._y + y[n - 1]) / 2);
       
  7838 				}
       
  7839 			}
       
  7840 			if (closed && handleIn) {
       
  7841 				var segment = this._segments[0];
       
  7842 				segment.setHandleIn(handleIn.subtract(segment._point));
       
  7843 			}
       
  7844 		}
       
  7845 	};
       
  7846 }, new function() {
       
  7847 	function getCurrentSegment(that) {
       
  7848 		var segments = that._segments;
       
  7849 		if (segments.length === 0)
       
  7850 			throw new Error('Use a moveTo() command first');
       
  7851 		return segments[segments.length - 1];
       
  7852 	}
       
  7853 
       
  7854 	return {
       
  7855 		moveTo: function() {
       
  7856 			var segments = this._segments;
       
  7857 			if (segments.length === 1)
       
  7858 				this.removeSegment(0);
       
  7859 			if (!segments.length)
       
  7860 				this._add([ new Segment(Point.read(arguments)) ]);
       
  7861 		},
       
  7862 
       
  7863 		moveBy: function() {
       
  7864 			throw new Error('moveBy() is unsupported on Path items.');
       
  7865 		},
       
  7866 
       
  7867 		lineTo: function() {
       
  7868 			this._add([ new Segment(Point.read(arguments)) ]);
       
  7869 		},
       
  7870 
       
  7871 		cubicCurveTo: function() {
       
  7872 			var handle1 = Point.read(arguments),
       
  7873 				handle2 = Point.read(arguments),
       
  7874 				to = Point.read(arguments),
       
  7875 				current = getCurrentSegment(this);
       
  7876 			current.setHandleOut(handle1.subtract(current._point));
       
  7877 			this._add([ new Segment(to, handle2.subtract(to)) ]);
       
  7878 		},
       
  7879 
       
  7880 		quadraticCurveTo: function() {
       
  7881 			var handle = Point.read(arguments),
       
  7882 				to = Point.read(arguments),
       
  7883 				current = getCurrentSegment(this)._point;
       
  7884 			this.cubicCurveTo(
       
  7885 				handle.add(current.subtract(handle).multiply(1 / 3)),
       
  7886 				handle.add(to.subtract(handle).multiply(1 / 3)),
       
  7887 				to
       
  7888 			);
       
  7889 		},
       
  7890 
       
  7891 		curveTo: function() {
       
  7892 			var through = Point.read(arguments),
       
  7893 				to = Point.read(arguments),
       
  7894 				t = Base.pick(Base.read(arguments), 0.5),
       
  7895 				t1 = 1 - t,
       
  7896 				current = getCurrentSegment(this)._point,
       
  7897 				handle = through.subtract(current.multiply(t1 * t1))
       
  7898 					.subtract(to.multiply(t * t)).divide(2 * t * t1);
       
  7899 			if (handle.isNaN())
       
  7900 				throw new Error(
       
  7901 					'Cannot put a curve through points with parameter = ' + t);
       
  7902 			this.quadraticCurveTo(handle, to);
       
  7903 		},
       
  7904 
       
  7905 		arcTo: function() {
       
  7906 			var current = getCurrentSegment(this),
       
  7907 				from = current._point,
       
  7908 				to = Point.read(arguments),
       
  7909 				through,
       
  7910 				peek = Base.peek(arguments),
       
  7911 				clockwise = Base.pick(peek, true),
       
  7912 				center, extent, vector, matrix;
       
  7913 			if (typeof clockwise === 'boolean') {
       
  7914 				var middle = from.add(to).divide(2),
       
  7915 				through = middle.add(middle.subtract(from).rotate(
       
  7916 						clockwise ? -90 : 90));
       
  7917 			} else if (Base.remain(arguments) <= 2) {
       
  7918 				through = to;
       
  7919 				to = Point.read(arguments);
       
  7920 			} else {
       
  7921 				var radius = Size.read(arguments);
       
  7922 				if (radius.isZero())
       
  7923 					return this.lineTo(to);
       
  7924 				var rotation = Base.read(arguments),
       
  7925 					clockwise = !!Base.read(arguments),
       
  7926 					large = !!Base.read(arguments),
       
  7927 					middle = from.add(to).divide(2),
       
  7928 					pt = from.subtract(middle).rotate(-rotation),
       
  7929 					x = pt.x,
       
  7930 					y = pt.y,
       
  7931 					abs = Math.abs,
       
  7932 					epsilon = 1e-12,
       
  7933 					rx = abs(radius.width),
       
  7934 					ry = abs(radius.height),
       
  7935 					rxSq = rx * rx,
       
  7936 					rySq = ry * ry,
       
  7937 					xSq =  x * x,
       
  7938 					ySq =  y * y;
       
  7939 				var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
       
  7940 				if (factor > 1) {
       
  7941 					rx *= factor;
       
  7942 					ry *= factor;
       
  7943 					rxSq = rx * rx;
       
  7944 					rySq = ry * ry;
       
  7945 				}
       
  7946 				factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
       
  7947 						(rxSq * ySq + rySq * xSq);
       
  7948 				if (abs(factor) < epsilon)
       
  7949 					factor = 0;
       
  7950 				if (factor < 0)
       
  7951 					throw new Error(
       
  7952 							'Cannot create an arc with the given arguments');
       
  7953 				center = new Point(rx * y / ry, -ry * x / rx)
       
  7954 						.multiply((large === clockwise ? -1 : 1)
       
  7955 							* Math.sqrt(factor))
       
  7956 						.rotate(rotation).add(middle);
       
  7957 				matrix = new Matrix().translate(center).rotate(rotation)
       
  7958 						.scale(rx, ry);
       
  7959 				vector = matrix._inverseTransform(from);
       
  7960 				extent = vector.getDirectedAngle(matrix._inverseTransform(to));
       
  7961 				if (!clockwise && extent > 0)
       
  7962 					extent -= 360;
       
  7963 				else if (clockwise && extent < 0)
       
  7964 					extent += 360;
       
  7965 			}
       
  7966 			if (through) {
       
  7967 				var l1 = new Line(from.add(through).divide(2),
       
  7968 							through.subtract(from).rotate(90), true),
       
  7969 					l2 = new Line(through.add(to).divide(2),
       
  7970 							to.subtract(through).rotate(90), true),
       
  7971 					line = new Line(from, to),
       
  7972 					throughSide = line.getSide(through);
       
  7973 				center = l1.intersect(l2, true);
       
  7974 				if (!center) {
       
  7975 					if (!throughSide)
       
  7976 						return this.lineTo(to);
       
  7977 					throw new Error(
       
  7978 							'Cannot create an arc with the given arguments');
       
  7979 				}
       
  7980 				vector = from.subtract(center);
       
  7981 				extent = vector.getDirectedAngle(to.subtract(center));
       
  7982 				var centerSide = line.getSide(center);
       
  7983 				if (centerSide === 0) {
       
  7984 					extent = throughSide * Math.abs(extent);
       
  7985 				} else if (throughSide === centerSide) {
       
  7986 					extent += extent < 0 ? 360 : -360;
       
  7987 				}
       
  7988 			}
       
  7989 			var ext = Math.abs(extent),
       
  7990 				count = ext >= 360 ? 4 : Math.ceil(ext / 90),
       
  7991 				inc = extent / count,
       
  7992 				half = inc * Math.PI / 360,
       
  7993 				z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
       
  7994 				segments = [];
       
  7995 			for (var i = 0; i <= count; i++) {
       
  7996 				var pt = to,
       
  7997 					out = null;
       
  7998 				if (i < count) {
       
  7999 					out = vector.rotate(90).multiply(z);
       
  8000 					if (matrix) {
       
  8001 						pt = matrix._transformPoint(vector);
       
  8002 						out = matrix._transformPoint(vector.add(out))
       
  8003 								.subtract(pt);
       
  8004 					} else {
       
  8005 						pt = center.add(vector);
       
  8006 					}
       
  8007 				}
       
  8008 				if (i === 0) {
       
  8009 					current.setHandleOut(out);
       
  8010 				} else {
       
  8011 					var _in = vector.rotate(-90).multiply(z);
       
  8012 					if (matrix) {
       
  8013 						_in = matrix._transformPoint(vector.add(_in))
       
  8014 								.subtract(pt);
       
  8015 					}
       
  8016 					segments.push(new Segment(pt, _in, out));
       
  8017 				}
       
  8018 				vector = vector.rotate(inc);
       
  8019 			}
       
  8020 			this._add(segments);
       
  8021 		},
       
  8022 
       
  8023 		lineBy: function() {
       
  8024 			var to = Point.read(arguments),
       
  8025 				current = getCurrentSegment(this)._point;
       
  8026 			this.lineTo(current.add(to));
       
  8027 		},
       
  8028 
       
  8029 		curveBy: function() {
       
  8030 			var through = Point.read(arguments),
       
  8031 				to = Point.read(arguments),
       
  8032 				parameter = Base.read(arguments),
       
  8033 				current = getCurrentSegment(this)._point;
       
  8034 			this.curveTo(current.add(through), current.add(to), parameter);
       
  8035 		},
       
  8036 
       
  8037 		cubicCurveBy: function() {
       
  8038 			var handle1 = Point.read(arguments),
       
  8039 				handle2 = Point.read(arguments),
       
  8040 				to = Point.read(arguments),
       
  8041 				current = getCurrentSegment(this)._point;
       
  8042 			this.cubicCurveTo(current.add(handle1), current.add(handle2),
       
  8043 					current.add(to));
       
  8044 		},
       
  8045 
       
  8046 		quadraticCurveBy: function() {
       
  8047 			var handle = Point.read(arguments),
       
  8048 				to = Point.read(arguments),
       
  8049 				current = getCurrentSegment(this)._point;
       
  8050 			this.quadraticCurveTo(current.add(handle), current.add(to));
       
  8051 		},
       
  8052 
       
  8053 		arcBy: function() {
       
  8054 			var current = getCurrentSegment(this)._point,
       
  8055 				point = current.add(Point.read(arguments)),
       
  8056 				clockwise = Base.pick(Base.peek(arguments), true);
       
  8057 			if (typeof clockwise === 'boolean') {
       
  8058 				this.arcTo(point, clockwise);
       
  8059 			} else {
       
  8060 				this.arcTo(point, current.add(Point.read(arguments)));
       
  8061 			}
       
  8062 		},
       
  8063 
       
  8064 		closePath: function(join) {
       
  8065 			this.setClosed(true);
       
  8066 			if (join)
       
  8067 				this.join();
       
  8068 		}
       
  8069 	};
       
  8070 }, {
       
  8071 
       
  8072 	_getBounds: function(getter, matrix) {
       
  8073 		return Path[getter](this._segments, this._closed, this.getStyle(),
       
  8074 				matrix);
       
  8075 	},
       
  8076 
       
  8077 statics: {
       
  8078 	isClockwise: function(segments) {
       
  8079 		var sum = 0;
       
  8080 		for (var i = 0, l = segments.length; i < l; i++)
       
  8081 			sum += Curve.getEdgeSum(Curve.getValues(
       
  8082 					segments[i], segments[i + 1 < l ? i + 1 : 0]));
       
  8083 		return sum > 0;
       
  8084 	},
       
  8085 
       
  8086 	getBounds: function(segments, closed, style, matrix, strokePadding) {
       
  8087 		var first = segments[0];
       
  8088 		if (!first)
       
  8089 			return new Rectangle();
       
  8090 		var coords = new Array(6),
       
  8091 			prevCoords = first._transformCoordinates(matrix, new Array(6), false),
       
  8092 			min = prevCoords.slice(0, 2),
       
  8093 			max = min.slice(),
       
  8094 			roots = new Array(2);
       
  8095 
       
  8096 		function processSegment(segment) {
       
  8097 			segment._transformCoordinates(matrix, coords, false);
       
  8098 			for (var i = 0; i < 2; i++) {
       
  8099 				Curve._addBounds(
       
  8100 					prevCoords[i],
       
  8101 					prevCoords[i + 4],
       
  8102 					coords[i + 2],
       
  8103 					coords[i],
       
  8104 					i, strokePadding ? strokePadding[i] : 0, min, max, roots);
       
  8105 			}
       
  8106 			var tmp = prevCoords;
       
  8107 			prevCoords = coords;
       
  8108 			coords = tmp;
       
  8109 		}
       
  8110 
       
  8111 		for (var i = 1, l = segments.length; i < l; i++)
       
  8112 			processSegment(segments[i]);
       
  8113 		if (closed)
       
  8114 			processSegment(first);
       
  8115 		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
       
  8116 	},
       
  8117 
       
  8118 	getStrokeBounds: function(segments, closed, style, matrix) {
       
  8119 		if (!style.hasStroke())
       
  8120 			return Path.getBounds(segments, closed, style, matrix);
       
  8121 		var length = segments.length - (closed ? 0 : 1),
       
  8122 			radius = style.getStrokeWidth() / 2,
       
  8123 			padding = Path._getPenPadding(radius, matrix),
       
  8124 			bounds = Path.getBounds(segments, closed, style, matrix, padding),
       
  8125 			join = style.getStrokeJoin(),
       
  8126 			cap = style.getStrokeCap(),
       
  8127 			miterLimit = radius * style.getMiterLimit();
       
  8128 		var joinBounds = new Rectangle(new Size(padding).multiply(2));
       
  8129 
       
  8130 		function add(point) {
       
  8131 			bounds = bounds.include(matrix
       
  8132 				? matrix._transformPoint(point, point) : point);
       
  8133 		}
       
  8134 
       
  8135 		function addRound(segment) {
       
  8136 			bounds = bounds.unite(joinBounds.setCenter(matrix
       
  8137 				? matrix._transformPoint(segment._point) : segment._point));
       
  8138 		}
       
  8139 
       
  8140 		function addJoin(segment, join) {
       
  8141 			var handleIn = segment._handleIn,
       
  8142 				handleOut = segment._handleOut;
       
  8143 			if (join === 'round' || !handleIn.isZero() && !handleOut.isZero()
       
  8144 					&& handleIn.isCollinear(handleOut)) {
       
  8145 				addRound(segment);
       
  8146 			} else {
       
  8147 				Path._addBevelJoin(segment, join, radius, miterLimit, add);
       
  8148 			}
       
  8149 		}
       
  8150 
       
  8151 		function addCap(segment, cap) {
       
  8152 			if (cap === 'round') {
       
  8153 				addRound(segment);
       
  8154 			} else {
       
  8155 				Path._addSquareCap(segment, cap, radius, add);
       
  8156 			}
       
  8157 		}
       
  8158 
       
  8159 		for (var i = 1; i < length; i++)
       
  8160 			addJoin(segments[i], join);
       
  8161 		if (closed) {
       
  8162 			addJoin(segments[0], join);
       
  8163 		} else if (length > 0) {
       
  8164 			addCap(segments[0], cap);
       
  8165 			addCap(segments[segments.length - 1], cap);
       
  8166 		}
       
  8167 		return bounds;
       
  8168 	},
       
  8169 
       
  8170 	_getPenPadding: function(radius, matrix) {
       
  8171 		if (!matrix)
       
  8172 			return [radius, radius];
       
  8173 		var mx = matrix.shiftless(),
       
  8174 			hor = mx.transform(new Point(radius, 0)),
       
  8175 			ver = mx.transform(new Point(0, radius)),
       
  8176 			phi = hor.getAngleInRadians(),
       
  8177 			a = hor.getLength(),
       
  8178 			b = ver.getLength();
       
  8179 		var sin = Math.sin(phi),
       
  8180 			cos = Math.cos(phi),
       
  8181 			tan = Math.tan(phi),
       
  8182 			tx = -Math.atan(b * tan / a),
       
  8183 			ty = Math.atan(b / (tan * a));
       
  8184 		return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
       
  8185 				Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
       
  8186 	},
       
  8187 
       
  8188 	_addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) {
       
  8189 		var curve2 = segment.getCurve(),
       
  8190 			curve1 = curve2.getPrevious(),
       
  8191 			point = curve2.getPointAt(0, true),
       
  8192 			normal1 = curve1.getNormalAt(1, true),
       
  8193 			normal2 = curve2.getNormalAt(0, true),
       
  8194 			step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
       
  8195 		normal1.setLength(step);
       
  8196 		normal2.setLength(step);
       
  8197 		if (area) {
       
  8198 			addPoint(point);
       
  8199 			addPoint(point.add(normal1));
       
  8200 		}
       
  8201 		if (join === 'miter') {
       
  8202 			var corner = new Line(
       
  8203 					point.add(normal1),
       
  8204 					new Point(-normal1.y, normal1.x), true
       
  8205 				).intersect(new Line(
       
  8206 					point.add(normal2),
       
  8207 					new Point(-normal2.y, normal2.x), true
       
  8208 				), true);
       
  8209 			if (corner && point.getDistance(corner) <= miterLimit) {
       
  8210 				addPoint(corner);
       
  8211 				if (!area)
       
  8212 					return;
       
  8213 			}
       
  8214 		}
       
  8215 		if (!area)
       
  8216 			addPoint(point.add(normal1));
       
  8217 		addPoint(point.add(normal2));
       
  8218 	},
       
  8219 
       
  8220 	_addSquareCap: function(segment, cap, radius, addPoint, area) {
       
  8221 		var point = segment._point,
       
  8222 			loc = segment.getLocation(),
       
  8223 			normal = loc.getNormal().multiply(radius);
       
  8224 		if (area) {
       
  8225 			addPoint(point.subtract(normal));
       
  8226 			addPoint(point.add(normal));
       
  8227 		}
       
  8228 		if (cap === 'square')
       
  8229 			point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90));
       
  8230 		addPoint(point.add(normal));
       
  8231 		addPoint(point.subtract(normal));
       
  8232 	},
       
  8233 
       
  8234 	getHandleBounds: function(segments, closed, style, matrix, strokePadding,
       
  8235 			joinPadding) {
       
  8236 		var coords = new Array(6),
       
  8237 			x1 = Infinity,
       
  8238 			x2 = -x1,
       
  8239 			y1 = x1,
       
  8240 			y2 = x2;
       
  8241 		for (var i = 0, l = segments.length; i < l; i++) {
       
  8242 			var segment = segments[i];
       
  8243 			segment._transformCoordinates(matrix, coords, false);
       
  8244 			for (var j = 0; j < 6; j += 2) {
       
  8245 				var padding = j === 0 ? joinPadding : strokePadding,
       
  8246 					paddingX = padding ? padding[0] : 0,
       
  8247 					paddingY = padding ? padding[1] : 0,
       
  8248 					x = coords[j],
       
  8249 					y = coords[j + 1],
       
  8250 					xn = x - paddingX,
       
  8251 					xx = x + paddingX,
       
  8252 					yn = y - paddingY,
       
  8253 					yx = y + paddingY;
       
  8254 				if (xn < x1) x1 = xn;
       
  8255 				if (xx > x2) x2 = xx;
       
  8256 				if (yn < y1) y1 = yn;
       
  8257 				if (yx > y2) y2 = yx;
       
  8258 			}
       
  8259 		}
       
  8260 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  8261 	},
       
  8262 
       
  8263 	getRoughBounds: function(segments, closed, style, matrix) {
       
  8264 		var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
       
  8265 			joinRadius = strokeRadius;
       
  8266 		if (strokeRadius > 0) {
       
  8267 			if (style.getStrokeJoin() === 'miter')
       
  8268 				joinRadius = strokeRadius * style.getMiterLimit();
       
  8269 			if (style.getStrokeCap() === 'square')
       
  8270 				joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
       
  8271 		}
       
  8272 		return Path.getHandleBounds(segments, closed, style, matrix,
       
  8273 				Path._getPenPadding(strokeRadius, matrix),
       
  8274 				Path._getPenPadding(joinRadius, matrix));
       
  8275 	}
       
  8276 }});
       
  8277 
       
  8278 Path.inject({ statics: new function() {
       
  8279 
       
  8280 	var kappa = 0.5522847498307936,
       
  8281 		ellipseSegments = [
       
  8282 			new Segment([-1, 0], [0, kappa ], [0, -kappa]),
       
  8283 			new Segment([0, -1], [-kappa, 0], [kappa, 0 ]),
       
  8284 			new Segment([1, 0], [0, -kappa], [0, kappa ]),
       
  8285 			new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
       
  8286 		];
       
  8287 
       
  8288 	function createPath(segments, closed, args) {
       
  8289 		var props = Base.getNamed(args),
       
  8290 			path = new Path(props && props.insert === false && Item.NO_INSERT);
       
  8291 		path._add(segments);
       
  8292 		path._closed = closed;
       
  8293 		return path.set(props);
       
  8294 	}
       
  8295 
       
  8296 	function createEllipse(center, radius, args) {
       
  8297 		var segments = new Array(4);
       
  8298 		for (var i = 0; i < 4; i++) {
       
  8299 			var segment = ellipseSegments[i];
       
  8300 			segments[i] = new Segment(
       
  8301 				segment._point.multiply(radius).add(center),
       
  8302 				segment._handleIn.multiply(radius),
       
  8303 				segment._handleOut.multiply(radius)
       
  8304 			);
       
  8305 		}
       
  8306 		return createPath(segments, true, args);
       
  8307 	}
       
  8308 
       
  8309 	return {
       
  8310 		Line: function() {
       
  8311 			return createPath([
       
  8312 				new Segment(Point.readNamed(arguments, 'from')),
       
  8313 				new Segment(Point.readNamed(arguments, 'to'))
       
  8314 			], false, arguments);
       
  8315 		},
       
  8316 
       
  8317 		Circle: function() {
       
  8318 			var center = Point.readNamed(arguments, 'center'),
       
  8319 				radius = Base.readNamed(arguments, 'radius');
       
  8320 			return createEllipse(center, new Size(radius), arguments);
       
  8321 		},
       
  8322 
       
  8323 		Rectangle: function() {
       
  8324 			var rect = Rectangle.readNamed(arguments, 'rectangle'),
       
  8325 				radius = Size.readNamed(arguments, 'radius', 0,
       
  8326 						{ readNull: true }),
       
  8327 				bl = rect.getBottomLeft(true),
       
  8328 				tl = rect.getTopLeft(true),
       
  8329 				tr = rect.getTopRight(true),
       
  8330 				br = rect.getBottomRight(true),
       
  8331 				segments;
       
  8332 			if (!radius || radius.isZero()) {
       
  8333 				segments = [
       
  8334 					new Segment(bl),
       
  8335 					new Segment(tl),
       
  8336 					new Segment(tr),
       
  8337 					new Segment(br)
       
  8338 				];
       
  8339 			} else {
       
  8340 				radius = Size.min(radius, rect.getSize(true).divide(2));
       
  8341 				var rx = radius.width,
       
  8342 					ry = radius.height,
       
  8343 					hx = rx * kappa,
       
  8344 					hy = ry * kappa;
       
  8345 				segments = [
       
  8346 					new Segment(bl.add(rx, 0), null, [-hx, 0]),
       
  8347 					new Segment(bl.subtract(0, ry), [0, hy]),
       
  8348 					new Segment(tl.add(0, ry), null, [0, -hy]),
       
  8349 					new Segment(tl.add(rx, 0), [-hx, 0], null),
       
  8350 					new Segment(tr.subtract(rx, 0), null, [hx, 0]),
       
  8351 					new Segment(tr.add(0, ry), [0, -hy], null),
       
  8352 					new Segment(br.subtract(0, ry), null, [0, hy]),
       
  8353 					new Segment(br.subtract(rx, 0), [hx, 0])
       
  8354 				];
       
  8355 			}
       
  8356 			return createPath(segments, true, arguments);
       
  8357 		},
       
  8358 
       
  8359 		RoundRectangle: '#Rectangle',
       
  8360 
       
  8361 		Ellipse: function() {
       
  8362 			var ellipse = Shape._readEllipse(arguments);
       
  8363 			return createEllipse(ellipse.center, ellipse.radius, arguments);
       
  8364 		},
       
  8365 
       
  8366 		Oval: '#Ellipse',
       
  8367 
       
  8368 		Arc: function() {
       
  8369 			var from = Point.readNamed(arguments, 'from'),
       
  8370 				through = Point.readNamed(arguments, 'through'),
       
  8371 				to = Point.readNamed(arguments, 'to'),
       
  8372 				props = Base.getNamed(arguments),
       
  8373 				path = new Path(props && props.insert === false
       
  8374 						&& Item.NO_INSERT);
       
  8375 			path.moveTo(from);
       
  8376 			path.arcTo(through, to);
       
  8377 			return path.set(props);
       
  8378 		},
       
  8379 
       
  8380 		RegularPolygon: function() {
       
  8381 			var center = Point.readNamed(arguments, 'center'),
       
  8382 				sides = Base.readNamed(arguments, 'sides'),
       
  8383 				radius = Base.readNamed(arguments, 'radius'),
       
  8384 				step = 360 / sides,
       
  8385 				three = !(sides % 3),
       
  8386 				vector = new Point(0, three ? -radius : radius),
       
  8387 				offset = three ? -1 : 0.5,
       
  8388 				segments = new Array(sides);
       
  8389 			for (var i = 0; i < sides; i++)
       
  8390 				segments[i] = new Segment(center.add(
       
  8391 					vector.rotate((i + offset) * step)));
       
  8392 			return createPath(segments, true, arguments);
       
  8393 		},
       
  8394 
       
  8395 		Star: function() {
       
  8396 			var center = Point.readNamed(arguments, 'center'),
       
  8397 				points = Base.readNamed(arguments, 'points') * 2,
       
  8398 				radius1 = Base.readNamed(arguments, 'radius1'),
       
  8399 				radius2 = Base.readNamed(arguments, 'radius2'),
       
  8400 				step = 360 / points,
       
  8401 				vector = new Point(0, -1),
       
  8402 				segments = new Array(points);
       
  8403 			for (var i = 0; i < points; i++)
       
  8404 				segments[i] = new Segment(center.add(vector.rotate(step * i)
       
  8405 						.multiply(i % 2 ? radius2 : radius1)));
       
  8406 			return createPath(segments, true, arguments);
       
  8407 		}
       
  8408 	};
       
  8409 }});
       
  8410 
       
  8411 var CompoundPath = PathItem.extend({
       
  8412 	_class: 'CompoundPath',
       
  8413 	_serializeFields: {
       
  8414 		children: []
       
  8415 	},
       
  8416 
       
  8417 	initialize: function CompoundPath(arg) {
       
  8418 		this._children = [];
       
  8419 		this._namedChildren = {};
       
  8420 		if (!this._initialize(arg)) {
       
  8421 			if (typeof arg === 'string') {
       
  8422 				this.setPathData(arg);
       
  8423 			} else {
       
  8424 				this.addChildren(Array.isArray(arg) ? arg : arguments);
       
  8425 			}
       
  8426 		}
       
  8427 	},
       
  8428 
       
  8429 	insertChildren: function insertChildren(index, items, _preserve) {
       
  8430 		items = insertChildren.base.call(this, index, items, _preserve, Path);
       
  8431 		for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
       
  8432 			var item = items[i];
       
  8433 			if (item._clockwise === undefined)
       
  8434 				item.setClockwise(item._index === 0);
       
  8435 		}
       
  8436 		return items;
       
  8437 	},
       
  8438 
       
  8439 	reverse: function() {
       
  8440 		var children = this._children;
       
  8441 		for (var i = 0, l = children.length; i < l; i++)
       
  8442 			children[i].reverse();
       
  8443 	},
       
  8444 
       
  8445 	smooth: function() {
       
  8446 		for (var i = 0, l = this._children.length; i < l; i++)
       
  8447 			this._children[i].smooth();
       
  8448 	},
       
  8449 
       
  8450 	reduce: function reduce() {
       
  8451 		if (this._children.length === 0) {
       
  8452 			var path = new Path(Item.NO_INSERT);
       
  8453 			path.insertAbove(this);
       
  8454 			path.setStyle(this._style);
       
  8455 			this.remove();
       
  8456 			return path;
       
  8457 		} else {
       
  8458 			return reduce.base.call(this);
       
  8459 		}
       
  8460 	},
       
  8461 
       
  8462 	isClockwise: function() {
       
  8463 		var child = this.getFirstChild();
       
  8464 		return child && child.isClockwise();
       
  8465 	},
       
  8466 
       
  8467 	setClockwise: function(clockwise) {
       
  8468 		if (this.isClockwise() !== !!clockwise)
       
  8469 			this.reverse();
       
  8470 	},
       
  8471 
       
  8472 	getFirstSegment: function() {
       
  8473 		var first = this.getFirstChild();
       
  8474 		return first && first.getFirstSegment();
       
  8475 	},
       
  8476 
       
  8477 	getLastSegment: function() {
       
  8478 		var last = this.getLastChild();
       
  8479 		return last && last.getLastSegment();
       
  8480 	},
       
  8481 
       
  8482 	getCurves: function() {
       
  8483 		var children = this._children,
       
  8484 			curves = [];
       
  8485 		for (var i = 0, l = children.length; i < l; i++)
       
  8486 			curves.push.apply(curves, children[i].getCurves());
       
  8487 		return curves;
       
  8488 	},
       
  8489 
       
  8490 	getFirstCurve: function() {
       
  8491 		var first = this.getFirstChild();
       
  8492 		return first && first.getFirstCurve();
       
  8493 	},
       
  8494 
       
  8495 	getLastCurve: function() {
       
  8496 		var last = this.getLastChild();
       
  8497 		return last && last.getFirstCurve();
       
  8498 	},
       
  8499 
       
  8500 	getArea: function() {
       
  8501 		var children = this._children,
       
  8502 			area = 0;
       
  8503 		for (var i = 0, l = children.length; i < l; i++)
       
  8504 			area += children[i].getArea();
       
  8505 		return area;
       
  8506 	}
       
  8507 }, {
       
  8508 	beans: true,
       
  8509 
       
  8510 	getPathData: function(_matrix, _precision) {
       
  8511 		var children = this._children,
       
  8512 			paths = [];
       
  8513 		for (var i = 0, l = children.length; i < l; i++) {
       
  8514 			var child = children[i],
       
  8515 				mx = child._matrix;
       
  8516 			paths.push(child.getPathData(_matrix && !mx.isIdentity()
       
  8517 					? _matrix.chain(mx) : mx, _precision));
       
  8518 		}
       
  8519 		return paths.join(' ');
       
  8520 	}
       
  8521 }, {
       
  8522 	_getChildHitTestOptions: function(options) {
       
  8523 		return options.class === Path || options.type === 'path'
       
  8524 				? options
       
  8525 				: new Base(options, { fill: false });
       
  8526 	},
       
  8527 
       
  8528 	_draw: function(ctx, param, strokeMatrix) {
       
  8529 		var children = this._children;
       
  8530 		if (children.length === 0)
       
  8531 			return;
       
  8532 
       
  8533 		if (this._currentPath) {
       
  8534 			ctx.currentPath = this._currentPath;
       
  8535 		} else {
       
  8536 			param = param.extend({ dontStart: true, dontFinish: true });
       
  8537 			ctx.beginPath();
       
  8538 			for (var i = 0, l = children.length; i < l; i++)
       
  8539 				children[i].draw(ctx, param, strokeMatrix);
       
  8540 			this._currentPath = ctx.currentPath;
       
  8541 		}
       
  8542 
       
  8543 		if (!param.clip) {
       
  8544 			this._setStyles(ctx);
       
  8545 			var style = this._style;
       
  8546 			if (style.hasFill()) {
       
  8547 				ctx.fill(style.getWindingRule());
       
  8548 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  8549 			}
       
  8550 			if (style.hasStroke())
       
  8551 				ctx.stroke();
       
  8552 		}
       
  8553 	},
       
  8554 
       
  8555 	_drawSelected: function(ctx, matrix, selectedItems) {
       
  8556 		var children = this._children;
       
  8557 		for (var i = 0, l = children.length; i < l; i++) {
       
  8558 			var child = children[i],
       
  8559 				mx = child._matrix;
       
  8560 			if (!selectedItems[child._id])
       
  8561 				child._drawSelected(ctx, mx.isIdentity() ? matrix
       
  8562 						: matrix.chain(mx));
       
  8563 		}
       
  8564 	}
       
  8565 }, new function() {
       
  8566 	function getCurrentPath(that, check) {
       
  8567 		var children = that._children;
       
  8568 		if (check && children.length === 0)
       
  8569 			throw new Error('Use a moveTo() command first');
       
  8570 		return children[children.length - 1];
       
  8571 	}
       
  8572 
       
  8573 	var fields = {
       
  8574 		moveTo: function() {
       
  8575 			var current = getCurrentPath(this),
       
  8576 				path = current && current.isEmpty() ? current : new Path();
       
  8577 			if (path !== current)
       
  8578 				this.addChild(path);
       
  8579 			path.moveTo.apply(path, arguments);
       
  8580 		},
       
  8581 
       
  8582 		moveBy: function() {
       
  8583 			var current = getCurrentPath(this, true),
       
  8584 				last = current && current.getLastSegment(),
       
  8585 				point = Point.read(arguments);
       
  8586 			this.moveTo(last ? point.add(last._point) : point);
       
  8587 		},
       
  8588 
       
  8589 		closePath: function(join) {
       
  8590 			getCurrentPath(this, true).closePath(join);
       
  8591 		}
       
  8592 	};
       
  8593 
       
  8594 	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
       
  8595 			'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
       
  8596 			function(key) {
       
  8597 				fields[key] = function() {
       
  8598 					var path = getCurrentPath(this, true);
       
  8599 					path[key].apply(path, arguments);
       
  8600 				};
       
  8601 			}
       
  8602 	);
       
  8603 
       
  8604 	return fields;
       
  8605 });
       
  8606 
       
  8607 PathItem.inject(new function() {
       
  8608 	var operators = {
       
  8609 		unite: function(w) {
       
  8610 			return w === 1 || w === 0;
       
  8611 		},
       
  8612 
       
  8613 		intersect: function(w) {
       
  8614 			return w === 2;
       
  8615 		},
       
  8616 
       
  8617 		subtract: function(w) {
       
  8618 			return w === 1;
       
  8619 		},
       
  8620 
       
  8621 		exclude: function(w) {
       
  8622 			return w === 1;
       
  8623 		}
       
  8624 	};
       
  8625 
       
  8626 	function computeBoolean(path1, path2, operation) {
       
  8627 		var operator = operators[operation];
       
  8628 		function preparePath(path) {
       
  8629 			return path.clone(false).reduce().reorient().transform(null, true,
       
  8630 					true);
       
  8631 		}
       
  8632 
       
  8633 		var _path1 = preparePath(path1),
       
  8634 			_path2 = path2 && path1 !== path2 && preparePath(path2);
       
  8635 		if (_path2 && /^(subtract|exclude)$/.test(operation)
       
  8636 				^ (_path2.isClockwise() !== _path1.isClockwise()))
       
  8637 			_path2.reverse();
       
  8638 		splitPath(_path1.getIntersections(_path2, null, true));
       
  8639 
       
  8640 		var chain = [],
       
  8641 			segments = [],
       
  8642 			monoCurves = [],
       
  8643 			tolerance = 0.000001;
       
  8644 
       
  8645 		function collect(paths) {
       
  8646 			for (var i = 0, l = paths.length; i < l; i++) {
       
  8647 				var path = paths[i];
       
  8648 				segments.push.apply(segments, path._segments);
       
  8649 				monoCurves.push.apply(monoCurves, path._getMonoCurves());
       
  8650 			}
       
  8651 		}
       
  8652 
       
  8653 		collect(_path1._children || [_path1]);
       
  8654 		if (_path2)
       
  8655 			collect(_path2._children || [_path2]);
       
  8656 		segments.sort(function(a, b) {
       
  8657 			var _a = a._intersection,
       
  8658 				_b = b._intersection;
       
  8659 			return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1;
       
  8660 		});
       
  8661 		for (var i = 0, l = segments.length; i < l; i++) {
       
  8662 			var segment = segments[i];
       
  8663 			if (segment._winding != null)
       
  8664 				continue;
       
  8665 			chain.length = 0;
       
  8666 			var startSeg = segment,
       
  8667 				totalLength = 0,
       
  8668 				windingSum = 0;
       
  8669 			do {
       
  8670 				var length = segment.getCurve().getLength();
       
  8671 				chain.push({ segment: segment, length: length });
       
  8672 				totalLength += length;
       
  8673 				segment = segment.getNext();
       
  8674 			} while (segment && !segment._intersection && segment !== startSeg);
       
  8675 			for (var j = 0; j < 3; j++) {
       
  8676 				var length = totalLength * (j + 1) / 4;
       
  8677 				for (var k = 0, m = chain.length; k < m; k++) {
       
  8678 					var node = chain[k],
       
  8679 						curveLength = node.length;
       
  8680 					if (length <= curveLength) {
       
  8681 						if (length < tolerance
       
  8682 								|| curveLength - length < tolerance)
       
  8683 							length = curveLength / 2;
       
  8684 						var curve = node.segment.getCurve(),
       
  8685 							pt = curve.getPointAt(length),
       
  8686 							hor = curve.isLinear() && Math.abs(curve
       
  8687 									.getTangentAt(0.5, true).y) < tolerance,
       
  8688 							path = curve._path;
       
  8689 						if (path._parent instanceof CompoundPath)
       
  8690 							path = path._parent;
       
  8691 						windingSum += operation === 'subtract' && _path2
       
  8692 							&& (path === _path1 && _path2._getWinding(pt, hor)
       
  8693 							|| path === _path2 && !_path1._getWinding(pt, hor))
       
  8694 							? 0
       
  8695 							: getWinding(pt, monoCurves, hor);
       
  8696 						break;
       
  8697 					}
       
  8698 					length -= curveLength;
       
  8699 				}
       
  8700 			}
       
  8701 			var winding = Math.round(windingSum / 3);
       
  8702 			for (var j = chain.length - 1; j >= 0; j--)
       
  8703 				chain[j].segment._winding = winding;
       
  8704 		}
       
  8705 		var result = new CompoundPath(Item.NO_INSERT);
       
  8706 		result.insertAbove(path1);
       
  8707 		result.addChildren(tracePaths(segments, operator), true);
       
  8708 		result = result.reduce();
       
  8709 		result.setStyle(path1._style);
       
  8710 		return result;
       
  8711 	}
       
  8712 
       
  8713 	function splitPath(intersections) {
       
  8714 		var tMin = 0.000001,
       
  8715 			tMax = 1 - tMin,
       
  8716 			linearHandles;
       
  8717 
       
  8718 		function resetLinear() {
       
  8719 			for (var i = 0, l = linearHandles.length; i < l; i++)
       
  8720 				linearHandles[i].set(0, 0);
       
  8721 		}
       
  8722 
       
  8723 		for (var i = intersections.length - 1, curve, prev; i >= 0; i--) {
       
  8724 			var loc = intersections[i],
       
  8725 				t = loc._parameter;
       
  8726 			if (prev && prev._curve === loc._curve && prev._parameter > 0) {
       
  8727 				t /= prev._parameter;
       
  8728 			} else {
       
  8729 				curve = loc._curve;
       
  8730 				if (linearHandles)
       
  8731 					resetLinear();
       
  8732 				linearHandles = curve.isLinear() ? [
       
  8733 						curve._segment1._handleOut,
       
  8734 						curve._segment2._handleIn
       
  8735 					] : null;
       
  8736 			}
       
  8737 			var newCurve,
       
  8738 				segment;
       
  8739 			if (newCurve = curve.divide(t, true, true)) {
       
  8740 				segment = newCurve._segment1;
       
  8741 				curve = newCurve.getPrevious();
       
  8742 				if (linearHandles)
       
  8743 					linearHandles.push(segment._handleOut, segment._handleIn);
       
  8744 			} else {
       
  8745 				segment = t < tMin
       
  8746 					? curve._segment1
       
  8747 					: t > tMax
       
  8748 						? curve._segment2
       
  8749 						: curve.getPartLength(0, t) < curve.getPartLength(t, 1)
       
  8750 							? curve._segment1
       
  8751 							: curve._segment2;
       
  8752 			}
       
  8753 			segment._intersection = loc.getIntersection();
       
  8754 			loc._segment = segment;
       
  8755 			prev = loc;
       
  8756 		}
       
  8757 		if (linearHandles)
       
  8758 			resetLinear();
       
  8759 	}
       
  8760 
       
  8761 	function getWinding(point, curves, horizontal, testContains) {
       
  8762 		var tolerance = 0.000001,
       
  8763 			tMin = tolerance,
       
  8764 			tMax = 1 - tMin,
       
  8765 			px = point.x,
       
  8766 			py = point.y,
       
  8767 			windLeft = 0,
       
  8768 			windRight = 0,
       
  8769 			roots = [],
       
  8770 			abs = Math.abs;
       
  8771 		if (horizontal) {
       
  8772 			var yTop = -Infinity,
       
  8773 				yBottom = Infinity,
       
  8774 				yBefore = py - tolerance,
       
  8775 				yAfter = py + tolerance;
       
  8776 			for (var i = 0, l = curves.length; i < l; i++) {
       
  8777 				var values = curves[i].values;
       
  8778 				if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) {
       
  8779 					for (var j = roots.length - 1; j >= 0; j--) {
       
  8780 						var y = Curve.getPoint(values, roots[j]).y;
       
  8781 						if (y < yBefore && y > yTop) {
       
  8782 							yTop = y;
       
  8783 						} else if (y > yAfter && y < yBottom) {
       
  8784 							yBottom = y;
       
  8785 						}
       
  8786 					}
       
  8787 				}
       
  8788 			}
       
  8789 			yTop = (yTop + py) / 2;
       
  8790 			yBottom = (yBottom + py) / 2;
       
  8791 			if (yTop > -Infinity)
       
  8792 				windLeft = getWinding(new Point(px, yTop), curves);
       
  8793 			if (yBottom < Infinity)
       
  8794 				windRight = getWinding(new Point(px, yBottom), curves);
       
  8795 		} else {
       
  8796 			var xBefore = px - tolerance,
       
  8797 				xAfter = px + tolerance;
       
  8798 			var startCounted = false,
       
  8799 				prevCurve,
       
  8800 				prevT;
       
  8801 			for (var i = 0, l = curves.length; i < l; i++) {
       
  8802 				var curve = curves[i],
       
  8803 					values = curve.values,
       
  8804 					winding = curve.winding;
       
  8805 				if (winding && (winding === 1
       
  8806 						&& py >= values[1] && py <= values[7]
       
  8807 						|| py >= values[7] && py <= values[1])
       
  8808 					&& Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) {
       
  8809 					var t = roots[0];
       
  8810 					if (!(
       
  8811 						t > tMax && startCounted && curve.next !== curves[i + 1]
       
  8812 						|| t < tMin && prevT > tMax
       
  8813 							&& curve.previous === prevCurve)) {
       
  8814 						var x = Curve.getPoint(values, t).x,
       
  8815 							slope = Curve.getTangent(values, t).y,
       
  8816 							counted = false;
       
  8817 						if (Numerical.isZero(slope) && !Curve.isLinear(values)
       
  8818 								|| t < tMin && slope * Curve.getTangent(
       
  8819 									curve.previous.values, 1).y < 0
       
  8820 								|| t > tMax && slope * Curve.getTangent(
       
  8821 									curve.next.values, 0).y < 0) {
       
  8822 							if (testContains && x >= xBefore && x <= xAfter) {
       
  8823 								++windLeft;
       
  8824 								++windRight;
       
  8825 								counted = true;
       
  8826 							}
       
  8827 						} else if (x <= xBefore) {
       
  8828 							windLeft += winding;
       
  8829 							counted = true;
       
  8830 						} else if (x >= xAfter) {
       
  8831 							windRight += winding;
       
  8832 							counted = true;
       
  8833 						}
       
  8834 						if (curve.previous !== curves[i - 1])
       
  8835 							startCounted = t < tMin && counted;
       
  8836 					}
       
  8837 					prevCurve = curve;
       
  8838 					prevT = t;
       
  8839 				}
       
  8840 			}
       
  8841 		}
       
  8842 		return Math.max(abs(windLeft), abs(windRight));
       
  8843 	}
       
  8844 
       
  8845 	function tracePaths(segments, operator, selfOp) {
       
  8846 		var paths = [],
       
  8847 			tMin = 0.000001,
       
  8848 			tMax = 1 - tMin;
       
  8849 		for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) {
       
  8850 			seg = startSeg = segments[i];
       
  8851 			if (seg._visited || !operator(seg._winding))
       
  8852 				continue;
       
  8853 			var path = new Path(Item.NO_INSERT),
       
  8854 				inter = seg._intersection,
       
  8855 				startInterSeg = inter && inter._segment,
       
  8856 				added = false,
       
  8857 				dir = 1;
       
  8858 			do {
       
  8859 				var handleIn = dir > 0 ? seg._handleIn : seg._handleOut,
       
  8860 					handleOut = dir > 0 ? seg._handleOut : seg._handleIn,
       
  8861 					interSeg;
       
  8862 				if (added && (!operator(seg._winding) || selfOp)
       
  8863 						&& (inter = seg._intersection)
       
  8864 						&& (interSeg = inter._segment)
       
  8865 						&& interSeg !== startSeg) {
       
  8866 					if (selfOp) {
       
  8867 						seg._visited = interSeg._visited;
       
  8868 						seg = interSeg;
       
  8869 						dir = 1;
       
  8870 					} else {
       
  8871 						var c1 = seg.getCurve();
       
  8872 						if (dir > 0)
       
  8873 							c1 = c1.getPrevious();
       
  8874 						var t1 = c1.getTangentAt(dir < 1 ? tMin : tMax, true),
       
  8875 							c4 = interSeg.getCurve(),
       
  8876 							c3 = c4.getPrevious(),
       
  8877 							t3 = c3.getTangentAt(tMax, true),
       
  8878 							t4 = c4.getTangentAt(tMin, true),
       
  8879 							w3 = t1.cross(t3),
       
  8880 							w4 = t1.cross(t4);
       
  8881 						if (w3 * w4 !== 0) {
       
  8882 							var curve = w3 < w4 ? c3 : c4,
       
  8883 								nextCurve = operator(curve._segment1._winding)
       
  8884 									? curve
       
  8885 									: w3 < w4 ? c4 : c3,
       
  8886 								nextSeg = nextCurve._segment1;
       
  8887 							dir = nextCurve === c3 ? -1 : 1;
       
  8888 							if (nextSeg._visited && seg._path !== nextSeg._path
       
  8889 										|| !operator(nextSeg._winding)) {
       
  8890 								dir = 1;
       
  8891 							} else {
       
  8892 								seg._visited = interSeg._visited;
       
  8893 								seg = interSeg;
       
  8894 								if (nextSeg._visited)
       
  8895 									dir = 1;
       
  8896 							}
       
  8897 						} else {
       
  8898 							dir = 1;
       
  8899 						}
       
  8900 					}
       
  8901 					handleOut = dir > 0 ? seg._handleOut : seg._handleIn;
       
  8902 				}
       
  8903 				path.add(new Segment(seg._point, added && handleIn, handleOut));
       
  8904 				added = true;
       
  8905 				seg._visited = true;
       
  8906 				seg = dir > 0 ? seg.getNext() : seg. getPrevious();
       
  8907 			} while (seg && !seg._visited
       
  8908 					&& seg !== startSeg && seg !== startInterSeg
       
  8909 					&& (seg._intersection || operator(seg._winding)));
       
  8910 			if (seg && (seg === startSeg || seg === startInterSeg)) {
       
  8911 				path.firstSegment.setHandleIn((seg === startInterSeg
       
  8912 						? startInterSeg : seg)._handleIn);
       
  8913 				path.setClosed(true);
       
  8914 			} else {
       
  8915 				path.lastSegment._handleOut.set(0, 0);
       
  8916 			}
       
  8917 			if (path._segments.length >
       
  8918 					(path._closed ? path.isLinear() ? 2 : 0 : 1))
       
  8919 				paths.push(path);
       
  8920 		}
       
  8921 		return paths;
       
  8922 	}
       
  8923 
       
  8924 	return {
       
  8925 		_getWinding: function(point, horizontal, testContains) {
       
  8926 			return getWinding(point, this._getMonoCurves(),
       
  8927 					horizontal, testContains);
       
  8928 		},
       
  8929 
       
  8930 		unite: function(path) {
       
  8931 			return computeBoolean(this, path, 'unite');
       
  8932 		},
       
  8933 
       
  8934 		intersect: function(path) {
       
  8935 			return computeBoolean(this, path, 'intersect');
       
  8936 		},
       
  8937 
       
  8938 		subtract: function(path) {
       
  8939 			return computeBoolean(this, path, 'subtract');
       
  8940 		},
       
  8941 
       
  8942 		exclude: function(path) {
       
  8943 			return computeBoolean(this, path, 'exclude');
       
  8944 		},
       
  8945 
       
  8946 		divide: function(path) {
       
  8947 			return new Group([this.subtract(path), this.intersect(path)]);
       
  8948 		}
       
  8949 	};
       
  8950 });
       
  8951 
       
  8952 Path.inject({
       
  8953 	_getMonoCurves: function() {
       
  8954 		var monoCurves = this._monoCurves,
       
  8955 			prevCurve;
       
  8956 
       
  8957 		function insertCurve(v) {
       
  8958 			var y0 = v[1],
       
  8959 				y1 = v[7],
       
  8960 				curve = {
       
  8961 					values: v,
       
  8962 					winding: y0 === y1
       
  8963 						? 0
       
  8964 						: y0 > y1
       
  8965 							? -1
       
  8966 							: 1,
       
  8967 					previous: prevCurve,
       
  8968 					next: null
       
  8969 				};
       
  8970 			if (prevCurve)
       
  8971 				prevCurve.next = curve;
       
  8972 			monoCurves.push(curve);
       
  8973 			prevCurve = curve;
       
  8974 		}
       
  8975 
       
  8976 		function handleCurve(v) {
       
  8977 			if (Curve.getLength(v) === 0)
       
  8978 				return;
       
  8979 			var y0 = v[1],
       
  8980 				y1 = v[3],
       
  8981 				y2 = v[5],
       
  8982 				y3 = v[7];
       
  8983 			if (Curve.isLinear(v)) {
       
  8984 				insertCurve(v);
       
  8985 			} else {
       
  8986 				var a = 3 * (y1 - y2) - y0 + y3,
       
  8987 					b = 2 * (y0 + y2) - 4 * y1,
       
  8988 					c = y1 - y0,
       
  8989 					tolerance = 0.000001,
       
  8990 					roots = [];
       
  8991 				var count = Numerical.solveQuadratic(a, b, c, roots, tolerance,
       
  8992 						1 - tolerance);
       
  8993 				if (count === 0) {
       
  8994 					insertCurve(v);
       
  8995 				} else {
       
  8996 					roots.sort();
       
  8997 					var t = roots[0],
       
  8998 						parts = Curve.subdivide(v, t);
       
  8999 					insertCurve(parts[0]);
       
  9000 					if (count > 1) {
       
  9001 						t = (roots[1] - t) / (1 - t);
       
  9002 						parts = Curve.subdivide(parts[1], t);
       
  9003 						insertCurve(parts[0]);
       
  9004 					}
       
  9005 					insertCurve(parts[1]);
       
  9006 				}
       
  9007 			}
       
  9008 		}
       
  9009 
       
  9010 		if (!monoCurves) {
       
  9011 			monoCurves = this._monoCurves = [];
       
  9012 			var curves = this.getCurves(),
       
  9013 				segments = this._segments;
       
  9014 			for (var i = 0, l = curves.length; i < l; i++)
       
  9015 				handleCurve(curves[i].getValues());
       
  9016 			if (!this._closed && segments.length > 1) {
       
  9017 				var p1 = segments[segments.length - 1]._point,
       
  9018 					p2 = segments[0]._point,
       
  9019 					p1x = p1._x, p1y = p1._y,
       
  9020 					p2x = p2._x, p2y = p2._y;
       
  9021 				handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
       
  9022 			}
       
  9023 			if (monoCurves.length > 0) {
       
  9024 				var first = monoCurves[0],
       
  9025 					last = monoCurves[monoCurves.length - 1];
       
  9026 				first.previous = last;
       
  9027 				last.next = first;
       
  9028 			}
       
  9029 		}
       
  9030 		return monoCurves;
       
  9031 	},
       
  9032 
       
  9033 	getInteriorPoint: function() {
       
  9034 		var bounds = this.getBounds(),
       
  9035 			point = bounds.getCenter(true);
       
  9036 		if (!this.contains(point)) {
       
  9037 			var curves = this._getMonoCurves(),
       
  9038 				roots = [],
       
  9039 				y = point.y,
       
  9040 				xIntercepts = [];
       
  9041 			for (var i = 0, l = curves.length; i < l; i++) {
       
  9042 				var values = curves[i].values;
       
  9043 				if ((curves[i].winding === 1
       
  9044 						&& y >= values[1] && y <= values[7]
       
  9045 						|| y >= values[7] && y <= values[1])
       
  9046 						&& Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
       
  9047 					for (var j = roots.length - 1; j >= 0; j--)
       
  9048 						xIntercepts.push(Curve.getPoint(values, roots[j]).x);
       
  9049 				}
       
  9050 				if (xIntercepts.length > 1)
       
  9051 					break;
       
  9052 			}
       
  9053 			point.x = (xIntercepts[0] + xIntercepts[1]) / 2;
       
  9054 		}
       
  9055 		return point;
       
  9056 	},
       
  9057 
       
  9058 	reorient: function() {
       
  9059 		this.setClockwise(true);
       
  9060 		return this;
       
  9061 	}
       
  9062 });
       
  9063 
       
  9064 CompoundPath.inject({
       
  9065 	_getMonoCurves: function() {
       
  9066 		var children = this._children,
       
  9067 			monoCurves = [];
       
  9068 		for (var i = 0, l = children.length; i < l; i++)
       
  9069 			monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
       
  9070 		return monoCurves;
       
  9071 	},
       
  9072 
       
  9073 	reorient: function() {
       
  9074 		var children = this.removeChildren().sort(function(a, b) {
       
  9075 			return b.getBounds().getArea() - a.getBounds().getArea();
       
  9076 		});
       
  9077 		if (children.length > 0) {
       
  9078 			this.addChildren(children);
       
  9079 			var clockwise = children[0].isClockwise();
       
  9080 			for (var i = 1, l = children.length; i < l; i++) {
       
  9081 				var point = children[i].getInteriorPoint(),
       
  9082 					counters = 0;
       
  9083 				for (var j = i - 1; j >= 0; j--) {
       
  9084 					if (children[j].contains(point))
       
  9085 						counters++;
       
  9086 				}
       
  9087 				children[i].setClockwise(counters % 2 === 0 && clockwise);
       
  9088 			}
       
  9089 		}
       
  9090 		return this;
       
  9091 	}
       
  9092 });
       
  9093 
       
  9094 var PathIterator = Base.extend({
       
  9095 	_class: 'PathIterator',
       
  9096 
       
  9097 	initialize: function(path, maxRecursion, tolerance, matrix) {
       
  9098 		var curves = [],
       
  9099 			parts = [],
       
  9100 			length = 0,
       
  9101 			minDifference = 1 / (maxRecursion || 32),
       
  9102 			segments = path._segments,
       
  9103 			segment1 = segments[0],
       
  9104 			segment2;
       
  9105 
       
  9106 		function addCurve(segment1, segment2) {
       
  9107 			var curve = Curve.getValues(segment1, segment2, matrix);
       
  9108 			curves.push(curve);
       
  9109 			computeParts(curve, segment1._index, 0, 1);
       
  9110 		}
       
  9111 
       
  9112 		function computeParts(curve, index, minT, maxT) {
       
  9113 			if ((maxT - minT) > minDifference
       
  9114 					&& !Curve.isFlatEnough(curve, tolerance || 0.25)) {
       
  9115 				var split = Curve.subdivide(curve),
       
  9116 					halfT = (minT + maxT) / 2;
       
  9117 				computeParts(split[0], index, minT, halfT);
       
  9118 				computeParts(split[1], index, halfT, maxT);
       
  9119 			} else {
       
  9120 				var x = curve[6] - curve[0],
       
  9121 					y = curve[7] - curve[1],
       
  9122 					dist = Math.sqrt(x * x + y * y);
       
  9123 				if (dist > 0.000001) {
       
  9124 					length += dist;
       
  9125 					parts.push({
       
  9126 						offset: length,
       
  9127 						value: maxT,
       
  9128 						index: index
       
  9129 					});
       
  9130 				}
       
  9131 			}
       
  9132 		}
       
  9133 
       
  9134 		for (var i = 1, l = segments.length; i < l; i++) {
       
  9135 			segment2 = segments[i];
       
  9136 			addCurve(segment1, segment2);
       
  9137 			segment1 = segment2;
       
  9138 		}
       
  9139 		if (path._closed)
       
  9140 			addCurve(segment2, segments[0]);
       
  9141 
       
  9142 		this.curves = curves;
       
  9143 		this.parts = parts;
       
  9144 		this.length = length;
       
  9145 		this.index = 0;
       
  9146 	},
       
  9147 
       
  9148 	getParameterAt: function(offset) {
       
  9149 		var i, j = this.index;
       
  9150 		for (;;) {
       
  9151 			i = j;
       
  9152 			if (j == 0 || this.parts[--j].offset < offset)
       
  9153 				break;
       
  9154 		}
       
  9155 		for (var l = this.parts.length; i < l; i++) {
       
  9156 			var part = this.parts[i];
       
  9157 			if (part.offset >= offset) {
       
  9158 				this.index = i;
       
  9159 				var prev = this.parts[i - 1];
       
  9160 				var prevVal = prev && prev.index == part.index ? prev.value : 0,
       
  9161 					prevLen = prev ? prev.offset : 0;
       
  9162 				return {
       
  9163 					value: prevVal + (part.value - prevVal)
       
  9164 						* (offset - prevLen) / (part.offset - prevLen),
       
  9165 					index: part.index
       
  9166 				};
       
  9167 			}
       
  9168 		}
       
  9169 		var part = this.parts[this.parts.length - 1];
       
  9170 		return {
       
  9171 			value: 1,
       
  9172 			index: part.index
       
  9173 		};
       
  9174 	},
       
  9175 
       
  9176 	drawPart: function(ctx, from, to) {
       
  9177 		from = this.getParameterAt(from);
       
  9178 		to = this.getParameterAt(to);
       
  9179 		for (var i = from.index; i <= to.index; i++) {
       
  9180 			var curve = Curve.getPart(this.curves[i],
       
  9181 					i == from.index ? from.value : 0,
       
  9182 					i == to.index ? to.value : 1);
       
  9183 			if (i == from.index)
       
  9184 				ctx.moveTo(curve[0], curve[1]);
       
  9185 			ctx.bezierCurveTo.apply(ctx, curve.slice(2));
       
  9186 		}
       
  9187 	}
       
  9188 }, Base.each(Curve.evaluateMethods,
       
  9189 	function(name) {
       
  9190 		this[name + 'At'] = function(offset, weighted) {
       
  9191 			var param = this.getParameterAt(offset);
       
  9192 			return Curve[name](this.curves[param.index], param.value, weighted);
       
  9193 		};
       
  9194 	}, {})
       
  9195 );
       
  9196 
       
  9197 var PathFitter = Base.extend({
       
  9198 	initialize: function(path, error) {
       
  9199 		var points = this.points = [],
       
  9200 			segments = path._segments,
       
  9201 			prev;
       
  9202 		for (var i = 0, l = segments.length; i < l; i++) {
       
  9203 			var point = segments[i].point.clone();
       
  9204 			if (!prev || !prev.equals(point)) {
       
  9205 				points.push(point);
       
  9206 				prev = point;
       
  9207 			}
       
  9208 		}
       
  9209 
       
  9210 		if (path._closed) {
       
  9211 			this.closed = true;
       
  9212 			points.unshift(points[points.length - 1]);
       
  9213 			points.push(points[1]);
       
  9214 		}
       
  9215 
       
  9216 		this.error = error;
       
  9217 	},
       
  9218 
       
  9219 	fit: function() {
       
  9220 		var points = this.points,
       
  9221 			length = points.length,
       
  9222 			segments = this.segments = length > 0
       
  9223 					? [new Segment(points[0])] : [];
       
  9224 		if (length > 1)
       
  9225 			this.fitCubic(0, length - 1,
       
  9226 				points[1].subtract(points[0]).normalize(),
       
  9227 				points[length - 2].subtract(points[length - 1]).normalize());
       
  9228 
       
  9229 		if (this.closed) {
       
  9230 			segments.shift();
       
  9231 			segments.pop();
       
  9232 		}
       
  9233 
       
  9234 		return segments;
       
  9235 	},
       
  9236 
       
  9237 	fitCubic: function(first, last, tan1, tan2) {
       
  9238 		if (last - first == 1) {
       
  9239 			var pt1 = this.points[first],
       
  9240 				pt2 = this.points[last],
       
  9241 				dist = pt1.getDistance(pt2) / 3;
       
  9242 			this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
       
  9243 					pt2.add(tan2.normalize(dist)), pt2]);
       
  9244 			return;
       
  9245 		}
       
  9246 		var uPrime = this.chordLengthParameterize(first, last),
       
  9247 			maxError = Math.max(this.error, this.error * this.error),
       
  9248 			split,
       
  9249 			parametersInOrder = true;
       
  9250 		for (var i = 0; i <= 4; i++) {
       
  9251 			var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
       
  9252 			var max = this.findMaxError(first, last, curve, uPrime);
       
  9253 			if (max.error < this.error && parametersInOrder) {
       
  9254 				this.addCurve(curve);
       
  9255 				return;
       
  9256 			}
       
  9257 			split = max.index;
       
  9258 			if (max.error >= maxError)
       
  9259 				break;
       
  9260 			parametersInOrder = this.reparameterize(first, last, uPrime, curve);
       
  9261 			maxError = max.error;
       
  9262 		}
       
  9263 		var V1 = this.points[split - 1].subtract(this.points[split]),
       
  9264 			V2 = this.points[split].subtract(this.points[split + 1]),
       
  9265 			tanCenter = V1.add(V2).divide(2).normalize();
       
  9266 		this.fitCubic(first, split, tan1, tanCenter);
       
  9267 		this.fitCubic(split, last, tanCenter.negate(), tan2);
       
  9268 	},
       
  9269 
       
  9270 	addCurve: function(curve) {
       
  9271 		var prev = this.segments[this.segments.length - 1];
       
  9272 		prev.setHandleOut(curve[1].subtract(curve[0]));
       
  9273 		this.segments.push(
       
  9274 				new Segment(curve[3], curve[2].subtract(curve[3])));
       
  9275 	},
       
  9276 
       
  9277 	generateBezier: function(first, last, uPrime, tan1, tan2) {
       
  9278 		var epsilon = 1e-12,
       
  9279 			pt1 = this.points[first],
       
  9280 			pt2 = this.points[last],
       
  9281 			C = [[0, 0], [0, 0]],
       
  9282 			X = [0, 0];
       
  9283 
       
  9284 		for (var i = 0, l = last - first + 1; i < l; i++) {
       
  9285 			var u = uPrime[i],
       
  9286 				t = 1 - u,
       
  9287 				b = 3 * u * t,
       
  9288 				b0 = t * t * t,
       
  9289 				b1 = b * t,
       
  9290 				b2 = b * u,
       
  9291 				b3 = u * u * u,
       
  9292 				a1 = tan1.normalize(b1),
       
  9293 				a2 = tan2.normalize(b2),
       
  9294 				tmp = this.points[first + i]
       
  9295 					.subtract(pt1.multiply(b0 + b1))
       
  9296 					.subtract(pt2.multiply(b2 + b3));
       
  9297 			C[0][0] += a1.dot(a1);
       
  9298 			C[0][1] += a1.dot(a2);
       
  9299 			C[1][0] = C[0][1];
       
  9300 			C[1][1] += a2.dot(a2);
       
  9301 			X[0] += a1.dot(tmp);
       
  9302 			X[1] += a2.dot(tmp);
       
  9303 		}
       
  9304 
       
  9305 		var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
       
  9306 			alpha1, alpha2;
       
  9307 		if (Math.abs(detC0C1) > epsilon) {
       
  9308 			var detC0X	= C[0][0] * X[1]	- C[1][0] * X[0],
       
  9309 				detXC1	= X[0]	  * C[1][1] - X[1]	  * C[0][1];
       
  9310 			alpha1 = detXC1 / detC0C1;
       
  9311 			alpha2 = detC0X / detC0C1;
       
  9312 		} else {
       
  9313 			var c0 = C[0][0] + C[0][1],
       
  9314 				c1 = C[1][0] + C[1][1];
       
  9315 			if (Math.abs(c0) > epsilon) {
       
  9316 				alpha1 = alpha2 = X[0] / c0;
       
  9317 			} else if (Math.abs(c1) > epsilon) {
       
  9318 				alpha1 = alpha2 = X[1] / c1;
       
  9319 			} else {
       
  9320 				alpha1 = alpha2 = 0;
       
  9321 			}
       
  9322 		}
       
  9323 
       
  9324 		var segLength = pt2.getDistance(pt1),
       
  9325 			eps = epsilon * segLength,
       
  9326 			handle1,
       
  9327 			handle2;
       
  9328 		if (alpha1 < eps || alpha2 < eps) {
       
  9329 			alpha1 = alpha2 = segLength / 3;
       
  9330 		} else {
       
  9331 			var line = pt2.subtract(pt1);
       
  9332 			handle1 = tan1.normalize(alpha1);
       
  9333 			handle2 = tan2.normalize(alpha2);
       
  9334 			if (handle1.dot(line) - handle2.dot(line) > segLength * segLength) {
       
  9335 				alpha1 = alpha2 = segLength / 3;
       
  9336 				handle1 = handle2 = null;
       
  9337 			}
       
  9338 		}
       
  9339 
       
  9340 		return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)),
       
  9341 				pt2.add(handle2 || tan2.normalize(alpha2)), pt2];
       
  9342 	},
       
  9343 
       
  9344 	reparameterize: function(first, last, u, curve) {
       
  9345 		for (var i = first; i <= last; i++) {
       
  9346 			u[i - first] = this.findRoot(curve, this.points[i], u[i - first]);
       
  9347 		}
       
  9348 		for (var i = 1, l = u.length; i < l; i++) {
       
  9349 			if (u[i] <= u[i - 1])
       
  9350 				return false;
       
  9351 		}
       
  9352 		return true;
       
  9353 	},
       
  9354 
       
  9355 	findRoot: function(curve, point, u) {
       
  9356 		var curve1 = [],
       
  9357 			curve2 = [];
       
  9358 		for (var i = 0; i <= 2; i++) {
       
  9359 			curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3);
       
  9360 		}
       
  9361 		for (var i = 0; i <= 1; i++) {
       
  9362 			curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2);
       
  9363 		}
       
  9364 		var pt = this.evaluate(3, curve, u),
       
  9365 			pt1 = this.evaluate(2, curve1, u),
       
  9366 			pt2 = this.evaluate(1, curve2, u),
       
  9367 			diff = pt.subtract(point),
       
  9368 			df = pt1.dot(pt1) + diff.dot(pt2);
       
  9369 		if (Math.abs(df) < 0.000001)
       
  9370 			return u;
       
  9371 		return u - diff.dot(pt1) / df;
       
  9372 	},
       
  9373 
       
  9374 	evaluate: function(degree, curve, t) {
       
  9375 		var tmp = curve.slice();
       
  9376 		for (var i = 1; i <= degree; i++) {
       
  9377 			for (var j = 0; j <= degree - i; j++) {
       
  9378 				tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t));
       
  9379 			}
       
  9380 		}
       
  9381 		return tmp[0];
       
  9382 	},
       
  9383 
       
  9384 	chordLengthParameterize: function(first, last) {
       
  9385 		var u = [0];
       
  9386 		for (var i = first + 1; i <= last; i++) {
       
  9387 			u[i - first] = u[i - first - 1]
       
  9388 					+ this.points[i].getDistance(this.points[i - 1]);
       
  9389 		}
       
  9390 		for (var i = 1, m = last - first; i <= m; i++) {
       
  9391 			u[i] /= u[m];
       
  9392 		}
       
  9393 		return u;
       
  9394 	},
       
  9395 
       
  9396 	findMaxError: function(first, last, curve, u) {
       
  9397 		var index = Math.floor((last - first + 1) / 2),
       
  9398 			maxDist = 0;
       
  9399 		for (var i = first + 1; i < last; i++) {
       
  9400 			var P = this.evaluate(3, curve, u[i - first]);
       
  9401 			var v = P.subtract(this.points[i]);
       
  9402 			var dist = v.x * v.x + v.y * v.y;
       
  9403 			if (dist >= maxDist) {
       
  9404 				maxDist = dist;
       
  9405 				index = i;
       
  9406 			}
       
  9407 		}
       
  9408 		return {
       
  9409 			error: maxDist,
       
  9410 			index: index
       
  9411 		};
       
  9412 	}
       
  9413 });
       
  9414 
       
  9415 var TextItem = Item.extend({
       
  9416 	_class: 'TextItem',
       
  9417 	_boundsSelected: true,
       
  9418 	_applyMatrix: false,
       
  9419 	_canApplyMatrix: false,
       
  9420 	_serializeFields: {
       
  9421 		content: null
       
  9422 	},
       
  9423 	_boundsGetter: 'getBounds',
       
  9424 
       
  9425 	initialize: function TextItem(arg) {
       
  9426 		this._content = '';
       
  9427 		this._lines = [];
       
  9428 		var hasProps = arg && Base.isPlainObject(arg)
       
  9429 				&& arg.x === undefined && arg.y === undefined;
       
  9430 		this._initialize(hasProps && arg, !hasProps && Point.read(arguments));
       
  9431 	},
       
  9432 
       
  9433 	_equals: function(item) {
       
  9434 		return this._content === item._content;
       
  9435 	},
       
  9436 
       
  9437 	_clone: function _clone(copy, insert, includeMatrix) {
       
  9438 		copy.setContent(this._content);
       
  9439 		return _clone.base.call(this, copy, insert, includeMatrix);
       
  9440 	},
       
  9441 
       
  9442 	getContent: function() {
       
  9443 		return this._content;
       
  9444 	},
       
  9445 
       
  9446 	setContent: function(content) {
       
  9447 		this._content = '' + content;
       
  9448 		this._lines = this._content.split(/\r\n|\n|\r/mg);
       
  9449 		this._changed(265);
       
  9450 	},
       
  9451 
       
  9452 	isEmpty: function() {
       
  9453 		return !this._content;
       
  9454 	},
       
  9455 
       
  9456 	getCharacterStyle: '#getStyle',
       
  9457 	setCharacterStyle: '#setStyle',
       
  9458 
       
  9459 	getParagraphStyle: '#getStyle',
       
  9460 	setParagraphStyle: '#setStyle'
       
  9461 });
       
  9462 
       
  9463 var PointText = TextItem.extend({
       
  9464 	_class: 'PointText',
       
  9465 
       
  9466 	initialize: function PointText() {
       
  9467 		TextItem.apply(this, arguments);
       
  9468 	},
       
  9469 
       
  9470 	clone: function(insert) {
       
  9471 		return this._clone(new PointText(Item.NO_INSERT), insert);
       
  9472 	},
       
  9473 
       
  9474 	getPoint: function() {
       
  9475 		var point = this._matrix.getTranslation();
       
  9476 		return new LinkedPoint(point.x, point.y, this, 'setPoint');
       
  9477 	},
       
  9478 
       
  9479 	setPoint: function() {
       
  9480 		var point = Point.read(arguments);
       
  9481 		this.translate(point.subtract(this._matrix.getTranslation()));
       
  9482 	},
       
  9483 
       
  9484 	_draw: function(ctx) {
       
  9485 		if (!this._content)
       
  9486 			return;
       
  9487 		this._setStyles(ctx);
       
  9488 		var style = this._style,
       
  9489 			lines = this._lines,
       
  9490 			leading = style.getLeading(),
       
  9491 			shadowColor = ctx.shadowColor;
       
  9492 		ctx.font = style.getFontStyle();
       
  9493 		ctx.textAlign = style.getJustification();
       
  9494 		for (var i = 0, l = lines.length; i < l; i++) {
       
  9495 			ctx.shadowColor = shadowColor;
       
  9496 			var line = lines[i];
       
  9497 			if (style.hasFill()) {
       
  9498 				ctx.fillText(line, 0, 0);
       
  9499 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  9500 			}
       
  9501 			if (style.hasStroke())
       
  9502 				ctx.strokeText(line, 0, 0);
       
  9503 			ctx.translate(0, leading);
       
  9504 		}
       
  9505 	},
       
  9506 
       
  9507 	_getBounds: function(getter, matrix) {
       
  9508 		var style = this._style,
       
  9509 			lines = this._lines,
       
  9510 			numLines = lines.length,
       
  9511 			justification = style.getJustification(),
       
  9512 			leading = style.getLeading(),
       
  9513 			width = this.getView().getTextWidth(style.getFontStyle(), lines),
       
  9514 			x = 0;
       
  9515 		if (justification !== 'left')
       
  9516 			x -= width / (justification === 'center' ? 2: 1);
       
  9517 		var bounds = new Rectangle(x,
       
  9518 					numLines ? - 0.75 * leading : 0,
       
  9519 					width, numLines * leading);
       
  9520 		return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
       
  9521 	}
       
  9522 });
       
  9523 
       
  9524 var Color = Base.extend(new function() {
       
  9525 	var types = {
       
  9526 		gray: ['gray'],
       
  9527 		rgb: ['red', 'green', 'blue'],
       
  9528 		hsb: ['hue', 'saturation', 'brightness'],
       
  9529 		hsl: ['hue', 'saturation', 'lightness'],
       
  9530 		gradient: ['gradient', 'origin', 'destination', 'highlight']
       
  9531 	};
       
  9532 
       
  9533 	var componentParsers = {},
       
  9534 		colorCache = {},
       
  9535 		colorCtx;
       
  9536 
       
  9537 	function fromCSS(string) {
       
  9538 		var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
       
  9539 			components;
       
  9540 		if (match) {
       
  9541 			components = [0, 0, 0];
       
  9542 			for (var i = 0; i < 3; i++) {
       
  9543 				var value = match[i + 1];
       
  9544 				components[i] = parseInt(value.length == 1
       
  9545 						? value + value : value, 16) / 255;
       
  9546 			}
       
  9547 		} else if (match = string.match(/^rgba?\((.*)\)$/)) {
       
  9548 			components = match[1].split(',');
       
  9549 			for (var i = 0, l = components.length; i < l; i++) {
       
  9550 				var value = +components[i];
       
  9551 				components[i] = i < 3 ? value / 255 : value;
       
  9552 			}
       
  9553 		} else {
       
  9554 			var cached = colorCache[string];
       
  9555 			if (!cached) {
       
  9556 				if (!colorCtx) {
       
  9557 					colorCtx = CanvasProvider.getContext(1, 1);
       
  9558 					colorCtx.globalCompositeOperation = 'copy';
       
  9559 				}
       
  9560 				colorCtx.fillStyle = 'rgba(0,0,0,0)';
       
  9561 				colorCtx.fillStyle = string;
       
  9562 				colorCtx.fillRect(0, 0, 1, 1);
       
  9563 				var data = colorCtx.getImageData(0, 0, 1, 1).data;
       
  9564 				cached = colorCache[string] = [
       
  9565 					data[0] / 255,
       
  9566 					data[1] / 255,
       
  9567 					data[2] / 255
       
  9568 				];
       
  9569 			}
       
  9570 			components = cached.slice();
       
  9571 		}
       
  9572 		return components;
       
  9573 	}
       
  9574 
       
  9575 	var hsbIndices = [
       
  9576 		[0, 3, 1],
       
  9577 		[2, 0, 1],
       
  9578 		[1, 0, 3],
       
  9579 		[1, 2, 0],
       
  9580 		[3, 1, 0],
       
  9581 		[0, 1, 2]
       
  9582 	];
       
  9583 
       
  9584 	var converters = {
       
  9585 		'rgb-hsb': function(r, g, b) {
       
  9586 			var max = Math.max(r, g, b),
       
  9587 				min = Math.min(r, g, b),
       
  9588 				delta = max - min,
       
  9589 				h = delta === 0 ? 0
       
  9590 					:	( max == r ? (g - b) / delta + (g < b ? 6 : 0)
       
  9591 						: max == g ? (b - r) / delta + 2
       
  9592 						:			 (r - g) / delta + 4) * 60;
       
  9593 			return [h, max === 0 ? 0 : delta / max, max];
       
  9594 		},
       
  9595 
       
  9596 		'hsb-rgb': function(h, s, b) {
       
  9597 			h = (((h / 60) % 6) + 6) % 6;
       
  9598 			var i = Math.floor(h),
       
  9599 				f = h - i,
       
  9600 				i = hsbIndices[i],
       
  9601 				v = [
       
  9602 					b,
       
  9603 					b * (1 - s),
       
  9604 					b * (1 - s * f),
       
  9605 					b * (1 - s * (1 - f))
       
  9606 				];
       
  9607 			return [v[i[0]], v[i[1]], v[i[2]]];
       
  9608 		},
       
  9609 
       
  9610 		'rgb-hsl': function(r, g, b) {
       
  9611 			var max = Math.max(r, g, b),
       
  9612 				min = Math.min(r, g, b),
       
  9613 				delta = max - min,
       
  9614 				achromatic = delta === 0,
       
  9615 				h = achromatic ? 0
       
  9616 					:	( max == r ? (g - b) / delta + (g < b ? 6 : 0)
       
  9617 						: max == g ? (b - r) / delta + 2
       
  9618 						:			 (r - g) / delta + 4) * 60,
       
  9619 				l = (max + min) / 2,
       
  9620 				s = achromatic ? 0 : l < 0.5
       
  9621 						? delta / (max + min)
       
  9622 						: delta / (2 - max - min);
       
  9623 			return [h, s, l];
       
  9624 		},
       
  9625 
       
  9626 		'hsl-rgb': function(h, s, l) {
       
  9627 			h = (((h / 360) % 1) + 1) % 1;
       
  9628 			if (s === 0)
       
  9629 				return [l, l, l];
       
  9630 			var t3s = [ h + 1 / 3, h, h - 1 / 3 ],
       
  9631 				t2 = l < 0.5 ? l * (1 + s) : l + s - l * s,
       
  9632 				t1 = 2 * l - t2,
       
  9633 				c = [];
       
  9634 			for (var i = 0; i < 3; i++) {
       
  9635 				var t3 = t3s[i];
       
  9636 				if (t3 < 0) t3 += 1;
       
  9637 				if (t3 > 1) t3 -= 1;
       
  9638 				c[i] = 6 * t3 < 1
       
  9639 					? t1 + (t2 - t1) * 6 * t3
       
  9640 					: 2 * t3 < 1
       
  9641 						? t2
       
  9642 						: 3 * t3 < 2
       
  9643 							? t1 + (t2 - t1) * ((2 / 3) - t3) * 6
       
  9644 							: t1;
       
  9645 			}
       
  9646 			return c;
       
  9647 		},
       
  9648 
       
  9649 		'rgb-gray': function(r, g, b) {
       
  9650 			return [r * 0.2989 + g * 0.587 + b * 0.114];
       
  9651 		},
       
  9652 
       
  9653 		'gray-rgb': function(g) {
       
  9654 			return [g, g, g];
       
  9655 		},
       
  9656 
       
  9657 		'gray-hsb': function(g) {
       
  9658 			return [0, 0, g];
       
  9659 		},
       
  9660 
       
  9661 		'gray-hsl': function(g) {
       
  9662 			return [0, 0, g];
       
  9663 		},
       
  9664 
       
  9665 		'gradient-rgb': function() {
       
  9666 			return [];
       
  9667 		},
       
  9668 
       
  9669 		'rgb-gradient': function() {
       
  9670 			return [];
       
  9671 		}
       
  9672 
       
  9673 	};
       
  9674 
       
  9675 	return Base.each(types, function(properties, type) {
       
  9676 		componentParsers[type] = [];
       
  9677 		Base.each(properties, function(name, index) {
       
  9678 			var part = Base.capitalize(name),
       
  9679 				hasOverlap = /^(hue|saturation)$/.test(name),
       
  9680 				parser = componentParsers[type][index] = name === 'gradient'
       
  9681 					? function(value) {
       
  9682 						var current = this._components[0];
       
  9683 						value = Gradient.read(Array.isArray(value) ? value
       
  9684 								: arguments, 0, { readNull: true });
       
  9685 						if (current !== value) {
       
  9686 							if (current)
       
  9687 								current._removeOwner(this);
       
  9688 							if (value)
       
  9689 								value._addOwner(this);
       
  9690 						}
       
  9691 						return value;
       
  9692 					}
       
  9693 					: type === 'gradient'
       
  9694 						? function() {
       
  9695 							return Point.read(arguments, 0, {
       
  9696 									readNull: name === 'highlight',
       
  9697 									clone: true
       
  9698 							});
       
  9699 						}
       
  9700 						: function(value) {
       
  9701 							return value == null || isNaN(value) ? 0 : value;
       
  9702 						};
       
  9703 
       
  9704 			this['get' + part] = function() {
       
  9705 				return this._type === type
       
  9706 					|| hasOverlap && /^hs[bl]$/.test(this._type)
       
  9707 						? this._components[index]
       
  9708 						: this._convert(type)[index];
       
  9709 			};
       
  9710 
       
  9711 			this['set' + part] = function(value) {
       
  9712 				if (this._type !== type
       
  9713 						&& !(hasOverlap && /^hs[bl]$/.test(this._type))) {
       
  9714 					this._components = this._convert(type);
       
  9715 					this._properties = types[type];
       
  9716 					this._type = type;
       
  9717 				}
       
  9718 				this._components[index] = parser.call(this, value);
       
  9719 				this._changed();
       
  9720 			};
       
  9721 		}, this);
       
  9722 	}, {
       
  9723 		_class: 'Color',
       
  9724 		_readIndex: true,
       
  9725 
       
  9726 		initialize: function Color(arg) {
       
  9727 			var slice = Array.prototype.slice,
       
  9728 				args = arguments,
       
  9729 				read = 0,
       
  9730 				type,
       
  9731 				components,
       
  9732 				alpha,
       
  9733 				values;
       
  9734 			if (Array.isArray(arg)) {
       
  9735 				args = arg;
       
  9736 				arg = args[0];
       
  9737 			}
       
  9738 			var argType = arg != null && typeof arg;
       
  9739 			if (argType === 'string' && arg in types) {
       
  9740 				type = arg;
       
  9741 				arg = args[1];
       
  9742 				if (Array.isArray(arg)) {
       
  9743 					components = arg;
       
  9744 					alpha = args[2];
       
  9745 				} else {
       
  9746 					if (this.__read)
       
  9747 						read = 1;
       
  9748 					args = slice.call(args, 1);
       
  9749 					argType = typeof arg;
       
  9750 				}
       
  9751 			}
       
  9752 			if (!components) {
       
  9753 				values = argType === 'number'
       
  9754 						? args
       
  9755 						: argType === 'object' && arg.length != null
       
  9756 							? arg
       
  9757 							: null;
       
  9758 				if (values) {
       
  9759 					if (!type)
       
  9760 						type = values.length >= 3
       
  9761 								? 'rgb'
       
  9762 								: 'gray';
       
  9763 					var length = types[type].length;
       
  9764 					alpha = values[length];
       
  9765 					if (this.__read)
       
  9766 						read += values === arguments
       
  9767 							? length + (alpha != null ? 1 : 0)
       
  9768 							: 1;
       
  9769 					if (values.length > length)
       
  9770 						values = slice.call(values, 0, length);
       
  9771 				} else if (argType === 'string') {
       
  9772 					type = 'rgb';
       
  9773 					components = fromCSS(arg);
       
  9774 					if (components.length === 4) {
       
  9775 						alpha = components[3];
       
  9776 						components.length--;
       
  9777 					}
       
  9778 				} else if (argType === 'object') {
       
  9779 					if (arg.constructor === Color) {
       
  9780 						type = arg._type;
       
  9781 						components = arg._components.slice();
       
  9782 						alpha = arg._alpha;
       
  9783 						if (type === 'gradient') {
       
  9784 							for (var i = 1, l = components.length; i < l; i++) {
       
  9785 								var point = components[i];
       
  9786 								if (point)
       
  9787 									components[i] = point.clone();
       
  9788 							}
       
  9789 						}
       
  9790 					} else if (arg.constructor === Gradient) {
       
  9791 						type = 'gradient';
       
  9792 						values = args;
       
  9793 					} else {
       
  9794 						type = 'hue' in arg
       
  9795 							? 'lightness' in arg
       
  9796 								? 'hsl'
       
  9797 								: 'hsb'
       
  9798 							: 'gradient' in arg || 'stops' in arg
       
  9799 									|| 'radial' in arg
       
  9800 								? 'gradient'
       
  9801 								: 'gray' in arg
       
  9802 									? 'gray'
       
  9803 									: 'rgb';
       
  9804 						var properties = types[type];
       
  9805 							parsers = componentParsers[type];
       
  9806 						this._components = components = [];
       
  9807 						for (var i = 0, l = properties.length; i < l; i++) {
       
  9808 							var value = arg[properties[i]];
       
  9809 							if (value == null && i === 0 && type === 'gradient'
       
  9810 									&& 'stops' in arg) {
       
  9811 								value = {
       
  9812 									stops: arg.stops,
       
  9813 									radial: arg.radial
       
  9814 								};
       
  9815 							}
       
  9816 							value = parsers[i].call(this, value);
       
  9817 							if (value != null)
       
  9818 								components[i] = value;
       
  9819 						}
       
  9820 						alpha = arg.alpha;
       
  9821 					}
       
  9822 				}
       
  9823 				if (this.__read && type)
       
  9824 					read = 1;
       
  9825 			}
       
  9826 			this._type = type || 'rgb';
       
  9827 			this._id = UID.get(Color);
       
  9828 			if (!components) {
       
  9829 				this._components = components = [];
       
  9830 				var parsers = componentParsers[this._type];
       
  9831 				for (var i = 0, l = parsers.length; i < l; i++) {
       
  9832 					var value = parsers[i].call(this, values && values[i]);
       
  9833 					if (value != null)
       
  9834 						components[i] = value;
       
  9835 				}
       
  9836 			}
       
  9837 			this._components = components;
       
  9838 			this._properties = types[this._type];
       
  9839 			this._alpha = alpha;
       
  9840 			if (this.__read)
       
  9841 				this.__read = read;
       
  9842 		},
       
  9843 
       
  9844 		_serialize: function(options, dictionary) {
       
  9845 			var components = this.getComponents();
       
  9846 			return Base.serialize(
       
  9847 					/^(gray|rgb)$/.test(this._type)
       
  9848 						? components
       
  9849 						: [this._type].concat(components),
       
  9850 					options, true, dictionary);
       
  9851 		},
       
  9852 
       
  9853 		_changed: function() {
       
  9854 			this._canvasStyle = null;
       
  9855 			if (this._owner)
       
  9856 				this._owner._changed(65);
       
  9857 		},
       
  9858 
       
  9859 		_convert: function(type) {
       
  9860 			var converter;
       
  9861 			return this._type === type
       
  9862 					? this._components.slice()
       
  9863 					: (converter = converters[this._type + '-' + type])
       
  9864 						? converter.apply(this, this._components)
       
  9865 						: converters['rgb-' + type].apply(this,
       
  9866 							converters[this._type + '-rgb'].apply(this,
       
  9867 								this._components));
       
  9868 		},
       
  9869 
       
  9870 		convert: function(type) {
       
  9871 			return new Color(type, this._convert(type), this._alpha);
       
  9872 		},
       
  9873 
       
  9874 		getType: function() {
       
  9875 			return this._type;
       
  9876 		},
       
  9877 
       
  9878 		setType: function(type) {
       
  9879 			this._components = this._convert(type);
       
  9880 			this._properties = types[type];
       
  9881 			this._type = type;
       
  9882 		},
       
  9883 
       
  9884 		getComponents: function() {
       
  9885 			var components = this._components.slice();
       
  9886 			if (this._alpha != null)
       
  9887 				components.push(this._alpha);
       
  9888 			return components;
       
  9889 		},
       
  9890 
       
  9891 		getAlpha: function() {
       
  9892 			return this._alpha != null ? this._alpha : 1;
       
  9893 		},
       
  9894 
       
  9895 		setAlpha: function(alpha) {
       
  9896 			this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1);
       
  9897 			this._changed();
       
  9898 		},
       
  9899 
       
  9900 		hasAlpha: function() {
       
  9901 			return this._alpha != null;
       
  9902 		},
       
  9903 
       
  9904 		equals: function(color) {
       
  9905 			var col = Base.isPlainValue(color, true)
       
  9906 					? Color.read(arguments)
       
  9907 					: color;
       
  9908 			return col === this || col && this._class === col._class
       
  9909 					&& this._type === col._type
       
  9910 					&& this._alpha === col._alpha
       
  9911 					&& Base.equals(this._components, col._components)
       
  9912 					|| false;
       
  9913 		},
       
  9914 
       
  9915 		toString: function() {
       
  9916 			var properties = this._properties,
       
  9917 				parts = [],
       
  9918 				isGradient = this._type === 'gradient',
       
  9919 				f = Formatter.instance;
       
  9920 			for (var i = 0, l = properties.length; i < l; i++) {
       
  9921 				var value = this._components[i];
       
  9922 				if (value != null)
       
  9923 					parts.push(properties[i] + ': '
       
  9924 							+ (isGradient ? value : f.number(value)));
       
  9925 			}
       
  9926 			if (this._alpha != null)
       
  9927 				parts.push('alpha: ' + f.number(this._alpha));
       
  9928 			return '{ ' + parts.join(', ') + ' }';
       
  9929 		},
       
  9930 
       
  9931 		toCSS: function(hex) {
       
  9932 			var components = this._convert('rgb'),
       
  9933 				alpha = hex || this._alpha == null ? 1 : this._alpha;
       
  9934 			function convert(val) {
       
  9935 				return Math.round((val < 0 ? 0 : val > 1 ? 1 : val) * 255);
       
  9936 			}
       
  9937 			components = [
       
  9938 				convert(components[0]),
       
  9939 				convert(components[1]),
       
  9940 				convert(components[2])
       
  9941 			];
       
  9942 			if (alpha < 1)
       
  9943 				components.push(alpha < 0 ? 0 : alpha);
       
  9944 			return hex
       
  9945 					? '#' + ((1 << 24) + (components[0] << 16)
       
  9946 						+ (components[1] << 8)
       
  9947 						+ components[2]).toString(16).slice(1)
       
  9948 					: (components.length == 4 ? 'rgba(' : 'rgb(')
       
  9949 						+ components.join(',') + ')';
       
  9950 		},
       
  9951 
       
  9952 		toCanvasStyle: function(ctx) {
       
  9953 			if (this._canvasStyle)
       
  9954 				return this._canvasStyle;
       
  9955 			if (this._type !== 'gradient')
       
  9956 				return this._canvasStyle = this.toCSS();
       
  9957 			var components = this._components,
       
  9958 				gradient = components[0],
       
  9959 				stops = gradient._stops,
       
  9960 				origin = components[1],
       
  9961 				destination = components[2],
       
  9962 				canvasGradient;
       
  9963 			if (gradient._radial) {
       
  9964 				var radius = destination.getDistance(origin),
       
  9965 					highlight = components[3];
       
  9966 				if (highlight) {
       
  9967 					var vector = highlight.subtract(origin);
       
  9968 					if (vector.getLength() > radius)
       
  9969 						highlight = origin.add(vector.normalize(radius - 0.1));
       
  9970 				}
       
  9971 				var start = highlight || origin;
       
  9972 				canvasGradient = ctx.createRadialGradient(start.x, start.y,
       
  9973 						0, origin.x, origin.y, radius);
       
  9974 			} else {
       
  9975 				canvasGradient = ctx.createLinearGradient(origin.x, origin.y,
       
  9976 						destination.x, destination.y);
       
  9977 			}
       
  9978 			for (var i = 0, l = stops.length; i < l; i++) {
       
  9979 				var stop = stops[i];
       
  9980 				canvasGradient.addColorStop(stop._rampPoint,
       
  9981 						stop._color.toCanvasStyle());
       
  9982 			}
       
  9983 			return this._canvasStyle = canvasGradient;
       
  9984 		},
       
  9985 
       
  9986 		transform: function(matrix) {
       
  9987 			if (this._type === 'gradient') {
       
  9988 				var components = this._components;
       
  9989 				for (var i = 1, l = components.length; i < l; i++) {
       
  9990 					var point = components[i];
       
  9991 					matrix._transformPoint(point, point, true);
       
  9992 				}
       
  9993 				this._changed();
       
  9994 			}
       
  9995 		},
       
  9996 
       
  9997 		statics: {
       
  9998 			_types: types,
       
  9999 
       
 10000 			random: function() {
       
 10001 				var random = Math.random;
       
 10002 				return new Color(random(), random(), random());
       
 10003 			}
       
 10004 		}
       
 10005 	});
       
 10006 }, new function() {
       
 10007 	var operators = {
       
 10008 		add: function(a, b) {
       
 10009 			return a + b;
       
 10010 		},
       
 10011 
       
 10012 		subtract: function(a, b) {
       
 10013 			return a - b;
       
 10014 		},
       
 10015 
       
 10016 		multiply: function(a, b) {
       
 10017 			return a * b;
       
 10018 		},
       
 10019 
       
 10020 		divide: function(a, b) {
       
 10021 			return a / b;
       
 10022 		}
       
 10023 	};
       
 10024 
       
 10025 	return Base.each(operators, function(operator, name) {
       
 10026 		this[name] = function(color) {
       
 10027 			color = Color.read(arguments);
       
 10028 			var type = this._type,
       
 10029 				components1 = this._components,
       
 10030 				components2 = color._convert(type);
       
 10031 			for (var i = 0, l = components1.length; i < l; i++)
       
 10032 				components2[i] = operator(components1[i], components2[i]);
       
 10033 			return new Color(type, components2,
       
 10034 					this._alpha != null
       
 10035 							? operator(this._alpha, color.getAlpha())
       
 10036 							: null);
       
 10037 		};
       
 10038 	}, {
       
 10039 	});
       
 10040 });
       
 10041 
       
 10042 Base.each(Color._types, function(properties, type) {
       
 10043 	var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) {
       
 10044 			var argType = arg != null && typeof arg,
       
 10045 				components = argType === 'object' && arg.length != null
       
 10046 					? arg
       
 10047 					: argType === 'string'
       
 10048 						? null
       
 10049 						: arguments;
       
 10050 			return components
       
 10051 					? new Color(type, components)
       
 10052 					: new Color(arg);
       
 10053 		};
       
 10054 	if (type.length == 3) {
       
 10055 		var acronym = type.toUpperCase();
       
 10056 		Color[acronym] = this[acronym + 'Color'] = ctor;
       
 10057 	}
       
 10058 }, Base.exports);
       
 10059 
       
 10060 var Gradient = Base.extend({
       
 10061 	_class: 'Gradient',
       
 10062 
       
 10063 	initialize: function Gradient(stops, radial) {
       
 10064 		this._id = UID.get();
       
 10065 		if (stops && this._set(stops))
       
 10066 			stops = radial = null;
       
 10067 		if (!this._stops)
       
 10068 			this.setStops(stops || ['white', 'black']);
       
 10069 		if (this._radial == null)
       
 10070 			this.setRadial(typeof radial === 'string' && radial === 'radial'
       
 10071 					|| radial || false);
       
 10072 	},
       
 10073 
       
 10074 	_serialize: function(options, dictionary) {
       
 10075 		return dictionary.add(this, function() {
       
 10076 			return Base.serialize([this._stops, this._radial],
       
 10077 					options, true, dictionary);
       
 10078 		});
       
 10079 	},
       
 10080 
       
 10081 	_changed: function() {
       
 10082 		for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
       
 10083 			this._owners[i]._changed();
       
 10084 	},
       
 10085 
       
 10086 	_addOwner: function(color) {
       
 10087 		if (!this._owners)
       
 10088 			this._owners = [];
       
 10089 		this._owners.push(color);
       
 10090 	},
       
 10091 
       
 10092 	_removeOwner: function(color) {
       
 10093 		var index = this._owners ? this._owners.indexOf(color) : -1;
       
 10094 		if (index != -1) {
       
 10095 			this._owners.splice(index, 1);
       
 10096 			if (this._owners.length === 0)
       
 10097 				this._owners = undefined;
       
 10098 		}
       
 10099 	},
       
 10100 
       
 10101 	clone: function() {
       
 10102 		var stops = [];
       
 10103 		for (var i = 0, l = this._stops.length; i < l; i++)
       
 10104 			stops[i] = this._stops[i].clone();
       
 10105 		return new Gradient(stops, this._radial);
       
 10106 	},
       
 10107 
       
 10108 	getStops: function() {
       
 10109 		return this._stops;
       
 10110 	},
       
 10111 
       
 10112 	setStops: function(stops) {
       
 10113 		if (this.stops) {
       
 10114 			for (var i = 0, l = this._stops.length; i < l; i++)
       
 10115 				this._stops[i]._owner = undefined;
       
 10116 		}
       
 10117 		if (stops.length < 2)
       
 10118 			throw new Error(
       
 10119 					'Gradient stop list needs to contain at least two stops.');
       
 10120 		this._stops = GradientStop.readAll(stops, 0, { clone: true });
       
 10121 		for (var i = 0, l = this._stops.length; i < l; i++) {
       
 10122 			var stop = this._stops[i];
       
 10123 			stop._owner = this;
       
 10124 			if (stop._defaultRamp)
       
 10125 				stop.setRampPoint(i / (l - 1));
       
 10126 		}
       
 10127 		this._changed();
       
 10128 	},
       
 10129 
       
 10130 	getRadial: function() {
       
 10131 		return this._radial;
       
 10132 	},
       
 10133 
       
 10134 	setRadial: function(radial) {
       
 10135 		this._radial = radial;
       
 10136 		this._changed();
       
 10137 	},
       
 10138 
       
 10139 	equals: function(gradient) {
       
 10140 		if (gradient === this)
       
 10141 			return true;
       
 10142 		if (gradient && this._class === gradient._class
       
 10143 				&& this._stops.length === gradient._stops.length) {
       
 10144 			for (var i = 0, l = this._stops.length; i < l; i++) {
       
 10145 				if (!this._stops[i].equals(gradient._stops[i]))
       
 10146 					return false;
       
 10147 			}
       
 10148 			return true;
       
 10149 		}
       
 10150 		return false;
       
 10151 	}
       
 10152 });
       
 10153 
       
 10154 var GradientStop = Base.extend({
       
 10155 	_class: 'GradientStop',
       
 10156 
       
 10157 	initialize: function GradientStop(arg0, arg1) {
       
 10158 		if (arg0) {
       
 10159 			var color, rampPoint;
       
 10160 			if (arg1 === undefined && Array.isArray(arg0)) {
       
 10161 				color = arg0[0];
       
 10162 				rampPoint = arg0[1];
       
 10163 			} else if (arg0.color) {
       
 10164 				color = arg0.color;
       
 10165 				rampPoint = arg0.rampPoint;
       
 10166 			} else {
       
 10167 				color = arg0;
       
 10168 				rampPoint = arg1;
       
 10169 			}
       
 10170 			this.setColor(color);
       
 10171 			this.setRampPoint(rampPoint);
       
 10172 		}
       
 10173 	},
       
 10174 
       
 10175 	clone: function() {
       
 10176 		return new GradientStop(this._color.clone(), this._rampPoint);
       
 10177 	},
       
 10178 
       
 10179 	_serialize: function(options, dictionary) {
       
 10180 		return Base.serialize([this._color, this._rampPoint], options, true,
       
 10181 				dictionary);
       
 10182 	},
       
 10183 
       
 10184 	_changed: function() {
       
 10185 		if (this._owner)
       
 10186 			this._owner._changed(65);
       
 10187 	},
       
 10188 
       
 10189 	getRampPoint: function() {
       
 10190 		return this._rampPoint;
       
 10191 	},
       
 10192 
       
 10193 	setRampPoint: function(rampPoint) {
       
 10194 		this._defaultRamp = rampPoint == null;
       
 10195 		this._rampPoint = rampPoint || 0;
       
 10196 		this._changed();
       
 10197 	},
       
 10198 
       
 10199 	getColor: function() {
       
 10200 		return this._color;
       
 10201 	},
       
 10202 
       
 10203 	setColor: function(color) {
       
 10204 		this._color = Color.read(arguments);
       
 10205 		if (this._color === color)
       
 10206 			this._color = color.clone();
       
 10207 		this._color._owner = this;
       
 10208 		this._changed();
       
 10209 	},
       
 10210 
       
 10211 	equals: function(stop) {
       
 10212 		return stop === this || stop && this._class === stop._class
       
 10213 				&& this._color.equals(stop._color)
       
 10214 				&& this._rampPoint == stop._rampPoint
       
 10215 				|| false;
       
 10216 	}
       
 10217 });
       
 10218 
       
 10219 var Style = Base.extend(new function() {
       
 10220 	var defaults = {
       
 10221 		fillColor: undefined,
       
 10222 		strokeColor: undefined,
       
 10223 		strokeWidth: 1,
       
 10224 		strokeCap: 'butt',
       
 10225 		strokeJoin: 'miter',
       
 10226 		strokeScaling: true,
       
 10227 		miterLimit: 10,
       
 10228 		dashOffset: 0,
       
 10229 		dashArray: [],
       
 10230 		windingRule: 'nonzero',
       
 10231 		shadowColor: undefined,
       
 10232 		shadowBlur: 0,
       
 10233 		shadowOffset: new Point(),
       
 10234 		selectedColor: undefined,
       
 10235 		fontFamily: 'sans-serif',
       
 10236 		fontWeight: 'normal',
       
 10237 		fontSize: 12,
       
 10238 		font: 'sans-serif',
       
 10239 		leading: null,
       
 10240 		justification: 'left'
       
 10241 	};
       
 10242 
       
 10243 	var flags = {
       
 10244 		strokeWidth: 97,
       
 10245 		strokeCap: 97,
       
 10246 		strokeJoin: 97,
       
 10247 		strokeScaling: 105,
       
 10248 		miterLimit: 97,
       
 10249 		fontFamily: 9,
       
 10250 		fontWeight: 9,
       
 10251 		fontSize: 9,
       
 10252 		font: 9,
       
 10253 		leading: 9,
       
 10254 		justification: 9
       
 10255 	};
       
 10256 
       
 10257 	var item = { beans: true },
       
 10258 		fields = {
       
 10259 			_defaults: defaults,
       
 10260 			_textDefaults: new Base(defaults, {
       
 10261 				fillColor: new Color()
       
 10262 			}),
       
 10263 			beans: true
       
 10264 		};
       
 10265 
       
 10266 	Base.each(defaults, function(value, key) {
       
 10267 		var isColor = /Color$/.test(key),
       
 10268 			isPoint = key === 'shadowOffset',
       
 10269 			part = Base.capitalize(key),
       
 10270 			flag = flags[key],
       
 10271 			set = 'set' + part,
       
 10272 			get = 'get' + part;
       
 10273 
       
 10274 		fields[set] = function(value) {
       
 10275 			var owner = this._owner,
       
 10276 				children = owner && owner._children;
       
 10277 			if (children && children.length > 0
       
 10278 					&& !(owner instanceof CompoundPath)) {
       
 10279 				for (var i = 0, l = children.length; i < l; i++)
       
 10280 					children[i]._style[set](value);
       
 10281 			} else {
       
 10282 				var old = this._values[key];
       
 10283 				if (old !== value) {
       
 10284 					if (isColor) {
       
 10285 						if (old)
       
 10286 							old._owner = undefined;
       
 10287 						if (value && value.constructor === Color) {
       
 10288 							if (value._owner)
       
 10289 								value = value.clone();
       
 10290 							value._owner = owner;
       
 10291 						}
       
 10292 					}
       
 10293 					this._values[key] = value;
       
 10294 					if (owner)
       
 10295 						owner._changed(flag || 65);
       
 10296 				}
       
 10297 			}
       
 10298 		};
       
 10299 
       
 10300 		fields[get] = function(_dontMerge) {
       
 10301 			var owner = this._owner,
       
 10302 				children = owner && owner._children,
       
 10303 				value;
       
 10304 			if (!children || children.length === 0 || _dontMerge
       
 10305 					|| owner instanceof CompoundPath) {
       
 10306 				var value = this._values[key];
       
 10307 				if (value === undefined) {
       
 10308 					value = this._defaults[key];
       
 10309 					if (value && value.clone)
       
 10310 						value = value.clone();
       
 10311 				} else {
       
 10312 					var ctor = isColor ? Color : isPoint ? Point : null;
       
 10313 					if (ctor && !(value && value.constructor === ctor)) {
       
 10314 						this._values[key] = value = ctor.read([value], 0,
       
 10315 								{ readNull: true, clone: true });
       
 10316 						if (value && isColor)
       
 10317 							value._owner = owner;
       
 10318 					}
       
 10319 				}
       
 10320 				return value;
       
 10321 			}
       
 10322 			for (var i = 0, l = children.length; i < l; i++) {
       
 10323 				var childValue = children[i]._style[get]();
       
 10324 				if (i === 0) {
       
 10325 					value = childValue;
       
 10326 				} else if (!Base.equals(value, childValue)) {
       
 10327 					return undefined;
       
 10328 				}
       
 10329 			}
       
 10330 			return value;
       
 10331 		};
       
 10332 
       
 10333 		item[get] = function(_dontMerge) {
       
 10334 			return this._style[get](_dontMerge);
       
 10335 		};
       
 10336 
       
 10337 		item[set] = function(value) {
       
 10338 			this._style[set](value);
       
 10339 		};
       
 10340 	});
       
 10341 
       
 10342 	Item.inject(item);
       
 10343 	return fields;
       
 10344 }, {
       
 10345 	_class: 'Style',
       
 10346 
       
 10347 	initialize: function Style(style, _owner, _project) {
       
 10348 		this._values = {};
       
 10349 		this._owner = _owner;
       
 10350 		this._project = _owner && _owner._project || _project || paper.project;
       
 10351 		if (_owner instanceof TextItem)
       
 10352 			this._defaults = this._textDefaults;
       
 10353 		if (style)
       
 10354 			this.set(style);
       
 10355 	},
       
 10356 
       
 10357 	set: function(style) {
       
 10358 		var isStyle = style instanceof Style,
       
 10359 			values = isStyle ? style._values : style;
       
 10360 		if (values) {
       
 10361 			for (var key in values) {
       
 10362 				if (key in this._defaults) {
       
 10363 					var value = values[key];
       
 10364 					this[key] = value && isStyle && value.clone
       
 10365 							? value.clone() : value;
       
 10366 				}
       
 10367 			}
       
 10368 		}
       
 10369 	},
       
 10370 
       
 10371 	equals: function(style) {
       
 10372 		return style === this || style && this._class === style._class
       
 10373 				&& Base.equals(this._values, style._values)
       
 10374 				|| false;
       
 10375 	},
       
 10376 
       
 10377 	hasFill: function() {
       
 10378 		return !!this.getFillColor();
       
 10379 	},
       
 10380 
       
 10381 	hasStroke: function() {
       
 10382 		return !!this.getStrokeColor() && this.getStrokeWidth() > 0;
       
 10383 	},
       
 10384 
       
 10385 	hasShadow: function() {
       
 10386 		return !!this.getShadowColor() && this.getShadowBlur() > 0;
       
 10387 	},
       
 10388 
       
 10389 	getView: function() {
       
 10390 		return this._project.getView();
       
 10391 	},
       
 10392 
       
 10393 	getFontStyle: function() {
       
 10394 		var fontSize = this.getFontSize();
       
 10395 		return this.getFontWeight()
       
 10396 				+ ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')
       
 10397 				+ this.getFontFamily();
       
 10398 	},
       
 10399 
       
 10400 	getFont: '#getFontFamily',
       
 10401 	setFont: '#setFontFamily',
       
 10402 
       
 10403 	getLeading: function getLeading() {
       
 10404 		var leading = getLeading.base.call(this),
       
 10405 			fontSize = this.getFontSize();
       
 10406 		if (/pt|em|%|px/.test(fontSize))
       
 10407 			fontSize = this.getView().getPixelSize(fontSize);
       
 10408 		return leading != null ? leading : fontSize * 1.2;
       
 10409 	}
       
 10410 
       
 10411 });
       
 10412 
       
 10413 var DomElement = new function() {
       
 10414 	function handlePrefix(el, name, set, value) {
       
 10415 		var prefixes = ['', 'webkit', 'moz', 'Moz', 'ms', 'o'],
       
 10416 			suffix = name[0].toUpperCase() + name.substring(1);
       
 10417 		for (var i = 0; i < 6; i++) {
       
 10418 			var prefix = prefixes[i],
       
 10419 				key = prefix ? prefix + suffix : name;
       
 10420 			if (key in el) {
       
 10421 				if (set) {
       
 10422 					el[key] = value;
       
 10423 				} else {
       
 10424 					return el[key];
       
 10425 				}
       
 10426 				break;
       
 10427 			}
       
 10428 		}
       
 10429 	}
       
 10430 
       
 10431 	return {
       
 10432 		getStyles: function(el) {
       
 10433 			var doc = el && el.nodeType !== 9 ? el.ownerDocument : el,
       
 10434 				view = doc && doc.defaultView;
       
 10435 			return view && view.getComputedStyle(el, '');
       
 10436 		},
       
 10437 
       
 10438 		getBounds: function(el, viewport) {
       
 10439 			var doc = el.ownerDocument,
       
 10440 				body = doc.body,
       
 10441 				html = doc.documentElement,
       
 10442 				rect;
       
 10443 			try {
       
 10444 				rect = el.getBoundingClientRect();
       
 10445 			} catch (e) {
       
 10446 				rect = { left: 0, top: 0, width: 0, height: 0 };
       
 10447 			}
       
 10448 			var x = rect.left - (html.clientLeft || body.clientLeft || 0),
       
 10449 				y = rect.top - (html.clientTop || body.clientTop || 0);
       
 10450 			if (!viewport) {
       
 10451 				var view = doc.defaultView;
       
 10452 				x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
       
 10453 				y += view.pageYOffset || html.scrollTop || body.scrollTop;
       
 10454 			}
       
 10455 			return new Rectangle(x, y, rect.width, rect.height);
       
 10456 		},
       
 10457 
       
 10458 		getViewportBounds: function(el) {
       
 10459 			var doc = el.ownerDocument,
       
 10460 				view = doc.defaultView,
       
 10461 				html = doc.documentElement;
       
 10462 			return new Rectangle(0, 0,
       
 10463 				view.innerWidth || html.clientWidth,
       
 10464 				view.innerHeight || html.clientHeight
       
 10465 			);
       
 10466 		},
       
 10467 
       
 10468 		getOffset: function(el, viewport) {
       
 10469 			return DomElement.getBounds(el, viewport).getPoint();
       
 10470 		},
       
 10471 
       
 10472 		getSize: function(el) {
       
 10473 			return DomElement.getBounds(el, true).getSize();
       
 10474 		},
       
 10475 
       
 10476 		isInvisible: function(el) {
       
 10477 			return DomElement.getSize(el).equals(new Size(0, 0));
       
 10478 		},
       
 10479 
       
 10480 		isInView: function(el) {
       
 10481 			return !DomElement.isInvisible(el)
       
 10482 					&& DomElement.getViewportBounds(el).intersects(
       
 10483 						DomElement.getBounds(el, true));
       
 10484 		},
       
 10485 
       
 10486 		getPrefixed: function(el, name) {
       
 10487 			return handlePrefix(el, name);
       
 10488 		},
       
 10489 
       
 10490 		setPrefixed: function(el, name, value) {
       
 10491 			if (typeof name === 'object') {
       
 10492 				for (var key in name)
       
 10493 					handlePrefix(el, key, true, name[key]);
       
 10494 			} else {
       
 10495 				handlePrefix(el, name, true, value);
       
 10496 			}
       
 10497 		}
       
 10498 	};
       
 10499 };
       
 10500 
       
 10501 var DomEvent = {
       
 10502 	add: function(el, events) {
       
 10503 		for (var type in events) {
       
 10504 			var func = events[type],
       
 10505 				parts = type.split(/[\s,]+/g);
       
 10506 			for (var i = 0, l = parts.length; i < l; i++)
       
 10507 				el.addEventListener(parts[i], func, false);
       
 10508 		}
       
 10509 	},
       
 10510 
       
 10511 	remove: function(el, events) {
       
 10512 		for (var type in events) {
       
 10513 			var func = events[type],
       
 10514 				parts = type.split(/[\s,]+/g);
       
 10515 			for (var i = 0, l = parts.length; i < l; i++)
       
 10516 				el.removeEventListener(parts[i], func, false);
       
 10517 		}
       
 10518 	},
       
 10519 
       
 10520 	getPoint: function(event) {
       
 10521 		var pos = event.targetTouches
       
 10522 				? event.targetTouches.length
       
 10523 					? event.targetTouches[0]
       
 10524 					: event.changedTouches[0]
       
 10525 				: event;
       
 10526 		return new Point(
       
 10527 			pos.pageX || pos.clientX + document.documentElement.scrollLeft,
       
 10528 			pos.pageY || pos.clientY + document.documentElement.scrollTop
       
 10529 		);
       
 10530 	},
       
 10531 
       
 10532 	getTarget: function(event) {
       
 10533 		return event.target || event.srcElement;
       
 10534 	},
       
 10535 
       
 10536 	getRelatedTarget: function(event) {
       
 10537 		return event.relatedTarget || event.toElement;
       
 10538 	},
       
 10539 
       
 10540 	getOffset: function(event, target) {
       
 10541 		return DomEvent.getPoint(event).subtract(DomElement.getOffset(
       
 10542 				target || DomEvent.getTarget(event)));
       
 10543 	},
       
 10544 
       
 10545 	stop: function(event) {
       
 10546 		event.stopPropagation();
       
 10547 		event.preventDefault();
       
 10548 	}
       
 10549 };
       
 10550 
       
 10551 DomEvent.requestAnimationFrame = new function() {
       
 10552 	var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
       
 10553 		requested = false,
       
 10554 		callbacks = [],
       
 10555 		focused = true,
       
 10556 		timer;
       
 10557 
       
 10558 	DomEvent.add(window, {
       
 10559 		focus: function() {
       
 10560 			focused = true;
       
 10561 		},
       
 10562 		blur: function() {
       
 10563 			focused = false;
       
 10564 		}
       
 10565 	});
       
 10566 
       
 10567 	function handleCallbacks() {
       
 10568 		for (var i = callbacks.length - 1; i >= 0; i--) {
       
 10569 			var entry = callbacks[i],
       
 10570 				func = entry[0],
       
 10571 				el = entry[1];
       
 10572 			if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
       
 10573 					|| focused) && DomElement.isInView(el)) {
       
 10574 				callbacks.splice(i, 1);
       
 10575 				func();
       
 10576 			}
       
 10577 		}
       
 10578 		if (nativeRequest) {
       
 10579 			if (callbacks.length) {
       
 10580 				nativeRequest(handleCallbacks);
       
 10581 			} else {
       
 10582 				requested = false;
       
 10583 			}
       
 10584 		}
       
 10585 	}
       
 10586 
       
 10587 	return function(callback, element) {
       
 10588 		callbacks.push([callback, element]);
       
 10589 		if (nativeRequest) {
       
 10590 			if (!requested) {
       
 10591 				nativeRequest(handleCallbacks);
       
 10592 				requested = true;
       
 10593 			}
       
 10594 		} else if (!timer) {
       
 10595 			timer = setInterval(handleCallbacks, 1000 / 60);
       
 10596 		}
       
 10597 	};
       
 10598 };
       
 10599 
       
 10600 var View = Base.extend(Emitter, {
       
 10601 	_class: 'View',
       
 10602 
       
 10603 	initialize: function View(project, element) {
       
 10604 		this._project = project;
       
 10605 		this._scope = project._scope;
       
 10606 		this._element = element;
       
 10607 		var size;
       
 10608 		if (!this._pixelRatio)
       
 10609 			this._pixelRatio = window.devicePixelRatio || 1;
       
 10610 		this._id = element.getAttribute('id');
       
 10611 		if (this._id == null)
       
 10612 			element.setAttribute('id', this._id = 'view-' + View._id++);
       
 10613 		DomEvent.add(element, this._viewEvents);
       
 10614 		var none = 'none';
       
 10615 		DomElement.setPrefixed(element.style, {
       
 10616 			userSelect: none,
       
 10617 			touchAction: none,
       
 10618 			touchCallout: none,
       
 10619 			contentZooming: none,
       
 10620 			userDrag: none,
       
 10621 			tapHighlightColor: 'rgba(0,0,0,0)'
       
 10622 		});
       
 10623 
       
 10624 		function getSize(name) {
       
 10625 			return element[name] || parseInt(element.getAttribute(name), 10);
       
 10626 		};
       
 10627 
       
 10628 		function getCanvasSize() {
       
 10629 			var size = DomElement.getSize(element);
       
 10630 			return size.isNaN() || size.isZero()
       
 10631 					? new Size(getSize('width'), getSize('height'))
       
 10632 					: size;
       
 10633 		};
       
 10634 
       
 10635 		if (PaperScope.hasAttribute(element, 'resize')) {
       
 10636 			var that = this;
       
 10637 			DomEvent.add(window, this._windowEvents = {
       
 10638 				resize: function() {
       
 10639 					that.setViewSize(getCanvasSize());
       
 10640 				}
       
 10641 			});
       
 10642 		}
       
 10643 		this._setViewSize(size = getCanvasSize());
       
 10644 		if (PaperScope.hasAttribute(element, 'stats')
       
 10645 				&& typeof Stats !== 'undefined') {
       
 10646 			this._stats = new Stats();
       
 10647 			var stats = this._stats.domElement,
       
 10648 				style = stats.style,
       
 10649 				offset = DomElement.getOffset(element);
       
 10650 			style.position = 'absolute';
       
 10651 			style.left = offset.x + 'px';
       
 10652 			style.top = offset.y + 'px';
       
 10653 			document.body.appendChild(stats);
       
 10654 		}
       
 10655 		View._views.push(this);
       
 10656 		View._viewsById[this._id] = this;
       
 10657 		this._viewSize = size;
       
 10658 		(this._matrix = new Matrix())._owner = this;
       
 10659 		this._zoom = 1;
       
 10660 		if (!View._focused)
       
 10661 			View._focused = this;
       
 10662 		this._frameItems = {};
       
 10663 		this._frameItemCount = 0;
       
 10664 	},
       
 10665 
       
 10666 	remove: function() {
       
 10667 		if (!this._project)
       
 10668 			return false;
       
 10669 		if (View._focused === this)
       
 10670 			View._focused = null;
       
 10671 		View._views.splice(View._views.indexOf(this), 1);
       
 10672 		delete View._viewsById[this._id];
       
 10673 		if (this._project._view === this)
       
 10674 			this._project._view = null;
       
 10675 		DomEvent.remove(this._element, this._viewEvents);
       
 10676 		DomEvent.remove(window, this._windowEvents);
       
 10677 		this._element = this._project = null;
       
 10678 		this.off('frame');
       
 10679 		this._animate = false;
       
 10680 		this._frameItems = {};
       
 10681 		return true;
       
 10682 	},
       
 10683 
       
 10684 	_events: {
       
 10685 		onFrame: {
       
 10686 			install: function() {
       
 10687 				this.play();
       
 10688 			},
       
 10689 
       
 10690 			uninstall: function() {
       
 10691 				this.pause();
       
 10692 			}
       
 10693 		},
       
 10694 
       
 10695 		onResize: {}
       
 10696 	},
       
 10697 
       
 10698 	_animate: false,
       
 10699 	_time: 0,
       
 10700 	_count: 0,
       
 10701 
       
 10702 	_requestFrame: function() {
       
 10703 		var that = this;
       
 10704 		DomEvent.requestAnimationFrame(function() {
       
 10705 			that._requested = false;
       
 10706 			if (!that._animate)
       
 10707 				return;
       
 10708 			that._requestFrame();
       
 10709 			that._handleFrame();
       
 10710 		}, this._element);
       
 10711 		this._requested = true;
       
 10712 	},
       
 10713 
       
 10714 	_handleFrame: function() {
       
 10715 		paper = this._scope;
       
 10716 		var now = Date.now() / 1000,
       
 10717 			delta = this._before ? now - this._before : 0;
       
 10718 		this._before = now;
       
 10719 		this._handlingFrame = true;
       
 10720 		this.emit('frame', new Base({
       
 10721 			delta: delta,
       
 10722 			time: this._time += delta,
       
 10723 			count: this._count++
       
 10724 		}));
       
 10725 		if (this._stats)
       
 10726 			this._stats.update();
       
 10727 		this._handlingFrame = false;
       
 10728 		this.update();
       
 10729 	},
       
 10730 
       
 10731 	_animateItem: function(item, animate) {
       
 10732 		var items = this._frameItems;
       
 10733 		if (animate) {
       
 10734 			items[item._id] = {
       
 10735 				item: item,
       
 10736 				time: 0,
       
 10737 				count: 0
       
 10738 			};
       
 10739 			if (++this._frameItemCount === 1)
       
 10740 				this.on('frame', this._handleFrameItems);
       
 10741 		} else {
       
 10742 			delete items[item._id];
       
 10743 			if (--this._frameItemCount === 0) {
       
 10744 				this.off('frame', this._handleFrameItems);
       
 10745 			}
       
 10746 		}
       
 10747 	},
       
 10748 
       
 10749 	_handleFrameItems: function(event) {
       
 10750 		for (var i in this._frameItems) {
       
 10751 			var entry = this._frameItems[i];
       
 10752 			entry.item.emit('frame', new Base(event, {
       
 10753 				time: entry.time += event.delta,
       
 10754 				count: entry.count++
       
 10755 			}));
       
 10756 		}
       
 10757 	},
       
 10758 
       
 10759 	_update: function() {
       
 10760 		this._project._needsUpdate = true;
       
 10761 		if (this._handlingFrame)
       
 10762 			return;
       
 10763 		if (this._animate) {
       
 10764 			this._handleFrame();
       
 10765 		} else {
       
 10766 			this.update();
       
 10767 		}
       
 10768 	},
       
 10769 
       
 10770 	_changed: function(flags) {
       
 10771 		if (flags & 1)
       
 10772 			this._project._needsUpdate = true;
       
 10773 	},
       
 10774 
       
 10775 	_transform: function(matrix) {
       
 10776 		this._matrix.concatenate(matrix);
       
 10777 		this._bounds = null;
       
 10778 		this._update();
       
 10779 	},
       
 10780 
       
 10781 	getElement: function() {
       
 10782 		return this._element;
       
 10783 	},
       
 10784 
       
 10785 	getPixelRatio: function() {
       
 10786 		return this._pixelRatio;
       
 10787 	},
       
 10788 
       
 10789 	getResolution: function() {
       
 10790 		return this._pixelRatio * 72;
       
 10791 	},
       
 10792 
       
 10793 	getViewSize: function() {
       
 10794 		var size = this._viewSize;
       
 10795 		return new LinkedSize(size.width, size.height, this, 'setViewSize');
       
 10796 	},
       
 10797 
       
 10798 	setViewSize: function() {
       
 10799 		var size = Size.read(arguments),
       
 10800 			delta = size.subtract(this._viewSize);
       
 10801 		if (delta.isZero())
       
 10802 			return;
       
 10803 		this._viewSize.set(size.width, size.height);
       
 10804 		this._setViewSize(size);
       
 10805 		this._bounds = null;
       
 10806 		this.emit('resize', {
       
 10807 			size: size,
       
 10808 			delta: delta
       
 10809 		});
       
 10810 		this._update();
       
 10811 	},
       
 10812 
       
 10813 	_setViewSize: function(size) {
       
 10814 		var element = this._element;
       
 10815 		element.width = size.width;
       
 10816 		element.height = size.height;
       
 10817 	},
       
 10818 
       
 10819 	getBounds: function() {
       
 10820 		if (!this._bounds)
       
 10821 			this._bounds = this._matrix.inverted()._transformBounds(
       
 10822 					new Rectangle(new Point(), this._viewSize));
       
 10823 		return this._bounds;
       
 10824 	},
       
 10825 
       
 10826 	getSize: function() {
       
 10827 		return this.getBounds().getSize();
       
 10828 	},
       
 10829 
       
 10830 	getCenter: function() {
       
 10831 		return this.getBounds().getCenter();
       
 10832 	},
       
 10833 
       
 10834 	setCenter: function() {
       
 10835 		var center = Point.read(arguments);
       
 10836 		this.scrollBy(center.subtract(this.getCenter()));
       
 10837 	},
       
 10838 
       
 10839 	getZoom: function() {
       
 10840 		return this._zoom;
       
 10841 	},
       
 10842 
       
 10843 	setZoom: function(zoom) {
       
 10844 		this._transform(new Matrix().scale(zoom / this._zoom,
       
 10845 			this.getCenter()));
       
 10846 		this._zoom = zoom;
       
 10847 	},
       
 10848 
       
 10849 	isVisible: function() {
       
 10850 		return DomElement.isInView(this._element);
       
 10851 	},
       
 10852 
       
 10853 	scrollBy: function() {
       
 10854 		this._transform(new Matrix().translate(Point.read(arguments).negate()));
       
 10855 	},
       
 10856 
       
 10857 	play: function() {
       
 10858 		this._animate = true;
       
 10859 		if (!this._requested)
       
 10860 			this._requestFrame();
       
 10861 	},
       
 10862 
       
 10863 	pause: function() {
       
 10864 		this._animate = false;
       
 10865 	},
       
 10866 
       
 10867 	draw: function() {
       
 10868 		this.update();
       
 10869 	},
       
 10870 
       
 10871 	projectToView: function() {
       
 10872 		return this._matrix._transformPoint(Point.read(arguments));
       
 10873 	},
       
 10874 
       
 10875 	viewToProject: function() {
       
 10876 		return this._matrix._inverseTransform(Point.read(arguments));
       
 10877 	}
       
 10878 
       
 10879 }, {
       
 10880 	statics: {
       
 10881 		_views: [],
       
 10882 		_viewsById: {},
       
 10883 		_id: 0,
       
 10884 
       
 10885 		create: function(project, element) {
       
 10886 			if (typeof element === 'string')
       
 10887 				element = document.getElementById(element);
       
 10888 			return new CanvasView(project, element);
       
 10889 		}
       
 10890 	}
       
 10891 }, new function() {
       
 10892 	var tool,
       
 10893 		prevFocus,
       
 10894 		tempFocus,
       
 10895 		dragging = false;
       
 10896 
       
 10897 	function getView(event) {
       
 10898 		var target = DomEvent.getTarget(event);
       
 10899 		return target.getAttribute && View._viewsById[target.getAttribute('id')];
       
 10900 	}
       
 10901 
       
 10902 	function viewToProject(view, event) {
       
 10903 		return view.viewToProject(DomEvent.getOffset(event, view._element));
       
 10904 	}
       
 10905 
       
 10906 	function updateFocus() {
       
 10907 		if (!View._focused || !View._focused.isVisible()) {
       
 10908 			for (var i = 0, l = View._views.length; i < l; i++) {
       
 10909 				var view = View._views[i];
       
 10910 				if (view && view.isVisible()) {
       
 10911 					View._focused = tempFocus = view;
       
 10912 					break;
       
 10913 				}
       
 10914 			}
       
 10915 		}
       
 10916 	}
       
 10917 
       
 10918 	function handleMouseMove(view, point, event) {
       
 10919 		view._handleEvent('mousemove', point, event);
       
 10920 		var tool = view._scope.tool;
       
 10921 		if (tool) {
       
 10922 			tool._handleEvent(dragging && tool.responds('mousedrag')
       
 10923 					? 'mousedrag' : 'mousemove', point, event);
       
 10924 		}
       
 10925 		view.update();
       
 10926 		return tool;
       
 10927 	}
       
 10928 
       
 10929 	var navigator = window.navigator,
       
 10930 		mousedown, mousemove, mouseup;
       
 10931 	if (navigator.pointerEnabled || navigator.msPointerEnabled) {
       
 10932 		mousedown = 'pointerdown MSPointerDown';
       
 10933 		mousemove = 'pointermove MSPointerMove';
       
 10934 		mouseup = 'pointerup pointercancel MSPointerUp MSPointerCancel';
       
 10935 	} else {
       
 10936 		mousedown = 'touchstart';
       
 10937 		mousemove = 'touchmove';
       
 10938 		mouseup = 'touchend touchcancel';
       
 10939 		if (!('ontouchstart' in window && navigator.userAgent.match(
       
 10940 				/mobile|tablet|ip(ad|hone|od)|android|silk/i))) {
       
 10941 			mousedown += ' mousedown';
       
 10942 			mousemove += ' mousemove';
       
 10943 			mouseup += ' mouseup';
       
 10944 		}
       
 10945 	}
       
 10946 
       
 10947 	var viewEvents = {
       
 10948 		'selectstart dragstart': function(event) {
       
 10949 			if (dragging)
       
 10950 				event.preventDefault();
       
 10951 		}
       
 10952 	};
       
 10953 
       
 10954 	var docEvents = {
       
 10955 		mouseout: function(event) {
       
 10956 			var view = View._focused,
       
 10957 				target = DomEvent.getRelatedTarget(event);
       
 10958 			if (view && (!target || target.nodeName === 'HTML'))
       
 10959 				handleMouseMove(view, viewToProject(view, event), event);
       
 10960 		},
       
 10961 
       
 10962 		scroll: updateFocus
       
 10963 	};
       
 10964 
       
 10965 	viewEvents[mousedown] = function(event) {
       
 10966 		var view = View._focused = getView(event),
       
 10967 			point = viewToProject(view, event);
       
 10968 		dragging = true;
       
 10969 		view._handleEvent('mousedown', point, event);
       
 10970 		if (tool = view._scope.tool)
       
 10971 			tool._handleEvent('mousedown', point, event);
       
 10972 		view.update();
       
 10973 	};
       
 10974 
       
 10975 	docEvents[mousemove] = function(event) {
       
 10976 		var view = View._focused;
       
 10977 		if (!dragging) {
       
 10978 			var target = getView(event);
       
 10979 			if (target) {
       
 10980 				if (view !== target)
       
 10981 					handleMouseMove(view, viewToProject(view, event), event);
       
 10982 				prevFocus = view;
       
 10983 				view = View._focused = tempFocus = target;
       
 10984 			} else if (tempFocus && tempFocus === view) {
       
 10985 				view = View._focused = prevFocus;
       
 10986 				updateFocus();
       
 10987 			}
       
 10988 		}
       
 10989 		if (view) {
       
 10990 			var point = viewToProject(view, event);
       
 10991 			if (dragging || view.getBounds().contains(point))
       
 10992 				tool = handleMouseMove(view, point, event);
       
 10993 		}
       
 10994 	};
       
 10995 
       
 10996 	docEvents[mouseup] = function(event) {
       
 10997 		var view = View._focused;
       
 10998 		if (!view || !dragging)
       
 10999 			return;
       
 11000 		var point = viewToProject(view, event);
       
 11001 		dragging = false;
       
 11002 		view._handleEvent('mouseup', point, event);
       
 11003 		if (tool)
       
 11004 			tool._handleEvent('mouseup', point, event);
       
 11005 		view.update();
       
 11006 	};
       
 11007 
       
 11008 	DomEvent.add(document, docEvents);
       
 11009 
       
 11010 	DomEvent.add(window, {
       
 11011 		load: updateFocus
       
 11012 	});
       
 11013 
       
 11014 	return {
       
 11015 		_viewEvents: viewEvents,
       
 11016 
       
 11017 		_handleEvent: function() {},
       
 11018 
       
 11019 		statics: {
       
 11020 			updateFocus: updateFocus
       
 11021 		}
       
 11022 	};
       
 11023 });
       
 11024 
       
 11025 var CanvasView = View.extend({
       
 11026 	_class: 'CanvasView',
       
 11027 
       
 11028 	initialize: function CanvasView(project, canvas) {
       
 11029 		if (!(canvas instanceof HTMLCanvasElement)) {
       
 11030 			var size = Size.read(arguments, 1);
       
 11031 			if (size.isZero())
       
 11032 				throw new Error(
       
 11033 						'Cannot create CanvasView with the provided argument: '
       
 11034 						+ [].slice.call(arguments, 1));
       
 11035 			canvas = CanvasProvider.getCanvas(size);
       
 11036 		}
       
 11037 		this._context = canvas.getContext('2d');
       
 11038 		this._eventCounters = {};
       
 11039 		this._pixelRatio = 1;
       
 11040 		if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) {
       
 11041 			var deviceRatio = window.devicePixelRatio || 1,
       
 11042 				backingStoreRatio = DomElement.getPrefixed(this._context,
       
 11043 						'backingStorePixelRatio') || 1;
       
 11044 			this._pixelRatio = deviceRatio / backingStoreRatio;
       
 11045 		}
       
 11046 		View.call(this, project, canvas);
       
 11047 	},
       
 11048 
       
 11049 	_setViewSize: function(size) {
       
 11050 		var element = this._element,
       
 11051 			pixelRatio = this._pixelRatio,
       
 11052 			width = size.width,
       
 11053 			height = size.height;
       
 11054 		element.width = width * pixelRatio;
       
 11055 		element.height = height * pixelRatio;
       
 11056 		if (pixelRatio !== 1) {
       
 11057 			if (!PaperScope.hasAttribute(element, 'resize')) {
       
 11058 				var style = element.style;
       
 11059 				style.width = width + 'px';
       
 11060 				style.height = height + 'px';
       
 11061 			}
       
 11062 			this._context.scale(pixelRatio, pixelRatio);
       
 11063 		}
       
 11064 	},
       
 11065 
       
 11066 	getPixelSize: function(size) {
       
 11067 		var browser = paper.browser,
       
 11068 			pixels;
       
 11069 		if (browser && browser.firefox) {
       
 11070 			var parent = this._element.parentNode,
       
 11071 				temp = document.createElement('div');
       
 11072 			temp.style.fontSize = size;
       
 11073 			parent.appendChild(temp);
       
 11074 			pixels = parseFloat(DomElement.getStyles(temp).fontSize);
       
 11075 			parent.removeChild(temp);
       
 11076 		} else {
       
 11077 			var ctx = this._context,
       
 11078 				prevFont = ctx.font;
       
 11079 			ctx.font = size + ' serif';
       
 11080 			pixels = parseFloat(ctx.font);
       
 11081 			ctx.font = prevFont;
       
 11082 		}
       
 11083 		return pixels;
       
 11084 	},
       
 11085 
       
 11086 	getTextWidth: function(font, lines) {
       
 11087 		var ctx = this._context,
       
 11088 			prevFont = ctx.font,
       
 11089 			width = 0;
       
 11090 		ctx.font = font;
       
 11091 		for (var i = 0, l = lines.length; i < l; i++)
       
 11092 			width = Math.max(width, ctx.measureText(lines[i]).width);
       
 11093 		ctx.font = prevFont;
       
 11094 		return width;
       
 11095 	},
       
 11096 
       
 11097 	update: function(force) {
       
 11098 		var project = this._project;
       
 11099 		if (!project || !force && !project._needsUpdate)
       
 11100 			return false;
       
 11101 		var ctx = this._context,
       
 11102 			size = this._viewSize;
       
 11103 		ctx.clearRect(0, 0, size.width + 1, size.height + 1);
       
 11104 		project.draw(ctx, this._matrix, this._pixelRatio);
       
 11105 		project._needsUpdate = false;
       
 11106 		return true;
       
 11107 	}
       
 11108 }, new function() {
       
 11109 
       
 11110 	var downPoint,
       
 11111 		lastPoint,
       
 11112 		overPoint,
       
 11113 		downItem,
       
 11114 		lastItem,
       
 11115 		overItem,
       
 11116 		dragItem,
       
 11117 		dblClick,
       
 11118 		clickTime;
       
 11119 
       
 11120 	function callEvent(view, type, event, point, target, lastPoint) {
       
 11121 		var item = target,
       
 11122 			mouseEvent;
       
 11123 
       
 11124 		function call(obj) {
       
 11125 			if (obj.responds(type)) {
       
 11126 				if (!mouseEvent) {
       
 11127 					mouseEvent = new MouseEvent(type, event, point, target,
       
 11128 							lastPoint ? point.subtract(lastPoint) : null);
       
 11129 				}
       
 11130 				if (obj.emit(type, mouseEvent) && mouseEvent.isStopped) {
       
 11131 					event.preventDefault();
       
 11132 					return true;
       
 11133 				}
       
 11134 			}
       
 11135 		}
       
 11136 
       
 11137 		while (item) {
       
 11138 			if (call(item))
       
 11139 				return true;
       
 11140 			item = item.getParent();
       
 11141 		}
       
 11142 		if (call(view))
       
 11143 			return true;
       
 11144 		return false;
       
 11145 	}
       
 11146 
       
 11147 	return {
       
 11148 		_handleEvent: function(type, point, event) {
       
 11149 			if (!this._eventCounters[type])
       
 11150 				return;
       
 11151 			var project = this._project,
       
 11152 				hit = project.hitTest(point, {
       
 11153 					tolerance: 0,
       
 11154 					fill: true,
       
 11155 					stroke: true
       
 11156 				}),
       
 11157 				item = hit && hit.item,
       
 11158 				stopped = false;
       
 11159 			switch (type) {
       
 11160 			case 'mousedown':
       
 11161 				stopped = callEvent(this, type, event, point, item);
       
 11162 				dblClick = lastItem == item && (Date.now() - clickTime < 300);
       
 11163 				downItem = lastItem = item;
       
 11164 				downPoint = lastPoint = overPoint = point;
       
 11165 				dragItem = !stopped && item;
       
 11166 				while (dragItem && !dragItem.responds('mousedrag'))
       
 11167 					dragItem = dragItem._parent;
       
 11168 				break;
       
 11169 			case 'mouseup':
       
 11170 				stopped = callEvent(this, type, event, point, item, downPoint);
       
 11171 				if (dragItem) {
       
 11172 					if (lastPoint && !lastPoint.equals(point))
       
 11173 						callEvent(this, 'mousedrag', event, point, dragItem,
       
 11174 								lastPoint);
       
 11175 					if (item !== dragItem) {
       
 11176 						overPoint = point;
       
 11177 						callEvent(this, 'mousemove', event, point, item,
       
 11178 								overPoint);
       
 11179 					}
       
 11180 				}
       
 11181 				if (!stopped && item && item === downItem) {
       
 11182 					clickTime = Date.now();
       
 11183 					callEvent(this, dblClick && downItem.responds('doubleclick')
       
 11184 							? 'doubleclick' : 'click', event, downPoint, item);
       
 11185 					dblClick = false;
       
 11186 				}
       
 11187 				downItem = dragItem = null;
       
 11188 				break;
       
 11189 			case 'mousemove':
       
 11190 				if (dragItem)
       
 11191 					stopped = callEvent(this, 'mousedrag', event, point,
       
 11192 							dragItem, lastPoint);
       
 11193 				if (!stopped) {
       
 11194 					if (item !== overItem)
       
 11195 						overPoint = point;
       
 11196 					stopped = callEvent(this, type, event, point, item,
       
 11197 							overPoint);
       
 11198 				}
       
 11199 				lastPoint = overPoint = point;
       
 11200 				if (item !== overItem) {
       
 11201 					callEvent(this, 'mouseleave', event, point, overItem);
       
 11202 					overItem = item;
       
 11203 					callEvent(this, 'mouseenter', event, point, item);
       
 11204 				}
       
 11205 				break;
       
 11206 			}
       
 11207 			return stopped;
       
 11208 		}
       
 11209 	};
       
 11210 });
       
 11211 
       
 11212 var Event = Base.extend({
       
 11213 	_class: 'Event',
       
 11214 
       
 11215 	initialize: function Event(event) {
       
 11216 		this.event = event;
       
 11217 	},
       
 11218 
       
 11219 	isPrevented: false,
       
 11220 	isStopped: false,
       
 11221 
       
 11222 	preventDefault: function() {
       
 11223 		this.isPrevented = true;
       
 11224 		this.event.preventDefault();
       
 11225 	},
       
 11226 
       
 11227 	stopPropagation: function() {
       
 11228 		this.isStopped = true;
       
 11229 		this.event.stopPropagation();
       
 11230 	},
       
 11231 
       
 11232 	stop: function() {
       
 11233 		this.stopPropagation();
       
 11234 		this.preventDefault();
       
 11235 	},
       
 11236 
       
 11237 	getModifiers: function() {
       
 11238 		return Key.modifiers;
       
 11239 	}
       
 11240 });
       
 11241 
       
 11242 var KeyEvent = Event.extend({
       
 11243 	_class: 'KeyEvent',
       
 11244 
       
 11245 	initialize: function KeyEvent(down, key, character, event) {
       
 11246 		Event.call(this, event);
       
 11247 		this.type = down ? 'keydown' : 'keyup';
       
 11248 		this.key = key;
       
 11249 		this.character = character;
       
 11250 	},
       
 11251 
       
 11252 	toString: function() {
       
 11253 		return "{ type: '" + this.type
       
 11254 				+ "', key: '" + this.key
       
 11255 				+ "', character: '" + this.character
       
 11256 				+ "', modifiers: " + this.getModifiers()
       
 11257 				+ " }";
       
 11258 	}
       
 11259 });
       
 11260 
       
 11261 var Key = new function() {
       
 11262 
       
 11263 	var specialKeys = {
       
 11264 		8: 'backspace',
       
 11265 		9: 'tab',
       
 11266 		13: 'enter',
       
 11267 		16: 'shift',
       
 11268 		17: 'control',
       
 11269 		18: 'option',
       
 11270 		19: 'pause',
       
 11271 		20: 'caps-lock',
       
 11272 		27: 'escape',
       
 11273 		32: 'space',
       
 11274 		35: 'end',
       
 11275 		36: 'home',
       
 11276 		37: 'left',
       
 11277 		38: 'up',
       
 11278 		39: 'right',
       
 11279 		40: 'down',
       
 11280 		46: 'delete',
       
 11281 		91: 'command',
       
 11282 		93: 'command',
       
 11283 		224: 'command'
       
 11284 	},
       
 11285 
       
 11286 	specialChars = {
       
 11287 		9: true,
       
 11288 		13: true,
       
 11289 		32: true
       
 11290 	},
       
 11291 
       
 11292 	modifiers = new Base({
       
 11293 		shift: false,
       
 11294 		control: false,
       
 11295 		option: false,
       
 11296 		command: false,
       
 11297 		capsLock: false,
       
 11298 		space: false
       
 11299 	}),
       
 11300 
       
 11301 	charCodeMap = {},
       
 11302 	keyMap = {},
       
 11303 	commandFixMap,
       
 11304 	downCode;
       
 11305 
       
 11306 	function handleKey(down, keyCode, charCode, event) {
       
 11307 		var character = charCode ? String.fromCharCode(charCode) : '',
       
 11308 			specialKey = specialKeys[keyCode],
       
 11309 			key = specialKey || character.toLowerCase(),
       
 11310 			type = down ? 'keydown' : 'keyup',
       
 11311 			view = View._focused,
       
 11312 			scope = view && view.isVisible() && view._scope,
       
 11313 			tool = scope && scope.tool,
       
 11314 			name;
       
 11315 		keyMap[key] = down;
       
 11316 		if (down) {
       
 11317 			charCodeMap[keyCode] = charCode;
       
 11318 		} else {
       
 11319 			delete charCodeMap[keyCode];
       
 11320 		}
       
 11321 		if (specialKey && (name = Base.camelize(specialKey)) in modifiers) {
       
 11322 			modifiers[name] = down;
       
 11323 			var browser = paper.browser;
       
 11324 			if (name === 'command' && browser && browser.mac) {
       
 11325 				if (down) {
       
 11326 					commandFixMap = {};
       
 11327 				} else {
       
 11328 					for (var code in commandFixMap) {
       
 11329 						if (code in charCodeMap)
       
 11330 							handleKey(false, code, commandFixMap[code], event);
       
 11331 					}
       
 11332 					commandFixMap = null;
       
 11333 				}
       
 11334 			}
       
 11335 		} else if (down && commandFixMap) {
       
 11336 			commandFixMap[keyCode] = charCode;
       
 11337 		}
       
 11338 		if (tool && tool.responds(type)) {
       
 11339 			paper = scope;
       
 11340 			tool.emit(type, new KeyEvent(down, key, character, event));
       
 11341 			if (view)
       
 11342 				view.update();
       
 11343 		}
       
 11344 	}
       
 11345 
       
 11346 	DomEvent.add(document, {
       
 11347 		keydown: function(event) {
       
 11348 			var code = event.which || event.keyCode;
       
 11349 			if (code in specialKeys || modifiers.command) {
       
 11350 				handleKey(true, code,
       
 11351 						code in specialChars || modifiers.command ? code : 0,
       
 11352 						event);
       
 11353 			} else {
       
 11354 				downCode = code;
       
 11355 			}
       
 11356 		},
       
 11357 
       
 11358 		keypress: function(event) {
       
 11359 			if (downCode != null) {
       
 11360 				handleKey(true, downCode, event.which || event.keyCode, event);
       
 11361 				downCode = null;
       
 11362 			}
       
 11363 		},
       
 11364 
       
 11365 		keyup: function(event) {
       
 11366 			var code = event.which || event.keyCode;
       
 11367 			if (code in charCodeMap)
       
 11368 				handleKey(false, code, charCodeMap[code], event);
       
 11369 		}
       
 11370 	});
       
 11371 
       
 11372 	DomEvent.add(window, {
       
 11373 		blur: function(event) {
       
 11374 			for (var code in charCodeMap)
       
 11375 				handleKey(false, code, charCodeMap[code], event);
       
 11376 		}
       
 11377 	});
       
 11378 
       
 11379 	return {
       
 11380 		modifiers: modifiers,
       
 11381 
       
 11382 		isDown: function(key) {
       
 11383 			return !!keyMap[key];
       
 11384 		}
       
 11385 	};
       
 11386 };
       
 11387 
       
 11388 var MouseEvent = Event.extend({
       
 11389 	_class: 'MouseEvent',
       
 11390 
       
 11391 	initialize: function MouseEvent(type, event, point, target, delta) {
       
 11392 		Event.call(this, event);
       
 11393 		this.type = type;
       
 11394 		this.point = point;
       
 11395 		this.target = target;
       
 11396 		this.delta = delta;
       
 11397 	},
       
 11398 
       
 11399 	toString: function() {
       
 11400 		return "{ type: '" + this.type
       
 11401 				+ "', point: " + this.point
       
 11402 				+ ', target: ' + this.target
       
 11403 				+ (this.delta ? ', delta: ' + this.delta : '')
       
 11404 				+ ', modifiers: ' + this.getModifiers()
       
 11405 				+ ' }';
       
 11406 	}
       
 11407 });
       
 11408 
       
 11409 var ToolEvent = Event.extend({
       
 11410 	_class: 'ToolEvent',
       
 11411 	_item: null,
       
 11412 
       
 11413 	initialize: function ToolEvent(tool, type, event) {
       
 11414 		this.tool = tool;
       
 11415 		this.type = type;
       
 11416 		this.event = event;
       
 11417 	},
       
 11418 
       
 11419 	_choosePoint: function(point, toolPoint) {
       
 11420 		return point ? point : toolPoint ? toolPoint.clone() : null;
       
 11421 	},
       
 11422 
       
 11423 	getPoint: function() {
       
 11424 		return this._choosePoint(this._point, this.tool._point);
       
 11425 	},
       
 11426 
       
 11427 	setPoint: function(point) {
       
 11428 		this._point = point;
       
 11429 	},
       
 11430 
       
 11431 	getLastPoint: function() {
       
 11432 		return this._choosePoint(this._lastPoint, this.tool._lastPoint);
       
 11433 	},
       
 11434 
       
 11435 	setLastPoint: function(lastPoint) {
       
 11436 		this._lastPoint = lastPoint;
       
 11437 	},
       
 11438 
       
 11439 	getDownPoint: function() {
       
 11440 		return this._choosePoint(this._downPoint, this.tool._downPoint);
       
 11441 	},
       
 11442 
       
 11443 	setDownPoint: function(downPoint) {
       
 11444 		this._downPoint = downPoint;
       
 11445 	},
       
 11446 
       
 11447 	getMiddlePoint: function() {
       
 11448 		if (!this._middlePoint && this.tool._lastPoint) {
       
 11449 			return this.tool._point.add(this.tool._lastPoint).divide(2);
       
 11450 		}
       
 11451 		return this._middlePoint;
       
 11452 	},
       
 11453 
       
 11454 	setMiddlePoint: function(middlePoint) {
       
 11455 		this._middlePoint = middlePoint;
       
 11456 	},
       
 11457 
       
 11458 	getDelta: function() {
       
 11459 		return !this._delta && this.tool._lastPoint
       
 11460 				? this.tool._point.subtract(this.tool._lastPoint)
       
 11461 				: this._delta;
       
 11462 	},
       
 11463 
       
 11464 	setDelta: function(delta) {
       
 11465 		this._delta = delta;
       
 11466 	},
       
 11467 
       
 11468 	getCount: function() {
       
 11469 		return /^mouse(down|up)$/.test(this.type)
       
 11470 				? this.tool._downCount
       
 11471 				: this.tool._count;
       
 11472 	},
       
 11473 
       
 11474 	setCount: function(count) {
       
 11475 		this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
       
 11476 			= count;
       
 11477 	},
       
 11478 
       
 11479 	getItem: function() {
       
 11480 		if (!this._item) {
       
 11481 			var result = this.tool._scope.project.hitTest(this.getPoint());
       
 11482 			if (result) {
       
 11483 				var item = result.item,
       
 11484 					parent = item._parent;
       
 11485 				while (/^(Group|CompoundPath)$/.test(parent._class)) {
       
 11486 					item = parent;
       
 11487 					parent = parent._parent;
       
 11488 				}
       
 11489 				this._item = item;
       
 11490 			}
       
 11491 		}
       
 11492 		return this._item;
       
 11493 	},
       
 11494 
       
 11495 	setItem: function(item) {
       
 11496 		this._item = item;
       
 11497 	},
       
 11498 
       
 11499 	toString: function() {
       
 11500 		return '{ type: ' + this.type
       
 11501 				+ ', point: ' + this.getPoint()
       
 11502 				+ ', count: ' + this.getCount()
       
 11503 				+ ', modifiers: ' + this.getModifiers()
       
 11504 				+ ' }';
       
 11505 	}
       
 11506 });
       
 11507 
       
 11508 var Tool = PaperScopeItem.extend({
       
 11509 	_class: 'Tool',
       
 11510 	_list: 'tools',
       
 11511 	_reference: 'tool',
       
 11512 	_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
       
 11513 			'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
       
 11514 			'onKeyDown', 'onKeyUp' ],
       
 11515 
       
 11516 	initialize: function Tool(props) {
       
 11517 		PaperScopeItem.call(this);
       
 11518 		this._firstMove = true;
       
 11519 		this._count = 0;
       
 11520 		this._downCount = 0;
       
 11521 		this._set(props);
       
 11522 	},
       
 11523 
       
 11524 	getMinDistance: function() {
       
 11525 		return this._minDistance;
       
 11526 	},
       
 11527 
       
 11528 	setMinDistance: function(minDistance) {
       
 11529 		this._minDistance = minDistance;
       
 11530 		if (minDistance != null && this._maxDistance != null
       
 11531 				&& minDistance > this._maxDistance) {
       
 11532 			this._maxDistance = minDistance;
       
 11533 		}
       
 11534 	},
       
 11535 
       
 11536 	getMaxDistance: function() {
       
 11537 		return this._maxDistance;
       
 11538 	},
       
 11539 
       
 11540 	setMaxDistance: function(maxDistance) {
       
 11541 		this._maxDistance = maxDistance;
       
 11542 		if (this._minDistance != null && maxDistance != null
       
 11543 				&& maxDistance < this._minDistance) {
       
 11544 			this._minDistance = maxDistance;
       
 11545 		}
       
 11546 	},
       
 11547 
       
 11548 	getFixedDistance: function() {
       
 11549 		return this._minDistance == this._maxDistance
       
 11550 			? this._minDistance : null;
       
 11551 	},
       
 11552 
       
 11553 	setFixedDistance: function(distance) {
       
 11554 		this._minDistance = distance;
       
 11555 		this._maxDistance = distance;
       
 11556 	},
       
 11557 
       
 11558 	_updateEvent: function(type, point, minDistance, maxDistance, start,
       
 11559 			needsChange, matchMaxDistance) {
       
 11560 		if (!start) {
       
 11561 			if (minDistance != null || maxDistance != null) {
       
 11562 				var minDist = minDistance != null ? minDistance : 0,
       
 11563 					vector = point.subtract(this._point),
       
 11564 					distance = vector.getLength();
       
 11565 				if (distance < minDist)
       
 11566 					return false;
       
 11567 				if (maxDistance != null && maxDistance != 0) {
       
 11568 					if (distance > maxDistance) {
       
 11569 						point = this._point.add(vector.normalize(maxDistance));
       
 11570 					} else if (matchMaxDistance) {
       
 11571 						return false;
       
 11572 					}
       
 11573 				}
       
 11574 			}
       
 11575 			if (needsChange && point.equals(this._point))
       
 11576 				return false;
       
 11577 		}
       
 11578 		this._lastPoint = start && type == 'mousemove' ? point : this._point;
       
 11579 		this._point = point;
       
 11580 		switch (type) {
       
 11581 		case 'mousedown':
       
 11582 			this._lastPoint = this._downPoint;
       
 11583 			this._downPoint = this._point;
       
 11584 			this._downCount++;
       
 11585 			break;
       
 11586 		case 'mouseup':
       
 11587 			this._lastPoint = this._downPoint;
       
 11588 			break;
       
 11589 		}
       
 11590 		this._count = start ? 0 : this._count + 1;
       
 11591 		return true;
       
 11592 	},
       
 11593 
       
 11594 	_fireEvent: function(type, event) {
       
 11595 		var sets = paper.project._removeSets;
       
 11596 		if (sets) {
       
 11597 			if (type === 'mouseup')
       
 11598 				sets.mousedrag = null;
       
 11599 			var set = sets[type];
       
 11600 			if (set) {
       
 11601 				for (var id in set) {
       
 11602 					var item = set[id];
       
 11603 					for (var key in sets) {
       
 11604 						var other = sets[key];
       
 11605 						if (other && other != set)
       
 11606 							delete other[item._id];
       
 11607 					}
       
 11608 					item.remove();
       
 11609 				}
       
 11610 				sets[type] = null;
       
 11611 			}
       
 11612 		}
       
 11613 		return this.responds(type)
       
 11614 				&& this.emit(type, new ToolEvent(this, type, event));
       
 11615 	},
       
 11616 
       
 11617 	_handleEvent: function(type, point, event) {
       
 11618 		paper = this._scope;
       
 11619 		var called = false;
       
 11620 		switch (type) {
       
 11621 		case 'mousedown':
       
 11622 			this._updateEvent(type, point, null, null, true, false, false);
       
 11623 			called = this._fireEvent(type, event);
       
 11624 			break;
       
 11625 		case 'mousedrag':
       
 11626 			var needsChange = false,
       
 11627 				matchMaxDistance = false;
       
 11628 			while (this._updateEvent(type, point, this.minDistance,
       
 11629 					this.maxDistance, false, needsChange, matchMaxDistance)) {
       
 11630 				called = this._fireEvent(type, event) || called;
       
 11631 				needsChange = true;
       
 11632 				matchMaxDistance = true;
       
 11633 			}
       
 11634 			break;
       
 11635 		case 'mouseup':
       
 11636 			if (!point.equals(this._point)
       
 11637 					&& this._updateEvent('mousedrag', point, this.minDistance,
       
 11638 							this.maxDistance, false, false, false)) {
       
 11639 				called = this._fireEvent('mousedrag', event);
       
 11640 			}
       
 11641 			this._updateEvent(type, point, null, this.maxDistance, false,
       
 11642 					false, false);
       
 11643 			called = this._fireEvent(type, event) || called;
       
 11644 			this._updateEvent(type, point, null, null, true, false, false);
       
 11645 			this._firstMove = true;
       
 11646 			break;
       
 11647 		case 'mousemove':
       
 11648 			while (this._updateEvent(type, point, this.minDistance,
       
 11649 					this.maxDistance, this._firstMove, true, false)) {
       
 11650 				called = this._fireEvent(type, event) || called;
       
 11651 				this._firstMove = false;
       
 11652 			}
       
 11653 			break;
       
 11654 		}
       
 11655 		if (called)
       
 11656 			event.preventDefault();
       
 11657 		return called;
       
 11658 	}
       
 11659 
       
 11660 });
       
 11661 
       
 11662 var Http = {
       
 11663 	request: function(method, url, callback, async) {
       
 11664 		async = (async === undefined) ? true : async;
       
 11665 		var xhr = new (window.ActiveXObject || XMLHttpRequest)(
       
 11666 					'Microsoft.XMLHTTP');
       
 11667 		xhr.open(method.toUpperCase(), url, async);
       
 11668 		if ('overrideMimeType' in xhr)
       
 11669 			xhr.overrideMimeType('text/plain');
       
 11670 		xhr.onreadystatechange = function() {
       
 11671 			if (xhr.readyState === 4) {
       
 11672 				var status = xhr.status;
       
 11673 				if (status === 0 || status === 200) {
       
 11674 					callback.call(xhr, xhr.responseText);
       
 11675 				} else {
       
 11676 					throw new Error('Could not load ' + url + ' (Error '
       
 11677 							+ status + ')');
       
 11678 				}
       
 11679 			}
       
 11680 		};
       
 11681 		return xhr.send(null);
       
 11682 	}
       
 11683 };
       
 11684 
       
 11685 var CanvasProvider = {
       
 11686 	canvases: [],
       
 11687 
       
 11688 	getCanvas: function(width, height) {
       
 11689 		var canvas,
       
 11690 			clear = true;
       
 11691 		if (typeof width === 'object') {
       
 11692 			height = width.height;
       
 11693 			width = width.width;
       
 11694 		}
       
 11695 		if (this.canvases.length) {
       
 11696 			canvas = this.canvases.pop();
       
 11697 		} else {
       
 11698 			canvas = document.createElement('canvas');
       
 11699 		}
       
 11700 		var ctx = canvas.getContext('2d');
       
 11701 		if (canvas.width === width && canvas.height === height) {
       
 11702 			if (clear)
       
 11703 				ctx.clearRect(0, 0, width + 1, height + 1);
       
 11704 		} else {
       
 11705 			canvas.width = width;
       
 11706 			canvas.height = height;
       
 11707 		}
       
 11708 		ctx.save();
       
 11709 		return canvas;
       
 11710 	},
       
 11711 
       
 11712 	getContext: function(width, height) {
       
 11713 		return this.getCanvas(width, height).getContext('2d');
       
 11714 	},
       
 11715 
       
 11716 	release: function(obj) {
       
 11717 		var canvas = obj.canvas ? obj.canvas : obj;
       
 11718 		canvas.getContext('2d').restore();
       
 11719 		this.canvases.push(canvas);
       
 11720 	}
       
 11721 };
       
 11722 
       
 11723 var BlendMode = new function() {
       
 11724 	var min = Math.min,
       
 11725 		max = Math.max,
       
 11726 		abs = Math.abs,
       
 11727 		sr, sg, sb, sa,
       
 11728 		br, bg, bb, ba,
       
 11729 		dr, dg, db;
       
 11730 
       
 11731 	function getLum(r, g, b) {
       
 11732 		return 0.2989 * r + 0.587 * g + 0.114 * b;
       
 11733 	}
       
 11734 
       
 11735 	function setLum(r, g, b, l) {
       
 11736 		var d = l - getLum(r, g, b);
       
 11737 		dr = r + d;
       
 11738 		dg = g + d;
       
 11739 		db = b + d;
       
 11740 		var l = getLum(dr, dg, db),
       
 11741 			mn = min(dr, dg, db),
       
 11742 			mx = max(dr, dg, db);
       
 11743 		if (mn < 0) {
       
 11744 			var lmn = l - mn;
       
 11745 			dr = l + (dr - l) * l / lmn;
       
 11746 			dg = l + (dg - l) * l / lmn;
       
 11747 			db = l + (db - l) * l / lmn;
       
 11748 		}
       
 11749 		if (mx > 255) {
       
 11750 			var ln = 255 - l,
       
 11751 				mxl = mx - l;
       
 11752 			dr = l + (dr - l) * ln / mxl;
       
 11753 			dg = l + (dg - l) * ln / mxl;
       
 11754 			db = l + (db - l) * ln / mxl;
       
 11755 		}
       
 11756 	}
       
 11757 
       
 11758 	function getSat(r, g, b) {
       
 11759 		return max(r, g, b) - min(r, g, b);
       
 11760 	}
       
 11761 
       
 11762 	function setSat(r, g, b, s) {
       
 11763 		var col = [r, g, b],
       
 11764 			mx = max(r, g, b),
       
 11765 			mn = min(r, g, b),
       
 11766 			md;
       
 11767 		mn = mn === r ? 0 : mn === g ? 1 : 2;
       
 11768 		mx = mx === r ? 0 : mx === g ? 1 : 2;
       
 11769 		md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;
       
 11770 		if (col[mx] > col[mn]) {
       
 11771 			col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]);
       
 11772 			col[mx] = s;
       
 11773 		} else {
       
 11774 			col[md] = col[mx] = 0;
       
 11775 		}
       
 11776 		col[mn] = 0;
       
 11777 		dr = col[0];
       
 11778 		dg = col[1];
       
 11779 		db = col[2];
       
 11780 	}
       
 11781 
       
 11782 	var modes = {
       
 11783 		multiply: function() {
       
 11784 			dr = br * sr / 255;
       
 11785 			dg = bg * sg / 255;
       
 11786 			db = bb * sb / 255;
       
 11787 		},
       
 11788 
       
 11789 		screen: function() {
       
 11790 			dr = br + sr - (br * sr / 255);
       
 11791 			dg = bg + sg - (bg * sg / 255);
       
 11792 			db = bb + sb - (bb * sb / 255);
       
 11793 		},
       
 11794 
       
 11795 		overlay: function() {
       
 11796 			dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255;
       
 11797 			dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255;
       
 11798 			db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255;
       
 11799 		},
       
 11800 
       
 11801 		'soft-light': function() {
       
 11802 			var t = sr * br / 255;
       
 11803 			dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255;
       
 11804 			t = sg * bg / 255;
       
 11805 			dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255;
       
 11806 			t = sb * bb / 255;
       
 11807 			db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255;
       
 11808 		},
       
 11809 
       
 11810 		'hard-light': function() {
       
 11811 			dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255;
       
 11812 			dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255;
       
 11813 			db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255;
       
 11814 		},
       
 11815 
       
 11816 		'color-dodge': function() {
       
 11817 			dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr));
       
 11818 			dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg));
       
 11819 			db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb));
       
 11820 		},
       
 11821 
       
 11822 		'color-burn': function() {
       
 11823 			dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr);
       
 11824 			dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg);
       
 11825 			db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb);
       
 11826 		},
       
 11827 
       
 11828 		darken: function() {
       
 11829 			dr = br < sr ? br : sr;
       
 11830 			dg = bg < sg ? bg : sg;
       
 11831 			db = bb < sb ? bb : sb;
       
 11832 		},
       
 11833 
       
 11834 		lighten: function() {
       
 11835 			dr = br > sr ? br : sr;
       
 11836 			dg = bg > sg ? bg : sg;
       
 11837 			db = bb > sb ? bb : sb;
       
 11838 		},
       
 11839 
       
 11840 		difference: function() {
       
 11841 			dr = br - sr;
       
 11842 			if (dr < 0)
       
 11843 				dr = -dr;
       
 11844 			dg = bg - sg;
       
 11845 			if (dg < 0)
       
 11846 				dg = -dg;
       
 11847 			db = bb - sb;
       
 11848 			if (db < 0)
       
 11849 				db = -db;
       
 11850 		},
       
 11851 
       
 11852 		exclusion: function() {
       
 11853 			dr = br + sr * (255 - br - br) / 255;
       
 11854 			dg = bg + sg * (255 - bg - bg) / 255;
       
 11855 			db = bb + sb * (255 - bb - bb) / 255;
       
 11856 		},
       
 11857 
       
 11858 		hue: function() {
       
 11859 			setSat(sr, sg, sb, getSat(br, bg, bb));
       
 11860 			setLum(dr, dg, db, getLum(br, bg, bb));
       
 11861 		},
       
 11862 
       
 11863 		saturation: function() {
       
 11864 			setSat(br, bg, bb, getSat(sr, sg, sb));
       
 11865 			setLum(dr, dg, db, getLum(br, bg, bb));
       
 11866 		},
       
 11867 
       
 11868 		luminosity: function() {
       
 11869 			setLum(br, bg, bb, getLum(sr, sg, sb));
       
 11870 		},
       
 11871 
       
 11872 		color: function() {
       
 11873 			setLum(sr, sg, sb, getLum(br, bg, bb));
       
 11874 		},
       
 11875 
       
 11876 		add: function() {
       
 11877 			dr = min(br + sr, 255);
       
 11878 			dg = min(bg + sg, 255);
       
 11879 			db = min(bb + sb, 255);
       
 11880 		},
       
 11881 
       
 11882 		subtract: function() {
       
 11883 			dr = max(br - sr, 0);
       
 11884 			dg = max(bg - sg, 0);
       
 11885 			db = max(bb - sb, 0);
       
 11886 		},
       
 11887 
       
 11888 		average: function() {
       
 11889 			dr = (br + sr) / 2;
       
 11890 			dg = (bg + sg) / 2;
       
 11891 			db = (bb + sb) / 2;
       
 11892 		},
       
 11893 
       
 11894 		negation: function() {
       
 11895 			dr = 255 - abs(255 - sr - br);
       
 11896 			dg = 255 - abs(255 - sg - bg);
       
 11897 			db = 255 - abs(255 - sb - bb);
       
 11898 		}
       
 11899 	};
       
 11900 
       
 11901 	var nativeModes = this.nativeModes = Base.each([
       
 11902 		'source-over', 'source-in', 'source-out', 'source-atop',
       
 11903 		'destination-over', 'destination-in', 'destination-out',
       
 11904 		'destination-atop', 'lighter', 'darker', 'copy', 'xor'
       
 11905 	], function(mode) {
       
 11906 		this[mode] = true;
       
 11907 	}, {});
       
 11908 
       
 11909 	var ctx = CanvasProvider.getContext(1, 1);
       
 11910 	Base.each(modes, function(func, mode) {
       
 11911 		var darken = mode === 'darken',
       
 11912 			ok = false;
       
 11913 		ctx.save();
       
 11914 		try {
       
 11915 			ctx.fillStyle = darken ? '#300' : '#a00';
       
 11916 			ctx.fillRect(0, 0, 1, 1);
       
 11917 			ctx.globalCompositeOperation = mode;
       
 11918 			if (ctx.globalCompositeOperation === mode) {
       
 11919 				ctx.fillStyle = darken ? '#a00' : '#300';
       
 11920 				ctx.fillRect(0, 0, 1, 1);
       
 11921 				ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51;
       
 11922 			}
       
 11923 		} catch (e) {}
       
 11924 		ctx.restore();
       
 11925 		nativeModes[mode] = ok;
       
 11926 	});
       
 11927 	CanvasProvider.release(ctx);
       
 11928 
       
 11929 	this.process = function(mode, srcContext, dstContext, alpha, offset) {
       
 11930 		var srcCanvas = srcContext.canvas,
       
 11931 			normal = mode === 'normal';
       
 11932 		if (normal || nativeModes[mode]) {
       
 11933 			dstContext.save();
       
 11934 			dstContext.setTransform(1, 0, 0, 1, 0, 0);
       
 11935 			dstContext.globalAlpha = alpha;
       
 11936 			if (!normal)
       
 11937 				dstContext.globalCompositeOperation = mode;
       
 11938 			dstContext.drawImage(srcCanvas, offset.x, offset.y);
       
 11939 			dstContext.restore();
       
 11940 		} else {
       
 11941 			var process = modes[mode];
       
 11942 			if (!process)
       
 11943 				return;
       
 11944 			var dstData = dstContext.getImageData(offset.x, offset.y,
       
 11945 					srcCanvas.width, srcCanvas.height),
       
 11946 				dst = dstData.data,
       
 11947 				src = srcContext.getImageData(0, 0,
       
 11948 					srcCanvas.width, srcCanvas.height).data;
       
 11949 			for (var i = 0, l = dst.length; i < l; i += 4) {
       
 11950 				sr = src[i];
       
 11951 				br = dst[i];
       
 11952 				sg = src[i + 1];
       
 11953 				bg = dst[i + 1];
       
 11954 				sb = src[i + 2];
       
 11955 				bb = dst[i + 2];
       
 11956 				sa = src[i + 3];
       
 11957 				ba = dst[i + 3];
       
 11958 				process();
       
 11959 				var a1 = sa * alpha / 255,
       
 11960 					a2 = 1 - a1;
       
 11961 				dst[i] = a1 * dr + a2 * br;
       
 11962 				dst[i + 1] = a1 * dg + a2 * bg;
       
 11963 				dst[i + 2] = a1 * db + a2 * bb;
       
 11964 				dst[i + 3] = sa * alpha + a2 * ba;
       
 11965 			}
       
 11966 			dstContext.putImageData(dstData, offset.x, offset.y);
       
 11967 		}
       
 11968 	};
       
 11969 };
       
 11970 
       
 11971 var SVGStyles = Base.each({
       
 11972 	fillColor: ['fill', 'color'],
       
 11973 	strokeColor: ['stroke', 'color'],
       
 11974 	strokeWidth: ['stroke-width', 'number'],
       
 11975 	strokeCap: ['stroke-linecap', 'string'],
       
 11976 	strokeJoin: ['stroke-linejoin', 'string'],
       
 11977 	strokeScaling: ['vector-effect', 'lookup', {
       
 11978 		true: 'none',
       
 11979 		false: 'non-scaling-stroke'
       
 11980 	}, function(item, value) {
       
 11981 		return !value
       
 11982 				&& (item instanceof PathItem
       
 11983 					|| item instanceof Shape
       
 11984 					|| item instanceof TextItem);
       
 11985 	}],
       
 11986 	miterLimit: ['stroke-miterlimit', 'number'],
       
 11987 	dashArray: ['stroke-dasharray', 'array'],
       
 11988 	dashOffset: ['stroke-dashoffset', 'number'],
       
 11989 	fontFamily: ['font-family', 'string'],
       
 11990 	fontWeight: ['font-weight', 'string'],
       
 11991 	fontSize: ['font-size', 'number'],
       
 11992 	justification: ['text-anchor', 'lookup', {
       
 11993 		left: 'start',
       
 11994 		center: 'middle',
       
 11995 		right: 'end'
       
 11996 	}],
       
 11997 	opacity: ['opacity', 'number'],
       
 11998 	blendMode: ['mix-blend-mode', 'string']
       
 11999 }, function(entry, key) {
       
 12000 	var part = Base.capitalize(key),
       
 12001 		lookup = entry[2];
       
 12002 	this[key] = {
       
 12003 		type: entry[1],
       
 12004 		property: key,
       
 12005 		attribute: entry[0],
       
 12006 		toSVG: lookup,
       
 12007 		fromSVG: lookup && Base.each(lookup, function(value, name) {
       
 12008 			this[value] = name;
       
 12009 		}, {}),
       
 12010 		exportFilter: entry[3],
       
 12011 		get: 'get' + part,
       
 12012 		set: 'set' + part
       
 12013 	};
       
 12014 }, {});
       
 12015 
       
 12016 var SVGNamespaces = {
       
 12017 	href: 'http://www.w3.org/1999/xlink',
       
 12018 	xlink: 'http://www.w3.org/2000/xmlns'
       
 12019 };
       
 12020 
       
 12021 new function() {
       
 12022 	var formatter;
       
 12023 
       
 12024 	function setAttributes(node, attrs) {
       
 12025 		for (var key in attrs) {
       
 12026 			var val = attrs[key],
       
 12027 				namespace = SVGNamespaces[key];
       
 12028 			if (typeof val === 'number')
       
 12029 				val = formatter.number(val);
       
 12030 			if (namespace) {
       
 12031 				node.setAttributeNS(namespace, key, val);
       
 12032 			} else {
       
 12033 				node.setAttribute(key, val);
       
 12034 			}
       
 12035 		}
       
 12036 		return node;
       
 12037 	}
       
 12038 
       
 12039 	function createElement(tag, attrs) {
       
 12040 		return setAttributes(
       
 12041 			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
       
 12042 	}
       
 12043 
       
 12044 	function getTransform(matrix, coordinates, center) {
       
 12045 		var attrs = new Base(),
       
 12046 			trans = matrix.getTranslation();
       
 12047 		if (coordinates) {
       
 12048 			matrix = matrix.shiftless();
       
 12049 			var point = matrix._inverseTransform(trans);
       
 12050 			attrs[center ? 'cx' : 'x'] = point.x;
       
 12051 			attrs[center ? 'cy' : 'y'] = point.y;
       
 12052 			trans = null;
       
 12053 		}
       
 12054 		if (!matrix.isIdentity()) {
       
 12055 			var decomposed = matrix.decompose();
       
 12056 			if (decomposed && !decomposed.shearing) {
       
 12057 				var parts = [],
       
 12058 					angle = decomposed.rotation,
       
 12059 					scale = decomposed.scaling;
       
 12060 				if (trans && !trans.isZero())
       
 12061 					parts.push('translate(' + formatter.point(trans) + ')');
       
 12062 				if (!Numerical.isZero(scale.x - 1)
       
 12063 						|| !Numerical.isZero(scale.y - 1))
       
 12064 					parts.push('scale(' + formatter.point(scale) +')');
       
 12065 				if (angle)
       
 12066 					parts.push('rotate(' + formatter.number(angle) + ')');
       
 12067 				attrs.transform = parts.join(' ');
       
 12068 			} else {
       
 12069 				attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
       
 12070 			}
       
 12071 		}
       
 12072 		return attrs;
       
 12073 	}
       
 12074 
       
 12075 	function exportGroup(item, options) {
       
 12076 		var attrs = getTransform(item._matrix),
       
 12077 			children = item._children;
       
 12078 		var node = createElement('g', attrs);
       
 12079 		for (var i = 0, l = children.length; i < l; i++) {
       
 12080 			var child = children[i];
       
 12081 			var childNode = exportSVG(child, options);
       
 12082 			if (childNode) {
       
 12083 				if (child.isClipMask()) {
       
 12084 					var clip = createElement('clipPath');
       
 12085 					clip.appendChild(childNode);
       
 12086 					setDefinition(child, clip, 'clip');
       
 12087 					setAttributes(node, {
       
 12088 						'clip-path': 'url(#' + clip.id + ')'
       
 12089 					});
       
 12090 				} else {
       
 12091 					node.appendChild(childNode);
       
 12092 				}
       
 12093 			}
       
 12094 		}
       
 12095 		return node;
       
 12096 	}
       
 12097 
       
 12098 	function exportRaster(item, options) {
       
 12099 		var attrs = getTransform(item._matrix, true),
       
 12100 			size = item.getSize(),
       
 12101 			image = item.getImage();
       
 12102 		attrs.x -= size.width / 2;
       
 12103 		attrs.y -= size.height / 2;
       
 12104 		attrs.width = size.width;
       
 12105 		attrs.height = size.height;
       
 12106 		attrs.href = options.embedImages === false && image && image.src
       
 12107 				|| item.toDataURL();
       
 12108 		return createElement('image', attrs);
       
 12109 	}
       
 12110 
       
 12111 	function exportPath(item, options) {
       
 12112 		var matchShapes = options.matchShapes;
       
 12113 		if (matchShapes) {
       
 12114 			var shape = item.toShape(false);
       
 12115 			if (shape)
       
 12116 				return exportShape(shape, options);
       
 12117 		}
       
 12118 		var segments = item._segments,
       
 12119 			type,
       
 12120 			attrs = getTransform(item._matrix);
       
 12121 		if (segments.length === 0)
       
 12122 			return null;
       
 12123 		if (matchShapes && !item.hasHandles()) {
       
 12124 			if (segments.length >= 3) {
       
 12125 				type = item._closed ? 'polygon' : 'polyline';
       
 12126 				var parts = [];
       
 12127 				for(var i = 0, l = segments.length; i < l; i++)
       
 12128 					parts.push(formatter.point(segments[i]._point));
       
 12129 				attrs.points = parts.join(' ');
       
 12130 			} else {
       
 12131 				type = 'line';
       
 12132 				var first = segments[0]._point,
       
 12133 					last = segments[segments.length - 1]._point;
       
 12134 				attrs.set({
       
 12135 					x1: first.x,
       
 12136 					y1: first.y,
       
 12137 					x2: last.x,
       
 12138 					y2: last.y
       
 12139 				});
       
 12140 			}
       
 12141 		} else {
       
 12142 			type = 'path';
       
 12143 			attrs.d = item.getPathData(null, options.precision);
       
 12144 		}
       
 12145 		return createElement(type, attrs);
       
 12146 	}
       
 12147 
       
 12148 	function exportShape(item) {
       
 12149 		var type = item._type,
       
 12150 			radius = item._radius,
       
 12151 			attrs = getTransform(item._matrix, true, type !== 'rectangle');
       
 12152 		if (type === 'rectangle') {
       
 12153 			type = 'rect';
       
 12154 			var size = item._size,
       
 12155 				width = size.width,
       
 12156 				height = size.height;
       
 12157 			attrs.x -= width / 2;
       
 12158 			attrs.y -= height / 2;
       
 12159 			attrs.width = width;
       
 12160 			attrs.height = height;
       
 12161 			if (radius.isZero())
       
 12162 				radius = null;
       
 12163 		}
       
 12164 		if (radius) {
       
 12165 			if (type === 'circle') {
       
 12166 				attrs.r = radius;
       
 12167 			} else {
       
 12168 				attrs.rx = radius.width;
       
 12169 				attrs.ry = radius.height;
       
 12170 			}
       
 12171 		}
       
 12172 		return createElement(type, attrs);
       
 12173 	}
       
 12174 
       
 12175 	function exportCompoundPath(item, options) {
       
 12176 		var attrs = getTransform(item._matrix);
       
 12177 		var data = item.getPathData(null, options.precision);
       
 12178 		if (data)
       
 12179 			attrs.d = data;
       
 12180 		return createElement('path', attrs);
       
 12181 	}
       
 12182 
       
 12183 	function exportPlacedSymbol(item, options) {
       
 12184 		var attrs = getTransform(item._matrix, true),
       
 12185 			symbol = item.getSymbol(),
       
 12186 			symbolNode = getDefinition(symbol, 'symbol'),
       
 12187 			definition = symbol.getDefinition(),
       
 12188 			bounds = definition.getBounds();
       
 12189 		if (!symbolNode) {
       
 12190 			symbolNode = createElement('symbol', {
       
 12191 				viewBox: formatter.rectangle(bounds)
       
 12192 			});
       
 12193 			symbolNode.appendChild(exportSVG(definition, options));
       
 12194 			setDefinition(symbol, symbolNode, 'symbol');
       
 12195 		}
       
 12196 		attrs.href = '#' + symbolNode.id;
       
 12197 		attrs.x += bounds.x;
       
 12198 		attrs.y += bounds.y;
       
 12199 		attrs.width = formatter.number(bounds.width);
       
 12200 		attrs.height = formatter.number(bounds.height);
       
 12201 		attrs.overflow = 'visible';
       
 12202 		return createElement('use', attrs);
       
 12203 	}
       
 12204 
       
 12205 	function exportGradient(color) {
       
 12206 		var gradientNode = getDefinition(color, 'color');
       
 12207 		if (!gradientNode) {
       
 12208 			var gradient = color.getGradient(),
       
 12209 				radial = gradient._radial,
       
 12210 				origin = color.getOrigin().transform(),
       
 12211 				destination = color.getDestination().transform(),
       
 12212 				attrs;
       
 12213 			if (radial) {
       
 12214 				attrs = {
       
 12215 					cx: origin.x,
       
 12216 					cy: origin.y,
       
 12217 					r: origin.getDistance(destination)
       
 12218 				};
       
 12219 				var highlight = color.getHighlight();
       
 12220 				if (highlight) {
       
 12221 					highlight = highlight.transform();
       
 12222 					attrs.fx = highlight.x;
       
 12223 					attrs.fy = highlight.y;
       
 12224 				}
       
 12225 			} else {
       
 12226 				attrs = {
       
 12227 					x1: origin.x,
       
 12228 					y1: origin.y,
       
 12229 					x2: destination.x,
       
 12230 					y2: destination.y
       
 12231 				};
       
 12232 			}
       
 12233 			attrs.gradientUnits = 'userSpaceOnUse';
       
 12234 			gradientNode = createElement(
       
 12235 					(radial ? 'radial' : 'linear') + 'Gradient', attrs);
       
 12236 			var stops = gradient._stops;
       
 12237 			for (var i = 0, l = stops.length; i < l; i++) {
       
 12238 				var stop = stops[i],
       
 12239 					stopColor = stop._color,
       
 12240 					alpha = stopColor.getAlpha();
       
 12241 				attrs = {
       
 12242 					offset: stop._rampPoint,
       
 12243 					'stop-color': stopColor.toCSS(true)
       
 12244 				};
       
 12245 				if (alpha < 1)
       
 12246 					attrs['stop-opacity'] = alpha;
       
 12247 				gradientNode.appendChild(createElement('stop', attrs));
       
 12248 			}
       
 12249 			setDefinition(color, gradientNode, 'color');
       
 12250 		}
       
 12251 		return 'url(#' + gradientNode.id + ')';
       
 12252 	}
       
 12253 
       
 12254 	function exportText(item) {
       
 12255 		var node = createElement('text', getTransform(item._matrix, true));
       
 12256 		node.textContent = item._content;
       
 12257 		return node;
       
 12258 	}
       
 12259 
       
 12260 	var exporters = {
       
 12261 		Group: exportGroup,
       
 12262 		Layer: exportGroup,
       
 12263 		Raster: exportRaster,
       
 12264 		Path: exportPath,
       
 12265 		Shape: exportShape,
       
 12266 		CompoundPath: exportCompoundPath,
       
 12267 		PlacedSymbol: exportPlacedSymbol,
       
 12268 		PointText: exportText
       
 12269 	};
       
 12270 
       
 12271 	function applyStyle(item, node, isRoot) {
       
 12272 		var attrs = {},
       
 12273 			parent = !isRoot && item.getParent();
       
 12274 
       
 12275 		if (item._name != null)
       
 12276 			attrs.id = item._name;
       
 12277 
       
 12278 		Base.each(SVGStyles, function(entry) {
       
 12279 			var get = entry.get,
       
 12280 				type = entry.type,
       
 12281 				value = item[get]();
       
 12282 			if (entry.exportFilter
       
 12283 					? entry.exportFilter(item, value)
       
 12284 					: !parent || !Base.equals(parent[get](), value)) {
       
 12285 				if (type === 'color' && value != null) {
       
 12286 					var alpha = value.getAlpha();
       
 12287 					if (alpha < 1)
       
 12288 						attrs[entry.attribute + '-opacity'] = alpha;
       
 12289 				}
       
 12290 				attrs[entry.attribute] = value == null
       
 12291 					? 'none'
       
 12292 					: type === 'number'
       
 12293 						? formatter.number(value)
       
 12294 						: type === 'color'
       
 12295 							? value.gradient
       
 12296 								? exportGradient(value, item)
       
 12297 								: value.toCSS(true)
       
 12298 							: type === 'array'
       
 12299 								? value.join(',')
       
 12300 								: type === 'lookup'
       
 12301 									? entry.toSVG[value]
       
 12302 									: value;
       
 12303 			}
       
 12304 		});
       
 12305 
       
 12306 		if (attrs.opacity === 1)
       
 12307 			delete attrs.opacity;
       
 12308 
       
 12309 		if (!item._visible)
       
 12310 			attrs.visibility = 'hidden';
       
 12311 
       
 12312 		return setAttributes(node, attrs);
       
 12313 	}
       
 12314 
       
 12315 	var definitions;
       
 12316 	function getDefinition(item, type) {
       
 12317 		if (!definitions)
       
 12318 			definitions = { ids: {}, svgs: {} };
       
 12319 		return item && definitions.svgs[type + '-' + item._id];
       
 12320 	}
       
 12321 
       
 12322 	function setDefinition(item, node, type) {
       
 12323 		if (!definitions)
       
 12324 			getDefinition();
       
 12325 		var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
       
 12326 		node.id = type + '-' + id;
       
 12327 		definitions.svgs[type + '-' + item._id] = node;
       
 12328 	}
       
 12329 
       
 12330 	function exportDefinitions(node, options) {
       
 12331 		var svg = node,
       
 12332 			defs = null;
       
 12333 		if (definitions) {
       
 12334 			svg = node.nodeName.toLowerCase() === 'svg' && node;
       
 12335 			for (var i in definitions.svgs) {
       
 12336 				if (!defs) {
       
 12337 					if (!svg) {
       
 12338 						svg = createElement('svg');
       
 12339 						svg.appendChild(node);
       
 12340 					}
       
 12341 					defs = svg.insertBefore(createElement('defs'),
       
 12342 							svg.firstChild);
       
 12343 				}
       
 12344 				defs.appendChild(definitions.svgs[i]);
       
 12345 			}
       
 12346 			definitions = null;
       
 12347 		}
       
 12348 		return options.asString
       
 12349 				? new XMLSerializer().serializeToString(svg)
       
 12350 				: svg;
       
 12351 	}
       
 12352 
       
 12353 	function exportSVG(item, options, isRoot) {
       
 12354 		var exporter = exporters[item._class],
       
 12355 			node = exporter && exporter(item, options);
       
 12356 		if (node) {
       
 12357 			var onExport = options.onExport;
       
 12358 			if (onExport)
       
 12359 				node = onExport(item, node, options) || node;
       
 12360 			var data = JSON.stringify(item._data);
       
 12361 			if (data && data !== '{}' && data !== 'null')
       
 12362 				node.setAttribute('data-paper-data', data);
       
 12363 		}
       
 12364 		return node && applyStyle(item, node, isRoot);
       
 12365 	}
       
 12366 
       
 12367 	function setOptions(options) {
       
 12368 		if (!options)
       
 12369 			options = {};
       
 12370 		formatter = new Formatter(options.precision);
       
 12371 		return options;
       
 12372 	}
       
 12373 
       
 12374 	Item.inject({
       
 12375 		exportSVG: function(options) {
       
 12376 			options = setOptions(options);
       
 12377 			return exportDefinitions(exportSVG(this, options, true), options);
       
 12378 		}
       
 12379 	});
       
 12380 
       
 12381 	Project.inject({
       
 12382 		exportSVG: function(options) {
       
 12383 			options = setOptions(options);
       
 12384 			var layers = this.layers,
       
 12385 				view = this.getView(),
       
 12386 				size = view.getViewSize(),
       
 12387 				node = createElement('svg', {
       
 12388 					x: 0,
       
 12389 					y: 0,
       
 12390 					width: size.width,
       
 12391 					height: size.height,
       
 12392 					version: '1.1',
       
 12393 					xmlns: 'http://www.w3.org/2000/svg',
       
 12394 					'xmlns:xlink': 'http://www.w3.org/1999/xlink'
       
 12395 				}),
       
 12396 				parent = node,
       
 12397 				matrix = view._matrix;
       
 12398 			if (!matrix.isIdentity())
       
 12399 				parent = node.appendChild(
       
 12400 						createElement('g', getTransform(matrix)));
       
 12401 			for (var i = 0, l = layers.length; i < l; i++)
       
 12402 				parent.appendChild(exportSVG(layers[i], options, true));
       
 12403 			return exportDefinitions(node, options);
       
 12404 		}
       
 12405 	});
       
 12406 };
       
 12407 
       
 12408 new function() {
       
 12409 
       
 12410 	function getValue(node, name, isString, allowNull) {
       
 12411 		var namespace = SVGNamespaces[name],
       
 12412 			value = namespace
       
 12413 				? node.getAttributeNS(namespace, name)
       
 12414 				: node.getAttribute(name);
       
 12415 		if (value === 'null')
       
 12416 			value = null;
       
 12417 		return value == null
       
 12418 				? allowNull
       
 12419 					? null
       
 12420 					: isString
       
 12421 						? ''
       
 12422 						: 0
       
 12423 				: isString
       
 12424 					? value
       
 12425 					: parseFloat(value);
       
 12426 	}
       
 12427 
       
 12428 	function getPoint(node, x, y, allowNull) {
       
 12429 		x = getValue(node, x, false, allowNull);
       
 12430 		y = getValue(node, y, false, allowNull);
       
 12431 		return allowNull && (x == null || y == null) ? null
       
 12432 				: new Point(x, y);
       
 12433 	}
       
 12434 
       
 12435 	function getSize(node, w, h, allowNull) {
       
 12436 		w = getValue(node, w, false, allowNull);
       
 12437 		h = getValue(node, h, false, allowNull);
       
 12438 		return allowNull && (w == null || h == null) ? null
       
 12439 				: new Size(w, h);
       
 12440 	}
       
 12441 
       
 12442 	function convertValue(value, type, lookup) {
       
 12443 		return value === 'none'
       
 12444 				? null
       
 12445 				: type === 'number'
       
 12446 					? parseFloat(value)
       
 12447 					: type === 'array'
       
 12448 						? value ? value.split(/[\s,]+/g).map(parseFloat) : []
       
 12449 						: type === 'color'
       
 12450 							? getDefinition(value) || value
       
 12451 							: type === 'lookup'
       
 12452 								? lookup[value]
       
 12453 								: value;
       
 12454 	}
       
 12455 
       
 12456 	function importGroup(node, type, options, isRoot) {
       
 12457 		var nodes = node.childNodes,
       
 12458 			isClip = type === 'clippath',
       
 12459 			item = new Group(),
       
 12460 			project = item._project,
       
 12461 			currentStyle = project._currentStyle,
       
 12462 			children = [];
       
 12463 		if (!isClip) {
       
 12464 			item = applyAttributes(item, node, isRoot);
       
 12465 			project._currentStyle = item._style.clone();
       
 12466 		}
       
 12467 		if (isRoot) {
       
 12468 			var defs = node.querySelectorAll('defs');
       
 12469 			for (var i = 0, l = defs.length; i < l; i++) {
       
 12470 				importSVG(defs[i], options, false);
       
 12471 			}
       
 12472 		}
       
 12473 		for (var i = 0, l = nodes.length; i < l; i++) {
       
 12474 			var childNode = nodes[i],
       
 12475 				child;
       
 12476 			if (childNode.nodeType === 1
       
 12477 					&& childNode.nodeName.toLowerCase() !== 'defs'
       
 12478 					&& (child = importSVG(childNode, options, false))
       
 12479 					&& !(child instanceof Symbol))
       
 12480 				children.push(child);
       
 12481 		}
       
 12482 		item.addChildren(children);
       
 12483 		if (isClip)
       
 12484 			item = applyAttributes(item.reduce(), node, isRoot);
       
 12485 		project._currentStyle = currentStyle;
       
 12486 		if (isClip || type === 'defs') {
       
 12487 			item.remove();
       
 12488 			item = null;
       
 12489 		}
       
 12490 		return item;
       
 12491 	}
       
 12492 
       
 12493 	function importPoly(node, type) {
       
 12494 		var coords = node.getAttribute('points').match(
       
 12495 					/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g),
       
 12496 			points = [];
       
 12497 		for (var i = 0, l = coords.length; i < l; i += 2)
       
 12498 			points.push(new Point(
       
 12499 					parseFloat(coords[i]),
       
 12500 					parseFloat(coords[i + 1])));
       
 12501 		var path = new Path(points);
       
 12502 		if (type === 'polygon')
       
 12503 			path.closePath();
       
 12504 		return path;
       
 12505 	}
       
 12506 
       
 12507 	function importPath(node) {
       
 12508 		var data = node.getAttribute('d'),
       
 12509 			param = { pathData: data };
       
 12510 		return (data.match(/m/gi) || []).length > 1 || /z\S+/i.test(data)
       
 12511 				? new CompoundPath(param)
       
 12512 				: new Path(param);
       
 12513 	}
       
 12514 
       
 12515 	function importGradient(node, type) {
       
 12516 		var id = (getValue(node, 'href', true) || '').substring(1),
       
 12517 			isRadial = type === 'radialgradient',
       
 12518 			gradient;
       
 12519 		if (id) {
       
 12520 			gradient = definitions[id].getGradient();
       
 12521 		} else {
       
 12522 			var nodes = node.childNodes,
       
 12523 				stops = [];
       
 12524 			for (var i = 0, l = nodes.length; i < l; i++) {
       
 12525 				var child = nodes[i];
       
 12526 				if (child.nodeType === 1)
       
 12527 					stops.push(applyAttributes(new GradientStop(), child));
       
 12528 			}
       
 12529 			gradient = new Gradient(stops, isRadial);
       
 12530 		}
       
 12531 		var origin, destination, highlight;
       
 12532 		if (isRadial) {
       
 12533 			origin = getPoint(node, 'cx', 'cy');
       
 12534 			destination = origin.add(getValue(node, 'r'), 0);
       
 12535 			highlight = getPoint(node, 'fx', 'fy', true);
       
 12536 		} else {
       
 12537 			origin = getPoint(node, 'x1', 'y1');
       
 12538 			destination = getPoint(node, 'x2', 'y2');
       
 12539 		}
       
 12540 		applyAttributes(
       
 12541 			new Color(gradient, origin, destination, highlight), node);
       
 12542 		return null;
       
 12543 	}
       
 12544 
       
 12545 	var importers = {
       
 12546 		'#document': function (node, type, options, isRoot) {
       
 12547 			var nodes = node.childNodes;
       
 12548 			for (var i = 0, l = nodes.length; i < l; i++) {
       
 12549 				var child = nodes[i];
       
 12550 				if (child.nodeType === 1) {
       
 12551 					var next = child.nextSibling;
       
 12552 					document.body.appendChild(child);
       
 12553 					var item = importSVG(child, options, isRoot);
       
 12554 					if (next) {
       
 12555 						node.insertBefore(child, next);
       
 12556 					} else {
       
 12557 						node.appendChild(child);
       
 12558 					}
       
 12559 					return item;
       
 12560 				}
       
 12561 			}
       
 12562 		},
       
 12563 		g: importGroup,
       
 12564 		svg: importGroup,
       
 12565 		clippath: importGroup,
       
 12566 		polygon: importPoly,
       
 12567 		polyline: importPoly,
       
 12568 		path: importPath,
       
 12569 		lineargradient: importGradient,
       
 12570 		radialgradient: importGradient,
       
 12571 
       
 12572 		image: function (node) {
       
 12573 			var raster = new Raster(getValue(node, 'href', true));
       
 12574 			raster.on('load', function() {
       
 12575 				var size = getSize(node, 'width', 'height');
       
 12576 				this.setSize(size);
       
 12577 				var center = this._matrix._transformPoint(
       
 12578 						getPoint(node, 'x', 'y').add(size.divide(2)));
       
 12579 				this.translate(center);
       
 12580 			});
       
 12581 			return raster;
       
 12582 		},
       
 12583 
       
 12584 		symbol: function(node, type, options, isRoot) {
       
 12585 			return new Symbol(importGroup(node, type, options, isRoot), true);
       
 12586 		},
       
 12587 
       
 12588 		defs: importGroup,
       
 12589 
       
 12590 		use: function(node) {
       
 12591 			var id = (getValue(node, 'href', true) || '').substring(1),
       
 12592 				definition = definitions[id],
       
 12593 				point = getPoint(node, 'x', 'y');
       
 12594 			return definition
       
 12595 					? definition instanceof Symbol
       
 12596 						? definition.place(point)
       
 12597 						: definition.clone().translate(point)
       
 12598 					: null;
       
 12599 		},
       
 12600 
       
 12601 		circle: function(node) {
       
 12602 			return new Shape.Circle(getPoint(node, 'cx', 'cy'),
       
 12603 					getValue(node, 'r'));
       
 12604 		},
       
 12605 
       
 12606 		ellipse: function(node) {
       
 12607 			return new Shape.Ellipse({
       
 12608 				center: getPoint(node, 'cx', 'cy'),
       
 12609 				radius: getSize(node, 'rx', 'ry')
       
 12610 			});
       
 12611 		},
       
 12612 
       
 12613 		rect: function(node) {
       
 12614 			var point = getPoint(node, 'x', 'y'),
       
 12615 				size = getSize(node, 'width', 'height'),
       
 12616 				radius = getSize(node, 'rx', 'ry');
       
 12617 			return new Shape.Rectangle(new Rectangle(point, size), radius);
       
 12618 		},
       
 12619 
       
 12620 		line: function(node) {
       
 12621 			return new Path.Line(getPoint(node, 'x1', 'y1'),
       
 12622 					getPoint(node, 'x2', 'y2'));
       
 12623 		},
       
 12624 
       
 12625 		text: function(node) {
       
 12626 			var text = new PointText(getPoint(node, 'x', 'y')
       
 12627 					.add(getPoint(node, 'dx', 'dy')));
       
 12628 			text.setContent(node.textContent.trim() || '');
       
 12629 			return text;
       
 12630 		}
       
 12631 	};
       
 12632 
       
 12633 	function applyTransform(item, value, name, node) {
       
 12634 		var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
       
 12635 			matrix = new Matrix();
       
 12636 		for (var i = 0, l = transforms.length; i < l; i++) {
       
 12637 			var transform = transforms[i];
       
 12638 			if (!transform)
       
 12639 				break;
       
 12640 			var parts = transform.split(/\(\s*/),
       
 12641 				command = parts[0],
       
 12642 				v = parts[1].split(/[\s,]+/g);
       
 12643 			for (var j = 0, m = v.length; j < m; j++)
       
 12644 				v[j] = parseFloat(v[j]);
       
 12645 			switch (command) {
       
 12646 			case 'matrix':
       
 12647 				matrix.concatenate(
       
 12648 						new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
       
 12649 				break;
       
 12650 			case 'rotate':
       
 12651 				matrix.rotate(v[0], v[1], v[2]);
       
 12652 				break;
       
 12653 			case 'translate':
       
 12654 				matrix.translate(v[0], v[1]);
       
 12655 				break;
       
 12656 			case 'scale':
       
 12657 				matrix.scale(v);
       
 12658 				break;
       
 12659 			case 'skewX':
       
 12660 				matrix.skew(v[0], 0);
       
 12661 				break;
       
 12662 			case 'skewY':
       
 12663 				matrix.skew(0, v[0]);
       
 12664 				break;
       
 12665 			}
       
 12666 		}
       
 12667 		item.transform(matrix);
       
 12668 	}
       
 12669 
       
 12670 	function applyOpacity(item, value, name) {
       
 12671 		var color = item[name === 'fill-opacity' ? 'getFillColor'
       
 12672 				: 'getStrokeColor']();
       
 12673 		if (color)
       
 12674 			color.setAlpha(parseFloat(value));
       
 12675 	}
       
 12676 
       
 12677 	var attributes = Base.set(Base.each(SVGStyles, function(entry) {
       
 12678 		this[entry.attribute] = function(item, value) {
       
 12679 			item[entry.set](convertValue(value, entry.type, entry.fromSVG));
       
 12680 			if (entry.type === 'color' && item instanceof Shape) {
       
 12681 				var color = item[entry.get]();
       
 12682 				if (color)
       
 12683 					color.transform(new Matrix().translate(
       
 12684 							item.getPosition(true).negate()));
       
 12685 			}
       
 12686 		};
       
 12687 	}, {}), {
       
 12688 		id: function(item, value) {
       
 12689 			definitions[value] = item;
       
 12690 			if (item.setName)
       
 12691 				item.setName(value);
       
 12692 		},
       
 12693 
       
 12694 		'clip-path': function(item, value) {
       
 12695 			var clip = getDefinition(value);
       
 12696 			if (clip) {
       
 12697 				clip = clip.clone();
       
 12698 				clip.setClipMask(true);
       
 12699 				if (item instanceof Group) {
       
 12700 					item.insertChild(0, clip);
       
 12701 				} else {
       
 12702 					return new Group(clip, item);
       
 12703 				}
       
 12704 			}
       
 12705 		},
       
 12706 
       
 12707 		gradientTransform: applyTransform,
       
 12708 		transform: applyTransform,
       
 12709 
       
 12710 		'fill-opacity': applyOpacity,
       
 12711 		'stroke-opacity': applyOpacity,
       
 12712 
       
 12713 		visibility: function(item, value) {
       
 12714 			item.setVisible(value === 'visible');
       
 12715 		},
       
 12716 
       
 12717 		display: function(item, value) {
       
 12718 			item.setVisible(value !== null);
       
 12719 		},
       
 12720 
       
 12721 		'stop-color': function(item, value) {
       
 12722 			if (item.setColor)
       
 12723 				item.setColor(value);
       
 12724 		},
       
 12725 
       
 12726 		'stop-opacity': function(item, value) {
       
 12727 			if (item._color)
       
 12728 				item._color.setAlpha(parseFloat(value));
       
 12729 		},
       
 12730 
       
 12731 		offset: function(item, value) {
       
 12732 			var percentage = value.match(/(.*)%$/);
       
 12733 			item.setRampPoint(percentage
       
 12734 					? percentage[1] / 100
       
 12735 					: parseFloat(value));
       
 12736 		},
       
 12737 
       
 12738 		viewBox: function(item, value, name, node, styles) {
       
 12739 			var rect = new Rectangle(convertValue(value, 'array')),
       
 12740 				size = getSize(node, 'width', 'height', true);
       
 12741 			if (item instanceof Group) {
       
 12742 				var scale = size ? rect.getSize().divide(size) : 1,
       
 12743 					matrix = new Matrix().translate(rect.getPoint()).scale(scale);
       
 12744 				item.transform(matrix.inverted());
       
 12745 			} else if (item instanceof Symbol) {
       
 12746 				if (size)
       
 12747 					rect.setSize(size);
       
 12748 				var clip = getAttribute(node, 'overflow', styles) != 'visible',
       
 12749 					group = item._definition;
       
 12750 				if (clip && !rect.contains(group.getBounds())) {
       
 12751 					clip = new Shape.Rectangle(rect).transform(group._matrix);
       
 12752 					clip.setClipMask(true);
       
 12753 					group.addChild(clip);
       
 12754 				}
       
 12755 			}
       
 12756 		}
       
 12757 	});
       
 12758 
       
 12759 	function getAttribute(node, name, styles) {
       
 12760 		var attr = node.attributes[name],
       
 12761 			value = attr && attr.value;
       
 12762 		if (!value) {
       
 12763 			var style = Base.camelize(name);
       
 12764 			value = node.style[style];
       
 12765 			if (!value && styles.node[style] !== styles.parent[style])
       
 12766 				value = styles.node[style];
       
 12767 		}
       
 12768 		return !value
       
 12769 				? undefined
       
 12770 				: value === 'none'
       
 12771 					? null
       
 12772 					: value;
       
 12773 	}
       
 12774 
       
 12775 	function applyAttributes(item, node, isRoot) {
       
 12776 		var styles = {
       
 12777 			node: DomElement.getStyles(node) || {},
       
 12778 			parent: !isRoot && DomElement.getStyles(node.parentNode) || {}
       
 12779 		};
       
 12780 		Base.each(attributes, function(apply, name) {
       
 12781 			var value = getAttribute(node, name, styles);
       
 12782 			if (value !== undefined)
       
 12783 				item = Base.pick(apply(item, value, name, node, styles), item);
       
 12784 		});
       
 12785 		return item;
       
 12786 	}
       
 12787 
       
 12788 	var definitions = {};
       
 12789 	function getDefinition(value) {
       
 12790 		var match = value && value.match(/\((?:#|)([^)']+)/);
       
 12791 		return match && definitions[match[1]];
       
 12792 	}
       
 12793 
       
 12794 	function importSVG(source, options, isRoot) {
       
 12795 		if (!source)
       
 12796 			return null;
       
 12797 		if (!options) {
       
 12798 			options = {};
       
 12799 		} else if (typeof options === 'function') {
       
 12800 			options = { onLoad: options };
       
 12801 		}
       
 12802 
       
 12803 		var node = source,
       
 12804 			scope = paper;
       
 12805 
       
 12806 		function onLoadCallback(svg) {
       
 12807 			paper = scope;
       
 12808 			var item = importSVG(svg, options, isRoot),
       
 12809 				onLoad = options.onLoad,
       
 12810 				view = scope.project && scope.getView();
       
 12811 			if (onLoad)
       
 12812 				onLoad.call(this, item);
       
 12813 			view.update();
       
 12814 		}
       
 12815 
       
 12816 		if (isRoot) {
       
 12817 			if (typeof source === 'string' && !/^.*</.test(source)) {
       
 12818 				node = document.getElementById(source);
       
 12819 				if (node) {
       
 12820 					source = null;
       
 12821 				} else {
       
 12822 					return Http.request('get', source, onLoadCallback);
       
 12823 				}
       
 12824 			} else if (typeof File !== 'undefined' && source instanceof File) {
       
 12825 				var reader = new FileReader();
       
 12826 				reader.onload = function() {
       
 12827 					onLoadCallback(reader.result);
       
 12828 				};
       
 12829 				return reader.readAsText(source);
       
 12830 			}
       
 12831 		}
       
 12832 
       
 12833 		if (typeof source === 'string')
       
 12834 			node = new DOMParser().parseFromString(source, 'image/svg+xml');
       
 12835 		if (!node.nodeName)
       
 12836 			throw new Error('Unsupported SVG source: ' + source);
       
 12837 		var type = node.nodeName.toLowerCase(),
       
 12838 			importer = importers[type],
       
 12839 			item,
       
 12840 			data = node.getAttribute && node.getAttribute('data-paper-data'),
       
 12841 			settings = scope.settings,
       
 12842 			applyMatrix = settings.applyMatrix;
       
 12843 		settings.applyMatrix = false;
       
 12844 		item = importer && importer(node, type, options, isRoot) || null;
       
 12845 		settings.applyMatrix = applyMatrix;
       
 12846 		if (item) {
       
 12847 			if (type !== '#document' && !(item instanceof Group))
       
 12848 				item = applyAttributes(item, node, isRoot);
       
 12849 			var onImport = options.onImport;
       
 12850 			if (onImport)
       
 12851 				item = onImport(node, item, options) || item;
       
 12852 			if (options.expandShapes && item instanceof Shape) {
       
 12853 				item.remove();
       
 12854 				item = item.toPath();
       
 12855 			}
       
 12856 			if (data)
       
 12857 				item._data = JSON.parse(data);
       
 12858 		}
       
 12859 		if (isRoot) {
       
 12860 			definitions = {};
       
 12861 			if (item && Base.pick(options.applyMatrix, applyMatrix))
       
 12862 				item.matrix.apply(true, true);
       
 12863 		}
       
 12864 		return item;
       
 12865 	}
       
 12866 
       
 12867 	Item.inject({
       
 12868 		importSVG: function(node, options) {
       
 12869 			return this.addChild(importSVG(node, options, true));
       
 12870 		}
       
 12871 	});
       
 12872 
       
 12873 	Project.inject({
       
 12874 		importSVG: function(node, options) {
       
 12875 			this.activate();
       
 12876 			return importSVG(node, options, true);
       
 12877 		}
       
 12878 	});
       
 12879 };
       
 12880 
       
 12881 Base.exports.PaperScript = (function() {
       
 12882 	var exports, define,
       
 12883 		scope = this;
       
 12884 !function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in mr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=mr[r]);hr=fr.sourceFile||null}function t(e,r){var t=vr(dr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=dr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(dr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,dr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=dr.charCodeAt(br+=2);pr>br&&10!==t&&13!==t&&8232!==t&&8233!==t;)++br,t=dr.charCodeAt(br);fr.onComment&&fr.onComment(!1,dr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;pr>br;){var e=dr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=dr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e||8232===e||8233===e)++br,fr.locations&&(++Ar,Sr=br);else if(e>8&&14>e)++br;else if(47===e){var r=dr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=dr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=dr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function d(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Dt,1)}function p(e){var r=dr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Tt,1)}function h(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Vt,1)}function m(e){var r=dr.charCodeAt(br+1);return r===e?45==r&&62==dr.charCodeAt(br+2)&&Gt.test(dr.slice(Lr,br))?(br+=3,c(),u(),g()):x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=dr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===dr.charCodeAt(br+2)?3:2,61===dr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):33==r&&60==e&&45==dr.charCodeAt(br+2)&&45==dr.charCodeAt(br+3)?(br+=4,c(),u(),g()):(61===r&&(t=61===dr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=dr.charCodeAt(br+1);return 61===r?x(qt,61===dr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(mt);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(dt);case 123:return++br,i(pt);case 125:return++br,i(ht);case 58:return++br,i(gt);case 63:return++br,i(kt);case 48:var r=dr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return d();case 124:case 38:return p(e);case 94:return h();case 43:case 45:return m(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=pr)return i(Br);var r=dr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=dr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=pr&&t(a,"Unterminated regular expression");var o=dr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=dr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=dr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===dr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===dr.charCodeAt(br)&&(++br,w(10),n=!0);var o=dr.charCodeAt(br);(69===o||101===o)&&(o=dr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=dr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Tr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=pr&&t(yr,"Unterminated string constant");var n=dr.charCodeAt(br);if(n===e)return++br,i(Dr,r);if(92===n){n=dr.charCodeAt(++br);var a=/^[0-7]+/.exec(dr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Tr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+="	";break;case 98:r+="\b";break;case 118:r+="";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===dr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8233===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=dr.charCodeAt(br);if(Yt(a))Bt&&(e+=dr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=dr.slice(n,br)),Bt=!0,117!=dr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:dr.slice(n,br)}function L(){var e=I(),r=Fr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Tr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){if(Tr=e,br=Lr,fr.locations)for(;Sr>br;)Sr=dr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function V(){this.type=null,this.start=yr,this.end=null}function T(){this.start=xr,this.end=null,null!==hr&&(this.source=hr)}function q(){var e=new V;return fr.locations&&(e.loc=new T),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new V;return r.start=e.start,fr.locations&&(r.loc=new T,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function D(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function F(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===ht||Gt.test(dr.slice(Lr,yr)))}function M(){F(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Tr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Tr=null,Vr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&D(n)&&R(!0),t=!1}return j(r,"Program")}function J(){(wr===wt||wr===Et&&"/="==Cr)&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;F(yt)||B()?r.label=null:wr!==Fr?X():(r.label=lr(),M());for(var a=0;a<Vr.length;++a){var o=Vr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Vr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Vr.push(Zt),r.body=J(),Vr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Vr.push(Zt),z(mt),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),j(i,"VariableDeclaration"),1===i.declarations.length&&F(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return F(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=F(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),F(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(pt),Vr.push(en);for(var s,c;wr!=ht;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Vr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(dr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(mt),l.param=lr(),Tr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=F($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),G(r),M(),j(r,"VariableDeclaration");case tt:return U(),r.test=P(),Vr.push(Zt),r.body=J(),Vr.pop(),j(r,"WhileStatement");case nt:return Tr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case pt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,d=K();if(e===Fr&&"Identifier"===d.type&&F(gt)){for(var a=0;a<Vr.length;++a)Vr[a].name===f&&t(d.start,"Label '"+f+"' is already declared");var p=wr.isLoop?"loop":wr===Yr?"switch":null;return Vr.push({name:f,kind:p}),r.body=J(),Vr.pop(),r.label=d,j(r,"LabeledStatement")}return r.expression=d,M(),j(r,"ExpressionStatement")}}function P(){z(mt);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(pt);!F(ht);){var o=J();t.body.push(o),n&&e&&D(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Tr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=F(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!F(bt))break}return e}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];F(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(F(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var o=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(o,r,t)}return e}function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,Er=!0,U(),e.argument=rr(),r?N(e.argument):Tr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(F(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(F(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(dt),nr(j(t,"MemberExpression"),r)}if(!r&&F(mt)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Fr:return lr();case Or:case Dr:case jr:var e=q();return e.value=Cr,e.raw=dr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case mt:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(dt,!0,!0),j(e,"ArrayExpression");case pt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=F(mt)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!F(ht);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&F(ht))break;var a,o={key:sr()},i=!1;if(F(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==mt&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Tr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Tr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Dr?ar():lr(!0)}function cr(e,r){wr===Fr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(mt);!F(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Vr;if(Rr=!0,Vr=[],e.body=H(!0),Rr=a,Vr=o,Tr||e.body.body.length&&D(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!F(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&F(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Fr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),Er=!1,U(),j(r,"Identifier")}e.version="0.4.0";var fr,dr,pr,hr;e.parse=function(e,t){return dr=String(e),pr=dr.length,r(t),o(),W(fr.program)};var mr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}dr=String(e),pr=dr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(dr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Vr,Tr,qr=[],Or={type:"num"},jr={type:"regexp"},Dr={type:"string"},Fr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"},tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},dt={type:"]"},pt={type:"{",beforeExpr:!0},ht={type:"}"},mt={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Vt={binop:4,beforeExpr:!0},Tt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Dt={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:dt,braceL:pt,braceR:ht,parenL:mt,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Fr,eof:Br,num:Or,regexp:jr,string:Dr};for(var Ft in lt)e.tokTypes["_"+Ft]=lt[Ft];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\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"}});
       
 12885 
       
 12886 	var binaryOperators = {
       
 12887 		'+': '__add',
       
 12888 		'-': '__subtract',
       
 12889 		'*': '__multiply',
       
 12890 		'/': '__divide',
       
 12891 		'%': '__modulo',
       
 12892 		'==': 'equals',
       
 12893 		'!=': 'equals'
       
 12894 	};
       
 12895 
       
 12896 	var unaryOperators = {
       
 12897 		'-': '__negate',
       
 12898 		'+': null
       
 12899 	};
       
 12900 
       
 12901 	var fields = Base.each(
       
 12902 		['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
       
 12903 		function(name) {
       
 12904 			this['__' + name] = '#' + name;
       
 12905 		},
       
 12906 		{}
       
 12907 	);
       
 12908 	Point.inject(fields);
       
 12909 	Size.inject(fields);
       
 12910 	Color.inject(fields);
       
 12911 
       
 12912 	function __$__(left, operator, right) {
       
 12913 		var handler = binaryOperators[operator];
       
 12914 		if (left && left[handler]) {
       
 12915 			var res = left[handler](right);
       
 12916 			return operator === '!=' ? !res : res;
       
 12917 		}
       
 12918 		switch (operator) {
       
 12919 		case '+': return left + right;
       
 12920 		case '-': return left - right;
       
 12921 		case '*': return left * right;
       
 12922 		case '/': return left / right;
       
 12923 		case '%': return left % right;
       
 12924 		case '==': return left == right;
       
 12925 		case '!=': return left != right;
       
 12926 		}
       
 12927 	}
       
 12928 
       
 12929 	function $__(operator, value) {
       
 12930 		var handler = unaryOperators[operator];
       
 12931 		if (handler && value && value[handler])
       
 12932 			return value[handler]();
       
 12933 		switch (operator) {
       
 12934 		case '+': return +value;
       
 12935 		case '-': return -value;
       
 12936 		}
       
 12937 	}
       
 12938 
       
 12939 	function parse(code, options) {
       
 12940 		return scope.acorn.parse(code, options);
       
 12941 	}
       
 12942 
       
 12943 	function compile(code, url, options) {
       
 12944 		if (!code)
       
 12945 			return '';
       
 12946 		options = options || {};
       
 12947 		url = url || '';
       
 12948 
       
 12949 		var insertions = [];
       
 12950 
       
 12951 		function getOffset(offset) {
       
 12952 			for (var i = 0, l = insertions.length; i < l; i++) {
       
 12953 				var insertion = insertions[i];
       
 12954 				if (insertion[0] >= offset)
       
 12955 					break;
       
 12956 				offset += insertion[1];
       
 12957 			}
       
 12958 			return offset;
       
 12959 		}
       
 12960 
       
 12961 		function getCode(node) {
       
 12962 			return code.substring(getOffset(node.range[0]),
       
 12963 					getOffset(node.range[1]));
       
 12964 		}
       
 12965 
       
 12966 		function getBetween(left, right) {
       
 12967 			return code.substring(getOffset(left.range[1]),
       
 12968 					getOffset(right.range[0]));
       
 12969 		}
       
 12970 
       
 12971 		function replaceCode(node, str) {
       
 12972 			var start = getOffset(node.range[0]),
       
 12973 				end = getOffset(node.range[1]),
       
 12974 				insert = 0;
       
 12975 			for (var i = insertions.length - 1; i >= 0; i--) {
       
 12976 				if (start > insertions[i][0]) {
       
 12977 					insert = i + 1;
       
 12978 					break;
       
 12979 				}
       
 12980 			}
       
 12981 			insertions.splice(insert, 0, [start, str.length - end + start]);
       
 12982 			code = code.substring(0, start) + str + code.substring(end);
       
 12983 		}
       
 12984 
       
 12985 		function walkAST(node, parent) {
       
 12986 			if (!node)
       
 12987 				return;
       
 12988 			for (var key in node) {
       
 12989 				if (key === 'range' || key === 'loc')
       
 12990 					continue;
       
 12991 				var value = node[key];
       
 12992 				if (Array.isArray(value)) {
       
 12993 					for (var i = 0, l = value.length; i < l; i++)
       
 12994 						walkAST(value[i], node);
       
 12995 				} else if (value && typeof value === 'object') {
       
 12996 					walkAST(value, node);
       
 12997 				}
       
 12998 			}
       
 12999 			switch (node.type) {
       
 13000 			case 'UnaryExpression':
       
 13001 				if (node.operator in unaryOperators
       
 13002 						&& node.argument.type !== 'Literal') {
       
 13003 					var arg = getCode(node.argument);
       
 13004 					replaceCode(node, '$__("' + node.operator + '", '
       
 13005 							+ arg + ')');
       
 13006 				}
       
 13007 				break;
       
 13008 			case 'BinaryExpression':
       
 13009 				if (node.operator in binaryOperators
       
 13010 						&& node.left.type !== 'Literal') {
       
 13011 					var left = getCode(node.left),
       
 13012 						right = getCode(node.right),
       
 13013 						between = getBetween(node.left, node.right),
       
 13014 						operator = node.operator;
       
 13015 					replaceCode(node, '__$__(' + left + ','
       
 13016 							+ between.replace(new RegExp('\\' + operator),
       
 13017 								'"' + operator + '"')
       
 13018 							+ ', ' + right + ')');
       
 13019 				}
       
 13020 				break;
       
 13021 			case 'UpdateExpression':
       
 13022 			case 'AssignmentExpression':
       
 13023 				var parentType = parent && parent.type;
       
 13024 				if (!(
       
 13025 						parentType === 'ForStatement'
       
 13026 						|| parentType === 'BinaryExpression'
       
 13027 							&& /^[=!<>]/.test(parent.operator)
       
 13028 						|| parentType === 'MemberExpression' && parent.computed
       
 13029 				)) {
       
 13030 					if (node.type === 'UpdateExpression') {
       
 13031 						var arg = getCode(node.argument),
       
 13032 							exp = '__$__(' + arg + ', "' + node.operator[0]
       
 13033 									+ '", 1)',
       
 13034 							str = arg + ' = ' + exp;
       
 13035 						if (!node.prefix
       
 13036 								&& (parentType === 'AssignmentExpression'
       
 13037 									|| parentType === 'VariableDeclarator')) {
       
 13038 							if (getCode(parent.left || parent.id) === arg)
       
 13039 								str = exp;
       
 13040 							str = arg + '; ' + str;
       
 13041 						}
       
 13042 						replaceCode(node, str);
       
 13043 					} else {
       
 13044 						if (/^.=$/.test(node.operator)
       
 13045 								&& node.left.type !== 'Literal') {
       
 13046 							var left = getCode(node.left),
       
 13047 								right = getCode(node.right);
       
 13048 							replaceCode(node, left + ' = __$__(' + left + ', "'
       
 13049 									+ node.operator[0] + '", ' + right + ')');
       
 13050 						}
       
 13051 					}
       
 13052 				}
       
 13053 				break;
       
 13054 			}
       
 13055 		}
       
 13056 		var sourceMap = null,
       
 13057 			browser = paper.browser,
       
 13058 			version = browser.versionNumber,
       
 13059 			lineBreaks = /\r\n|\n|\r/mg;
       
 13060 		if (browser.chrome && version >= 30
       
 13061 				|| browser.webkit && version >= 537.76
       
 13062 				|| browser.firefox && version >= 23) {
       
 13063 			var offset = 0;
       
 13064 			if (window.location.href.indexOf(url) === 0) {
       
 13065 				var html = document.getElementsByTagName('html')[0].innerHTML;
       
 13066 				offset = html.substr(0, html.indexOf(code) + 1).match(
       
 13067 						lineBreaks).length + 1;
       
 13068 			}
       
 13069 			var mappings = ['AAAA'];
       
 13070 			mappings.length = (code.match(lineBreaks) || []).length + 1 + offset;
       
 13071 			sourceMap = {
       
 13072 				version: 3,
       
 13073 				file: url,
       
 13074 				names:[],
       
 13075 				mappings: mappings.join(';AACA'),
       
 13076 				sourceRoot: '',
       
 13077 				sources: [url]
       
 13078 			};
       
 13079 			var source = options.source || !url && code;
       
 13080 			if (source)
       
 13081 				sourceMap.sourcesContent = [source];
       
 13082 		}
       
 13083 		walkAST(parse(code, { ranges: true }));
       
 13084 		if (sourceMap) {
       
 13085 			code = new Array(offset + 1).join('\n') + code
       
 13086 					+ "\n//# sourceMappingURL=data:application/json;base64,"
       
 13087 					+ (btoa(unescape(encodeURIComponent(
       
 13088 						JSON.stringify(sourceMap)))))
       
 13089 					+ "\n//# sourceURL=" + (url || 'paperscript');
       
 13090 		}
       
 13091 		return code;
       
 13092 	}
       
 13093 
       
 13094 	function execute(code, scope, url, options) {
       
 13095 		paper = scope;
       
 13096 		var view = scope.getView(),
       
 13097 			tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code)
       
 13098 					? new Tool()
       
 13099 					: null,
       
 13100 			toolHandlers = tool ? tool._events : [],
       
 13101 			handlers = ['onFrame', 'onResize'].concat(toolHandlers),
       
 13102 			params = [],
       
 13103 			args = [],
       
 13104 			func;
       
 13105 		code = compile(code, url, options);
       
 13106 		function expose(scope, hidden) {
       
 13107 			for (var key in scope) {
       
 13108 				if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)'
       
 13109 						+ key.replace(/\$/g, '\\$') + '\\b').test(code)) {
       
 13110 					params.push(key);
       
 13111 					args.push(scope[key]);
       
 13112 				}
       
 13113 			}
       
 13114 		}
       
 13115 		expose({ __$__: __$__, $__: $__, paper: scope, view: view, tool: tool },
       
 13116 				true);
       
 13117 		expose(scope);
       
 13118 		handlers = Base.each(handlers, function(key) {
       
 13119 			if (new RegExp('\\s+' + key + '\\b').test(code)) {
       
 13120 				params.push(key);
       
 13121 				this.push(key + ': ' + key);
       
 13122 			}
       
 13123 		}, []).join(', ');
       
 13124 		if (handlers)
       
 13125 			code += '\nreturn { ' + handlers + ' };';
       
 13126 		var browser = paper.browser;
       
 13127 		if (browser.chrome || browser.firefox) {
       
 13128 			var script = document.createElement('script'),
       
 13129 				head = document.head || document.getElementsByTagName('head')[0];
       
 13130 			if (browser.firefox)
       
 13131 				code = '\n' + code;
       
 13132 			script.appendChild(document.createTextNode(
       
 13133 				'paper._execute = function(' + params + ') {' + code + '\n}'
       
 13134 			));
       
 13135 			head.appendChild(script);
       
 13136 			func = paper._execute;
       
 13137 			delete paper._execute;
       
 13138 			head.removeChild(script);
       
 13139 		} else {
       
 13140 			func = Function(params, code);
       
 13141 		}
       
 13142 		var res = func.apply(scope, args) || {};
       
 13143 		Base.each(toolHandlers, function(key) {
       
 13144 			var value = res[key];
       
 13145 			if (value)
       
 13146 				tool[key] = value;
       
 13147 		});
       
 13148 		if (view) {
       
 13149 			if (res.onResize)
       
 13150 				view.setOnResize(res.onResize);
       
 13151 			view.emit('resize', {
       
 13152 				size: view.size,
       
 13153 				delta: new Point()
       
 13154 			});
       
 13155 			if (res.onFrame)
       
 13156 				view.setOnFrame(res.onFrame);
       
 13157 			view.update();
       
 13158 		}
       
 13159 	}
       
 13160 
       
 13161 	function loadScript(script) {
       
 13162 		if (/^text\/(?:x-|)paperscript$/.test(script.type)
       
 13163 				&& PaperScope.getAttribute(script, 'ignore') !== 'true') {
       
 13164 			var canvasId = PaperScope.getAttribute(script, 'canvas'),
       
 13165 				canvas = document.getElementById(canvasId),
       
 13166 				src = script.src || script.getAttribute('data-src'),
       
 13167 				async = PaperScope.hasAttribute(script, 'async'),
       
 13168 				scopeAttribute = 'data-paper-scope';
       
 13169 			if (!canvas)
       
 13170 				throw new Error('Unable to find canvas with id "'
       
 13171 						+ canvasId + '"');
       
 13172 			var scope = PaperScope.get(canvas.getAttribute(scopeAttribute))
       
 13173 						|| new PaperScope().setup(canvas);
       
 13174 			canvas.setAttribute(scopeAttribute, scope._id);
       
 13175 			if (src) {
       
 13176 				Http.request('get', src, function(code) {
       
 13177 					execute(code, scope, src);
       
 13178 				}, async);
       
 13179 			} else {
       
 13180 				execute(script.innerHTML, scope, script.baseURI);
       
 13181 			}
       
 13182 			script.setAttribute('data-paper-ignore', 'true');
       
 13183 			return scope;
       
 13184 		}
       
 13185 	}
       
 13186 
       
 13187 	function loadAll() {
       
 13188 		Base.each(document.getElementsByTagName('script'), loadScript);
       
 13189 	}
       
 13190 
       
 13191 	function load(script) {
       
 13192 		return script ? loadScript(script) : loadAll();
       
 13193 	}
       
 13194 
       
 13195 	if (document.readyState === 'complete') {
       
 13196 		setTimeout(loadAll);
       
 13197 	} else {
       
 13198 		DomEvent.add(window, { load: loadAll });
       
 13199 	}
       
 13200 
       
 13201 	return {
       
 13202 		compile: compile,
       
 13203 		execute: execute,
       
 13204 		load: load,
       
 13205 		parse: parse
       
 13206 	};
       
 13207 
       
 13208 }).call(this);
       
 13209 
       
 13210 paper = new (PaperScope.inject(Base.exports, {
       
 13211 	enumerable: true,
       
 13212 	Base: Base,
       
 13213 	Numerical: Numerical,
       
 13214 	Key: Key
       
 13215 }))();
       
 13216 
       
 13217 if (typeof define === 'function' && define.amd) {
       
 13218 	define('paper', paper);
       
 13219 } else if (typeof module === 'object' && module) {
       
 13220 	module.exports = paper;
       
 13221 }
       
 13222 
       
 13223 return paper;
       
 13224 };