server/python/django/renkanmanager/static/renkanmanager/lib/paper.js
changeset 404 40cff6e6b665
parent 318 815589aed866
equal deleted inserted replaced
403:96781c1a8bbe 404:40cff6e6b665
       
     1 /*!
       
     2  * Paper.js v0.9.15 - The Swiss Army Knife of Vector Graphics Scripting.
       
     3  * http://paperjs.org/
       
     4  *
       
     5  * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
       
     6  * http://lehni.org/ & http://jonathanpuckey.com/
       
     7  *
       
     8  * Distributed under the MIT license. See LICENSE file for details.
       
     9  *
       
    10  * All rights reserved.
       
    11  *
       
    12  * Date: Sun Dec 1 23:54:52 2013 +0100
       
    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://lehni.org/
       
    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|generics|preserve|enumerable|prototype|toString|valueOf)$/,
       
    37 		slice = [].slice,
       
    38 
       
    39 		forEach = [].forEach || function(iter, bind) {
       
    40 			for (var i = 0, l = this.length; i < l; i++)
       
    41 				iter.call(bind, this[i], i, this);
       
    42 		},
       
    43 
       
    44 		forIn = function(iter, bind) {
       
    45 			for (var i in this)
       
    46 				if (this.hasOwnProperty(i))
       
    47 					iter.call(bind, this[i], i, this);
       
    48 		},
       
    49 
       
    50 		create = Object.create || function(proto) {
       
    51 			return { __proto__: proto };
       
    52 		},
       
    53 
       
    54 		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
       
    55 			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
       
    56 			return get
       
    57 					? { get: get, set: obj.__lookupSetter__(name),
       
    58 						enumerable: true, configurable: true }
       
    59 					: obj.hasOwnProperty(name)
       
    60 						? { value: obj[name], enumerable: true,
       
    61 							configurable: true, writable: true }
       
    62 						: null;
       
    63 		},
       
    64 
       
    65 		_define = Object.defineProperty || function(obj, name, desc) {
       
    66 			if ((desc.get || desc.set) && obj.__defineGetter__) {
       
    67 				if (desc.get)
       
    68 					obj.__defineGetter__(name, desc.get);
       
    69 				if (desc.set)
       
    70 					obj.__defineSetter__(name, desc.set);
       
    71 			} else {
       
    72 				obj[name] = desc.value;
       
    73 			}
       
    74 			return obj;
       
    75 		},
       
    76 
       
    77 		define = function(obj, name, desc) {
       
    78 			delete obj[name];
       
    79 			return _define(obj, name, desc);
       
    80 		};
       
    81 
       
    82 	function inject(dest, src, enumerable, base, preserve, generics) {
       
    83 		var beans;
       
    84 
       
    85 		function field(name, val, dontCheck, generics) {
       
    86 			var val = val || (val = describe(src, name))
       
    87 					&& (val.get ? val : val.value);
       
    88 			if (typeof val === 'string' && val[0] === '#')
       
    89 				val = dest[val.substring(1)] || val;
       
    90 			var isFunc = typeof val === 'function',
       
    91 				res = val,
       
    92 				prev = preserve || isFunc
       
    93 					? (val && val.get ? name in dest : dest[name]) : null,
       
    94 				bean;
       
    95 			if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
       
    96 					&& (!preserve || !prev)) {
       
    97 				if (isFunc && prev)
       
    98 					val.base = prev;
       
    99 				if (isFunc && beans && val.length === 0
       
   100 						&& (bean = name.match(/^(get|is)(([A-Z])(.*))$/)))
       
   101 					beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]);
       
   102 				if (!res || isFunc || !res.get || typeof res.get !== 'function'
       
   103 						|| res.get.length !== 0)
       
   104 					res = { value: res, writable: true };
       
   105 				if ((describe(dest, name)
       
   106 						|| { configurable: true }).configurable) {
       
   107 					res.configurable = true;
       
   108 					res.enumerable = enumerable;
       
   109 				}
       
   110 				define(dest, name, res);
       
   111 			}
       
   112 			if (generics && isFunc && (!preserve || !generics[name])) {
       
   113 				generics[name] = function(bind) {
       
   114 					return bind && dest[name].apply(bind,
       
   115 							slice.call(arguments, 1));
       
   116 				};
       
   117 			}
       
   118 		}
       
   119 		if (src) {
       
   120 			beans = [];
       
   121 			for (var name in src)
       
   122 				if (src.hasOwnProperty(name) && !hidden.test(name))
       
   123 					field(name, null, true, generics);
       
   124 			field('toString');
       
   125 			field('valueOf');
       
   126 			for (var i = 0, l = beans.length; i < l; i++) {
       
   127 				var bean = beans[i],
       
   128 					part = bean[1];
       
   129 				field(bean[0], {
       
   130 					get: dest['get' + part] || dest['is' + part],
       
   131 					set: dest['set' + part]
       
   132 				}, true);
       
   133 			}
       
   134 		}
       
   135 		return dest;
       
   136 	}
       
   137 
       
   138 	function each(obj, iter, bind) {
       
   139 		if (obj)
       
   140 			('length' in obj && !obj.getLength
       
   141 					&& typeof obj.length === 'number'
       
   142 				? forEach
       
   143 				: forIn).call(obj, iter, bind = bind || obj);
       
   144 		return bind;
       
   145 	}
       
   146 
       
   147 	function copy(dest, source) {
       
   148 		for (var i in source)
       
   149 			if (source.hasOwnProperty(i))
       
   150 				dest[i] = source[i];
       
   151 		return dest;
       
   152 	}
       
   153 
       
   154 	function clone(obj) {
       
   155 		return copy(new obj.constructor(), obj);
       
   156 	}
       
   157 
       
   158 	return inject(function Base() {
       
   159 		for (var i = 0, l = arguments.length; i < l; i++)
       
   160 			copy(this, arguments[i]);
       
   161 	}, {
       
   162 		inject: function(src) {
       
   163 			if (src) {
       
   164 				var proto = this.prototype,
       
   165 					base = Object.getPrototypeOf(proto).constructor,
       
   166 					statics = src.statics === true ? src : src.statics;
       
   167 				if (statics != src)
       
   168 					inject(proto, src, src.enumerable, base && base.prototype,
       
   169 							src.preserve, src.generics && this);
       
   170 				inject(this, statics, true, base, src.preserve);
       
   171 			}
       
   172 			for (var i = 1, l = arguments.length; i < l; i++)
       
   173 				this.inject(arguments[i]);
       
   174 			return this;
       
   175 		},
       
   176 
       
   177 		extend: function() {
       
   178 			var base = this,
       
   179 				ctor;
       
   180 			for (var i = 0, l = arguments.length; i < l; i++)
       
   181 				if (ctor = arguments[i].initialize)
       
   182 					break;
       
   183 			ctor = ctor || function() {
       
   184 				base.apply(this, arguments);
       
   185 			};
       
   186 			ctor.prototype = create(this.prototype);
       
   187 			define(ctor.prototype, 'constructor',
       
   188 					{ value: ctor, writable: true, configurable: true });
       
   189 			inject(ctor, this, true);
       
   190 			return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
       
   191 		}
       
   192 	}, true).inject({
       
   193 		inject: function() {
       
   194 			for (var i = 0, l = arguments.length; i < l; i++)
       
   195 				inject(this, arguments[i], arguments[i].enumerable);
       
   196 			return this;
       
   197 		},
       
   198 
       
   199 		extend: function() {
       
   200 			var res = create(this);
       
   201 			return res.inject.apply(res, arguments);
       
   202 		},
       
   203 
       
   204 		each: function(iter, bind) {
       
   205 			return each(this, iter, bind);
       
   206 		},
       
   207 
       
   208 		clone: function() {
       
   209 			return new this.constructor(this);
       
   210 		},
       
   211 
       
   212 		statics: {
       
   213 			each: each,
       
   214 			create: create,
       
   215 			define: define,
       
   216 			describe: describe,
       
   217 			copy: copy,
       
   218 
       
   219 			clone: function(obj) {
       
   220 				return copy(new obj.constructor(), obj);
       
   221 			},
       
   222 
       
   223 			isPlainObject: function(obj) {
       
   224 				var ctor = obj != null && obj.constructor;
       
   225 				return ctor && (ctor === Object || ctor === Base
       
   226 						|| ctor.name === 'Object');
       
   227 			},
       
   228 
       
   229 			pick: function() {
       
   230 				for (var i = 0, l = arguments.length; i < l; i++)
       
   231 					if (arguments[i] !== undefined)
       
   232 						return arguments[i];
       
   233 				return null;
       
   234 			}
       
   235 		}
       
   236 	});
       
   237 };
       
   238 
       
   239 if (typeof module !== 'undefined')
       
   240 	module.exports = Base;
       
   241 
       
   242 Base.inject({
       
   243 	generics: true,
       
   244 
       
   245 	toString: function() {
       
   246 		return this._id != null
       
   247 			?  (this._class || 'Object') + (this._name
       
   248 				? " '" + this._name + "'"
       
   249 				: ' @' + this._id)
       
   250 			: '{ ' + Base.each(this, function(value, key) {
       
   251 				if (!/^_/.test(key)) {
       
   252 					var type = typeof value;
       
   253 					this.push(key + ': ' + (type === 'number'
       
   254 							? Formatter.instance.number(value)
       
   255 							: type === 'string' ? "'" + value + "'" : value));
       
   256 				}
       
   257 			}, []).join(', ') + ' }';
       
   258 	},
       
   259 
       
   260 	exportJSON: function(options) {
       
   261 		return Base.exportJSON(this, options);
       
   262 	},
       
   263 
       
   264 	toJSON: function() {
       
   265 		return Base.serialize(this);
       
   266 	},
       
   267 
       
   268 	_set: function(props, exclude) {
       
   269 		if (props && Base.isPlainObject(props)) {
       
   270 			var orig = props._filtering || props;
       
   271 			for (var key in orig) {
       
   272 				if (key in this && orig.hasOwnProperty(key)
       
   273 						&& (!exclude || !exclude[key])) {
       
   274 					var value = props[key];
       
   275 					if (value !== undefined)
       
   276 						this[key] = value;
       
   277 				}
       
   278 			}
       
   279 			return true;
       
   280 		}
       
   281 	},
       
   282 
       
   283 	statics: {
       
   284 
       
   285 		exports: {},
       
   286 
       
   287 		extend: function extend() {
       
   288 			var res = extend.base.apply(this, arguments),
       
   289 				name = res.prototype._class;
       
   290 			if (name && !Base.exports[name])
       
   291 				Base.exports[name] = res;
       
   292 			return res;
       
   293 		},
       
   294 
       
   295 		equals: function(obj1, obj2) {
       
   296 			function checkKeys(o1, o2) {
       
   297 				for (var i in o1)
       
   298 					if (o1.hasOwnProperty(i) && !o2.hasOwnProperty(i))
       
   299 						return false;
       
   300 				return true;
       
   301 			}
       
   302 			if (obj1 === obj2)
       
   303 				return true;
       
   304 			if (obj1 && obj1.equals)
       
   305 				return obj1.equals(obj2);
       
   306 			if (obj2 && obj2.equals)
       
   307 				return obj2.equals(obj1);
       
   308 			if (Array.isArray(obj1) && Array.isArray(obj2)) {
       
   309 				if (obj1.length !== obj2.length)
       
   310 					return false;
       
   311 				for (var i = 0, l = obj1.length; i < l; i++) {
       
   312 					if (!Base.equals(obj1[i], obj2[i]))
       
   313 						return false;
       
   314 				}
       
   315 				return true;
       
   316 			}
       
   317 			if (obj1 && typeof obj1 === 'object'
       
   318 					&& obj2 && typeof obj2 === 'object') {
       
   319 				if (!checkKeys(obj1, obj2) || !checkKeys(obj2, obj1))
       
   320 					return false;
       
   321 				for (var i in obj1) {
       
   322 					if (obj1.hasOwnProperty(i) && !Base.equals(obj1[i], obj2[i]))
       
   323 						return false;
       
   324 				}
       
   325 				return true;
       
   326 			}
       
   327 			return false;
       
   328 		},
       
   329 
       
   330 		read: function(list, start, length, options) {
       
   331 			if (this === Base) {
       
   332 				var value = this.peek(list, start);
       
   333 				list._index++;
       
   334 				list.__read = 1;
       
   335 				return value;
       
   336 			}
       
   337 			var proto = this.prototype,
       
   338 				readIndex = proto._readIndex,
       
   339 				index = start || readIndex && list._index || 0;
       
   340 			if (!length)
       
   341 				length = list.length - index;
       
   342 			var obj = list[index];
       
   343 			if (obj instanceof this
       
   344 				|| options && options.readNull && obj == null && length <= 1) {
       
   345 				if (readIndex)
       
   346 					list._index = index + 1;
       
   347 				return obj && options && options.clone ? obj.clone() : obj;
       
   348 			}
       
   349 			obj = Base.create(this.prototype);
       
   350 			if (readIndex)
       
   351 				obj.__read = true;
       
   352 			if (options)
       
   353 				obj.__options = options;
       
   354 			obj = obj.initialize.apply(obj, index > 0 || length < list.length
       
   355 				? Array.prototype.slice.call(list, index, index + length)
       
   356 				: list) || obj;
       
   357 			if (readIndex) {
       
   358 				list._index = index + obj.__read;
       
   359 				list.__read = obj.__read;
       
   360 				delete obj.__read;
       
   361 				if (options)
       
   362 					delete obj.__options;
       
   363 			}
       
   364 			return obj;
       
   365 		},
       
   366 
       
   367 		peek: function(list, start) {
       
   368 			return list[list._index = start || list._index || 0];
       
   369 		},
       
   370 
       
   371 		readAll: function(list, start, options) {
       
   372 			var res = [], entry;
       
   373 			for (var i = start || 0, l = list.length; i < l; i++) {
       
   374 				res.push(Array.isArray(entry = list[i])
       
   375 						? this.read(entry, 0, 0, options)
       
   376 						: this.read(list, i, 1, options));
       
   377 			}
       
   378 			return res;
       
   379 		},
       
   380 
       
   381 		readNamed: function(list, name, start, length, options) {
       
   382 			var value = this.getNamed(list, name),
       
   383 				hasObject = value !== undefined;
       
   384 			if (hasObject) {
       
   385 				var filtered = list._filtered;
       
   386 				if (!filtered) {
       
   387 					filtered = list._filtered = Base.create(list[0]);
       
   388 					filtered._filtering = list[0];
       
   389 				}
       
   390 				filtered[name] = undefined;
       
   391 			}
       
   392 			return this.read(hasObject ? [value] : list, start, length, options);
       
   393 		},
       
   394 
       
   395 		getNamed: function(list, name) {
       
   396 			var arg = list[0];
       
   397 			if (list._hasObject === undefined)
       
   398 				list._hasObject = list.length === 1 && Base.isPlainObject(arg);
       
   399 			if (list._hasObject)
       
   400 				return name ? arg[name] : list._filtered || arg;
       
   401 		},
       
   402 
       
   403 		hasNamed: function(list, name) {
       
   404 			return !!this.getNamed(list, name);
       
   405 		},
       
   406 
       
   407 		isPlainValue: function(obj) {
       
   408 			return this.isPlainObject(obj) || Array.isArray(obj);
       
   409 		},
       
   410 
       
   411 		serialize: function(obj, options, compact, dictionary) {
       
   412 			options = options || {};
       
   413 
       
   414 			var root = !dictionary,
       
   415 				res;
       
   416 			if (root) {
       
   417 				options.formatter = new Formatter(options.precision);
       
   418 				dictionary = {
       
   419 					length: 0,
       
   420 					definitions: {},
       
   421 					references: {},
       
   422 					add: function(item, create) {
       
   423 						var id = '#' + item._id,
       
   424 							ref = this.references[id];
       
   425 						if (!ref) {
       
   426 							this.length++;
       
   427 							var res = create.call(item),
       
   428 								name = item._class;
       
   429 							if (name && res[0] !== name)
       
   430 								res.unshift(name);
       
   431 							this.definitions[id] = res;
       
   432 							ref = this.references[id] = [id];
       
   433 						}
       
   434 						return ref;
       
   435 					}
       
   436 				};
       
   437 			}
       
   438 			if (obj && obj._serialize) {
       
   439 				res = obj._serialize(options, dictionary);
       
   440 				var name = obj._class;
       
   441 				if (name && !compact && !res._compact && res[0] !== name)
       
   442 					res.unshift(name);
       
   443 			} else if (Array.isArray(obj)) {
       
   444 				res = [];
       
   445 				for (var i = 0, l = obj.length; i < l; i++)
       
   446 					res[i] = Base.serialize(obj[i], options, compact,
       
   447 							dictionary);
       
   448 				if (compact)
       
   449 					res._compact = true;
       
   450 			} else if (Base.isPlainObject(obj)) {
       
   451 				res = {};
       
   452 				for (var i in obj)
       
   453 					if (obj.hasOwnProperty(i))
       
   454 						res[i] = Base.serialize(obj[i], options, compact,
       
   455 								dictionary);
       
   456 			} else if (typeof obj === 'number') {
       
   457 				res = options.formatter.number(obj, options.precision);
       
   458 			} else {
       
   459 				res = obj;
       
   460 			}
       
   461 			return root && dictionary.length > 0
       
   462 					? [['dictionary', dictionary.definitions], res]
       
   463 					: res;
       
   464 		},
       
   465 
       
   466 		deserialize: function(json, create, _data) {
       
   467 			var res = json;
       
   468 			_data = _data || {};
       
   469 			if (Array.isArray(json)) {
       
   470 				var type = json[0],
       
   471 					isDictionary = type === 'dictionary';
       
   472 				if (!isDictionary) {
       
   473 					if (_data.dictionary && json.length == 1 && /^#/.test(type))
       
   474 						return _data.dictionary[type];
       
   475 					type = Base.exports[type];
       
   476 				}
       
   477 				res = [];
       
   478 				for (var i = type ? 1 : 0, l = json.length; i < l; i++)
       
   479 					res.push(Base.deserialize(json[i], create, _data));
       
   480 				if (isDictionary) {
       
   481 					_data.dictionary = res[0];
       
   482 				} else if (type) {
       
   483 					var args = res;
       
   484 					if (create) {
       
   485 						res = create(type, args);
       
   486 					} else {
       
   487 						res = Base.create(type.prototype);
       
   488 						type.apply(res, args);
       
   489 					}
       
   490 				}
       
   491 			} else if (Base.isPlainObject(json)) {
       
   492 				res = {};
       
   493 				for (var key in json)
       
   494 					res[key] = Base.deserialize(json[key], create, _data);
       
   495 			}
       
   496 			return res;
       
   497 		},
       
   498 
       
   499 		exportJSON: function(obj, options) {
       
   500 			return JSON.stringify(Base.serialize(obj, options));
       
   501 		},
       
   502 
       
   503 		importJSON: function(json, target) {
       
   504 			return Base.deserialize(
       
   505 					typeof json === 'string' ? JSON.parse(json) : json,
       
   506 					function(type, args) {
       
   507 						var obj = target && target.constructor === type
       
   508 								? target
       
   509 								: Base.create(type.prototype),
       
   510 							isTarget = obj === target;
       
   511 						if (args.length === 1 && obj instanceof Item
       
   512 								&& (!(obj instanceof Layer) || isTarget)) {
       
   513 							var arg = args[0];
       
   514 							if (Base.isPlainObject(arg))
       
   515 								arg.insert = false;
       
   516 						}
       
   517 						type.apply(obj, args);
       
   518 						if (isTarget)
       
   519 							target = null;
       
   520 						return obj;
       
   521 					});
       
   522 		},
       
   523 
       
   524 		splice: function(list, items, index, remove) {
       
   525 			var amount = items && items.length,
       
   526 				append = index === undefined;
       
   527 			index = append ? list.length : index;
       
   528 			if (index > list.length)
       
   529 				index = list.length;
       
   530 			for (var i = 0; i < amount; i++)
       
   531 				items[i]._index = index + i;
       
   532 			if (append) {
       
   533 				list.push.apply(list, items);
       
   534 				return [];
       
   535 			} else {
       
   536 				var args = [index, remove];
       
   537 				if (items)
       
   538 					args.push.apply(args, items);
       
   539 				var removed = list.splice.apply(list, args);
       
   540 				for (var i = 0, l = removed.length; i < l; i++)
       
   541 					delete removed[i]._index;
       
   542 				for (var i = index + amount, l = list.length; i < l; i++)
       
   543 					list[i]._index = i;
       
   544 				return removed;
       
   545 			}
       
   546 		},
       
   547 
       
   548 		capitalize: function(str) {
       
   549 			return str.replace(/\b[a-z]/g, function(match) {
       
   550 				return match.toUpperCase();
       
   551 			});
       
   552 		},
       
   553 
       
   554 		camelize: function(str) {
       
   555 			return str.replace(/-(.)/g, function(all, chr) {
       
   556 				return chr.toUpperCase();
       
   557 			});
       
   558 		},
       
   559 
       
   560 		hyphenate: function(str) {
       
   561 			return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
       
   562 		}
       
   563 	}
       
   564 });
       
   565 
       
   566 var Callback = {
       
   567 	attach: function(type, func) {
       
   568 		if (typeof type !== 'string') {
       
   569 			Base.each(type, function(value, key) {
       
   570 				this.attach(key, value);
       
   571 			}, this);
       
   572 			return;
       
   573 		}
       
   574 		var entry = this._eventTypes[type];
       
   575 		if (entry) {
       
   576 			var handlers = this._handlers = this._handlers || {};
       
   577 			handlers = handlers[type] = handlers[type] || [];
       
   578 			if (handlers.indexOf(func) == -1) { 
       
   579 				handlers.push(func);
       
   580 				if (entry.install && handlers.length == 1)
       
   581 					entry.install.call(this, type);
       
   582 			}
       
   583 		}
       
   584 	},
       
   585 
       
   586 	detach: function(type, func) {
       
   587 		if (typeof type !== 'string') {
       
   588 			Base.each(type, function(value, key) {
       
   589 				this.detach(key, value);
       
   590 			}, this);
       
   591 			return;
       
   592 		}
       
   593 		var entry = this._eventTypes[type],
       
   594 			handlers = this._handlers && this._handlers[type],
       
   595 			index;
       
   596 		if (entry && handlers) {
       
   597 			if (!func || (index = handlers.indexOf(func)) != -1
       
   598 					&& handlers.length == 1) {
       
   599 				if (entry.uninstall)
       
   600 					entry.uninstall.call(this, type);
       
   601 				delete this._handlers[type];
       
   602 			} else if (index != -1) {
       
   603 				handlers.splice(index, 1);
       
   604 			}
       
   605 		}
       
   606 	},
       
   607 
       
   608 	once: function(type, func) {
       
   609 		this.attach(type, function() {
       
   610 			func.apply(this, arguments);
       
   611 			this.detach(type, func);
       
   612 		});
       
   613 	},
       
   614 
       
   615 	fire: function(type, event) {
       
   616 		var handlers = this._handlers && this._handlers[type];
       
   617 		if (!handlers)
       
   618 			return false;
       
   619 		var args = [].slice.call(arguments, 1),
       
   620 			PaperScript = paper.PaperScript,
       
   621 			handleException = PaperScript && PaperScript.handleException,
       
   622 			that = this;
       
   623 
       
   624 		function callHandlers() {
       
   625 			for (var i in handlers) {
       
   626 				if (handlers[i].apply(that, args) === false
       
   627 						&& event && event.stop)
       
   628 					event.stop();
       
   629 			}
       
   630 		}
       
   631 
       
   632 		if (handleException) {
       
   633 			try {
       
   634 				callHandlers();
       
   635 			} catch (e) {
       
   636 				handleException(e);
       
   637 			}
       
   638 		} else {
       
   639 			callHandlers();
       
   640 		}
       
   641 		return true;
       
   642 	},
       
   643 
       
   644 	responds: function(type) {
       
   645 		return !!(this._handlers && this._handlers[type]);
       
   646 	},
       
   647 
       
   648 	on: '#attach',
       
   649 	off: '#detach',
       
   650 	trigger: '#fire',
       
   651 
       
   652 	statics: {
       
   653 		inject: function inject() {
       
   654 			for (var i = 0, l = arguments.length; i < l; i++) {
       
   655 				var src = arguments[i],
       
   656 					events = src._events;
       
   657 				if (events) {
       
   658 					var types = {};
       
   659 					Base.each(events, function(entry, key) {
       
   660 						var isString = typeof entry === 'string',
       
   661 							name = isString ? entry : key,
       
   662 							part = Base.capitalize(name),
       
   663 							type = name.substring(2).toLowerCase();
       
   664 						types[type] = isString ? {} : entry;
       
   665 						name = '_' + name;
       
   666 						src['get' + part] = function() {
       
   667 							return this[name];
       
   668 						};
       
   669 						src['set' + part] = function(func) {
       
   670 							if (func) {
       
   671 								this.attach(type, func);
       
   672 							} else if (this[name]) {
       
   673 								this.detach(type, this[name]);
       
   674 							}
       
   675 							this[name] = func;
       
   676 						};
       
   677 					});
       
   678 					src._eventTypes = types;
       
   679 				}
       
   680 				inject.base.call(this, src);
       
   681 			}
       
   682 			return this;
       
   683 		}
       
   684 	}
       
   685 };
       
   686 
       
   687 var PaperScope = Base.extend({
       
   688 	_class: 'PaperScope',
       
   689 
       
   690 	initialize: function PaperScope(script) {
       
   691 		paper = this;
       
   692 		this.project = null;
       
   693 		this.projects = [];
       
   694 		this.tools = [];
       
   695 		this.palettes = [];
       
   696 		this._id = script && (script.getAttribute('id') || script.src)
       
   697 				|| ('paperscope-' + (PaperScope._id++));
       
   698 		if (script)
       
   699 			script.setAttribute('id', this._id);
       
   700 		PaperScope._scopes[this._id] = this;
       
   701 		if (!this.support) {
       
   702 			var ctx = CanvasProvider.getContext(1, 1);
       
   703 			PaperScope.prototype.support = {
       
   704 				nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
       
   705 				nativeBlendModes: BlendMode.nativeModes
       
   706 			};
       
   707 			CanvasProvider.release(ctx);
       
   708 		}
       
   709 	},
       
   710 
       
   711 	version: '0.9.15',
       
   712 
       
   713 	getView: function() {
       
   714 		return this.project && this.project.view;
       
   715 	},
       
   716 
       
   717 	getTool: function() {
       
   718 		if (!this._tool)
       
   719 			this._tool = new Tool();
       
   720 		return this._tool;
       
   721 	},
       
   722 
       
   723 	getPaper: function() {
       
   724 		return this;
       
   725 	},
       
   726 
       
   727 	evaluate: function(code) {
       
   728 		var res = paper.PaperScript.evaluate(code, this);
       
   729 		View.updateFocus();
       
   730 		return res;
       
   731 	},
       
   732 
       
   733 	install: function(scope) {
       
   734 		var that = this;
       
   735 		Base.each(['project', 'view', 'tool'], function(key) {
       
   736 			Base.define(scope, key, {
       
   737 				configurable: true,
       
   738 				get: function() {
       
   739 					return that[key];
       
   740 				}
       
   741 			});
       
   742 		});
       
   743 		for (var key in this) {
       
   744 			if (!/^(version|_id)/.test(key))
       
   745 				scope[key] = this[key];
       
   746 		}
       
   747 	},
       
   748 
       
   749 	setup: function(canvas) {
       
   750 		paper = this;
       
   751 		this.project = new Project(canvas);
       
   752 		return this;
       
   753 	},
       
   754 
       
   755 	activate: function() {
       
   756 		paper = this;
       
   757 	},
       
   758 
       
   759 	clear: function() {
       
   760 		for (var i = this.projects.length - 1; i >= 0; i--)
       
   761 			this.projects[i].remove();
       
   762 		for (var i = this.tools.length - 1; i >= 0; i--)
       
   763 			this.tools[i].remove();
       
   764 		for (var i = this.palettes.length - 1; i >= 0; i--)
       
   765 			this.palettes[i].remove();
       
   766 	},
       
   767 
       
   768 	remove: function() {
       
   769 		this.clear();
       
   770 		delete PaperScope._scopes[this._id];
       
   771 	},
       
   772 
       
   773 	statics: new function() {
       
   774 		function handleAttribute(name) {
       
   775 			name += 'Attribute';
       
   776 			return function(el, attr) {
       
   777 				return el[name](attr) || el[name]('data-paper-' + attr);
       
   778 			};
       
   779 		}
       
   780 
       
   781 		return {
       
   782 			_scopes: {},
       
   783 			_id: 0,
       
   784 
       
   785 			get: function(id) {
       
   786 				if (typeof id === 'object')
       
   787 					id = id.getAttribute('id');
       
   788 				return this._scopes[id] || null;
       
   789 			},
       
   790 
       
   791 			getAttribute: handleAttribute('get'),
       
   792 			hasAttribute: handleAttribute('has')
       
   793 		};
       
   794 	}
       
   795 });
       
   796 
       
   797 var PaperScopeItem = Base.extend(Callback, {
       
   798 
       
   799 	initialize: function(activate) {
       
   800 		this._scope = paper;
       
   801 		this._index = this._scope[this._list].push(this) - 1;
       
   802 		if (activate || !this._scope[this._reference])
       
   803 			this.activate();
       
   804 	},
       
   805 
       
   806 	activate: function() {
       
   807 		if (!this._scope)
       
   808 			return false;
       
   809 		var prev = this._scope[this._reference];
       
   810 		if (prev && prev !== this)
       
   811 			prev.fire('deactivate');
       
   812 		this._scope[this._reference] = this;
       
   813 		this.fire('activate', prev);
       
   814 		return true;
       
   815 	},
       
   816 
       
   817 	isActive: function() {
       
   818 		return this._scope[this._reference] === this;
       
   819 	},
       
   820 
       
   821 	remove: function() {
       
   822 		if (this._index == null)
       
   823 			return false;
       
   824 		Base.splice(this._scope[this._list], null, this._index, 1);
       
   825 		if (this._scope[this._reference] == this)
       
   826 			this._scope[this._reference] = null;
       
   827 		this._scope = null;
       
   828 		return true;
       
   829 	}
       
   830 });
       
   831 
       
   832 var Formatter = Base.extend({
       
   833 	initialize: function(precision) {
       
   834 		this.precision = precision || 5;
       
   835 		this.multiplier = Math.pow(10, this.precision);
       
   836 	},
       
   837 
       
   838 	number: function(val) {
       
   839 		return Math.round(val * this.multiplier) / this.multiplier;
       
   840 	},
       
   841 
       
   842 	point: function(val, separator) {
       
   843 		return this.number(val.x) + (separator || ',') + this.number(val.y);
       
   844 	},
       
   845 
       
   846 	size: function(val, separator) {
       
   847 		return this.number(val.width) + (separator || ',')
       
   848 				+ this.number(val.height);
       
   849 	},
       
   850 
       
   851 	rectangle: function(val, separator) {
       
   852 		return this.point(val, separator) + (separator || ',')
       
   853 				+ this.size(val, separator);
       
   854 	}
       
   855 });
       
   856 
       
   857 Formatter.instance = new Formatter();
       
   858 
       
   859 var Numerical = new function() {
       
   860 
       
   861 	var abscissas = [
       
   862 		[  0.5773502691896257645091488],
       
   863 		[0,0.7745966692414833770358531],
       
   864 		[  0.3399810435848562648026658,0.8611363115940525752239465],
       
   865 		[0,0.5384693101056830910363144,0.9061798459386639927976269],
       
   866 		[  0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016],
       
   867 		[0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897],
       
   868 		[  0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609],
       
   869 		[0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762],
       
   870 		[  0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640],
       
   871 		[0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380],
       
   872 		[  0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491],
       
   873 		[0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294],
       
   874 		[  0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973],
       
   875 		[0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657],
       
   876 		[  0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542]
       
   877 	];
       
   878 
       
   879 	var weights = [
       
   880 		[1],
       
   881 		[0.8888888888888888888888889,0.5555555555555555555555556],
       
   882 		[0.6521451548625461426269361,0.3478548451374538573730639],
       
   883 		[0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640],
       
   884 		[0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961],
       
   885 		[0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114],
       
   886 		[0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314],
       
   887 		[0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922],
       
   888 		[0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688],
       
   889 		[0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537],
       
   890 		[0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160],
       
   891 		[0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216],
       
   892 		[0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329],
       
   893 		[0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284],
       
   894 		[0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806]
       
   895 	];
       
   896 
       
   897 	var abs = Math.abs,
       
   898 		sqrt = Math.sqrt,
       
   899 		pow = Math.pow,
       
   900 		cos = Math.cos,
       
   901 		PI = Math.PI;
       
   902 
       
   903 	return {
       
   904 		TOLERANCE: 10e-6,
       
   905 		EPSILON: 10e-12,
       
   906 		KAPPA: 4 * (sqrt(2) - 1) / 3,
       
   907 
       
   908 		isZero: function(val) {
       
   909 			return abs(val) <= Numerical.EPSILON;
       
   910 		},
       
   911 
       
   912 		integrate: function(f, a, b, n) {
       
   913 			var x = abscissas[n - 2],
       
   914 				w = weights[n - 2],
       
   915 				A = 0.5 * (b - a),
       
   916 				B = A + a,
       
   917 				i = 0,
       
   918 				m = (n + 1) >> 1,
       
   919 				sum = n & 1 ? w[i++] * f(B) : 0; 
       
   920 			while (i < m) {
       
   921 				var Ax = A * x[i];
       
   922 				sum += w[i++] * (f(B + Ax) + f(B - Ax));
       
   923 			}
       
   924 			return A * sum;
       
   925 		},
       
   926 
       
   927 		findRoot: function(f, df, x, a, b, n, tolerance) {
       
   928 			for (var i = 0; i < n; i++) {
       
   929 				var fx = f(x),
       
   930 					dx = fx / df(x);
       
   931 				if (abs(dx) < tolerance)
       
   932 					return x;
       
   933 				var nx = x - dx;
       
   934 				if (fx > 0) {
       
   935 					b = x;
       
   936 					x = nx <= a ? 0.5 * (a + b) : nx;
       
   937 				} else {
       
   938 					a = x;
       
   939 					x = nx >= b ? 0.5 * (a + b) : nx;
       
   940 				}
       
   941 			}
       
   942 		},
       
   943 
       
   944 		solveQuadratic: function(a, b, c, roots, min, max) {
       
   945 			var epsilon = Numerical.EPSILON,
       
   946 				unbound = min === undefined,
       
   947 				minE = min - epsilon,
       
   948 				maxE = max + epsilon,
       
   949 				count = 0;
       
   950 
       
   951 			function add(root) {
       
   952 				if (unbound || root > minE && root < maxE)
       
   953 					roots[count++] = root < min ? min : root > max ? max : root;
       
   954 				return count;
       
   955 			}
       
   956 
       
   957 			if (abs(a) < epsilon) {
       
   958 				if (abs(b) >= epsilon)
       
   959 					return add(-c / b);
       
   960 				return abs(c) < epsilon ? -1 : 0; 
       
   961 			}
       
   962 			var p = b / (2 * a);
       
   963 			var q = c / a;
       
   964 			var p2 = p * p;
       
   965 			if (p2 < q - epsilon)
       
   966 				return 0;
       
   967 			var s = p2 > q ? sqrt(p2 - q) : 0;
       
   968 			add (s - p);
       
   969 			if (s > 0)
       
   970 				add(-s - p);
       
   971 			return count;
       
   972 		},
       
   973 
       
   974 		solveCubic: function(a, b, c, d, roots, min, max) {
       
   975 			var epsilon = Numerical.EPSILON;
       
   976 			if (abs(a) < epsilon)
       
   977 				return Numerical.solveQuadratic(b, c, d, roots, min, max);
       
   978 
       
   979 			var unbound = min === undefined,
       
   980 				minE = min - epsilon,
       
   981 				maxE = max + epsilon,
       
   982 				count = 0;
       
   983 
       
   984 			function add(root) {
       
   985 				if (unbound || root > minE && root < maxE)
       
   986 					roots[count++] = root < min ? min : root > max ? max : root;
       
   987 				return count;
       
   988 			}
       
   989 
       
   990 			b /= a;
       
   991 			c /= a;
       
   992 			d /= a;
       
   993 			var bb = b * b,
       
   994 				p = (bb - 3 * c) / 9,
       
   995 				q = (2 * bb * b - 9 * b * c + 27 * d) / 54,
       
   996 				ppp = p * p * p,
       
   997 				D = q * q - ppp;
       
   998 			b /= 3;
       
   999 			if (abs(D) < epsilon) {
       
  1000 				if (abs(q) < epsilon) 
       
  1001 					return add(-b);
       
  1002 				var sqp = sqrt(p),
       
  1003 					snq = q > 0 ? 1 : -1;
       
  1004 				add(-snq * 2 * sqp - b);
       
  1005 				return add(snq * sqp - b);
       
  1006 			}
       
  1007 			if (D < 0) { 
       
  1008 				var sqp = sqrt(p),
       
  1009 					phi = Math.acos(q / (sqp * sqp * sqp)) / 3,
       
  1010 					t = -2 * sqp,
       
  1011 					o = 2 * PI / 3;
       
  1012 				add(t * cos(phi) - b);
       
  1013 				add(t * cos(phi + o) - b);
       
  1014 				return add(t * cos(phi - o) - b);
       
  1015 			}
       
  1016 			var A = (q > 0 ? -1 : 1) * pow(abs(q) + sqrt(D), 1 / 3);
       
  1017 			return add(A + p / A - b);
       
  1018 		}
       
  1019 	};
       
  1020 };
       
  1021 
       
  1022 var Point = Base.extend({
       
  1023 	_class: 'Point',
       
  1024 	_readIndex: true,
       
  1025 
       
  1026 	initialize: function Point(arg0, arg1) {
       
  1027 		var type = typeof arg0;
       
  1028 		if (type === 'number') {
       
  1029 			var hasY = typeof arg1 === 'number';
       
  1030 			this.x = arg0;
       
  1031 			this.y = hasY ? arg1 : arg0;
       
  1032 			if (this.__read)
       
  1033 				this.__read = hasY ? 2 : 1;
       
  1034 		} else if (type === 'undefined' || arg0 === null) {
       
  1035 			this.x = this.y = 0;
       
  1036 			if (this.__read)
       
  1037 				this.__read = arg0 === null ? 1 : 0;
       
  1038 		} else {
       
  1039 			if (Array.isArray(arg0)) {
       
  1040 				this.x = arg0[0];
       
  1041 				this.y = arg0.length > 1 ? arg0[1] : arg0[0];
       
  1042 			} else if (arg0.x != null) {
       
  1043 				this.x = arg0.x;
       
  1044 				this.y = arg0.y;
       
  1045 			} else if (arg0.width != null) {
       
  1046 				this.x = arg0.width;
       
  1047 				this.y = arg0.height;
       
  1048 			} else if (arg0.angle != null) {
       
  1049 				this.x = arg0.length;
       
  1050 				this.y = 0;
       
  1051 				this.setAngle(arg0.angle);
       
  1052 			} else {
       
  1053 				this.x = this.y = 0;
       
  1054 				if (this.__read)
       
  1055 					this.__read = 0;
       
  1056 			}
       
  1057 			if (this.__read)
       
  1058 				this.__read = 1;
       
  1059 		}
       
  1060 	},
       
  1061 
       
  1062 	set: function(x, y) {
       
  1063 		this.x = x;
       
  1064 		this.y = y;
       
  1065 		return this;
       
  1066 	},
       
  1067 
       
  1068 	equals: function(point) {
       
  1069 		return point === this || point && (this.x === point.x
       
  1070 				&& this.y === point.y
       
  1071 				|| Array.isArray(point) && this.x === point[0]
       
  1072 					&& this.y === point[1]) || false;
       
  1073 	},
       
  1074 
       
  1075 	clone: function() {
       
  1076 		return new Point(this.x, this.y);
       
  1077 	},
       
  1078 
       
  1079 	toString: function() {
       
  1080 		var f = Formatter.instance;
       
  1081 		return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';
       
  1082 	},
       
  1083 
       
  1084 	_serialize: function(options) {
       
  1085 		var f = options.formatter;
       
  1086 		return [f.number(this.x), f.number(this.y)];
       
  1087 	},
       
  1088 
       
  1089 	add: function(point) {
       
  1090 		point = Point.read(arguments);
       
  1091 		return new Point(this.x + point.x, this.y + point.y);
       
  1092 	},
       
  1093 
       
  1094 	subtract: function(point) {
       
  1095 		point = Point.read(arguments);
       
  1096 		return new Point(this.x - point.x, this.y - point.y);
       
  1097 	},
       
  1098 
       
  1099 	multiply: function(point) {
       
  1100 		point = Point.read(arguments);
       
  1101 		return new Point(this.x * point.x, this.y * point.y);
       
  1102 	},
       
  1103 
       
  1104 	divide: function(point) {
       
  1105 		point = Point.read(arguments);
       
  1106 		return new Point(this.x / point.x, this.y / point.y);
       
  1107 	},
       
  1108 
       
  1109 	modulo: function(point) {
       
  1110 		point = Point.read(arguments);
       
  1111 		return new Point(this.x % point.x, this.y % point.y);
       
  1112 	},
       
  1113 
       
  1114 	negate: function() {
       
  1115 		return new Point(-this.x, -this.y);
       
  1116 	},
       
  1117 
       
  1118 	transform: function(matrix) {
       
  1119 		return matrix ? matrix._transformPoint(this) : this;
       
  1120 	},
       
  1121 
       
  1122 	getDistance: function(point, squared) {
       
  1123 		point = Point.read(arguments);
       
  1124 		var x = point.x - this.x,
       
  1125 			y = point.y - this.y,
       
  1126 			d = x * x + y * y;
       
  1127 		return squared ? d : Math.sqrt(d);
       
  1128 	},
       
  1129 
       
  1130 	getLength: function() {
       
  1131 		var length = this.x * this.x + this.y * this.y;
       
  1132 		return arguments.length && arguments[0] ? length : Math.sqrt(length);
       
  1133 	},
       
  1134 
       
  1135 	setLength: function(length) {
       
  1136 		if (this.isZero()) {
       
  1137 			var angle = this._angle || 0;
       
  1138 			this.set(
       
  1139 				Math.cos(angle) * length,
       
  1140 				Math.sin(angle) * length
       
  1141 			);
       
  1142 		} else {
       
  1143 			var scale = length / this.getLength();
       
  1144 			if (Numerical.isZero(scale))
       
  1145 				this.getAngle();
       
  1146 			this.set(
       
  1147 				this.x * scale,
       
  1148 				this.y * scale
       
  1149 			);
       
  1150 		}
       
  1151 		return this;
       
  1152 	},
       
  1153 
       
  1154 	normalize: function(length) {
       
  1155 		if (length === undefined)
       
  1156 			length = 1;
       
  1157 		var current = this.getLength(),
       
  1158 			scale = current !== 0 ? length / current : 0,
       
  1159 			point = new Point(this.x * scale, this.y * scale);
       
  1160 		point._angle = this._angle;
       
  1161 		return point;
       
  1162 	},
       
  1163 
       
  1164 	getAngle: function() {
       
  1165 		return this.getAngleInRadians(arguments[0]) * 180 / Math.PI;
       
  1166 	},
       
  1167 
       
  1168 	setAngle: function(angle) {
       
  1169 		angle = this._angle = angle * Math.PI / 180;
       
  1170 		if (!this.isZero()) {
       
  1171 			var length = this.getLength();
       
  1172 			this.set(
       
  1173 				Math.cos(angle) * length,
       
  1174 				Math.sin(angle) * length
       
  1175 			);
       
  1176 		}
       
  1177 		return this;
       
  1178 	},
       
  1179 
       
  1180 	getAngleInRadians: function() {
       
  1181 		if (arguments[0] === undefined) {
       
  1182 			return this.isZero()
       
  1183 					? this._angle || 0
       
  1184 					: this._angle = Math.atan2(this.y, this.x);
       
  1185 		} else {
       
  1186 			var point = Point.read(arguments),
       
  1187 				div = this.getLength() * point.getLength();
       
  1188 			if (Numerical.isZero(div)) {
       
  1189 				return NaN;
       
  1190 			} else {
       
  1191 				return Math.acos(this.dot(point) / div);
       
  1192 			}
       
  1193 		}
       
  1194 	},
       
  1195 
       
  1196 	getAngleInDegrees: function() {
       
  1197 		return this.getAngle(arguments[0]);
       
  1198 	},
       
  1199 
       
  1200 	getQuadrant: function() {
       
  1201 		return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3;
       
  1202 	},
       
  1203 
       
  1204 	getDirectedAngle: function(point) {
       
  1205 		point = Point.read(arguments);
       
  1206 		return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI;
       
  1207 	},
       
  1208 
       
  1209 	rotate: function(angle, center) {
       
  1210 		if (angle === 0)
       
  1211 			return this.clone();
       
  1212 		angle = angle * Math.PI / 180;
       
  1213 		var point = center ? this.subtract(center) : this,
       
  1214 			s = Math.sin(angle),
       
  1215 			c = Math.cos(angle);
       
  1216 		point = new Point(
       
  1217 			point.x * c - point.y * s,
       
  1218 			point.y * c + point.x * s
       
  1219 		);
       
  1220 		return center ? point.add(center) : point;
       
  1221 	},
       
  1222 
       
  1223 	isInside: function(rect) {
       
  1224 		return rect.contains(this);
       
  1225 	},
       
  1226 
       
  1227 	isClose: function(point, tolerance) {
       
  1228 		return this.getDistance(point) < tolerance;
       
  1229 	},
       
  1230 
       
  1231 	isColinear: function(point) {
       
  1232 		return this.cross(point) < 0.00001;
       
  1233 	},
       
  1234 
       
  1235 	isOrthogonal: function(point) {
       
  1236 		return this.dot(point) < 0.00001;
       
  1237 	},
       
  1238 
       
  1239 	isZero: function() {
       
  1240 		return Numerical.isZero(this.x) && Numerical.isZero(this.y);
       
  1241 	},
       
  1242 
       
  1243 	isNaN: function() {
       
  1244 		return isNaN(this.x) || isNaN(this.y);
       
  1245 	},
       
  1246 
       
  1247 	dot: function(point) {
       
  1248 		point = Point.read(arguments);
       
  1249 		return this.x * point.x + this.y * point.y;
       
  1250 	},
       
  1251 
       
  1252 	cross: function(point) {
       
  1253 		point = Point.read(arguments);
       
  1254 		return this.x * point.y - this.y * point.x;
       
  1255 	},
       
  1256 
       
  1257 	project: function(point) {
       
  1258 		point = Point.read(arguments);
       
  1259 		if (point.isZero()) {
       
  1260 			return new Point(0, 0);
       
  1261 		} else {
       
  1262 			var scale = this.dot(point) / point.dot(point);
       
  1263 			return new Point(
       
  1264 				point.x * scale,
       
  1265 				point.y * scale
       
  1266 			);
       
  1267 		}
       
  1268 	},
       
  1269 
       
  1270 	statics: {
       
  1271 		min: function() {
       
  1272 			var point1 = Point.read(arguments);
       
  1273 				point2 = Point.read(arguments);
       
  1274 			return new Point(
       
  1275 				Math.min(point1.x, point2.x),
       
  1276 				Math.min(point1.y, point2.y)
       
  1277 			);
       
  1278 		},
       
  1279 
       
  1280 		max: function() {
       
  1281 			var point1 = Point.read(arguments);
       
  1282 				point2 = Point.read(arguments);
       
  1283 			return new Point(
       
  1284 				Math.max(point1.x, point2.x),
       
  1285 				Math.max(point1.y, point2.y)
       
  1286 			);
       
  1287 		},
       
  1288 
       
  1289 		random: function() {
       
  1290 			return new Point(Math.random(), Math.random());
       
  1291 		}
       
  1292 	}
       
  1293 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
       
  1294 	var op = Math[name];
       
  1295 	this[name] = function() {
       
  1296 		return new Point(op(this.x), op(this.y));
       
  1297 	};
       
  1298 }, {}));
       
  1299 
       
  1300 var LinkedPoint = Point.extend({
       
  1301 	initialize: function Point(x, y, owner, setter) {
       
  1302 		this._x = x;
       
  1303 		this._y = y;
       
  1304 		this._owner = owner;
       
  1305 		this._setter = setter;
       
  1306 	},
       
  1307 
       
  1308 	set: function(x, y, _dontNotify) {
       
  1309 		this._x = x;
       
  1310 		this._y = y;
       
  1311 		if (!_dontNotify)
       
  1312 			this._owner[this._setter](this);
       
  1313 		return this;
       
  1314 	},
       
  1315 
       
  1316 	getX: function() {
       
  1317 		return this._x;
       
  1318 	},
       
  1319 
       
  1320 	setX: function(x) {
       
  1321 		this._x = x;
       
  1322 		this._owner[this._setter](this);
       
  1323 	},
       
  1324 
       
  1325 	getY: function() {
       
  1326 		return this._y;
       
  1327 	},
       
  1328 
       
  1329 	setY: function(y) {
       
  1330 		this._y = y;
       
  1331 		this._owner[this._setter](this);
       
  1332 	}
       
  1333 });
       
  1334 
       
  1335 var Size = Base.extend({
       
  1336 	_class: 'Size',
       
  1337 	_readIndex: true,
       
  1338 
       
  1339 	initialize: function Size(arg0, arg1) {
       
  1340 		var type = typeof arg0;
       
  1341 		if (type === 'number') {
       
  1342 			var hasHeight = typeof arg1 === 'number';
       
  1343 			this.width = arg0;
       
  1344 			this.height = hasHeight ? arg1 : arg0;
       
  1345 			if (this.__read)
       
  1346 				this.__read = hasHeight ? 2 : 1;
       
  1347 		} else if (type === 'undefined' || arg0 === null) {
       
  1348 			this.width = this.height = 0;
       
  1349 			if (this.__read)
       
  1350 				this.__read = arg0 === null ? 1 : 0;
       
  1351 		} else {
       
  1352 			if (Array.isArray(arg0)) {
       
  1353 				this.width = arg0[0];
       
  1354 				this.height = arg0.length > 1 ? arg0[1] : arg0[0];
       
  1355 			} else if (arg0.width != null) {
       
  1356 				this.width = arg0.width;
       
  1357 				this.height = arg0.height;
       
  1358 			} else if (arg0.x != null) {
       
  1359 				this.width = arg0.x;
       
  1360 				this.height = arg0.y;
       
  1361 			} else {
       
  1362 				this.width = this.height = 0;
       
  1363 				if (this.__read)
       
  1364 					this.__read = 0;
       
  1365 			}
       
  1366 			if (this.__read)
       
  1367 				this.__read = 1;
       
  1368 		}
       
  1369 	},
       
  1370 
       
  1371 	set: function(width, height) {
       
  1372 		this.width = width;
       
  1373 		this.height = height;
       
  1374 		return this;
       
  1375 	},
       
  1376 
       
  1377 	equals: function(size) {
       
  1378 		return size === this || size && (this.width === size.width
       
  1379 				&& this.height === size.height
       
  1380 				|| Array.isArray(size) && this.width === size[0]
       
  1381 					&& this.height === size[1]) || false;
       
  1382 	},
       
  1383 
       
  1384 	clone: function() {
       
  1385 		return new Size(this.width, this.height);
       
  1386 	},
       
  1387 
       
  1388 	toString: function() {
       
  1389 		var f = Formatter.instance;
       
  1390 		return '{ width: ' + f.number(this.width)
       
  1391 				+ ', height: ' + f.number(this.height) + ' }';
       
  1392 	},
       
  1393 
       
  1394 	_serialize: function(options) {
       
  1395 		var f = options.formatter;
       
  1396 		return [f.number(this.width),
       
  1397 				f.number(this.height)];
       
  1398 	},
       
  1399 
       
  1400 	add: function(size) {
       
  1401 		size = Size.read(arguments);
       
  1402 		return new Size(this.width + size.width, this.height + size.height);
       
  1403 	},
       
  1404 
       
  1405 	subtract: function(size) {
       
  1406 		size = Size.read(arguments);
       
  1407 		return new Size(this.width - size.width, this.height - size.height);
       
  1408 	},
       
  1409 
       
  1410 	multiply: function(size) {
       
  1411 		size = Size.read(arguments);
       
  1412 		return new Size(this.width * size.width, this.height * size.height);
       
  1413 	},
       
  1414 
       
  1415 	divide: function(size) {
       
  1416 		size = Size.read(arguments);
       
  1417 		return new Size(this.width / size.width, this.height / size.height);
       
  1418 	},
       
  1419 
       
  1420 	modulo: function(size) {
       
  1421 		size = Size.read(arguments);
       
  1422 		return new Size(this.width % size.width, this.height % size.height);
       
  1423 	},
       
  1424 
       
  1425 	negate: function() {
       
  1426 		return new Size(-this.width, -this.height);
       
  1427 	},
       
  1428 
       
  1429 	isZero: function() {
       
  1430 		return Numerical.isZero(this.width) && Numerical.isZero(this.height);
       
  1431 	},
       
  1432 
       
  1433 	isNaN: function() {
       
  1434 		return isNaN(this.width) || isNaN(this.height);
       
  1435 	},
       
  1436 
       
  1437 	statics: {
       
  1438 		min: function(size1, size2) {
       
  1439 			return new Size(
       
  1440 				Math.min(size1.width, size2.width),
       
  1441 				Math.min(size1.height, size2.height));
       
  1442 		},
       
  1443 
       
  1444 		max: function(size1, size2) {
       
  1445 			return new Size(
       
  1446 				Math.max(size1.width, size2.width),
       
  1447 				Math.max(size1.height, size2.height));
       
  1448 		},
       
  1449 
       
  1450 		random: function() {
       
  1451 			return new Size(Math.random(), Math.random());
       
  1452 		}
       
  1453 	}
       
  1454 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
       
  1455 	var op = Math[name];
       
  1456 	this[name] = function() {
       
  1457 		return new Size(op(this.width), op(this.height));
       
  1458 	};
       
  1459 }, {}));
       
  1460 
       
  1461 var LinkedSize = Size.extend({
       
  1462 	initialize: function Size(width, height, owner, setter) {
       
  1463 		this._width = width;
       
  1464 		this._height = height;
       
  1465 		this._owner = owner;
       
  1466 		this._setter = setter;
       
  1467 	},
       
  1468 
       
  1469 	set: function(width, height, _dontNotify) {
       
  1470 		this._width = width;
       
  1471 		this._height = height;
       
  1472 		if (!_dontNotify)
       
  1473 			this._owner[this._setter](this);
       
  1474 		return this;
       
  1475 	},
       
  1476 
       
  1477 	getWidth: function() {
       
  1478 		return this._width;
       
  1479 	},
       
  1480 
       
  1481 	setWidth: function(width) {
       
  1482 		this._width = width;
       
  1483 		this._owner[this._setter](this);
       
  1484 	},
       
  1485 
       
  1486 	getHeight: function() {
       
  1487 		return this._height;
       
  1488 	},
       
  1489 
       
  1490 	setHeight: function(height) {
       
  1491 		this._height = height;
       
  1492 		this._owner[this._setter](this);
       
  1493 	}
       
  1494 });
       
  1495 
       
  1496 var Rectangle = Base.extend({
       
  1497 	_class: 'Rectangle',
       
  1498 	_readIndex: true,
       
  1499 
       
  1500 	initialize: function Rectangle(arg0, arg1, arg2, arg3) {
       
  1501 		var type = typeof arg0,
       
  1502 			read = 0;
       
  1503 		if (type === 'number') {
       
  1504 			this.x = arg0;
       
  1505 			this.y = arg1;
       
  1506 			this.width = arg2;
       
  1507 			this.height = arg3;
       
  1508 			read = 4;
       
  1509 		} else if (type === 'undefined' || arg0 === null) {
       
  1510 			this.x = this.y = this.width = this.height = 0;
       
  1511 			read = arg0 === null ? 1 : 0;
       
  1512 		} else if (arguments.length === 1) {
       
  1513 			if (Array.isArray(arg0)) {
       
  1514 				this.x = arg0[0];
       
  1515 				this.y = arg0[1];
       
  1516 				this.width = arg0[2];
       
  1517 				this.height = arg0[3];
       
  1518 				read = 1;
       
  1519 			} else if (arg0.x !== undefined || arg0.width !== undefined) {
       
  1520 				this.x = arg0.x || 0;
       
  1521 				this.y = arg0.y || 0;
       
  1522 				this.width = arg0.width || 0;
       
  1523 				this.height = arg0.height || 0;
       
  1524 				read = 1;
       
  1525 			} else if (arg0.from === undefined && arg0.to === undefined) {
       
  1526 				this.x = this.y = this.width = this.height = 0;
       
  1527 				this._set(arg0);
       
  1528 				read = 1;
       
  1529 			}
       
  1530 		}
       
  1531 		if (!read) {
       
  1532 			var point = Point.readNamed(arguments, 'from'),
       
  1533 				next = Base.peek(arguments);
       
  1534 			this.x = point.x;
       
  1535 			this.y = point.y;
       
  1536 			if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
       
  1537 				var to = Point.readNamed(arguments, 'to');
       
  1538 				this.width = to.x - point.x;
       
  1539 				this.height = to.y - point.y;
       
  1540 				if (this.width < 0) {
       
  1541 					this.x = to.x;
       
  1542 					this.width = -this.width;
       
  1543 				}
       
  1544 				if (this.height < 0) {
       
  1545 					this.y = to.y;
       
  1546 					this.height = -this.height;
       
  1547 				}
       
  1548 			} else {
       
  1549 				var size = Size.read(arguments);
       
  1550 				this.width = size.width;
       
  1551 				this.height = size.height;
       
  1552 			}
       
  1553 			read = arguments._index;
       
  1554 		}
       
  1555 		if (this.__read)
       
  1556 			this.__read = read;
       
  1557 	},
       
  1558 
       
  1559 	set: function(x, y, width, height) {
       
  1560 		this.x = x;
       
  1561 		this.y = y;
       
  1562 		this.width = width;
       
  1563 		this.height = height;
       
  1564 		return this;
       
  1565 	},
       
  1566 
       
  1567 	clone: function() {
       
  1568 		return new Rectangle(this.x, this.y, this.width, this.height);
       
  1569 	},
       
  1570 
       
  1571 	equals: function(rect) {
       
  1572 		if (Base.isPlainValue(rect))
       
  1573 			rect = Rectangle.read(arguments);
       
  1574 		return rect === this
       
  1575 				|| rect && this.x === rect.x && this.y === rect.y
       
  1576 					&& this.width === rect.width && this.height === rect.height
       
  1577 				|| false;
       
  1578 	},
       
  1579 
       
  1580 	toString: function() {
       
  1581 		var f = Formatter.instance;
       
  1582 		return '{ x: ' + f.number(this.x)
       
  1583 				+ ', y: ' + f.number(this.y)
       
  1584 				+ ', width: ' + f.number(this.width)
       
  1585 				+ ', height: ' + f.number(this.height)
       
  1586 				+ ' }';
       
  1587 	},
       
  1588 
       
  1589 	_serialize: function(options) {
       
  1590 		var f = options.formatter;
       
  1591 		return [f.number(this.x),
       
  1592 				f.number(this.y),
       
  1593 				f.number(this.width),
       
  1594 				f.number(this.height)];
       
  1595 	},
       
  1596 
       
  1597 	getPoint: function() {
       
  1598 		return new (arguments[0] ? Point : LinkedPoint)
       
  1599 				(this.x, this.y, this, 'setPoint');
       
  1600 	},
       
  1601 
       
  1602 	setPoint: function(point) {
       
  1603 		point = Point.read(arguments);
       
  1604 		this.x = point.x;
       
  1605 		this.y = point.y;
       
  1606 	},
       
  1607 
       
  1608 	getSize: function() {
       
  1609 		return new (arguments[0] ? Size : LinkedSize)
       
  1610 				(this.width, this.height, this, 'setSize');
       
  1611 	},
       
  1612 
       
  1613 	setSize: function(size) {
       
  1614 		size = Size.read(arguments);
       
  1615 		if (this._fixX)
       
  1616 			this.x += (this.width - size.width) * this._fixX;
       
  1617 		if (this._fixY)
       
  1618 			this.y += (this.height - size.height) * this._fixY;
       
  1619 		this.width = size.width;
       
  1620 		this.height = size.height;
       
  1621 		this._fixW = 1;
       
  1622 		this._fixH = 1;
       
  1623 	},
       
  1624 
       
  1625 	getLeft: function() {
       
  1626 		return this.x;
       
  1627 	},
       
  1628 
       
  1629 	setLeft: function(left) {
       
  1630 		if (!this._fixW)
       
  1631 			this.width -= left - this.x;
       
  1632 		this.x = left;
       
  1633 		this._fixX = 0;
       
  1634 	},
       
  1635 
       
  1636 	getTop: function() {
       
  1637 		return this.y;
       
  1638 	},
       
  1639 
       
  1640 	setTop: function(top) {
       
  1641 		if (!this._fixH)
       
  1642 			this.height -= top - this.y;
       
  1643 		this.y = top;
       
  1644 		this._fixY = 0;
       
  1645 	},
       
  1646 
       
  1647 	getRight: function() {
       
  1648 		return this.x + this.width;
       
  1649 	},
       
  1650 
       
  1651 	setRight: function(right) {
       
  1652 		if (this._fixX !== undefined && this._fixX !== 1)
       
  1653 			this._fixW = 0;
       
  1654 		if (this._fixW)
       
  1655 			this.x = right - this.width;
       
  1656 		else
       
  1657 			this.width = right - this.x;
       
  1658 		this._fixX = 1;
       
  1659 	},
       
  1660 
       
  1661 	getBottom: function() {
       
  1662 		return this.y + this.height;
       
  1663 	},
       
  1664 
       
  1665 	setBottom: function(bottom) {
       
  1666 		if (this._fixY !== undefined && this._fixY !== 1)
       
  1667 			this._fixH = 0;
       
  1668 		if (this._fixH)
       
  1669 			this.y = bottom - this.height;
       
  1670 		else
       
  1671 			this.height = bottom - this.y;
       
  1672 		this._fixY = 1;
       
  1673 	},
       
  1674 
       
  1675 	getCenterX: function() {
       
  1676 		return this.x + this.width * 0.5;
       
  1677 	},
       
  1678 
       
  1679 	setCenterX: function(x) {
       
  1680 		this.x = x - this.width * 0.5;
       
  1681 		this._fixX = 0.5;
       
  1682 	},
       
  1683 
       
  1684 	getCenterY: function() {
       
  1685 		return this.y + this.height * 0.5;
       
  1686 	},
       
  1687 
       
  1688 	setCenterY: function(y) {
       
  1689 		this.y = y - this.height * 0.5;
       
  1690 		this._fixY = 0.5;
       
  1691 	},
       
  1692 
       
  1693 	getCenter: function() {
       
  1694 		return new (arguments[0] ? Point : LinkedPoint)
       
  1695 				(this.getCenterX(), this.getCenterY(), this, 'setCenter');
       
  1696 	},
       
  1697 
       
  1698 	setCenter: function(point) {
       
  1699 		point = Point.read(arguments);
       
  1700 		this.setCenterX(point.x);
       
  1701 		this.setCenterY(point.y);
       
  1702 		return this;
       
  1703 	},
       
  1704 
       
  1705 	isEmpty: function() {
       
  1706 		return this.width == 0 || this.height == 0;
       
  1707 	},
       
  1708 
       
  1709 	contains: function(arg) {
       
  1710 		return arg && arg.width !== undefined
       
  1711 				|| (Array.isArray(arg) ? arg : arguments).length == 4
       
  1712 				? this._containsRectangle(Rectangle.read(arguments))
       
  1713 				: this._containsPoint(Point.read(arguments));
       
  1714 	},
       
  1715 
       
  1716 	_containsPoint: function(point) {
       
  1717 		var x = point.x,
       
  1718 			y = point.y;
       
  1719 		return x >= this.x && y >= this.y
       
  1720 				&& x <= this.x + this.width
       
  1721 				&& y <= this.y + this.height;
       
  1722 	},
       
  1723 
       
  1724 	_containsRectangle: function(rect) {
       
  1725 		var x = rect.x,
       
  1726 			y = rect.y;
       
  1727 		return x >= this.x && y >= this.y
       
  1728 				&& x + rect.width <= this.x + this.width
       
  1729 				&& y + rect.height <= this.y + this.height;
       
  1730 	},
       
  1731 
       
  1732 	intersects: function(rect) {
       
  1733 		rect = Rectangle.read(arguments);
       
  1734 		return rect.x + rect.width > this.x
       
  1735 				&& rect.y + rect.height > this.y
       
  1736 				&& rect.x < this.x + this.width
       
  1737 				&& rect.y < this.y + this.height;
       
  1738 	},
       
  1739 
       
  1740 	touches: function(rect) {
       
  1741 		rect = Rectangle.read(arguments);
       
  1742 		return rect.x + rect.width >= this.x
       
  1743 				&& rect.y + rect.height >= this.y
       
  1744 				&& rect.x <= this.x + this.width
       
  1745 				&& rect.y <= this.y + this.height;
       
  1746 	},
       
  1747 
       
  1748 	intersect: function(rect) {
       
  1749 		rect = Rectangle.read(arguments);
       
  1750 		var x1 = Math.max(this.x, rect.x),
       
  1751 			y1 = Math.max(this.y, rect.y),
       
  1752 			x2 = Math.min(this.x + this.width, rect.x + rect.width),
       
  1753 			y2 = Math.min(this.y + this.height, rect.y + rect.height);
       
  1754 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1755 	},
       
  1756 
       
  1757 	unite: function(rect) {
       
  1758 		rect = Rectangle.read(arguments);
       
  1759 		var x1 = Math.min(this.x, rect.x),
       
  1760 			y1 = Math.min(this.y, rect.y),
       
  1761 			x2 = Math.max(this.x + this.width, rect.x + rect.width),
       
  1762 			y2 = Math.max(this.y + this.height, rect.y + rect.height);
       
  1763 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1764 	},
       
  1765 
       
  1766 	include: function(point) {
       
  1767 		point = Point.read(arguments);
       
  1768 		var x1 = Math.min(this.x, point.x),
       
  1769 			y1 = Math.min(this.y, point.y),
       
  1770 			x2 = Math.max(this.x + this.width, point.x),
       
  1771 			y2 = Math.max(this.y + this.height, point.y);
       
  1772 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  1773 	},
       
  1774 
       
  1775 	expand: function(hor, ver) {
       
  1776 		if (ver === undefined)
       
  1777 			ver = hor;
       
  1778 		return new Rectangle(this.x - hor / 2, this.y - ver / 2,
       
  1779 				this.width + hor, this.height + ver);
       
  1780 	},
       
  1781 
       
  1782 	scale: function(hor, ver) {
       
  1783 		return this.expand(this.width * hor - this.width,
       
  1784 				this.height * (ver === undefined ? hor : ver) - this.height);
       
  1785 	}
       
  1786 }, new function() {
       
  1787 	return Base.each([
       
  1788 			['Top', 'Left'], ['Top', 'Right'],
       
  1789 			['Bottom', 'Left'], ['Bottom', 'Right'],
       
  1790 			['Left', 'Center'], ['Top', 'Center'],
       
  1791 			['Right', 'Center'], ['Bottom', 'Center']
       
  1792 		],
       
  1793 		function(parts, index) {
       
  1794 			var part = parts.join('');
       
  1795 			var xFirst = /^[RL]/.test(part);
       
  1796 			if (index >= 4)
       
  1797 				parts[1] += xFirst ? 'Y' : 'X';
       
  1798 			var x = parts[xFirst ? 0 : 1],
       
  1799 				y = parts[xFirst ? 1 : 0],
       
  1800 				getX = 'get' + x,
       
  1801 				getY = 'get' + y,
       
  1802 				setX = 'set' + x,
       
  1803 				setY = 'set' + y,
       
  1804 				get = 'get' + part,
       
  1805 				set = 'set' + part;
       
  1806 			this[get] = function() {
       
  1807 				return new (arguments[0] ? Point : LinkedPoint)
       
  1808 						(this[getX](), this[getY](), this, set);
       
  1809 			};
       
  1810 			this[set] = function(point) {
       
  1811 				point = Point.read(arguments);
       
  1812 				this[setX](point.x);
       
  1813 				this[setY](point.y);
       
  1814 			};
       
  1815 		}, {});
       
  1816 });
       
  1817 
       
  1818 var LinkedRectangle = Rectangle.extend({
       
  1819 	initialize: function Rectangle(x, y, width, height, owner, setter) {
       
  1820 		this.set(x, y, width, height, true);
       
  1821 		this._owner = owner;
       
  1822 		this._setter = setter;
       
  1823 	},
       
  1824 
       
  1825 	set: function(x, y, width, height, _dontNotify) {
       
  1826 		this._x = x;
       
  1827 		this._y = y;
       
  1828 		this._width = width;
       
  1829 		this._height = height;
       
  1830 		if (!_dontNotify)
       
  1831 			this._owner[this._setter](this);
       
  1832 		return this;
       
  1833 	}
       
  1834 }, new function() {
       
  1835 	var proto = Rectangle.prototype;
       
  1836 
       
  1837 	return Base.each(['x', 'y', 'width', 'height'], function(key) {
       
  1838 		var part = Base.capitalize(key);
       
  1839 		var internal = '_' + key;
       
  1840 		this['get' + part] = function() {
       
  1841 			return this[internal];
       
  1842 		};
       
  1843 
       
  1844 		this['set' + part] = function(value) {
       
  1845 			this[internal] = value;
       
  1846 			if (!this._dontNotify)
       
  1847 				this._owner[this._setter](this);
       
  1848 		};
       
  1849 	}, Base.each(['Point', 'Size', 'Center',
       
  1850 			'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',
       
  1851 			'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
       
  1852 			'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],
       
  1853 		function(key) {
       
  1854 			var name = 'set' + key;
       
  1855 			this[name] = function() {
       
  1856 				this._dontNotify = true;
       
  1857 				proto[name].apply(this, arguments);
       
  1858 				delete this._dontNotify;
       
  1859 				this._owner[this._setter](this);
       
  1860 			};
       
  1861 		}, {
       
  1862 			isSelected: function() {
       
  1863 				return this._owner._boundsSelected;
       
  1864 			},
       
  1865 
       
  1866 			setSelected: function(selected) {
       
  1867 				var owner = this._owner;
       
  1868 				if (owner.setSelected) {
       
  1869 					owner._boundsSelected = selected;
       
  1870 					owner.setSelected(selected || owner._selectedSegmentState > 0);
       
  1871 				}
       
  1872 			}
       
  1873 		})
       
  1874 	);
       
  1875 });
       
  1876 
       
  1877 var Matrix = Base.extend({
       
  1878 	_class: 'Matrix',
       
  1879 
       
  1880 	initialize: function Matrix(arg) {
       
  1881 		var count = arguments.length,
       
  1882 			ok = true;
       
  1883 		if (count === 6) {
       
  1884 			this.set.apply(this, arguments);
       
  1885 		} else if (count === 1) {
       
  1886 			if (arg instanceof Matrix) {
       
  1887 				this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
       
  1888 			} else if (Array.isArray(arg)) {
       
  1889 				this.set.apply(this, arg);
       
  1890 			} else {
       
  1891 				ok = false;
       
  1892 			}
       
  1893 		} else if (count === 0) {
       
  1894 			this.reset();
       
  1895 		} else {
       
  1896 			ok = false;
       
  1897 		}
       
  1898 		if (!ok)
       
  1899 			throw new Error('Unsupported matrix parameters');
       
  1900 	},
       
  1901 
       
  1902 	set: function(a, c, b, d, tx, ty, _dontNotify) {
       
  1903 		this._a = a;
       
  1904 		this._c = c;
       
  1905 		this._b = b;
       
  1906 		this._d = d;
       
  1907 		this._tx = tx;
       
  1908 		this._ty = ty;
       
  1909 		if (!_dontNotify)
       
  1910 			this._changed();
       
  1911 		return this;
       
  1912 	},
       
  1913 
       
  1914 	_serialize: function(options) {
       
  1915 		return Base.serialize(this.getValues(), options);
       
  1916 	},
       
  1917 
       
  1918 	_changed: function() {
       
  1919 		if (this._owner)
       
  1920 			this._owner._changed(5);
       
  1921 	},
       
  1922 
       
  1923 	clone: function() {
       
  1924 		return new Matrix(this._a, this._c, this._b, this._d,
       
  1925 				this._tx, this._ty);
       
  1926 	},
       
  1927 
       
  1928 	equals: function(mx) {
       
  1929 		return mx === this || mx && this._a === mx._a && this._b === mx._b
       
  1930 				&& this._c === mx._c && this._d === mx._d
       
  1931 				&& this._tx === mx._tx && this._ty === mx._ty
       
  1932 				|| false;
       
  1933 	},
       
  1934 
       
  1935 	toString: function() {
       
  1936 		var f = Formatter.instance;
       
  1937 		return '[[' + [f.number(this._a), f.number(this._b),
       
  1938 					f.number(this._tx)].join(', ') + '], ['
       
  1939 				+ [f.number(this._c), f.number(this._d),
       
  1940 					f.number(this._ty)].join(', ') + ']]';
       
  1941 	},
       
  1942 
       
  1943 	reset: function() {
       
  1944 		this._a = this._d = 1;
       
  1945 		this._c = this._b = this._tx = this._ty = 0;
       
  1946 		this._changed();
       
  1947 		return this;
       
  1948 	},
       
  1949 
       
  1950 	scale: function() {
       
  1951 		var scale = Point.read(arguments),
       
  1952 			center = Point.read(arguments, 0, 0, { readNull: true });
       
  1953 		if (center)
       
  1954 			this.translate(center);
       
  1955 		this._a *= scale.x;
       
  1956 		this._c *= scale.x;
       
  1957 		this._b *= scale.y;
       
  1958 		this._d *= scale.y;
       
  1959 		if (center)
       
  1960 			this.translate(center.negate());
       
  1961 		this._changed();
       
  1962 		return this;
       
  1963 	},
       
  1964 
       
  1965 	translate: function(point) {
       
  1966 		point = Point.read(arguments);
       
  1967 		var x = point.x,
       
  1968 			y = point.y;
       
  1969 		this._tx += x * this._a + y * this._b;
       
  1970 		this._ty += x * this._c + y * this._d;
       
  1971 		this._changed();
       
  1972 		return this;
       
  1973 	},
       
  1974 
       
  1975 	rotate: function(angle, center) {
       
  1976 		center = Point.read(arguments, 1);
       
  1977 		angle = angle * Math.PI / 180;
       
  1978 		var x = center.x,
       
  1979 			y = center.y,
       
  1980 			cos = Math.cos(angle),
       
  1981 			sin = Math.sin(angle),
       
  1982 			tx = x - x * cos + y * sin,
       
  1983 			ty = y - x * sin - y * cos,
       
  1984 			a = this._a,
       
  1985 			b = this._b,
       
  1986 			c = this._c,
       
  1987 			d = this._d;
       
  1988 		this._a = cos * a + sin * b;
       
  1989 		this._b = -sin * a + cos * b;
       
  1990 		this._c = cos * c + sin * d;
       
  1991 		this._d = -sin * c + cos * d;
       
  1992 		this._tx += tx * a + ty * b;
       
  1993 		this._ty += tx * c + ty * d;
       
  1994 		this._changed();
       
  1995 		return this;
       
  1996 	},
       
  1997 
       
  1998 	shear: function() {
       
  1999 		var point = Point.read(arguments),
       
  2000 			center = Point.read(arguments, 0, 0, { readNull: true });
       
  2001 		if (center)
       
  2002 			this.translate(center);
       
  2003 		var a = this._a,
       
  2004 			c = this._c;
       
  2005 		this._a += point.y * this._b;
       
  2006 		this._c += point.y * this._d;
       
  2007 		this._b += point.x * a;
       
  2008 		this._d += point.x * c;
       
  2009 		if (center)
       
  2010 			this.translate(center.negate());
       
  2011 		this._changed();
       
  2012 		return this;
       
  2013 	},
       
  2014 
       
  2015 	concatenate: function(mx) {
       
  2016 		var a = this._a,
       
  2017 			b = this._b,
       
  2018 			c = this._c,
       
  2019 			d = this._d;
       
  2020 		this._a = mx._a * a + mx._c * b;
       
  2021 		this._b = mx._b * a + mx._d * b;
       
  2022 		this._c = mx._a * c + mx._c * d;
       
  2023 		this._d = mx._b * c + mx._d * d;
       
  2024 		this._tx += mx._tx * a + mx._ty * b;
       
  2025 		this._ty += mx._tx * c + mx._ty * d;
       
  2026 		this._changed();
       
  2027 		return this;
       
  2028 	},
       
  2029 
       
  2030 	preConcatenate: function(mx) {
       
  2031 		var a = this._a,
       
  2032 			b = this._b,
       
  2033 			c = this._c,
       
  2034 			d = this._d,
       
  2035 			tx = this._tx,
       
  2036 			ty = this._ty;
       
  2037 		this._a = mx._a * a + mx._b * c;
       
  2038 		this._b = mx._a * b + mx._b * d;
       
  2039 		this._c = mx._c * a + mx._d * c;
       
  2040 		this._d = mx._c * b + mx._d * d;
       
  2041 		this._tx = mx._a * tx + mx._b * ty + mx._tx;
       
  2042 		this._ty = mx._c * tx + mx._d * ty + mx._ty;
       
  2043 		this._changed();
       
  2044 		return this;
       
  2045 	},
       
  2046 
       
  2047 	isIdentity: function() {
       
  2048 		return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
       
  2049 				&& this._tx === 0 && this._ty === 0;
       
  2050 	},
       
  2051 
       
  2052 	isInvertible: function() {
       
  2053 		return !!this._getDeterminant();
       
  2054 	},
       
  2055 
       
  2056 	isSingular: function() {
       
  2057 		return !this._getDeterminant();
       
  2058 	},
       
  2059 
       
  2060 	transform: function( src, srcOffset, dst, dstOffset, count) {
       
  2061 		return arguments.length < 5
       
  2062 			? this._transformPoint(Point.read(arguments))
       
  2063 			: this._transformCoordinates(src, srcOffset, dst, dstOffset, count);
       
  2064 	},
       
  2065 
       
  2066 	_transformPoint: function(point, dest, _dontNotify) {
       
  2067 		var x = point.x,
       
  2068 			y = point.y;
       
  2069 		if (!dest)
       
  2070 			dest = new Point();
       
  2071 		return dest.set(
       
  2072 			x * this._a + y * this._b + this._tx,
       
  2073 			x * this._c + y * this._d + this._ty,
       
  2074 			_dontNotify
       
  2075 		);
       
  2076 	},
       
  2077 
       
  2078 	_transformCoordinates: function(src, srcOffset, dst, dstOffset, count) {
       
  2079 		var i = srcOffset,
       
  2080 			j = dstOffset,
       
  2081 			max = i + 2 * count;
       
  2082 		while (i < max) {
       
  2083 			var x = src[i++],
       
  2084 				y = src[i++];
       
  2085 			dst[j++] = x * this._a + y * this._b + this._tx;
       
  2086 			dst[j++] = x * this._c + y * this._d + this._ty;
       
  2087 		}
       
  2088 		return dst;
       
  2089 	},
       
  2090 
       
  2091 	_transformCorners: function(rect) {
       
  2092 		var x1 = rect.x,
       
  2093 			y1 = rect.y,
       
  2094 			x2 = x1 + rect.width,
       
  2095 			y2 = y1 + rect.height,
       
  2096 			coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
       
  2097 		return this._transformCoordinates(coords, 0, coords, 0, 4);
       
  2098 	},
       
  2099 
       
  2100 	_transformBounds: function(bounds, dest, _dontNotify) {
       
  2101 		var coords = this._transformCorners(bounds),
       
  2102 			min = coords.slice(0, 2),
       
  2103 			max = coords.slice();
       
  2104 		for (var i = 2; i < 8; i++) {
       
  2105 			var val = coords[i],
       
  2106 				j = i & 1;
       
  2107 			if (val < min[j])
       
  2108 				min[j] = val;
       
  2109 			else if (val > max[j])
       
  2110 				max[j] = val;
       
  2111 		}
       
  2112 		if (!dest)
       
  2113 			dest = new Rectangle();
       
  2114 		return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
       
  2115 				_dontNotify);
       
  2116 	},
       
  2117 
       
  2118 	inverseTransform: function() {
       
  2119 		return this._inverseTransform(Point.read(arguments));
       
  2120 	},
       
  2121 
       
  2122 	_getDeterminant: function() {
       
  2123 		var det = this._a * this._d - this._b * this._c;
       
  2124 		return isFinite(det) && !Numerical.isZero(det)
       
  2125 				&& isFinite(this._tx) && isFinite(this._ty)
       
  2126 				? det : null;
       
  2127 	},
       
  2128 
       
  2129 	_inverseTransform: function(point, dest, _dontNotify) {
       
  2130 		var det = this._getDeterminant();
       
  2131 		if (!det)
       
  2132 			return null;
       
  2133 		var x = point.x - this._tx,
       
  2134 			y = point.y - this._ty;
       
  2135 		if (!dest)
       
  2136 			dest = new Point();
       
  2137 		return dest.set(
       
  2138 			(x * this._d - y * this._b) / det,
       
  2139 			(y * this._a - x * this._c) / det,
       
  2140 			_dontNotify
       
  2141 		);
       
  2142 	},
       
  2143 
       
  2144 	decompose: function() {
       
  2145 		var a = this._a, b = this._b, c = this._c, d = this._d;
       
  2146 		if (Numerical.isZero(a * d - b * c))
       
  2147 			return null;
       
  2148 
       
  2149 		var scaleX = Math.sqrt(a * a + b * b);
       
  2150 		a /= scaleX;
       
  2151 		b /= scaleX;
       
  2152 
       
  2153 		var shear = a * c + b * d;
       
  2154 		c -= a * shear;
       
  2155 		d -= b * shear;
       
  2156 
       
  2157 		var scaleY = Math.sqrt(c * c + d * d);
       
  2158 		c /= scaleY;
       
  2159 		d /= scaleY;
       
  2160 		shear /= scaleY;
       
  2161 
       
  2162 		if (a * d < b * c) {
       
  2163 			a = -a;
       
  2164 			b = -b;
       
  2165 			shear = -shear;
       
  2166 			scaleX = -scaleX;
       
  2167 		}
       
  2168 
       
  2169 		return {
       
  2170 			translation: this.getTranslation(),
       
  2171 			scaling: new Point(scaleX, scaleY),
       
  2172 			rotation: -Math.atan2(b, a) * 180 / Math.PI,
       
  2173 			shearing: shear
       
  2174 		};
       
  2175 	},
       
  2176 
       
  2177 	getValues: function() {
       
  2178 		return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
       
  2179 	},
       
  2180 
       
  2181 	getTranslation: function() {
       
  2182 		return new Point(this._tx, this._ty);
       
  2183 	},
       
  2184 
       
  2185 	setTranslation: function() {
       
  2186 		var point = Point.read(arguments);
       
  2187 		this._tx = point.x;
       
  2188 		this._ty = point.y;
       
  2189 		this._changed();
       
  2190 	},
       
  2191 
       
  2192 	getScaling: function() {
       
  2193 		return (this.decompose() || {}).scaling;
       
  2194 	},
       
  2195 
       
  2196 	setScaling: function() {
       
  2197 		var scaling = this.getScaling();
       
  2198 		if (scaling != null) {
       
  2199 			var scale = Point.read(arguments);
       
  2200 			(this._owner || this).scale(
       
  2201 					scale.x / scaling.x, scale.y / scaling.y);
       
  2202 		}
       
  2203 	},
       
  2204 
       
  2205 	getRotation: function() {
       
  2206 		return (this.decompose() || {}).rotation;
       
  2207 	},
       
  2208 
       
  2209 	setRotation: function(angle) {
       
  2210 		var rotation = this.getRotation();
       
  2211 		if (rotation != null)
       
  2212 			(this._owner || this).rotate(angle - rotation);
       
  2213 	},
       
  2214 
       
  2215 	inverted: function() {
       
  2216 		var det = this._getDeterminant();
       
  2217 		return det && new Matrix(
       
  2218 				this._d / det,
       
  2219 				-this._c / det,
       
  2220 				-this._b / det,
       
  2221 				this._a / det,
       
  2222 				(this._b * this._ty - this._d * this._tx) / det,
       
  2223 				(this._c * this._tx - this._a * this._ty) / det);
       
  2224 	},
       
  2225 
       
  2226 	shiftless: function() {
       
  2227 		return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
       
  2228 	},
       
  2229 
       
  2230 	applyToContext: function(ctx) {
       
  2231 		ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
       
  2232 	}
       
  2233 }, new function() {
       
  2234 	return Base.each({
       
  2235 		scaleX: '_a',
       
  2236 		scaleY: '_d',
       
  2237 		translateX: '_tx',
       
  2238 		translateY: '_ty',
       
  2239 		shearX: '_b',
       
  2240 		shearY: '_c'
       
  2241 	}, function(prop, name) {
       
  2242 		name = Base.capitalize(name);
       
  2243 		this['get' + name] = function() {
       
  2244 			return this[prop];
       
  2245 		};
       
  2246 		this['set' + name] = function(value) {
       
  2247 			this[prop] = value;
       
  2248 			this._changed();
       
  2249 		};
       
  2250 	}, {});
       
  2251 });
       
  2252 
       
  2253 var Line = Base.extend({
       
  2254 	_class: 'Line',
       
  2255 
       
  2256 	initialize: function Line(arg0, arg1, arg2, arg3, arg4) {
       
  2257 		var asVector = false;
       
  2258 		if (arguments.length >= 4) {
       
  2259 			this._px = arg0;
       
  2260 			this._py = arg1;
       
  2261 			this._vx = arg2;
       
  2262 			this._vy = arg3;
       
  2263 			asVector = arg4;
       
  2264 		} else {
       
  2265 			this._px = arg0.x;
       
  2266 			this._py = arg0.y;
       
  2267 			this._vx = arg1.x;
       
  2268 			this._vy = arg1.y;
       
  2269 			asVector = arg2;
       
  2270 		}
       
  2271 		if (!asVector) {
       
  2272 			this._vx -= this._px;
       
  2273 			this._vy -= this._py;
       
  2274 		}
       
  2275 	},
       
  2276 
       
  2277 	getPoint: function() {
       
  2278 		return new Point(this._px, this._py);
       
  2279 	},
       
  2280 
       
  2281 	getVector: function() {
       
  2282 		return new Point(this._vx, this._vy);
       
  2283 	},
       
  2284 
       
  2285 	getLength: function() {
       
  2286 		return this.getVector().getLength();
       
  2287 	},
       
  2288 
       
  2289 	intersect: function(line, isInfinite) {
       
  2290 		return Line.intersect(
       
  2291 				this._px, this._py, this._vx, this._vy,
       
  2292 				line._px, line._py, line._vx, line._vy,
       
  2293 				true, isInfinite);
       
  2294 	},
       
  2295 
       
  2296 	getSide: function(point) {
       
  2297 		return Line.getSide(
       
  2298 				this._px, this._py, this._vx, this._vy,
       
  2299 				point.x, point.y, true);
       
  2300 	},
       
  2301 
       
  2302 	getDistance: function(point) {
       
  2303 		return Math.abs(Line.getSignedDistance(
       
  2304 				this._px, this._py, this._vx, this._vy,
       
  2305 				point.x, point.y, true));
       
  2306 	},
       
  2307 
       
  2308 	statics: {
       
  2309 		intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector,
       
  2310 				isInfinite) {
       
  2311 			if (!asVector) {
       
  2312 				avx -= apx;
       
  2313 				avy -= apy;
       
  2314 				bvx -= bpx;
       
  2315 				bvy -= bpy;
       
  2316 			}
       
  2317 			var cross = bvy * avx - bvx * avy;
       
  2318 			if (!Numerical.isZero(cross)) {
       
  2319 				var dx = apx - bpx,
       
  2320 					dy = apy - bpy,
       
  2321 					ta = (bvx * dy - bvy * dx) / cross,
       
  2322 					tb = (avx * dy - avy * dx) / cross;
       
  2323 				if ((isInfinite || 0 <= ta && ta <= 1)
       
  2324 						&& (isInfinite || 0 <= tb && tb <= 1))
       
  2325 					return new Point(
       
  2326 								apx + ta * avx,
       
  2327 								apy + ta * avy);
       
  2328 			}
       
  2329 		},
       
  2330 
       
  2331 		getSide: function(px, py, vx, vy, x, y, asVector) {
       
  2332 			if (!asVector) {
       
  2333 				vx -= px;
       
  2334 				vy -= py;
       
  2335 			}
       
  2336 			var v2x = x - px,
       
  2337 				v2y = y - py,
       
  2338 				ccw = v2x * vy - v2y * vx; 
       
  2339 			if (ccw === 0) {
       
  2340 				ccw = v2x * vx + v2y * vy; 
       
  2341 				if (ccw > 0) {
       
  2342 					v2x -= vx;
       
  2343 					v2y -= vy;
       
  2344 					ccw = v2x * vx + v2y * vy;
       
  2345 					if (ccw < 0)
       
  2346 						ccw = 0;
       
  2347 				}
       
  2348 			}
       
  2349 			return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
       
  2350 		},
       
  2351 
       
  2352 		getSignedDistance: function(px, py, vx, vy, x, y, asVector) {
       
  2353 			if (!asVector) {
       
  2354 				vx -= px;
       
  2355 				vy -= py;
       
  2356 			}
       
  2357 			var m = vy / vx, 
       
  2358 				b = py - m * px; 
       
  2359 			return (y - (m * x) - b) / Math.sqrt(m * m + 1);
       
  2360 		}
       
  2361 	}
       
  2362 });
       
  2363 
       
  2364 var Project = PaperScopeItem.extend({
       
  2365 	_class: 'Project',
       
  2366 	_list: 'projects',
       
  2367 	_reference: 'project',
       
  2368 
       
  2369 	initialize: function Project(view) {
       
  2370 		PaperScopeItem.call(this, true);
       
  2371 		this.layers = [];
       
  2372 		this.symbols = [];
       
  2373 		this._currentStyle = new Style();
       
  2374 		this.activeLayer = new Layer();
       
  2375 		if (view)
       
  2376 			this.view = view instanceof View ? view : View.create(view);
       
  2377 		this._selectedItems = {};
       
  2378 		this._selectedItemCount = 0;
       
  2379 		this._drawCount = 0;
       
  2380 		this.options = {};
       
  2381 	},
       
  2382 
       
  2383 	_serialize: function(options, dictionary) {
       
  2384 		return Base.serialize(this.layers, options, true, dictionary);
       
  2385 	},
       
  2386 
       
  2387 	clear: function() {
       
  2388 		for (var i = this.layers.length - 1; i >= 0; i--)
       
  2389 			this.layers[i].remove();
       
  2390 		this.symbols = [];
       
  2391 	},
       
  2392 
       
  2393 	isEmpty: function() {
       
  2394 		return this.layers.length <= 1
       
  2395 			&& (!this.activeLayer || this.activeLayer.isEmpty());
       
  2396 	},
       
  2397 
       
  2398 	remove: function remove() {
       
  2399 		if (!remove.base.call(this))
       
  2400 			return false;
       
  2401 		if (this.view)
       
  2402 			this.view.remove();
       
  2403 		return true;
       
  2404 	},
       
  2405 
       
  2406 	getCurrentStyle: function() {
       
  2407 		return this._currentStyle;
       
  2408 	},
       
  2409 
       
  2410 	setCurrentStyle: function(style) {
       
  2411 		this._currentStyle.initialize(style);
       
  2412 	},
       
  2413 
       
  2414 	getIndex: function() {
       
  2415 		return this._index;
       
  2416 	},
       
  2417 
       
  2418 	addChild: function(child) {
       
  2419 		if (child instanceof Layer) {
       
  2420 			Base.splice(this.layers, [child]);
       
  2421 			if (!this.activeLayer)
       
  2422 				this.activeLayer = child;
       
  2423 		} else if (child instanceof Item) {
       
  2424 			(this.activeLayer
       
  2425 				|| this.addChild(new Layer({ insert: false }))).addChild(child);
       
  2426 		} else {
       
  2427 			child = null;
       
  2428 		}
       
  2429 		return child;
       
  2430 	},
       
  2431 
       
  2432 	getSelectedItems: function() {
       
  2433 		var items = [];
       
  2434 		for (var id in this._selectedItems) {
       
  2435 			var item = this._selectedItems[id];
       
  2436 			if (item.isInserted())
       
  2437 				items.push(item);
       
  2438 		}
       
  2439 		return items;
       
  2440 	},
       
  2441 
       
  2442 	_updateSelection: function(item) {
       
  2443 		var id = item._id,
       
  2444 			selectedItems = this._selectedItems;
       
  2445 		if (item._selected) {
       
  2446 			if (selectedItems[id] !== item) {
       
  2447 				this._selectedItemCount++;
       
  2448 				selectedItems[id] = item;
       
  2449 			}
       
  2450 		} else if (selectedItems[id] === item) {
       
  2451 			this._selectedItemCount--;
       
  2452 			delete selectedItems[id];
       
  2453 		}
       
  2454 	},
       
  2455 
       
  2456 	selectAll: function() {
       
  2457 		var layers = this.layers;
       
  2458 		for (var i = 0, l = layers.length; i < l; i++)
       
  2459 			layers[i].setFullySelected(true);
       
  2460 	},
       
  2461 
       
  2462 	deselectAll: function() {
       
  2463 		var selectedItems = this._selectedItems;
       
  2464 		for (var i in selectedItems)
       
  2465 			selectedItems[i].setFullySelected(false);
       
  2466 	},
       
  2467 
       
  2468 	hitTest: function(point, options) {
       
  2469 		point = Point.read(arguments);
       
  2470 		options = HitResult.getOptions(Base.read(arguments));
       
  2471 		for (var i = this.layers.length - 1; i >= 0; i--) {
       
  2472 			var res = this.layers[i].hitTest(point, options);
       
  2473 			if (res) return res;
       
  2474 		}
       
  2475 		return null;
       
  2476 	}
       
  2477 }, new function() {
       
  2478 	function getItems(project, match, list) {
       
  2479 		var layers = project.layers,
       
  2480 			items = list && [];
       
  2481 		for (var i = 0, l = layers.length; i < l; i++) {
       
  2482 			var res = layers[i][list ? 'getItems' : 'getItem'](match);
       
  2483 			if (list) {
       
  2484 				items.push.apply(items, res);
       
  2485 			} else if (res)
       
  2486 				return res;
       
  2487 		}
       
  2488 		return list ? items : null;
       
  2489 	}
       
  2490 
       
  2491 	return {
       
  2492 		getItems: function(match) {
       
  2493 			return getItems(this, match, true);
       
  2494 		},
       
  2495 
       
  2496 		getItem: function(match) {
       
  2497 			return getItems(this, match, false);
       
  2498 		}
       
  2499 	};
       
  2500 }, {
       
  2501 
       
  2502 	importJSON: function(json) {
       
  2503 		this.activate();
       
  2504 		var layer = this.activeLayer;
       
  2505 		return Base.importJSON(json, layer && layer.isEmpty() && layer);
       
  2506 	},
       
  2507 
       
  2508 	draw: function(ctx, matrix, ratio) {
       
  2509 		this._drawCount++;
       
  2510 		ctx.save();
       
  2511 		matrix.applyToContext(ctx);
       
  2512 		var param = new Base({
       
  2513 			offset: new Point(0, 0),
       
  2514 			ratio: ratio,
       
  2515 			transforms: [matrix],
       
  2516 			trackTransforms: true
       
  2517 		});
       
  2518 		for (var i = 0, l = this.layers.length; i < l; i++)
       
  2519 			this.layers[i].draw(ctx, param);
       
  2520 		ctx.restore();
       
  2521 
       
  2522 		if (this._selectedItemCount > 0) {
       
  2523 			ctx.save();
       
  2524 			ctx.strokeWidth = 1;
       
  2525 			for (var id in this._selectedItems) {
       
  2526 				var item = this._selectedItems[id];
       
  2527 				if (item._drawCount === this._drawCount
       
  2528 						&& (item._drawSelected || item._boundsSelected)) {
       
  2529 					var color = item.getSelectedColor()
       
  2530 							|| item.getLayer().getSelectedColor();
       
  2531 					ctx.strokeStyle = ctx.fillStyle = color
       
  2532 							? color.toCanvasStyle(ctx) : '#009dec';
       
  2533 					var mx = item._globalMatrix;
       
  2534 					if (item._drawSelected)
       
  2535 						item._drawSelected(ctx, mx);
       
  2536 					if (item._boundsSelected) {
       
  2537 						var coords = mx._transformCorners(
       
  2538 								item._getBounds('getBounds'));
       
  2539 						ctx.beginPath();
       
  2540 						for (var i = 0; i < 8; i++)
       
  2541 							ctx[i === 0 ? 'moveTo' : 'lineTo'](
       
  2542 									coords[i], coords[++i]);
       
  2543 						ctx.closePath();
       
  2544 						ctx.stroke();
       
  2545 						for (var i = 0; i < 8; i++) {
       
  2546 							ctx.beginPath();
       
  2547 							ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4);
       
  2548 							ctx.fill();
       
  2549 						}
       
  2550 					}
       
  2551 				}
       
  2552 			}
       
  2553 			ctx.restore();
       
  2554 		}
       
  2555 	}
       
  2556 });
       
  2557 
       
  2558 var Symbol = Base.extend({
       
  2559 	_class: 'Symbol',
       
  2560 
       
  2561 	initialize: function Symbol(item, dontCenter) {
       
  2562 		this._id = Symbol._id = (Symbol._id || 0) + 1;
       
  2563 		this.project = paper.project;
       
  2564 		this.project.symbols.push(this);
       
  2565 		if (item)
       
  2566 			this.setDefinition(item, dontCenter);
       
  2567 		this._instances = {};
       
  2568 	},
       
  2569 
       
  2570 	_serialize: function(options, dictionary) {
       
  2571 		return dictionary.add(this, function() {
       
  2572 			return Base.serialize([this._class, this._definition],
       
  2573 					options, false, dictionary);
       
  2574 		});
       
  2575 	},
       
  2576 
       
  2577 	_changed: function(flags) {
       
  2578 		Base.each(this._instances, function(item) {
       
  2579 			item._changed(flags);
       
  2580 		});
       
  2581 	},
       
  2582 
       
  2583 	getDefinition: function() {
       
  2584 		return this._definition;
       
  2585 	},
       
  2586 
       
  2587 	setDefinition: function(item ) {
       
  2588 		if (item._parentSymbol)
       
  2589 			item = item.clone();
       
  2590 		if (this._definition)
       
  2591 			delete this._definition._parentSymbol;
       
  2592 		this._definition = item;
       
  2593 		item.remove();
       
  2594 		item.setSelected(false);
       
  2595 		if (!arguments[1])
       
  2596 			item.setPosition(new Point());
       
  2597 		item._parentSymbol = this;
       
  2598 		this._changed(5);
       
  2599 	},
       
  2600 
       
  2601 	place: function(position) {
       
  2602 		return new PlacedSymbol(this, position);
       
  2603 	},
       
  2604 
       
  2605 	clone: function() {
       
  2606 		return new Symbol(this._definition.clone(false));
       
  2607 	}
       
  2608 });
       
  2609 
       
  2610 var Item = Base.extend(Callback, {
       
  2611 	statics: {
       
  2612 		extend: function extend(src) {
       
  2613 			if (src._serializeFields)
       
  2614 				src._serializeFields = new Base(
       
  2615 						this.prototype._serializeFields, src._serializeFields);
       
  2616 			var res = extend.base.apply(this, arguments),
       
  2617 				proto = res.prototype,
       
  2618 				name = proto._class;
       
  2619 			if (name)
       
  2620 				proto._type = Base.hyphenate(name);
       
  2621 			return res;
       
  2622 		}
       
  2623 	},
       
  2624 
       
  2625 	_class: 'Item',
       
  2626 	_transformContent: true,
       
  2627 	_boundsSelected: false,
       
  2628 	_serializeFields: {
       
  2629 		name: null,
       
  2630 		matrix: new Matrix(),
       
  2631 		locked: false,
       
  2632 		visible: true,
       
  2633 		blendMode: 'normal',
       
  2634 		opacity: 1,
       
  2635 		guide: false,
       
  2636 		selected: false,
       
  2637 		clipMask: false,
       
  2638 		data: {}
       
  2639 	},
       
  2640 
       
  2641 	initialize: function Item() {
       
  2642 	},
       
  2643 
       
  2644 	_initialize: function(props, point) {
       
  2645 		this._id = Item._id = (Item._id || 0) + 1;
       
  2646 		if (!this._project) {
       
  2647 			var project = paper.project;
       
  2648 			if (props && props.insert === false) {
       
  2649 				this._setProject(project);
       
  2650 			} else {
       
  2651 				(project.activeLayer || new Layer()).addChild(this);
       
  2652 			}
       
  2653 		}
       
  2654 		this._style = new Style(this._project._currentStyle, this);
       
  2655 		var matrix = this._matrix = new Matrix();
       
  2656 		if (point)
       
  2657 			matrix.translate(point);
       
  2658 		matrix._owner = this;
       
  2659 		return props ? this._set(props, { insert: true }) : true;
       
  2660 	},
       
  2661 
       
  2662 	_events: new function() {
       
  2663 
       
  2664 		var mouseFlags = {
       
  2665 			mousedown: {
       
  2666 				mousedown: 1,
       
  2667 				mousedrag: 1,
       
  2668 				click: 1,
       
  2669 				doubleclick: 1
       
  2670 			},
       
  2671 			mouseup: {
       
  2672 				mouseup: 1,
       
  2673 				mousedrag: 1,
       
  2674 				click: 1,
       
  2675 				doubleclick: 1
       
  2676 			},
       
  2677 			mousemove: {
       
  2678 				mousedrag: 1,
       
  2679 				mousemove: 1,
       
  2680 				mouseenter: 1,
       
  2681 				mouseleave: 1
       
  2682 			}
       
  2683 		};
       
  2684 
       
  2685 		var mouseEvent = {
       
  2686 			install: function(type) {
       
  2687 				var counters = this._project.view._eventCounters;
       
  2688 				if (counters) {
       
  2689 					for (var key in mouseFlags) {
       
  2690 						counters[key] = (counters[key] || 0)
       
  2691 								+ (mouseFlags[key][type] || 0);
       
  2692 					}
       
  2693 				}
       
  2694 			},
       
  2695 			uninstall: function(type) {
       
  2696 				var counters = this._project.view._eventCounters;
       
  2697 				if (counters) {
       
  2698 					for (var key in mouseFlags)
       
  2699 						counters[key] -= mouseFlags[key][type] || 0;
       
  2700 				}
       
  2701 			}
       
  2702 		};
       
  2703 
       
  2704 		return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
       
  2705 			'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
       
  2706 			function(name) {
       
  2707 				this[name] = mouseEvent;
       
  2708 			}, {
       
  2709 				onFrame: {
       
  2710 					install: function() {
       
  2711 						this._animateItem(true);
       
  2712 					},
       
  2713 					uninstall: function() {
       
  2714 						this._animateItem(false);
       
  2715 					}
       
  2716 				},
       
  2717 
       
  2718 				onLoad: {}
       
  2719 			}
       
  2720 		);
       
  2721 	},
       
  2722 
       
  2723 	_animateItem: function(animate) {
       
  2724 		this._project.view._animateItem(this, animate);
       
  2725 	},
       
  2726 
       
  2727 	_serialize: function(options, dictionary) {
       
  2728 		var props = {},
       
  2729 			that = this;
       
  2730 
       
  2731 		function serialize(fields) {
       
  2732 			for (var key in fields) {
       
  2733 				var value = that[key];
       
  2734 				if (!Base.equals(value, key === 'leading'
       
  2735 						? fields.fontSize * 1.2 : fields[key])) {
       
  2736 					props[key] = Base.serialize(value, options,
       
  2737 							key !== 'data', dictionary);
       
  2738 				}
       
  2739 			}
       
  2740 		}
       
  2741 
       
  2742 		serialize(this._serializeFields);
       
  2743 		if (!(this instanceof Group))
       
  2744 			serialize(this._style._defaults);
       
  2745 		return [ this._class, props ];
       
  2746 	},
       
  2747 
       
  2748 	_changed: function(flags) {
       
  2749 		var parent = this._parent,
       
  2750 			project = this._project,
       
  2751 			symbol = this._parentSymbol;
       
  2752 		this._drawCount = null;
       
  2753 		if (flags & 4) {
       
  2754 			delete this._bounds;
       
  2755 			delete this._position;
       
  2756 		}
       
  2757 		if (parent && (flags
       
  2758 				& (4 | 8))) {
       
  2759 			parent._clearBoundsCache();
       
  2760 		}
       
  2761 		if (flags & 2) {
       
  2762 			this._clearBoundsCache();
       
  2763 		}
       
  2764 		if (project) {
       
  2765 			if (flags & 1) {
       
  2766 				project._needsRedraw = true;
       
  2767 			}
       
  2768 			if (project._changes) {
       
  2769 				var entry = project._changesById[this._id];
       
  2770 				if (entry) {
       
  2771 					entry.flags |= flags;
       
  2772 				} else {
       
  2773 					entry = { item: this, flags: flags };
       
  2774 					project._changesById[this._id] = entry;
       
  2775 					project._changes.push(entry);
       
  2776 				}
       
  2777 			}
       
  2778 		}
       
  2779 		if (symbol)
       
  2780 			symbol._changed(flags);
       
  2781 	},
       
  2782 
       
  2783 	set: function(props) {
       
  2784 		if (props)
       
  2785 			this._set(props);
       
  2786 		return this;
       
  2787 	},
       
  2788 
       
  2789 	getId: function() {
       
  2790 		return this._id;
       
  2791 	},
       
  2792 
       
  2793 	getType: function() {
       
  2794 		return this._type;
       
  2795 	},
       
  2796 
       
  2797 	getName: function() {
       
  2798 		return this._name;
       
  2799 	},
       
  2800 
       
  2801 	setName: function(name, unique) {
       
  2802 
       
  2803 		if (this._name)
       
  2804 			this._removeNamed();
       
  2805 		if (name === (+name) + '')
       
  2806 			throw new Error(
       
  2807 					'Names consisting only of numbers are not supported.');
       
  2808 		if (name && this._parent) {
       
  2809 			var children = this._parent._children,
       
  2810 				namedChildren = this._parent._namedChildren,
       
  2811 				orig = name,
       
  2812 				i = 1;
       
  2813 			while (unique && children[name])
       
  2814 				name = orig + ' ' + (i++);
       
  2815 			(namedChildren[name] = namedChildren[name] || []).push(this);
       
  2816 			children[name] = this;
       
  2817 		}
       
  2818 		this._name = name || undefined;
       
  2819 		this._changed(32);
       
  2820 	},
       
  2821 
       
  2822 	getStyle: function() {
       
  2823 		return this._style;
       
  2824 	},
       
  2825 
       
  2826 	setStyle: function(style) {
       
  2827 		this.getStyle().set(style);
       
  2828 	},
       
  2829 
       
  2830 	hasFill: function() {
       
  2831 		return this.getStyle().hasFill();
       
  2832 	},
       
  2833 
       
  2834 	hasStroke: function() {
       
  2835 		return this.getStyle().hasStroke();
       
  2836 	},
       
  2837 
       
  2838 	hasShadow: function() {
       
  2839 		return this.getStyle().hasShadow();
       
  2840 	}
       
  2841 }, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
       
  2842 	function(name) {
       
  2843 		var part = Base.capitalize(name),
       
  2844 			name = '_' + name;
       
  2845 		this['get' + part] = function() {
       
  2846 			return this[name];
       
  2847 		};
       
  2848 		this['set' + part] = function(value) {
       
  2849 			if (value != this[name]) {
       
  2850 				this[name] = value;
       
  2851 				this._changed(name === '_locked'
       
  2852 						? 32 : 33);
       
  2853 			}
       
  2854 		};
       
  2855 }, {}), {
       
  2856 
       
  2857 	_locked: false,
       
  2858 
       
  2859 	_visible: true,
       
  2860 
       
  2861 	_blendMode: 'normal',
       
  2862 
       
  2863 	_opacity: 1,
       
  2864 
       
  2865 	_guide: false,
       
  2866 
       
  2867 	isSelected: function() {
       
  2868 		if (this._children) {
       
  2869 			for (var i = 0, l = this._children.length; i < l; i++)
       
  2870 				if (this._children[i].isSelected())
       
  2871 					return true;
       
  2872 		}
       
  2873 		return this._selected;
       
  2874 	},
       
  2875 
       
  2876 	setSelected: function(selected ) {
       
  2877 		if (this._children && !arguments[1]) {
       
  2878 			for (var i = 0, l = this._children.length; i < l; i++)
       
  2879 				this._children[i].setSelected(selected);
       
  2880 		}
       
  2881 		if ((selected = !!selected) != this._selected) {
       
  2882 			this._selected = selected;
       
  2883 			this._project._updateSelection(this);
       
  2884 			this._changed(33);
       
  2885 		}
       
  2886 	},
       
  2887 
       
  2888 	_selected: false,
       
  2889 
       
  2890 	isFullySelected: function() {
       
  2891 		if (this._children && this._selected) {
       
  2892 			for (var i = 0, l = this._children.length; i < l; i++)
       
  2893 				if (!this._children[i].isFullySelected())
       
  2894 					return false;
       
  2895 			return true;
       
  2896 		}
       
  2897 		return this._selected;
       
  2898 	},
       
  2899 
       
  2900 	setFullySelected: function(selected) {
       
  2901 		if (this._children) {
       
  2902 			for (var i = 0, l = this._children.length; i < l; i++)
       
  2903 				this._children[i].setFullySelected(selected);
       
  2904 		}
       
  2905 		this.setSelected(selected, true);
       
  2906 	},
       
  2907 
       
  2908 	isClipMask: function() {
       
  2909 		return this._clipMask;
       
  2910 	},
       
  2911 
       
  2912 	setClipMask: function(clipMask) {
       
  2913 		if (this._clipMask != (clipMask = !!clipMask)) {
       
  2914 			this._clipMask = clipMask;
       
  2915 			if (clipMask) {
       
  2916 				this.setFillColor(null);
       
  2917 				this.setStrokeColor(null);
       
  2918 			}
       
  2919 			this._changed(33);
       
  2920 			if (this._parent)
       
  2921 				this._parent._changed(256);
       
  2922 		}
       
  2923 	},
       
  2924 
       
  2925 	_clipMask: false,
       
  2926 
       
  2927 	getData: function() {
       
  2928 		if (!this._data)
       
  2929 			this._data = {};
       
  2930 		return this._data;
       
  2931 	},
       
  2932 
       
  2933 	setData: function(data) {
       
  2934 		this._data = data;		
       
  2935 	},
       
  2936 
       
  2937 	getPosition: function() {
       
  2938 		var pos = this._position
       
  2939 				|| (this._position = this.getBounds().getCenter(true));
       
  2940 		return new (arguments[0] ? Point : LinkedPoint)
       
  2941 				(pos.x, pos.y, this, 'setPosition');
       
  2942 	},
       
  2943 
       
  2944 	setPosition: function() {
       
  2945 		this.translate(Point.read(arguments).subtract(this.getPosition(true)));
       
  2946 	}
       
  2947 }, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
       
  2948 	function(name) {
       
  2949 		this[name] = function() {
       
  2950 			var getter = this._boundsGetter,
       
  2951 				bounds = this._getCachedBounds(typeof getter == 'string'
       
  2952 						? getter : getter && getter[name] || name, arguments[0]);
       
  2953 			return name === 'getBounds'
       
  2954 					? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
       
  2955 							bounds.height, this, 'setBounds') 
       
  2956 					: bounds;
       
  2957 		};
       
  2958 	},
       
  2959 {
       
  2960 	_getCachedBounds: function(getter, matrix, cacheItem) {
       
  2961 		var cache = (!matrix || matrix.equals(this._matrix)) && getter;
       
  2962 		if (cacheItem && this._parent) {
       
  2963 			var id = cacheItem._id,
       
  2964 				ref = this._parent._boundsCache
       
  2965 					= this._parent._boundsCache || {
       
  2966 				ids: {},
       
  2967 				list: []
       
  2968 			};
       
  2969 			if (!ref.ids[id]) {
       
  2970 				ref.list.push(cacheItem);
       
  2971 				ref.ids[id] = cacheItem;
       
  2972 			}
       
  2973 		}
       
  2974 		if (cache && this._bounds && this._bounds[cache])
       
  2975 			return this._bounds[cache].clone();
       
  2976 		var identity = this._matrix.isIdentity();
       
  2977 		matrix = !matrix || matrix.isIdentity()
       
  2978 				? identity ? null : this._matrix
       
  2979 				: identity ? matrix : matrix.clone().concatenate(this._matrix);
       
  2980 		var bounds = this._getBounds(getter, matrix, cache ? this : cacheItem);
       
  2981 		if (cache) {
       
  2982 			if (!this._bounds)
       
  2983 				this._bounds = {};
       
  2984 			this._bounds[cache] = bounds.clone();
       
  2985 		}
       
  2986 		return bounds;
       
  2987 	},
       
  2988 
       
  2989 	_clearBoundsCache: function() {
       
  2990 		if (this._boundsCache) {
       
  2991 			for (var i = 0, list = this._boundsCache.list, l = list.length;
       
  2992 					i < l; i++) {
       
  2993 				var item = list[i];
       
  2994 				delete item._bounds;
       
  2995 				if (item != this && item._boundsCache)
       
  2996 					item._clearBoundsCache();
       
  2997 			}
       
  2998 			delete this._boundsCache;
       
  2999 		}
       
  3000 	},
       
  3001 
       
  3002 	_getBounds: function(getter, matrix, cacheItem) {
       
  3003 		var children = this._children;
       
  3004 		if (!children || children.length == 0)
       
  3005 			return new Rectangle();
       
  3006 		var x1 = Infinity,
       
  3007 			x2 = -x1,
       
  3008 			y1 = x1,
       
  3009 			y2 = x2;
       
  3010 		for (var i = 0, l = children.length; i < l; i++) {
       
  3011 			var child = children[i];
       
  3012 			if (child._visible && !child.isEmpty()) {
       
  3013 				var rect = child._getCachedBounds(getter, matrix, cacheItem);
       
  3014 				x1 = Math.min(rect.x, x1);
       
  3015 				y1 = Math.min(rect.y, y1);
       
  3016 				x2 = Math.max(rect.x + rect.width, x2);
       
  3017 				y2 = Math.max(rect.y + rect.height, y2);
       
  3018 			}
       
  3019 		}
       
  3020 		return isFinite(x1)
       
  3021 				? new Rectangle(x1, y1, x2 - x1, y2 - y1)
       
  3022 				: new Rectangle();
       
  3023 	},
       
  3024 
       
  3025 	setBounds: function(rect) {
       
  3026 		rect = Rectangle.read(arguments);
       
  3027 		var bounds = this.getBounds(),
       
  3028 			matrix = new Matrix(),
       
  3029 			center = rect.getCenter();
       
  3030 		matrix.translate(center);
       
  3031 		if (rect.width != bounds.width || rect.height != bounds.height) {
       
  3032 			matrix.scale(
       
  3033 					bounds.width != 0 ? rect.width / bounds.width : 1,
       
  3034 					bounds.height != 0 ? rect.height / bounds.height : 1);
       
  3035 		}
       
  3036 		center = bounds.getCenter();
       
  3037 		matrix.translate(-center.x, -center.y);
       
  3038 		this.transform(matrix);
       
  3039 	}
       
  3040 
       
  3041 }), {
       
  3042 	getMatrix: function() {
       
  3043 		return this._matrix;
       
  3044 	},
       
  3045 
       
  3046 	setMatrix: function(matrix) {
       
  3047 		this._matrix.initialize(matrix);
       
  3048 		if (this._transformContent)
       
  3049 			this.applyMatrix(true);
       
  3050 		this._changed(5);
       
  3051 	},
       
  3052 
       
  3053 	getGlobalMatrix: function() {
       
  3054 		return this._drawCount === this._project._drawCount
       
  3055 				&& this._globalMatrix || null;
       
  3056 	},
       
  3057 
       
  3058 	getTransformContent: function() {
       
  3059 		return this._transformContent;
       
  3060 	},
       
  3061 
       
  3062 	setTransformContent: function(transform) {
       
  3063 		this._transformContent = transform;
       
  3064 		if (transform)
       
  3065 			this.applyMatrix();
       
  3066 	},
       
  3067 
       
  3068 	getProject: function() {
       
  3069 		return this._project;
       
  3070 	},
       
  3071 
       
  3072 	_setProject: function(project) {
       
  3073 		if (this._project != project) {
       
  3074 			var hasOnFrame = this.responds('frame');
       
  3075 			if (hasOnFrame)
       
  3076 				this._animateItem(false);
       
  3077 			this._project = project;
       
  3078 			if (hasOnFrame)
       
  3079 				this._animateItem(true);
       
  3080 			if (this._children) {
       
  3081 				for (var i = 0, l = this._children.length; i < l; i++) {
       
  3082 					this._children[i]._setProject(project);
       
  3083 				}
       
  3084 			}
       
  3085 		}
       
  3086 	},
       
  3087 
       
  3088 	getLayer: function() {
       
  3089 		var parent = this;
       
  3090 		while (parent = parent._parent) {
       
  3091 			if (parent instanceof Layer)
       
  3092 				return parent;
       
  3093 		}
       
  3094 		return null;
       
  3095 	},
       
  3096 
       
  3097 	getParent: function() {
       
  3098 		return this._parent;
       
  3099 	},
       
  3100 
       
  3101 	setParent: function(item) {
       
  3102 		return item.addChild(this);
       
  3103 	},
       
  3104 
       
  3105 	getChildren: function() {
       
  3106 		return this._children;
       
  3107 	},
       
  3108 
       
  3109 	setChildren: function(items) {
       
  3110 		this.removeChildren();
       
  3111 		this.addChildren(items);
       
  3112 	},
       
  3113 
       
  3114 	getFirstChild: function() {
       
  3115 		return this._children && this._children[0] || null;
       
  3116 	},
       
  3117 
       
  3118 	getLastChild: function() {
       
  3119 		return this._children && this._children[this._children.length - 1]
       
  3120 				|| null;
       
  3121 	},
       
  3122 
       
  3123 	getNextSibling: function() {
       
  3124 		return this._parent && this._parent._children[this._index + 1] || null;
       
  3125 	},
       
  3126 
       
  3127 	getPreviousSibling: function() {
       
  3128 		return this._parent && this._parent._children[this._index - 1] || null;
       
  3129 	},
       
  3130 
       
  3131 	getIndex: function() {
       
  3132 		return this._index;
       
  3133 	},
       
  3134 
       
  3135 	isInserted: function() {
       
  3136 		return this._parent ? this._parent.isInserted() : false;
       
  3137 	},
       
  3138 
       
  3139 	equals: function(item) {
       
  3140 		return item === this || item && this._class === item._class
       
  3141 				&& this._style.equals(item._style) 
       
  3142 				&& this._matrix.equals(item._matrix)
       
  3143 				&& this._locked === item._locked
       
  3144 				&& this._visible === item._visible
       
  3145 				&& this._blendMode === item._blendMode
       
  3146 				&& this._opacity === item._opacity
       
  3147 				&& this._clipMask === item._clipMask
       
  3148 				&& this._guide === item._guide
       
  3149 				&& this._equals(item)
       
  3150 				|| false;
       
  3151 	},
       
  3152 
       
  3153 	_equals: function(item) {
       
  3154 		return Base.equals(this._children, item._children);
       
  3155 	},
       
  3156 
       
  3157 	clone: function(insert) {
       
  3158 		return this._clone(new this.constructor({ insert: false }), insert);
       
  3159 	},
       
  3160 
       
  3161 	_clone: function(copy, insert) {
       
  3162 		copy.setStyle(this._style);
       
  3163 		if (this._children) {
       
  3164 			for (var i = 0, l = this._children.length; i < l; i++)
       
  3165 				copy.addChild(this._children[i].clone(false), true);
       
  3166 		}
       
  3167 		if (insert || insert === undefined)
       
  3168 			copy.insertAbove(this);
       
  3169 		var keys = ['_locked', '_visible', '_blendMode', '_opacity',
       
  3170 				'_clipMask', '_guide'];
       
  3171 		for (var i = 0, l = keys.length; i < l; i++) {
       
  3172 			var key = keys[i];
       
  3173 			if (this.hasOwnProperty(key))
       
  3174 				copy[key] = this[key];
       
  3175 		}
       
  3176 		copy._matrix.initialize(this._matrix);
       
  3177 		copy._data = this._data ? Base.clone(this._data) : null;
       
  3178 		copy.setSelected(this._selected);
       
  3179 		if (this._name)
       
  3180 			copy.setName(this._name, true);
       
  3181 		return copy;
       
  3182 	},
       
  3183 
       
  3184 	copyTo: function(itemOrProject) {
       
  3185 		return itemOrProject.addChild(this.clone(false));
       
  3186 	},
       
  3187 
       
  3188 	rasterize: function(resolution) {
       
  3189 		var bounds = this.getStrokeBounds(),
       
  3190 			scale = (resolution || 72) / 72,
       
  3191 			topLeft = bounds.getTopLeft().floor(),
       
  3192 			bottomRight = bounds.getBottomRight().ceil()
       
  3193 			size = new Size(bottomRight.subtract(topLeft)),
       
  3194 			canvas = CanvasProvider.getCanvas(size),
       
  3195 			ctx = canvas.getContext('2d'),
       
  3196 			matrix = new Matrix().scale(scale).translate(topLeft.negate());
       
  3197 		ctx.save();
       
  3198 		matrix.applyToContext(ctx);
       
  3199 		this.draw(ctx, new Base({ transforms: [matrix] }));
       
  3200 		ctx.restore();
       
  3201 		var raster = new Raster({
       
  3202 			canvas: canvas,
       
  3203 			insert: false
       
  3204 		});
       
  3205 		raster.setPosition(topLeft.add(size.divide(2)));
       
  3206 		raster.insertAbove(this);
       
  3207 		return raster;
       
  3208 	},
       
  3209 
       
  3210 	contains: function() {
       
  3211 		return !!this._contains(
       
  3212 				this._matrix._inverseTransform(Point.read(arguments)));
       
  3213 	},
       
  3214 
       
  3215 	_contains: function(point) {
       
  3216 		if (this._children) {
       
  3217 			for (var i = this._children.length - 1; i >= 0; i--) {
       
  3218 				if (this._children[i].contains(point))
       
  3219 					return true;
       
  3220 			}
       
  3221 			return false;
       
  3222 		}
       
  3223 		return point.isInside(this._getBounds('getBounds'));
       
  3224 	},
       
  3225 
       
  3226 	hitTest: function(point, options) {
       
  3227 		point = Point.read(arguments);
       
  3228 		options = HitResult.getOptions(Base.read(arguments));
       
  3229 
       
  3230 		if (this._locked || !this._visible || this._guide && !options.guides)
       
  3231 			return null;
       
  3232 
       
  3233 		if (!this._children && !this.getRoughBounds()
       
  3234 				.expand(2 * options.tolerance)._containsPoint(point))
       
  3235 			return null;
       
  3236 		point = this._matrix._inverseTransform(point);
       
  3237 
       
  3238 		var that = this,
       
  3239 			res;
       
  3240 		function checkBounds(type, part) {
       
  3241 			var pt = bounds['get' + part]();
       
  3242 			if (point.getDistance(pt) < options.tolerance)
       
  3243 				return new HitResult(type, that,
       
  3244 						{ name: Base.hyphenate(part), point: pt });
       
  3245 		}
       
  3246 
       
  3247 		if ((options.center || options.bounds) &&
       
  3248 				!(this instanceof Layer && !this._parent)) {
       
  3249 			var bounds = this._getBounds('getBounds');
       
  3250 			if (options.center)
       
  3251 				res = checkBounds('center', 'Center');
       
  3252 			if (!res && options.bounds) {
       
  3253 				var points = [
       
  3254 					'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
       
  3255 					'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
       
  3256 				];
       
  3257 				for (var i = 0; i < 8 && !res; i++)
       
  3258 					res = checkBounds('bounds', points[i]);
       
  3259 			}
       
  3260 		}
       
  3261 
       
  3262 		if ((res || (res = this._children || !(options.guides && !this._guide
       
  3263 				|| options.selected && !this._selected)
       
  3264 					? this._hitTest(point, options) : null))
       
  3265 				&& res.point) {
       
  3266 			res.point = that._matrix.transform(res.point);
       
  3267 		}
       
  3268 		return res;
       
  3269 	},
       
  3270 
       
  3271 	_hitTest: function(point, options) {
       
  3272 		var children = this._children;
       
  3273 		if (children) {
       
  3274 			for (var i = children.length - 1, res; i >= 0; i--)
       
  3275 				if (res = children[i].hitTest(point, options))
       
  3276 					return res;
       
  3277 		} else if (options.fill && this.hasFill() && this._contains(point)) {
       
  3278 			return new HitResult('fill', this);
       
  3279 		}
       
  3280 	},
       
  3281 
       
  3282 	matches: function(match) {
       
  3283 		function matchObject(obj1, obj2) {
       
  3284 			for (var i in obj1) {
       
  3285 				if (obj1.hasOwnProperty(i)) {
       
  3286 					var val1 = obj1[i],
       
  3287 						val2 = obj2[i];
       
  3288 					if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {
       
  3289 						if (!matchObject(val1, val2))
       
  3290 							return false;
       
  3291 					} else if (!Base.equals(val1, val2)) {
       
  3292 						return false;
       
  3293 					}
       
  3294 				}
       
  3295 			}
       
  3296 			return true;
       
  3297 		}
       
  3298 		for (var key in match) {
       
  3299 			if (match.hasOwnProperty(key)) {
       
  3300 				var value = this[key],
       
  3301 					compare = match[key];
       
  3302 				if (compare instanceof RegExp) {
       
  3303 					if (!compare.test(value))
       
  3304 						return false;
       
  3305 				} else if (typeof compare === 'function') {
       
  3306 					if (!compare(value))
       
  3307 						return false;
       
  3308 				} else if (Base.isPlainObject(compare)) {
       
  3309 					if (!matchObject(compare, value))
       
  3310 						return false;
       
  3311 				} else if (!Base.equals(value, compare)) {
       
  3312 					return false;
       
  3313 				}
       
  3314 			}
       
  3315 		}
       
  3316 		return true;
       
  3317 	}
       
  3318 }, new function() {
       
  3319 	function getItems(item, match, list) {
       
  3320 		var children = item._children,
       
  3321 			items = list && [];
       
  3322 		for (var i = 0, l = children && children.length; i < l; i++) {
       
  3323 			var child = children[i];
       
  3324 			if (child.matches(match)) {
       
  3325 				if (list) {
       
  3326 					items.push(child);
       
  3327 				} else {
       
  3328 					return child;
       
  3329 				}
       
  3330 			}
       
  3331 			var res = getItems(child, match, list);
       
  3332 			if (list) {
       
  3333 				items.push.apply(items, res);
       
  3334 			} else if (res) {
       
  3335 				return res;
       
  3336 			}
       
  3337 		}
       
  3338 		return list ? items : null;
       
  3339 	}
       
  3340 
       
  3341 	return {
       
  3342 		getItems: function(match) {
       
  3343 			return getItems(this, match, true);
       
  3344 		},
       
  3345 
       
  3346 		getItem: function(match) {
       
  3347 			return getItems(this, match, false);
       
  3348 		}
       
  3349 	};
       
  3350 }, {
       
  3351 
       
  3352 	importJSON: function(json) {
       
  3353 		var res = Base.importJSON(json, this);
       
  3354 		return res !== this
       
  3355 				? this.addChild(res)
       
  3356 				: res;
       
  3357 	},
       
  3358 
       
  3359 	addChild: function(item, _preserve) {
       
  3360 		return this.insertChild(undefined, item, _preserve);
       
  3361 	},
       
  3362 
       
  3363 	insertChild: function(index, item, _preserve) {
       
  3364 		var res = this.insertChildren(index, [item], _preserve);
       
  3365 		return res && res[0];
       
  3366 	},
       
  3367 
       
  3368 	addChildren: function(items, _preserve) {
       
  3369 		return this.insertChildren(this._children.length, items, _preserve);
       
  3370 	},
       
  3371 
       
  3372 	insertChildren: function(index, items, _preserve, _type) {
       
  3373 		var children = this._children;
       
  3374 		if (children && items && items.length > 0) {
       
  3375 			items = Array.prototype.slice.apply(items);
       
  3376 			for (var i = items.length - 1; i >= 0; i--) {
       
  3377 				var item = items[i];
       
  3378 				if (_type && item._type !== _type)
       
  3379 					items.splice(i, 1);
       
  3380 				else
       
  3381 					item._remove(true);
       
  3382 			}
       
  3383 			Base.splice(children, items, index, 0);
       
  3384 			for (var i = 0, l = items.length; i < l; i++) {
       
  3385 				var item = items[i];
       
  3386 				item._parent = this;
       
  3387 				item._setProject(this._project);
       
  3388 				if (item._name)
       
  3389 					item.setName(item._name);
       
  3390 			}
       
  3391 			this._changed(7);
       
  3392 		} else {
       
  3393 			items = null;
       
  3394 		}
       
  3395 		return items;
       
  3396 	},
       
  3397 
       
  3398 	_insert: function(above, item, _preserve) {
       
  3399 		if (!item._parent)
       
  3400 			return null;
       
  3401 		var index = item._index + (above ? 1 : 0);
       
  3402 		if (item._parent === this._parent && index > this._index)
       
  3403 			 index--;
       
  3404 		return item._parent.insertChild(index, this, _preserve);
       
  3405 	},
       
  3406 
       
  3407 	insertAbove: function(item, _preserve) {
       
  3408 		return this._insert(true, item, _preserve);
       
  3409 	},
       
  3410 
       
  3411 	insertBelow: function(item, _preserve) {
       
  3412 	 	return this._insert(false, item, _preserve);
       
  3413 	 },
       
  3414 
       
  3415 	sendToBack: function() {
       
  3416 		return this._parent.insertChild(0, this);
       
  3417 	},
       
  3418 
       
  3419 	bringToFront: function() {
       
  3420 		return this._parent.addChild(this);
       
  3421 	},
       
  3422 
       
  3423 	appendTop: '#addChild',
       
  3424 
       
  3425 	appendBottom: function(item) {
       
  3426 		return this.insertChild(0, item);
       
  3427 	},
       
  3428 
       
  3429 	moveAbove: '#insertAbove',
       
  3430 
       
  3431 	moveBelow: '#insertBelow',
       
  3432 
       
  3433 	reduce: function() {
       
  3434 		if (this._children && this._children.length === 1) {
       
  3435 			var child = this._children[0];
       
  3436 			child.insertAbove(this);
       
  3437 			child.setStyle(this._style);
       
  3438 			this.remove();
       
  3439 			return child;
       
  3440 		}
       
  3441 		return this;
       
  3442 	},
       
  3443 
       
  3444 	_removeNamed: function() {
       
  3445 		var children = this._parent._children,
       
  3446 			namedChildren = this._parent._namedChildren,
       
  3447 			name = this._name,
       
  3448 			namedArray = namedChildren[name],
       
  3449 			index = namedArray ? namedArray.indexOf(this) : -1;
       
  3450 		if (index == -1)
       
  3451 			return;
       
  3452 		if (children[name] == this)
       
  3453 			delete children[name];
       
  3454 		namedArray.splice(index, 1);
       
  3455 		if (namedArray.length) {
       
  3456 			children[name] = namedArray[namedArray.length - 1];
       
  3457 		} else {
       
  3458 			delete namedChildren[name];
       
  3459 		}
       
  3460 	},
       
  3461 
       
  3462 	_remove: function(notify) {
       
  3463 		if (this._parent) {
       
  3464 			if (this._name)
       
  3465 				this._removeNamed();
       
  3466 			if (this._index != null)
       
  3467 				Base.splice(this._parent._children, null, this._index, 1);
       
  3468 			if (this.responds('frame'))
       
  3469 				this._animateItem(false);
       
  3470 			if (notify)
       
  3471 				this._parent._changed(7);
       
  3472 			this._parent = null;
       
  3473 			return true;
       
  3474 		}
       
  3475 		return false;
       
  3476 	},
       
  3477 
       
  3478 	remove: function() {
       
  3479 		return this._remove(true);
       
  3480 	},
       
  3481 
       
  3482 	removeChildren: function(from, to) {
       
  3483 		if (!this._children)
       
  3484 			return null;
       
  3485 		from = from || 0;
       
  3486 		to = Base.pick(to, this._children.length);
       
  3487 		var removed = Base.splice(this._children, null, from, to - from);
       
  3488 		for (var i = removed.length - 1; i >= 0; i--)
       
  3489 			removed[i]._remove(false);
       
  3490 		if (removed.length > 0)
       
  3491 			this._changed(7);
       
  3492 		return removed;
       
  3493 	},
       
  3494 
       
  3495 	clear: '#removeChildren',
       
  3496 
       
  3497 	reverseChildren: function() {
       
  3498 		if (this._children) {
       
  3499 			this._children.reverse();
       
  3500 			for (var i = 0, l = this._children.length; i < l; i++)
       
  3501 				this._children[i]._index = i;
       
  3502 			this._changed(7);
       
  3503 		}
       
  3504 	},
       
  3505 
       
  3506 	isEmpty: function() {
       
  3507 		return !this._children || this._children.length == 0;
       
  3508 	},
       
  3509 
       
  3510 	isEditable: function() {
       
  3511 		var item = this;
       
  3512 		while (item) {
       
  3513 			if (!item._visible || item._locked)
       
  3514 				return false;
       
  3515 			item = item._parent;
       
  3516 		}
       
  3517 		return true;
       
  3518 	},
       
  3519 
       
  3520 	_getOrder: function(item) {
       
  3521 		function getList(item) {
       
  3522 			var list = [];
       
  3523 			do {
       
  3524 				list.unshift(item);
       
  3525 			} while (item = item._parent);
       
  3526 			return list;
       
  3527 		}
       
  3528 		var list1 = getList(this),
       
  3529 			list2 = getList(item);
       
  3530 		for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {
       
  3531 			if (list1[i] != list2[i]) {
       
  3532 				return list1[i]._index < list2[i]._index ? 1 : -1;
       
  3533 			}
       
  3534 		}
       
  3535 		return 0;
       
  3536 	},
       
  3537 
       
  3538 	hasChildren: function() {
       
  3539 		return this._children && this._children.length > 0;
       
  3540 	},
       
  3541 
       
  3542 	isAbove: function(item) {
       
  3543 		return this._getOrder(item) === -1;
       
  3544 	},
       
  3545 
       
  3546 	isBelow: function(item) {
       
  3547 		return this._getOrder(item) === 1;
       
  3548 	},
       
  3549 
       
  3550 	isParent: function(item) {
       
  3551 		return this._parent === item;
       
  3552 	},
       
  3553 
       
  3554 	isChild: function(item) {
       
  3555 		return item && item._parent === this;
       
  3556 	},
       
  3557 
       
  3558 	isDescendant: function(item) {
       
  3559 		var parent = this;
       
  3560 		while (parent = parent._parent) {
       
  3561 			if (parent == item)
       
  3562 				return true;
       
  3563 		}
       
  3564 		return false;
       
  3565 	},
       
  3566 
       
  3567 	isAncestor: function(item) {
       
  3568 		return item ? item.isDescendant(this) : false;
       
  3569 	},
       
  3570 
       
  3571 	isGroupedWith: function(item) {
       
  3572 		var parent = this._parent;
       
  3573 		while (parent) {
       
  3574 			if (parent._parent
       
  3575 				&& /^(group|layer|compound-path)$/.test(parent._type)
       
  3576 				&& item.isDescendant(parent))
       
  3577 					return true;
       
  3578 			parent = parent._parent;
       
  3579 		}
       
  3580 		return false;
       
  3581 	},
       
  3582 
       
  3583 	scale: function(hor, ver , center) {
       
  3584 		if (arguments.length < 2 || typeof ver === 'object') {
       
  3585 			center = ver;
       
  3586 			ver = hor;
       
  3587 		}
       
  3588 		return this.transform(new Matrix().scale(hor, ver,
       
  3589 				center || this.getPosition(true)));
       
  3590 	},
       
  3591 
       
  3592 	translate: function() {
       
  3593 		var mx = new Matrix();
       
  3594 		return this.transform(mx.translate.apply(mx, arguments));
       
  3595 	},
       
  3596 
       
  3597 	rotate: function(angle, center) {
       
  3598 		return this.transform(new Matrix().rotate(angle,
       
  3599 				center || this.getPosition(true)));
       
  3600 	},
       
  3601 
       
  3602 	shear: function(hor, ver, center) {
       
  3603 		if (arguments.length < 2 || typeof ver === 'object') {
       
  3604 			center = ver;
       
  3605 			ver = hor;
       
  3606 		}
       
  3607 		return this.transform(new Matrix().shear(hor, ver,
       
  3608 				center || this.getPosition(true)));
       
  3609 	},
       
  3610 
       
  3611 	transform: function(matrix ) {
       
  3612 		var bounds = this._bounds,
       
  3613 			position = this._position;
       
  3614 		this._matrix.preConcatenate(matrix);
       
  3615 		if (this._transformContent || arguments[1])
       
  3616 			this.applyMatrix(true);
       
  3617 		this._changed(5);
       
  3618 		if (bounds && matrix.getRotation() % 90 === 0) {
       
  3619 			for (var key in bounds) {
       
  3620 				var rect = bounds[key];
       
  3621 				matrix._transformBounds(rect, rect);
       
  3622 			}
       
  3623 			var getter = this._boundsGetter,
       
  3624 				rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
       
  3625 			if (rect)
       
  3626 				this._position = rect.getCenter(true);
       
  3627 			this._bounds = bounds;
       
  3628 		} else if (position) {
       
  3629 			this._position = matrix._transformPoint(position, position);
       
  3630 		}
       
  3631 		return this;
       
  3632 	},
       
  3633 
       
  3634 	_applyMatrix: function(matrix, applyMatrix) {
       
  3635 		var children = this._children;
       
  3636 		if (children && children.length > 0) {
       
  3637 			for (var i = 0, l = children.length; i < l; i++)
       
  3638 				children[i].transform(matrix, applyMatrix);
       
  3639 			return true;
       
  3640 		}
       
  3641 	},
       
  3642 
       
  3643 	applyMatrix: function(_dontNotify) {
       
  3644 		var matrix = this._matrix;
       
  3645 		if (this._applyMatrix(matrix, true)) {
       
  3646 			var style = this._style,
       
  3647 				fillColor = style.getFillColor(true),
       
  3648 				strokeColor = style.getStrokeColor(true);
       
  3649 			if (fillColor)
       
  3650 				fillColor.transform(matrix);
       
  3651 			if (strokeColor)
       
  3652 				strokeColor.transform(matrix);
       
  3653 			matrix.reset();
       
  3654 		}
       
  3655 		if (!_dontNotify)
       
  3656 			this._changed(5);
       
  3657 	},
       
  3658 
       
  3659 	globalToLocal: function() {
       
  3660 		var matrix = this.getGlobalMatrix();
       
  3661 		return matrix && matrix._transformPoint(Point.read(arguments));
       
  3662 	},
       
  3663 
       
  3664 	localToGlobal: function() {
       
  3665 		var matrix = this.getGlobalMatrix();
       
  3666 		return matrix && matrix._inverseTransform(Point.read(arguments));
       
  3667 	},
       
  3668 
       
  3669 	fitBounds: function(rectangle, fill) {
       
  3670 		rectangle = Rectangle.read(arguments);
       
  3671 		var bounds = this.getBounds(),
       
  3672 			itemRatio = bounds.height / bounds.width,
       
  3673 			rectRatio = rectangle.height / rectangle.width,
       
  3674 			scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
       
  3675 					? rectangle.width / bounds.width
       
  3676 					: rectangle.height / bounds.height,
       
  3677 			newBounds = new Rectangle(new Point(),
       
  3678 					new Size(bounds.width * scale, bounds.height * scale));
       
  3679 		newBounds.setCenter(rectangle.getCenter());
       
  3680 		this.setBounds(newBounds);
       
  3681 	},
       
  3682 
       
  3683 	_setStyles: function(ctx) {
       
  3684 		var style = this._style,
       
  3685 			fillColor = style.getFillColor(),
       
  3686 			strokeColor = style.getStrokeColor(),
       
  3687 			shadowColor = style.getShadowColor();
       
  3688 		if (fillColor)
       
  3689 			ctx.fillStyle = fillColor.toCanvasStyle(ctx);
       
  3690 		if (strokeColor) {
       
  3691 			var strokeWidth = style.getStrokeWidth();
       
  3692 			if (strokeWidth > 0) {
       
  3693 				ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
       
  3694 				ctx.lineWidth = strokeWidth;
       
  3695 				var strokeJoin = style.getStrokeJoin(),
       
  3696 					strokeCap = style.getStrokeCap(),
       
  3697 					miterLimit = style.getMiterLimit();
       
  3698 				if (strokeJoin)
       
  3699 					ctx.lineJoin = strokeJoin;
       
  3700 				if (strokeCap)
       
  3701 					ctx.lineCap = strokeCap;
       
  3702 				if (miterLimit)
       
  3703 					ctx.miterLimit = miterLimit;
       
  3704 				if (paper.support.nativeDash) {
       
  3705 					var dashArray = style.getDashArray(),
       
  3706 						dashOffset = style.getDashOffset();
       
  3707 					if (dashArray && dashArray.length) {
       
  3708 						if ('setLineDash' in ctx) {
       
  3709 							ctx.setLineDash(dashArray);
       
  3710 							ctx.lineDashOffset = dashOffset;
       
  3711 						} else {
       
  3712 							ctx.mozDash = dashArray;
       
  3713 							ctx.mozDashOffset = dashOffset;
       
  3714 						}
       
  3715 					}
       
  3716 				}
       
  3717 			}
       
  3718 		}
       
  3719 		if (shadowColor) {
       
  3720 			var shadowBlur = style.getShadowBlur();
       
  3721 			if (shadowBlur > 0) {
       
  3722 				ctx.shadowColor = shadowColor.toCanvasStyle(ctx);
       
  3723 				ctx.shadowBlur = shadowBlur;
       
  3724 				var offset = this.getShadowOffset();
       
  3725 				ctx.shadowOffsetX = offset.x;
       
  3726 				ctx.shadowOffsetY = offset.y;
       
  3727 			}
       
  3728 		}
       
  3729 	},
       
  3730 
       
  3731 	draw: function(ctx, param) {
       
  3732 		if (!this._visible || this._opacity === 0)
       
  3733 			return;
       
  3734 		this._drawCount = this._project._drawCount;
       
  3735 		var trackTransforms = param.trackTransforms,
       
  3736 			transforms = param.transforms,
       
  3737 			parentMatrix = transforms[transforms.length - 1],
       
  3738 			globalMatrix = parentMatrix.clone().concatenate(this._matrix);
       
  3739 		if (trackTransforms)
       
  3740 			transforms.push(this._globalMatrix = globalMatrix);
       
  3741 		var blendMode = this._blendMode,
       
  3742 			opacity = this._opacity,
       
  3743 			normalBlend = blendMode === 'normal',
       
  3744 			nativeBlend = BlendMode.nativeModes[blendMode],
       
  3745 			direct = normalBlend && opacity === 1
       
  3746 					|| (nativeBlend || normalBlend && opacity < 1)
       
  3747 						&& this._canComposite(),
       
  3748 			mainCtx, itemOffset, prevOffset;
       
  3749 		if (!direct) {
       
  3750 			var bounds = this.getStrokeBounds(parentMatrix);
       
  3751 			if (!bounds.width || !bounds.height)
       
  3752 				return;
       
  3753 			prevOffset = param.offset;
       
  3754 			itemOffset = param.offset = bounds.getTopLeft().floor();
       
  3755 			mainCtx = ctx;
       
  3756 			ctx = CanvasProvider.getContext(
       
  3757 					bounds.getSize().ceil().add(new Size(1, 1)), param.ratio);
       
  3758 		}
       
  3759 		ctx.save();
       
  3760 		if (direct) {
       
  3761 			ctx.globalAlpha = opacity;
       
  3762 			if (nativeBlend)
       
  3763 				ctx.globalCompositeOperation = blendMode;
       
  3764 		} else {
       
  3765 			ctx.translate(-itemOffset.x, -itemOffset.y);
       
  3766 		}
       
  3767 		(direct ? this._matrix : globalMatrix).applyToContext(ctx);
       
  3768 		if (!direct && param.clipItem)
       
  3769 			param.clipItem.draw(ctx, param.extend({ clip: true }));
       
  3770 		this._draw(ctx, param);
       
  3771 		ctx.restore();
       
  3772 		if (trackTransforms)
       
  3773 			transforms.pop();
       
  3774 		if (param.clip)
       
  3775 			ctx.clip();
       
  3776 		if (!direct) {
       
  3777 			BlendMode.process(blendMode, ctx, mainCtx, opacity,
       
  3778 					itemOffset.subtract(prevOffset).multiply(param.ratio));
       
  3779 			CanvasProvider.release(ctx);
       
  3780 			param.offset = prevOffset;
       
  3781 		}
       
  3782 	},
       
  3783 
       
  3784 	_canComposite: function() {
       
  3785 		return false;
       
  3786 	}
       
  3787 }, Base.each(['down', 'drag', 'up', 'move'], function(name) {
       
  3788 	this['removeOn' + Base.capitalize(name)] = function() {
       
  3789 		var hash = {};
       
  3790 		hash[name] = true;
       
  3791 		return this.removeOn(hash);
       
  3792 	};
       
  3793 }, {
       
  3794 
       
  3795 	removeOn: function(obj) {
       
  3796 		for (var name in obj) {
       
  3797 			if (obj[name]) {
       
  3798 				var key = 'mouse' + name,
       
  3799 					project = this._project,
       
  3800 					sets = project._removeSets = project._removeSets || {};
       
  3801 				sets[key] = sets[key] || {};
       
  3802 				sets[key][this._id] = this;
       
  3803 			}
       
  3804 		}
       
  3805 		return this;
       
  3806 	}
       
  3807 }));
       
  3808 
       
  3809 var Group = Item.extend({
       
  3810 	_class: 'Group',
       
  3811 	_serializeFields: {
       
  3812 		children: []
       
  3813 	},
       
  3814 
       
  3815 	initialize: function Group(arg) {
       
  3816 		this._children = [];
       
  3817 		this._namedChildren = {};
       
  3818 		if (!this._initialize(arg))
       
  3819 			this.addChildren(Array.isArray(arg) ? arg : arguments);
       
  3820 	},
       
  3821 
       
  3822 	_changed: function _changed(flags) {
       
  3823 		_changed.base.call(this, flags);
       
  3824 		if (flags & 2 && this._transformContent
       
  3825 				&& !this._matrix.isIdentity()) {
       
  3826 			this.applyMatrix();
       
  3827 		}
       
  3828 		if (flags & (2 | 256)) {
       
  3829 			delete this._clipItem;
       
  3830 		}
       
  3831 	},
       
  3832 
       
  3833 	_getClipItem: function() {
       
  3834 		if (this._clipItem !== undefined)
       
  3835 			return this._clipItem;
       
  3836 		for (var i = 0, l = this._children.length; i < l; i++) {
       
  3837 			var child = this._children[i];
       
  3838 			if (child._clipMask)
       
  3839 				return this._clipItem = child;
       
  3840 		}
       
  3841 		return this._clipItem = null;
       
  3842 	},
       
  3843 
       
  3844 	isClipped: function() {
       
  3845 		return !!this._getClipItem();
       
  3846 	},
       
  3847 
       
  3848 	setClipped: function(clipped) {
       
  3849 		var child = this.getFirstChild();
       
  3850 		if (child)
       
  3851 			child.setClipMask(clipped);
       
  3852 	},
       
  3853 
       
  3854 	_draw: function(ctx, param) {
       
  3855 		var clipItem = param.clipItem = this._getClipItem();
       
  3856 		if (clipItem)
       
  3857 			clipItem.draw(ctx, param.extend({ clip: true }));
       
  3858 		for (var i = 0, l = this._children.length; i < l; i++) {
       
  3859 			var item = this._children[i];
       
  3860 			if (item !== clipItem)
       
  3861 				item.draw(ctx, param);
       
  3862 		}
       
  3863 		param.clipItem = null;
       
  3864 	}
       
  3865 });
       
  3866 
       
  3867 var Layer = Group.extend({
       
  3868 	_class: 'Layer',
       
  3869 
       
  3870 	initialize: function Layer(arg) {
       
  3871 		var props = Base.isPlainObject(arg)
       
  3872 				? new Base(arg) 
       
  3873 				: { children: Array.isArray(arg) ? arg : arguments },
       
  3874 			insert = props.insert;
       
  3875 		props.insert = false;
       
  3876 		Group.call(this, props);
       
  3877 		if (insert || insert === undefined) {
       
  3878 			this._project.addChild(this);
       
  3879 			this.activate();
       
  3880 		}
       
  3881 	},
       
  3882 
       
  3883 	_remove: function _remove(notify) {
       
  3884 		if (this._parent)
       
  3885 			return _remove.base.call(this, notify);
       
  3886 		if (this._index != null) {
       
  3887 			if (this._project.activeLayer === this)
       
  3888 				this._project.activeLayer = this.getNextSibling()
       
  3889 						|| this.getPreviousSibling();
       
  3890 			Base.splice(this._project.layers, null, this._index, 1);
       
  3891 			this._project._needsRedraw = true;
       
  3892 			return true;
       
  3893 		}
       
  3894 		return false;
       
  3895 	},
       
  3896 
       
  3897 	getNextSibling: function getNextSibling() {		
       
  3898 		return this._parent ? getNextSibling.base.call(this)
       
  3899 				: this._project.layers[this._index + 1] || null;
       
  3900 	},
       
  3901 
       
  3902 	getPreviousSibling: function getPreviousSibling() {
       
  3903 		return this._parent ? getPreviousSibling.base.call(this)
       
  3904 				: this._project.layers[this._index - 1] || null;
       
  3905 	},
       
  3906 
       
  3907 	isInserted: function isInserted() {
       
  3908 		return this._parent ? isInserted.base.call(this) : this._index != null;
       
  3909 	},
       
  3910 
       
  3911 	activate: function() {
       
  3912 		this._project.activeLayer = this;
       
  3913 	},
       
  3914 
       
  3915 	_insert: function _insert(above, item, _preserve) {
       
  3916 		if (item instanceof Layer && !item._parent) {
       
  3917 			this._remove(true);
       
  3918 			Base.splice(item._project.layers, [this],
       
  3919 					item._index + (above ? 1 : 0), 0);
       
  3920 			this._setProject(item._project);
       
  3921 			return this;
       
  3922 		}
       
  3923 		return _insert.base.call(this, above, item, _preserve);
       
  3924 	}
       
  3925 });
       
  3926 
       
  3927 var Shape = Item.extend({
       
  3928 	_class: 'Shape',
       
  3929 	_transformContent: false,
       
  3930 	_boundsSelected: true,
       
  3931 
       
  3932 	initialize: function Shape(shape, center, size, radius, props) {
       
  3933 		this._shape = shape;
       
  3934 		this._size = size;
       
  3935 		this._radius = radius;
       
  3936 		this._initialize(props, center);
       
  3937 	},
       
  3938 
       
  3939 	_equals: function(item) {
       
  3940 		return this._shape === item._shape
       
  3941 			&& this._size.equals(item._size)
       
  3942 			&& Base.equals(this._radius, item._radius);
       
  3943 	},
       
  3944 
       
  3945 	clone: function(insert) {
       
  3946 		return this._clone(new Shape(this._shape, this.getPosition(true),
       
  3947 				this._size.clone(),
       
  3948 				this._radius.clone ? this._radius.clone() : this._radius,
       
  3949 				{ insert: false }), insert);
       
  3950 	},
       
  3951 
       
  3952 	getShape: function() {
       
  3953 		return this._shape;
       
  3954 	},
       
  3955 
       
  3956 	getSize: function() {
       
  3957 		var size = this._size;
       
  3958 		return new LinkedSize(size.width, size.height, this, 'setSize');
       
  3959 	},
       
  3960 
       
  3961 	setSize: function() {
       
  3962 		var shape = this._shape,
       
  3963 			size = Size.read(arguments);
       
  3964 		if (!this._size.equals(size)) {
       
  3965 			var width = size.width,
       
  3966 				height = size.height;
       
  3967 			if (shape === 'rectangle') {
       
  3968 				var radius = Size.min(this._radius, size.divide(2));
       
  3969 				this._radius.set(radius.width, radius.height);
       
  3970 			} else if (shape === 'circle') {
       
  3971 				width = height = (width + height) / 2;
       
  3972 				this._radius = width / 2;
       
  3973 			} else if (shape === 'ellipse') {
       
  3974 				this._radius.set(width / 2, height / 2);
       
  3975 			}
       
  3976 			this._size.set(width, height);
       
  3977 			this._changed(5);
       
  3978 		}
       
  3979 	},
       
  3980 
       
  3981 	getRadius: function() {
       
  3982 		var rad = this._radius;
       
  3983 		return this._shape === 'circle'
       
  3984 				? rad
       
  3985 				: new LinkedSize(rad.width, rad.height, this, 'setRadius');
       
  3986 	},
       
  3987 
       
  3988 	setRadius: function(radius) {
       
  3989 		var shape = this._shape;
       
  3990 		if (shape === 'circle') {
       
  3991 			if (radius === this._radius)
       
  3992 				return;
       
  3993 			var size = radius * 2;
       
  3994 			this._radius = radius;
       
  3995 			this._size.set(size, size);
       
  3996 		} else {
       
  3997 			radius = Size.read(arguments);
       
  3998 			if (this._radius.equals(radius))
       
  3999 				return;
       
  4000 			this._radius.set(radius.width, radius.height);
       
  4001 			if (shape === 'rectangle') {
       
  4002 				var size = Size.max(this._size, radius.multiply(2));
       
  4003 				this._size.set(size.width, size.height);
       
  4004 			} else if (shape === 'ellipse') {
       
  4005 				this._size.set(radius.width * 2, radius.height * 2);
       
  4006 			}
       
  4007 		}
       
  4008 		this._changed(5);
       
  4009 	},
       
  4010 
       
  4011 	isEmpty: function() {
       
  4012 		return false;
       
  4013 	},
       
  4014 
       
  4015 	toPath: function(insert) {
       
  4016 		var path = new Path[Base.capitalize(this._shape)]({
       
  4017 			center: new Point(),
       
  4018 			size: this._size,
       
  4019 			radius: this._radius,
       
  4020 			insert: false
       
  4021 		});
       
  4022 		path.setStyle(this._style);
       
  4023 		path.transform(this._matrix);
       
  4024 		if (insert || insert === undefined)
       
  4025 			path.insertAbove(this);
       
  4026 		return path;
       
  4027 	},
       
  4028 
       
  4029 	_draw: function(ctx, param) {
       
  4030 		var style = this._style,
       
  4031 			hasFill = style.hasFill(),
       
  4032 			hasStroke = style.hasStroke(),
       
  4033 			clip = param.clip;
       
  4034 		if (hasFill || hasStroke || clip) {
       
  4035 			var radius = this._radius,
       
  4036 				shape = this._shape;
       
  4037 			ctx.beginPath();
       
  4038 			if (shape === 'circle') {
       
  4039 				ctx.arc(0, 0, radius, 0, Math.PI * 2, true);
       
  4040 			} else {
       
  4041 				var rx = radius.width,
       
  4042 					ry = radius.height,
       
  4043 					kappa = Numerical.KAPPA;
       
  4044 				if (shape === 'ellipse') {
       
  4045 					var	cx = rx * kappa,
       
  4046 						cy = ry * kappa;
       
  4047 					ctx.moveTo(-rx, 0);
       
  4048 					ctx.bezierCurveTo(-rx, -cy, -cx, -ry, 0, -ry);
       
  4049 					ctx.bezierCurveTo(cx, -ry, rx, -cy, rx, 0);
       
  4050 					ctx.bezierCurveTo(rx, cy, cx, ry, 0, ry);
       
  4051 					ctx.bezierCurveTo(-cx, ry, -rx, cy, -rx, 0);
       
  4052 				} else { 
       
  4053 					var size = this._size,
       
  4054 						width = size.width,
       
  4055 						height = size.height;
       
  4056 					if (rx === 0 && ry === 0) {
       
  4057 						ctx.rect(-width / 2, -height / 2, width, height);
       
  4058 					} else {
       
  4059 						kappa = 1 - kappa;
       
  4060 						var x = width / 2,
       
  4061 							y = height / 2,
       
  4062 							cx = rx * kappa,
       
  4063 							cy = ry * kappa;
       
  4064 						ctx.moveTo(-x, -y + ry);
       
  4065 						ctx.bezierCurveTo(-x, -y + cy, -x + cx, -y, -x + rx, -y);
       
  4066 						ctx.lineTo(x - rx, -y);
       
  4067 						ctx.bezierCurveTo(x - cx, -y, x, -y + cy, x, -y + ry);
       
  4068 						ctx.lineTo(x, y - ry);
       
  4069 						ctx.bezierCurveTo(x, y - cy, x - cx, y, x - rx, y);
       
  4070 						ctx.lineTo(-x + rx, y);
       
  4071 						ctx.bezierCurveTo(-x + cx, y, -x, y - cy, -x, y - ry);
       
  4072 					}
       
  4073 				}
       
  4074 			}
       
  4075 			ctx.closePath();
       
  4076 		}
       
  4077 		if (!clip && (hasFill || hasStroke)) {
       
  4078 			this._setStyles(ctx);
       
  4079 			if (hasFill) {
       
  4080 				ctx.fill(style.getWindingRule());
       
  4081 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  4082 			}
       
  4083 			if (hasStroke)
       
  4084 				ctx.stroke();
       
  4085 		}
       
  4086 	},
       
  4087 
       
  4088 	_canComposite: function() {
       
  4089 		return !(this.hasFill() && this.hasStroke());
       
  4090 	},
       
  4091 
       
  4092 	_getBounds: function(getter, matrix) {
       
  4093 		var rect = new Rectangle(this._size).setCenter(0, 0);
       
  4094 		if (getter !== 'getBounds' && this.hasStroke())
       
  4095 			rect = rect.expand(this.getStrokeWidth());
       
  4096 		return matrix ? matrix._transformBounds(rect) : rect;
       
  4097 	}
       
  4098 },
       
  4099 new function() { 
       
  4100 
       
  4101 	function getCornerCenter(that, point, expand) {
       
  4102 		var radius = that._radius;
       
  4103 		if (!radius.isZero()) {
       
  4104 			var halfSize = that._size.divide(2);
       
  4105 			for (var i = 0; i < 4; i++) {
       
  4106 				var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1),
       
  4107 					corner = dir.multiply(halfSize),
       
  4108 					center = corner.subtract(dir.multiply(radius)),
       
  4109 					rect = new Rectangle(corner, center);
       
  4110 				if ((expand ? rect.expand(expand) : rect).contains(point))
       
  4111 					return center;
       
  4112 			}
       
  4113 		}
       
  4114 	}
       
  4115 
       
  4116 	function getEllipseRadius(point, radius) {
       
  4117 		var angle = point.getAngleInRadians(),
       
  4118 			width = radius.width * 2,
       
  4119 			height = radius.height * 2,
       
  4120 			x = width * Math.sin(angle),
       
  4121 			y = height * Math.cos(angle);
       
  4122 		return width * height / (2 * Math.sqrt(x * x + y * y));
       
  4123 	}
       
  4124 
       
  4125 	return {
       
  4126 		_contains: function _contains(point) {
       
  4127 			if (this._shape === 'rectangle') {
       
  4128 				var center = getCornerCenter(this, point);
       
  4129 				return center
       
  4130 						? point.subtract(center).divide(this._radius)
       
  4131 							.getLength() <= 1
       
  4132 						: _contains.base.call(this, point);
       
  4133 			} else {
       
  4134 				return point.divide(this.size).getLength() <= 0.5;
       
  4135 			}
       
  4136 		},
       
  4137 
       
  4138 		_hitTest: function _hitTest(point, options) {
       
  4139 			var hit = false;
       
  4140 			if (this.hasStroke()) {
       
  4141 				var shape = this._shape,
       
  4142 					radius = this._radius,
       
  4143 					strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
       
  4144 				if (shape === 'rectangle') {
       
  4145 					var center = getCornerCenter(this, point, strokeWidth);
       
  4146 					if (center) {
       
  4147 						var pt = point.subtract(center);
       
  4148 						hit = 2 * Math.abs(pt.getLength()
       
  4149 								- getEllipseRadius(pt, radius)) <= strokeWidth;
       
  4150 					} else {
       
  4151 						var rect = new Rectangle(this._size).setCenter(0, 0),
       
  4152 							outer = rect.expand(strokeWidth),
       
  4153 							inner = rect.expand(-strokeWidth);
       
  4154 						hit = outer._containsPoint(point)
       
  4155 								&& !inner._containsPoint(point);
       
  4156 					}
       
  4157 				} else {
       
  4158 					if (shape === 'ellipse')
       
  4159 						radius = getEllipseRadius(point, radius);
       
  4160 					hit = 2 * Math.abs(point.getLength() - radius)
       
  4161 							<= strokeWidth;
       
  4162 				}
       
  4163 			}
       
  4164 			return hit
       
  4165 					? new HitResult('stroke', this)
       
  4166 					: _hitTest.base.apply(this, arguments);
       
  4167 		}
       
  4168 	};
       
  4169 }, {
       
  4170 
       
  4171 statics: new function() {
       
  4172 	function createShape(shape, point, size, radius, args) {
       
  4173 		return new Shape(shape, point, size, radius, Base.getNamed(args));
       
  4174 	}
       
  4175 
       
  4176 	return {
       
  4177 		Circle: function() {
       
  4178 			var center = Point.readNamed(arguments, 'center'),
       
  4179 				radius = Base.readNamed(arguments, 'radius');
       
  4180 			return createShape('circle', center, new Size(radius * 2), radius,
       
  4181 					arguments);
       
  4182 		},
       
  4183 
       
  4184 		Rectangle: function() {
       
  4185 			var rect = Rectangle.readNamed(arguments, 'rectangle'),
       
  4186 				radius = Size.min(Size.readNamed(arguments, 'radius'),
       
  4187 						rect.getSize(true).divide(2));
       
  4188 			return createShape('rectangle', rect.getCenter(true),
       
  4189 					rect.getSize(true), radius, arguments);
       
  4190 		},
       
  4191 
       
  4192 		Ellipse: function() {
       
  4193 			var ellipse = Shape._readEllipse(arguments);
       
  4194 				radius = ellipse.radius;
       
  4195 			return createShape('ellipse', ellipse.center, radius.multiply(2),
       
  4196 					radius, arguments);
       
  4197 		},
       
  4198 
       
  4199 		_readEllipse: function(args) {
       
  4200 			var center,
       
  4201 				radius;
       
  4202 			if (Base.hasNamed(args, 'radius')) {
       
  4203 				center = Point.readNamed(args, 'center');
       
  4204 				radius = Size.readNamed(args, 'radius');
       
  4205 			} else {
       
  4206 				var rect = Rectangle.readNamed(args, 'rectangle');
       
  4207 				center = rect.getCenter(true);
       
  4208 				radius = rect.getSize(true).divide(2);
       
  4209 			}
       
  4210 			return { center: center, radius: radius };
       
  4211 		}
       
  4212 	};
       
  4213 }});
       
  4214 
       
  4215 var Raster = Item.extend({
       
  4216 	_class: 'Raster',
       
  4217 	_transformContent: false,
       
  4218 	_boundsGetter: 'getBounds',
       
  4219 	_boundsSelected: true,
       
  4220 	_serializeFields: {
       
  4221 		source: null
       
  4222 	},
       
  4223 
       
  4224 	initialize: function Raster(object, position) {
       
  4225 		if (!this._initialize(object,
       
  4226 				position !== undefined && Point.read(arguments, 1))) {
       
  4227 			if (typeof object === 'string') {
       
  4228 				this.setSource(object);
       
  4229 			} else {
       
  4230 				this.setImage(object);
       
  4231 			}
       
  4232 		}
       
  4233 		if (!this._size)
       
  4234 			this._size = new Size();
       
  4235 	},
       
  4236 
       
  4237 	_equals: function(item) {
       
  4238 		return this.getSource() === item.getSource();
       
  4239 	},
       
  4240 
       
  4241 	clone: function(insert) {
       
  4242 		var param = { insert: false },
       
  4243 			image = this._image;
       
  4244 		if (image) {
       
  4245 			param.image = image;
       
  4246 		} else if (this._canvas) {
       
  4247 			var canvas = param.canvas = CanvasProvider.getCanvas(this._size);
       
  4248 			canvas.getContext('2d').drawImage(this._canvas, 0, 0);
       
  4249 		}
       
  4250 		return this._clone(new Raster(param), insert);
       
  4251 	},
       
  4252 
       
  4253 	getSize: function() {
       
  4254 		var size = this._size;
       
  4255 		return new LinkedSize(size.width, size.height, this, 'setSize');
       
  4256 	},
       
  4257 
       
  4258 	setSize: function() {
       
  4259 		var size = Size.read(arguments);
       
  4260 		if (!this._size.equals(size)) {
       
  4261 			var element = this.getElement();
       
  4262 			this.setCanvas(CanvasProvider.getCanvas(size));
       
  4263 			if (element)
       
  4264 				this.getContext(true).drawImage(element, 0, 0,
       
  4265 						size.width, size.height);
       
  4266 		}
       
  4267 	},
       
  4268 
       
  4269 	getWidth: function() {
       
  4270 		return this._size.width;
       
  4271 	},
       
  4272 
       
  4273 	getHeight: function() {
       
  4274 		return this._size.height;
       
  4275 	},
       
  4276 
       
  4277 	isEmpty: function() {
       
  4278 		return this._size.width == 0 && this._size.height == 0;
       
  4279 	},
       
  4280 
       
  4281 	getPpi: function() {
       
  4282 		var matrix = this._matrix,
       
  4283 			orig = new Point(0, 0).transform(matrix),
       
  4284 			u = new Point(1, 0).transform(matrix).subtract(orig),
       
  4285 			v = new Point(0, 1).transform(matrix).subtract(orig);
       
  4286 		return new Size(
       
  4287 			72 / u.getLength(),
       
  4288 			72 / v.getLength()
       
  4289 		);
       
  4290 	},
       
  4291 
       
  4292 	getImage: function() {
       
  4293 		return this._image;
       
  4294 	},
       
  4295 
       
  4296 	setImage: function(image) {
       
  4297 		if (this._canvas)
       
  4298 			CanvasProvider.release(this._canvas);
       
  4299 		if (image.getContext) {
       
  4300 			this._image = null;
       
  4301 			this._canvas = image;
       
  4302 		} else {
       
  4303 			this._image = image;
       
  4304 			this._canvas = null;
       
  4305 		}
       
  4306 		this._size = new Size(
       
  4307 				image.naturalWidth || image.width,
       
  4308 				image.naturalHeight || image.height);
       
  4309 		this._context = null;
       
  4310 		this._changed(5 | 129);
       
  4311 	},
       
  4312 
       
  4313 	getCanvas: function() {
       
  4314 		if (!this._canvas) {
       
  4315 			var ctx = CanvasProvider.getContext(this._size);
       
  4316 			try {
       
  4317 				if (this._image)
       
  4318 					ctx.drawImage(this._image, 0, 0);
       
  4319 				this._canvas = ctx.canvas;
       
  4320 			} catch (e) {
       
  4321 				CanvasProvider.release(ctx);
       
  4322 			}
       
  4323 		}
       
  4324 		return this._canvas;
       
  4325 	},
       
  4326 
       
  4327 	setCanvas: '#setImage',
       
  4328 
       
  4329 	getContext: function() {
       
  4330 		if (!this._context)
       
  4331 			this._context = this.getCanvas().getContext('2d');
       
  4332 		if (arguments[0]) {
       
  4333 			this._image = null;
       
  4334 			this._changed(129);
       
  4335 		}
       
  4336 		return this._context;
       
  4337 	},
       
  4338 
       
  4339 	setContext: function(context) {
       
  4340 		this._context = context;
       
  4341 	},
       
  4342 
       
  4343 	getSource: function() {
       
  4344 		return this._image && this._image.src || this.toDataURL();
       
  4345 	},
       
  4346 
       
  4347 	setSource: function(src) {
       
  4348 		var that = this,
       
  4349 			image = document.getElementById(src) || new Image();
       
  4350 
       
  4351 		function loaded() {
       
  4352 			var view = that._project.view;
       
  4353 			if (view)
       
  4354 				paper = view._scope;
       
  4355 			that.fire('load');
       
  4356 			if (view)
       
  4357 				view.draw(true);
       
  4358 		}
       
  4359 
       
  4360 		if (image.naturalWidth && image.naturalHeight) {
       
  4361 			setTimeout(loaded, 0);
       
  4362 		} else {
       
  4363 			DomEvent.add(image, {
       
  4364 				load: function() {
       
  4365 					that.setImage(image);
       
  4366 					loaded();
       
  4367 				}
       
  4368 			});
       
  4369 			if (!image.src)
       
  4370 				image.src = src;
       
  4371 		}
       
  4372 		this.setImage(image);
       
  4373 	},
       
  4374 
       
  4375 	getElement: function() {
       
  4376 		return this._canvas || this._image;
       
  4377 	},
       
  4378 
       
  4379 	getSubCanvas: function(rect) {
       
  4380 		rect = Rectangle.read(arguments);
       
  4381 		var ctx = CanvasProvider.getContext(rect.getSize());
       
  4382 		ctx.drawImage(this.getCanvas(), rect.x, rect.y,
       
  4383 				rect.width, rect.height, 0, 0, rect.width, rect.height);
       
  4384 		return ctx.canvas;
       
  4385 	},
       
  4386 
       
  4387 	getSubRaster: function(rect) {
       
  4388 		rect = Rectangle.read(arguments);
       
  4389 		var raster = new Raster({
       
  4390 			canvas: this.getSubCanvas(rect),
       
  4391 			insert: false
       
  4392 		});
       
  4393 		raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
       
  4394 		raster._matrix.preConcatenate(this._matrix);
       
  4395 		raster.insertAbove(this);
       
  4396 		return raster;
       
  4397 	},
       
  4398 
       
  4399 	toDataURL: function() {
       
  4400 		var src = this._image && this._image.src;
       
  4401 		if (/^data:/.test(src))
       
  4402 			return src;
       
  4403 		var canvas = this.getCanvas();
       
  4404 		return canvas ? canvas.toDataURL() : null;
       
  4405 	},
       
  4406 
       
  4407 	drawImage: function(image, point) {
       
  4408 		point = Point.read(arguments, 1);
       
  4409 		this.getContext(true).drawImage(image, point.x, point.y);
       
  4410 	},
       
  4411 
       
  4412 	getAverageColor: function(object) {
       
  4413 		var bounds, path;
       
  4414 		if (!object) {
       
  4415 			bounds = this.getBounds();
       
  4416 		} else if (object instanceof PathItem) {
       
  4417 			path = object;
       
  4418 			bounds = object.getBounds();
       
  4419 		} else if (object.width) {
       
  4420 			bounds = new Rectangle(object);
       
  4421 		} else if (object.x) {
       
  4422 			bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
       
  4423 		}
       
  4424 		var sampleSize = 32,
       
  4425 			width = Math.min(bounds.width, sampleSize),
       
  4426 			height = Math.min(bounds.height, sampleSize);
       
  4427 		var ctx = Raster._sampleContext;
       
  4428 		if (!ctx) {
       
  4429 			ctx = Raster._sampleContext = CanvasProvider.getContext(
       
  4430 					new Size(sampleSize));
       
  4431 		} else {
       
  4432 			ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);
       
  4433 		}
       
  4434 		ctx.save();
       
  4435 		var matrix = new Matrix()
       
  4436 				.scale(width / bounds.width, height / bounds.height)
       
  4437 				.translate(-bounds.x, -bounds.y);
       
  4438 		matrix.applyToContext(ctx);
       
  4439 		if (path)
       
  4440 			path.draw(ctx, new Base({ clip: true, transforms: [matrix] }));
       
  4441 		this._matrix.applyToContext(ctx);
       
  4442 		ctx.drawImage(this.getElement(),
       
  4443 				-this._size.width / 2, -this._size.height / 2);
       
  4444 		ctx.restore();
       
  4445 		var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
       
  4446 				Math.ceil(height)).data,
       
  4447 			channels = [0, 0, 0],
       
  4448 			total = 0;
       
  4449 		for (var i = 0, l = pixels.length; i < l; i += 4) {
       
  4450 			var alpha = pixels[i + 3];
       
  4451 			total += alpha;
       
  4452 			alpha /= 255;
       
  4453 			channels[0] += pixels[i] * alpha;
       
  4454 			channels[1] += pixels[i + 1] * alpha;
       
  4455 			channels[2] += pixels[i + 2] * alpha;
       
  4456 		}
       
  4457 		for (var i = 0; i < 3; i++)
       
  4458 			channels[i] /= total;
       
  4459 		return total ? Color.read(channels) : null;
       
  4460 	},
       
  4461 
       
  4462 	getPixel: function(point) {
       
  4463 		point = Point.read(arguments);
       
  4464 		var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
       
  4465 		return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
       
  4466 				data[3] / 255);
       
  4467 	},
       
  4468 
       
  4469 	setPixel: function() {
       
  4470 		var point = Point.read(arguments),
       
  4471 			color = Color.read(arguments),
       
  4472 			components = color._convert('rgb'),
       
  4473 			alpha = color._alpha,
       
  4474 			ctx = this.getContext(true),
       
  4475 			imageData = ctx.createImageData(1, 1),
       
  4476 			data = imageData.data;
       
  4477 		data[0] = components[0] * 255;
       
  4478 		data[1] = components[1] * 255;
       
  4479 		data[2] = components[2] * 255;
       
  4480 		data[3] = alpha != null ? alpha * 255 : 255;
       
  4481 		ctx.putImageData(imageData, point.x, point.y);
       
  4482 	},
       
  4483 
       
  4484 	createImageData: function(size) {
       
  4485 		size = Size.read(arguments);
       
  4486 		return this.getContext().createImageData(size.width, size.height);
       
  4487 	},
       
  4488 
       
  4489 	getImageData: function(rect) {
       
  4490 		rect = Rectangle.read(arguments);
       
  4491 		if (rect.isEmpty())
       
  4492 			rect = new Rectangle(this._size);
       
  4493 		return this.getContext().getImageData(rect.x, rect.y,
       
  4494 				rect.width, rect.height);
       
  4495 	},
       
  4496 
       
  4497 	setImageData: function(data, point) {
       
  4498 		point = Point.read(arguments, 1);
       
  4499 		this.getContext(true).putImageData(data, point.x, point.y);
       
  4500 	},
       
  4501 
       
  4502 	_getBounds: function(getter, matrix) {
       
  4503 		var rect = new Rectangle(this._size).setCenter(0, 0);
       
  4504 		return matrix ? matrix._transformBounds(rect) : rect;
       
  4505 	},
       
  4506 
       
  4507 	_hitTest: function(point) {
       
  4508 		if (this._contains(point)) {
       
  4509 			var that = this;
       
  4510 			return new HitResult('pixel', that, {
       
  4511 				offset: point.add(that._size.divide(2)).round(),
       
  4512 				color: {
       
  4513 					get: function() {
       
  4514 						return that.getPixel(this.offset);
       
  4515 					}
       
  4516 				}
       
  4517 			});
       
  4518 		}
       
  4519 	},
       
  4520 
       
  4521 	_draw: function(ctx) {
       
  4522 		var element = this.getElement();
       
  4523 		if (element) {
       
  4524 			ctx.globalAlpha = this._opacity;
       
  4525 			ctx.drawImage(element,
       
  4526 					-this._size.width / 2, -this._size.height / 2);
       
  4527 		}
       
  4528 	},
       
  4529 
       
  4530 	_canComposite: function() {
       
  4531 		return true;
       
  4532 	}
       
  4533 });
       
  4534 
       
  4535 var PlacedSymbol = Item.extend({
       
  4536 	_class: 'PlacedSymbol',
       
  4537 	_transformContent: false,
       
  4538 	_boundsGetter: { getBounds: 'getStrokeBounds' },
       
  4539 	_boundsSelected: true,
       
  4540 	_serializeFields: {
       
  4541 		symbol: null
       
  4542 	},
       
  4543 
       
  4544 	initialize: function PlacedSymbol(arg0, arg1) {
       
  4545 		if (!this._initialize(arg0,
       
  4546 				arg1 !== undefined && Point.read(arguments, 1)))
       
  4547 			this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
       
  4548 	},
       
  4549 
       
  4550 	_equals: function(item) {
       
  4551 		return this._symbol === item._symbol;
       
  4552 	},
       
  4553 
       
  4554 	getSymbol: function() {
       
  4555 		return this._symbol;
       
  4556 	},
       
  4557 
       
  4558 	setSymbol: function(symbol) {
       
  4559 		if (this._symbol)
       
  4560 			delete this._symbol._instances[this._id];
       
  4561 		this._symbol = symbol;
       
  4562 		symbol._instances[this._id] = this;
       
  4563 	},
       
  4564 
       
  4565 	clone: function(insert) {
       
  4566 		return this._clone(new PlacedSymbol({
       
  4567 			symbol: this.symbol,
       
  4568 			insert: false
       
  4569 		}), insert);
       
  4570 	},
       
  4571 
       
  4572 	isEmpty: function() {
       
  4573 		return this._symbol._definition.isEmpty();
       
  4574 	},
       
  4575 
       
  4576 	_getBounds: function(getter, matrix) {
       
  4577 		return this.symbol._definition._getCachedBounds(getter, matrix);
       
  4578 	},
       
  4579 
       
  4580 	_hitTest: function(point, options, matrix) {
       
  4581 		var result = this._symbol._definition._hitTest(point, options, matrix);
       
  4582 		if (result)
       
  4583 			result.item = this;
       
  4584 		return result;
       
  4585 	},
       
  4586 
       
  4587 	_draw: function(ctx, param) {
       
  4588 		this.symbol._definition.draw(ctx, param);
       
  4589 	}
       
  4590 
       
  4591 });
       
  4592 
       
  4593 var HitResult = Base.extend({
       
  4594 	_class: 'HitResult',
       
  4595 
       
  4596 	initialize: function HitResult(type, item, values) {
       
  4597 		this.type = type;
       
  4598 		this.item = item;
       
  4599 		if (values) {
       
  4600 			values.enumerable = true;
       
  4601 			this.inject(values);
       
  4602 		}
       
  4603 	},
       
  4604 
       
  4605 	statics: {
       
  4606 		getOptions: function(options) {
       
  4607 			return options && options._merged ? options : new Base({
       
  4608 				type: null,
       
  4609 				tolerance: paper.project.options.hitTolerance || 2,
       
  4610 				fill: !options,
       
  4611 				stroke: !options,
       
  4612 				segments: !options,
       
  4613 				handles: false,
       
  4614 				ends: false,
       
  4615 				center: false,
       
  4616 				bounds: false,
       
  4617 				guides: false,
       
  4618 				selected: false,
       
  4619 				_merged: true
       
  4620 			}, options);
       
  4621 		}
       
  4622 	}
       
  4623 });
       
  4624 
       
  4625 var Segment = Base.extend({
       
  4626 	_class: 'Segment',
       
  4627 
       
  4628 	initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
       
  4629 		var count = arguments.length,
       
  4630 			point, handleIn, handleOut;
       
  4631 		if (count === 0) {
       
  4632 		} else if (count === 1) {
       
  4633 			if (arg0.point) {
       
  4634 				point = arg0.point;
       
  4635 				handleIn = arg0.handleIn;
       
  4636 				handleOut = arg0.handleOut;
       
  4637 			} else {
       
  4638 				point = arg0;
       
  4639 			}
       
  4640 		} else if (count === 2 && typeof arg0 === 'number') {
       
  4641 			point = arguments;
       
  4642 		} else if (count <= 3) {
       
  4643 			point = arg0;
       
  4644 			handleIn = arg1;
       
  4645 			handleOut = arg2;
       
  4646 		} else { 
       
  4647 			point = arg0 !== undefined ? [ arg0, arg1 ] : null;
       
  4648 			handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
       
  4649 			handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
       
  4650 		}
       
  4651 		new SegmentPoint(point, this, '_point');
       
  4652 		new SegmentPoint(handleIn, this, '_handleIn');
       
  4653 		new SegmentPoint(handleOut, this, '_handleOut');
       
  4654 	},
       
  4655 
       
  4656 	_serialize: function(options) {
       
  4657 		return Base.serialize(this.isLinear() ? this._point
       
  4658 				: [this._point, this._handleIn, this._handleOut], options, true);
       
  4659 	},
       
  4660 
       
  4661 	_changed: function(point) {
       
  4662 		if (!this._path)
       
  4663 			return;
       
  4664 		var curve = this._path._curves && this.getCurve(),
       
  4665 			other;
       
  4666 		if (curve) {
       
  4667 			curve._changed();
       
  4668 			if (other = (curve[point == this._point
       
  4669 					|| point == this._handleIn && curve._segment1 == this
       
  4670 					? 'getPrevious' : 'getNext']())) {
       
  4671 				other._changed();
       
  4672 			}
       
  4673 		}
       
  4674 		this._path._changed(5);
       
  4675 	},
       
  4676 
       
  4677 	getPoint: function() {
       
  4678 		return this._point;
       
  4679 	},
       
  4680 
       
  4681 	setPoint: function(point) {
       
  4682 		point = Point.read(arguments);
       
  4683 		this._point.set(point.x, point.y);
       
  4684 	},
       
  4685 
       
  4686 	getHandleIn: function() {
       
  4687 		return this._handleIn;
       
  4688 	},
       
  4689 
       
  4690 	setHandleIn: function(point) {
       
  4691 		point = Point.read(arguments);
       
  4692 		this._handleIn.set(point.x, point.y);
       
  4693 	},
       
  4694 
       
  4695 	getHandleOut: function() {
       
  4696 		return this._handleOut;
       
  4697 	},
       
  4698 
       
  4699 	setHandleOut: function(point) {
       
  4700 		point = Point.read(arguments);
       
  4701 		this._handleOut.set(point.x, point.y);
       
  4702 	},
       
  4703 
       
  4704 	isLinear: function() {
       
  4705 		return this._handleIn.isZero() && this._handleOut.isZero();
       
  4706 	},
       
  4707 
       
  4708 	setLinear: function() {
       
  4709 		this._handleIn.set(0, 0);
       
  4710 		this._handleOut.set(0, 0);
       
  4711 	},
       
  4712 
       
  4713 	isColinear: function(segment) {
       
  4714 		var next1 = this.getNext(),
       
  4715 			next2 = segment.getNext();
       
  4716 		return this._handleOut.isZero() && next1._handleIn.isZero()
       
  4717 				&& segment._handleOut.isZero() && next2._handleIn.isZero()
       
  4718 				&& next1._point.subtract(this._point).isColinear(
       
  4719 					next2._point.subtract(segment._point));
       
  4720 	},
       
  4721 
       
  4722 	isOrthogonal: function() {
       
  4723 		var prev = this.getPrevious(),
       
  4724 			next = this.getNext();
       
  4725 		return prev._handleOut.isZero() && this._handleIn.isZero()
       
  4726 			&& this._handleOut.isZero() && next._handleIn.isZero()
       
  4727 			&& this._point.subtract(prev._point).isOrthogonal(
       
  4728 					next._point.subtract(this._point));
       
  4729 	},
       
  4730 
       
  4731 	isArc: function() {
       
  4732 		var next = this.getNext(),
       
  4733 			handle1 = this._handleOut,
       
  4734 			handle2 = next._handleIn,
       
  4735 			kappa = Numerical.KAPPA;
       
  4736 		if (handle1.isOrthogonal(handle2)) {
       
  4737 			var from = this._point,
       
  4738 				to = next._point,
       
  4739 				corner = new Line(from, handle1, true).intersect(
       
  4740 						new Line(to, handle2, true), true);
       
  4741 			return corner && Numerical.isZero(handle1.getLength() /
       
  4742 					corner.subtract(from).getLength() - kappa)
       
  4743 				&& Numerical.isZero(handle2.getLength() /
       
  4744 					corner.subtract(to).getLength() - kappa);
       
  4745 		}
       
  4746 		return false;
       
  4747 	},
       
  4748 
       
  4749 	_isSelected: function(point) {
       
  4750 		var state = this._selectionState;
       
  4751 		return point == this._point ? !!(state & 4)
       
  4752 			: point == this._handleIn ? !!(state & 1)
       
  4753 			: point == this._handleOut ? !!(state & 2)
       
  4754 			: false;
       
  4755 	},
       
  4756 
       
  4757 	_setSelected: function(point, selected) {
       
  4758 		var path = this._path,
       
  4759 			selected = !!selected, 
       
  4760 			state = this._selectionState || 0,
       
  4761 			selection = [
       
  4762 				!!(state & 4),
       
  4763 				!!(state & 1),
       
  4764 				!!(state & 2)
       
  4765 			];
       
  4766 		if (point === this._point) {
       
  4767 			if (selected) {
       
  4768 				selection[1] = selection[2] = false;
       
  4769 			} else {
       
  4770 				var previous = this.getPrevious(),
       
  4771 					next = this.getNext();
       
  4772 				selection[1] = previous && (previous._point.isSelected()
       
  4773 						|| previous._handleOut.isSelected());
       
  4774 				selection[2] = next && (next._point.isSelected()
       
  4775 						|| next._handleIn.isSelected());
       
  4776 			}
       
  4777 			selection[0] = selected;
       
  4778 		} else {
       
  4779 			var index = point === this._handleIn ? 1 : 2;
       
  4780 			if (selection[index] != selected) {
       
  4781 				if (selected)
       
  4782 					selection[0] = false;
       
  4783 				selection[index] = selected;
       
  4784 			}
       
  4785 		}
       
  4786 		this._selectionState = (selection[0] ? 4 : 0)
       
  4787 				| (selection[1] ? 1 : 0)
       
  4788 				| (selection[2] ? 2 : 0);
       
  4789 		if (path && state != this._selectionState) {
       
  4790 			path._updateSelection(this, state, this._selectionState);
       
  4791 			path._changed(33);
       
  4792 		}
       
  4793 	},
       
  4794 
       
  4795 	isSelected: function() {
       
  4796 		return this._isSelected(this._point);
       
  4797 	},
       
  4798 
       
  4799 	setSelected: function(selected) {
       
  4800 		this._setSelected(this._point, selected);
       
  4801 	},
       
  4802 
       
  4803 	getIndex: function() {
       
  4804 		return this._index !== undefined ? this._index : null;
       
  4805 	},
       
  4806 
       
  4807 	getPath: function() {
       
  4808 		return this._path || null;
       
  4809 	},
       
  4810 
       
  4811 	getCurve: function() {
       
  4812 		var path = this._path,
       
  4813 			index = this._index;
       
  4814 		if (path) {
       
  4815 			if (!path._closed && index == path._segments.length - 1)
       
  4816 				index--;
       
  4817 			return path.getCurves()[index] || null;
       
  4818 		}
       
  4819 		return null;
       
  4820 	},
       
  4821 
       
  4822 	getLocation: function() {
       
  4823 		var curve = this.getCurve();
       
  4824 		return curve ? new CurveLocation(curve, curve.getNext() ? 0 : 1) : null;
       
  4825 	},
       
  4826 
       
  4827 	getNext: function() {
       
  4828 		var segments = this._path && this._path._segments;
       
  4829 		return segments && (segments[this._index + 1]
       
  4830 				|| this._path._closed && segments[0]) || null;
       
  4831 	},
       
  4832 
       
  4833 	getPrevious: function() {
       
  4834 		var segments = this._path && this._path._segments;
       
  4835 		return segments && (segments[this._index - 1]
       
  4836 				|| this._path._closed && segments[segments.length - 1]) || null;
       
  4837 	},
       
  4838 
       
  4839 	reverse: function() {
       
  4840 		return new Segment(this._point, this._handleOut, this._handleIn);
       
  4841 	},
       
  4842 
       
  4843 	remove: function() {
       
  4844 		return this._path ? !!this._path.removeSegment(this._index) : false;
       
  4845 	},
       
  4846 
       
  4847 	clone: function() {
       
  4848 		return new Segment(this._point, this._handleIn, this._handleOut);
       
  4849 	},
       
  4850 
       
  4851 	equals: function(segment) {
       
  4852 		return segment === this || segment && this._class === segment._class
       
  4853 				&& this._point.equals(segment._point)
       
  4854 				&& this._handleIn.equals(segment._handleIn)
       
  4855 				&& this._handleOut.equals(segment._handleOut)
       
  4856 				|| false;
       
  4857 	},
       
  4858 
       
  4859 	toString: function() {
       
  4860 		var parts = [ 'point: ' + this._point ];
       
  4861 		if (!this._handleIn.isZero())
       
  4862 			parts.push('handleIn: ' + this._handleIn);
       
  4863 		if (!this._handleOut.isZero())
       
  4864 			parts.push('handleOut: ' + this._handleOut);
       
  4865 		return '{ ' + parts.join(', ') + ' }';
       
  4866 	},
       
  4867 
       
  4868 	_transformCoordinates: function(matrix, coords, change) {
       
  4869 		var point = this._point,
       
  4870 			handleIn =  !change || !this._handleIn.isZero()
       
  4871 					? this._handleIn : null,
       
  4872 			handleOut = !change || !this._handleOut.isZero()
       
  4873 					? this._handleOut : null,
       
  4874 			x = point._x,
       
  4875 			y = point._y,
       
  4876 			i = 2;
       
  4877 		coords[0] = x;
       
  4878 		coords[1] = y;
       
  4879 		if (handleIn) {
       
  4880 			coords[i++] = handleIn._x + x;
       
  4881 			coords[i++] = handleIn._y + y;
       
  4882 		}
       
  4883 		if (handleOut) {
       
  4884 			coords[i++] = handleOut._x + x;
       
  4885 			coords[i++] = handleOut._y + y;
       
  4886 		}
       
  4887 		if (matrix) {
       
  4888 			matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
       
  4889 			x = coords[0];
       
  4890 			y = coords[1];
       
  4891 			if (change) {
       
  4892 				point._x = x;
       
  4893 				point._y = y;
       
  4894 				i  = 2;
       
  4895 				if (handleIn) {
       
  4896 					handleIn._x = coords[i++] - x;
       
  4897 					handleIn._y = coords[i++] - y;
       
  4898 				}
       
  4899 				if (handleOut) {
       
  4900 					handleOut._x = coords[i++] - x;
       
  4901 					handleOut._y = coords[i++] - y;
       
  4902 				}
       
  4903 			} else {
       
  4904 				if (!handleIn) {
       
  4905 					coords[i++] = x;
       
  4906 					coords[i++] = y;
       
  4907 				}
       
  4908 				if (!handleOut) {
       
  4909 					coords[i++] = x;
       
  4910 					coords[i++] = y;
       
  4911 				}
       
  4912 			}
       
  4913 		}
       
  4914 		return coords;
       
  4915 	}
       
  4916 });
       
  4917 
       
  4918 var SegmentPoint = Point.extend({
       
  4919 	initialize: function SegmentPoint(point, owner, key) {
       
  4920 		var x, y, selected;
       
  4921 		if (!point) {
       
  4922 			x = y = 0;
       
  4923 		} else if ((x = point[0]) !== undefined) { 
       
  4924 			y = point[1];
       
  4925 		} else {
       
  4926 			if ((x = point.x) === undefined) {
       
  4927 				point = Point.read(arguments);
       
  4928 				x = point.x;
       
  4929 			}
       
  4930 			y = point.y;
       
  4931 			selected = point.selected;
       
  4932 		}
       
  4933 		this._x = x;
       
  4934 		this._y = y;
       
  4935 		this._owner = owner;
       
  4936 		owner[key] = this;
       
  4937 		if (selected)
       
  4938 			this.setSelected(true);
       
  4939 	},
       
  4940 
       
  4941 	set: function(x, y) {
       
  4942 		this._x = x;
       
  4943 		this._y = y;
       
  4944 		this._owner._changed(this);
       
  4945 		return this;
       
  4946 	},
       
  4947 
       
  4948 	_serialize: function(options) {
       
  4949 		var f = options.formatter,
       
  4950 			x = f.number(this._x),
       
  4951 			y = f.number(this._y);
       
  4952 		return this.isSelected()
       
  4953 				? { x: x, y: y, selected: true }
       
  4954 				: [x, y];
       
  4955 	},
       
  4956 
       
  4957 	getX: function() {
       
  4958 		return this._x;
       
  4959 	},
       
  4960 
       
  4961 	setX: function(x) {
       
  4962 		this._x = x;
       
  4963 		this._owner._changed(this);
       
  4964 	},
       
  4965 
       
  4966 	getY: function() {
       
  4967 		return this._y;
       
  4968 	},
       
  4969 
       
  4970 	setY: function(y) {
       
  4971 		this._y = y;
       
  4972 		this._owner._changed(this);
       
  4973 	},
       
  4974 
       
  4975 	isZero: function() {
       
  4976 		return Numerical.isZero(this._x) && Numerical.isZero(this._y);
       
  4977 	},
       
  4978 
       
  4979 	setSelected: function(selected) {
       
  4980 		this._owner._setSelected(this, selected);
       
  4981 	},
       
  4982 
       
  4983 	isSelected: function() {
       
  4984 		return this._owner._isSelected(this);
       
  4985 	}
       
  4986 });
       
  4987 
       
  4988 var Curve = Base.extend({
       
  4989 	_class: 'Curve',
       
  4990 	initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
       
  4991 		var count = arguments.length;
       
  4992 		if (count === 3) {
       
  4993 			this._path = arg0;
       
  4994 			this._segment1 = arg1;
       
  4995 			this._segment2 = arg2;
       
  4996 		} else if (count === 0) {
       
  4997 			this._segment1 = new Segment();
       
  4998 			this._segment2 = new Segment();
       
  4999 		} else if (count === 1) {
       
  5000 			this._segment1 = new Segment(arg0.segment1);
       
  5001 			this._segment2 = new Segment(arg0.segment2);
       
  5002 		} else if (count === 2) {
       
  5003 			this._segment1 = new Segment(arg0);
       
  5004 			this._segment2 = new Segment(arg1);
       
  5005 		} else {
       
  5006 			var point1, handle1, handle2, point2;
       
  5007 			if (count === 4) {
       
  5008 				point1 = arg0;
       
  5009 				handle1 = arg1;
       
  5010 				handle2 = arg2;
       
  5011 				point2 = arg3;
       
  5012 			} else if (count === 8) {
       
  5013 				point1 = [arg0, arg1];
       
  5014 				point2 = [arg6, arg7];
       
  5015 				handle1 = [arg2 - arg0, arg3 - arg1];
       
  5016 				handle2 = [arg4 - arg6, arg5 - arg7];
       
  5017 			}
       
  5018 			this._segment1 = new Segment(point1, null, handle1);
       
  5019 			this._segment2 = new Segment(point2, handle2, null);
       
  5020 		}
       
  5021 	},
       
  5022 
       
  5023 	_changed: function() {
       
  5024 		delete this._length;
       
  5025 		delete this._bounds;
       
  5026 	},
       
  5027 
       
  5028 	getPoint1: function() {
       
  5029 		return this._segment1._point;
       
  5030 	},
       
  5031 
       
  5032 	setPoint1: function(point) {
       
  5033 		point = Point.read(arguments);
       
  5034 		this._segment1._point.set(point.x, point.y);
       
  5035 	},
       
  5036 
       
  5037 	getPoint2: function() {
       
  5038 		return this._segment2._point;
       
  5039 	},
       
  5040 
       
  5041 	setPoint2: function(point) {
       
  5042 		point = Point.read(arguments);
       
  5043 		this._segment2._point.set(point.x, point.y);
       
  5044 	},
       
  5045 
       
  5046 	getHandle1: function() {
       
  5047 		return this._segment1._handleOut;
       
  5048 	},
       
  5049 
       
  5050 	setHandle1: function(point) {
       
  5051 		point = Point.read(arguments);
       
  5052 		this._segment1._handleOut.set(point.x, point.y);
       
  5053 	},
       
  5054 
       
  5055 	getHandle2: function() {
       
  5056 		return this._segment2._handleIn;
       
  5057 	},
       
  5058 
       
  5059 	setHandle2: function(point) {
       
  5060 		point = Point.read(arguments);
       
  5061 		this._segment2._handleIn.set(point.x, point.y);
       
  5062 	},
       
  5063 
       
  5064 	getSegment1: function() {
       
  5065 		return this._segment1;
       
  5066 	},
       
  5067 
       
  5068 	getSegment2: function() {
       
  5069 		return this._segment2;
       
  5070 	},
       
  5071 
       
  5072 	getPath: function() {
       
  5073 		return this._path;
       
  5074 	},
       
  5075 
       
  5076 	getIndex: function() {
       
  5077 		return this._segment1._index;
       
  5078 	},
       
  5079 
       
  5080 	getNext: function() {
       
  5081 		var curves = this._path && this._path._curves;
       
  5082 		return curves && (curves[this._segment1._index + 1]
       
  5083 				|| this._path._closed && curves[0]) || null;
       
  5084 	},
       
  5085 
       
  5086 	getPrevious: function() {
       
  5087 		var curves = this._path && this._path._curves;
       
  5088 		return curves && (curves[this._segment1._index - 1]
       
  5089 				|| this._path._closed && curves[curves.length - 1]) || null;
       
  5090 	},
       
  5091 
       
  5092 	isSelected: function() {
       
  5093 		return this.getHandle1().isSelected() && this.getHandle2().isSelected();
       
  5094 	},
       
  5095 
       
  5096 	setSelected: function(selected) {
       
  5097 		this.getHandle1().setSelected(selected);
       
  5098 		this.getHandle2().setSelected(selected);
       
  5099 	},
       
  5100 
       
  5101 	getValues: function() {
       
  5102 		return Curve.getValues(this._segment1, this._segment2);
       
  5103 	},
       
  5104 
       
  5105 	getPoints: function() {
       
  5106 		var coords = this.getValues(),
       
  5107 			points = [];
       
  5108 		for (var i = 0; i < 8; i += 2)
       
  5109 			points.push(new Point(coords[i], coords[i + 1]));
       
  5110 		return points;
       
  5111 	},
       
  5112 
       
  5113 	getLength: function() {
       
  5114 		var from = arguments[0],
       
  5115 			to = arguments[1],
       
  5116 			fullLength = arguments.length === 0 || from === 0 && to === 1;
       
  5117 		if (fullLength && this._length != null)
       
  5118 			return this._length;
       
  5119 		var length = Curve.getLength(this.getValues(), from, to);
       
  5120 		if (fullLength)
       
  5121 			this._length = length;
       
  5122 		return length;
       
  5123 	},
       
  5124 
       
  5125 	getArea: function() {
       
  5126 		return Curve.getArea(this.getValues());
       
  5127 	},
       
  5128 
       
  5129 	getPart: function(from, to) {
       
  5130 		return new Curve(Curve.getPart(this.getValues(), from, to));
       
  5131 	},
       
  5132 
       
  5133 	isLinear: function() {
       
  5134 		return this._segment1._handleOut.isZero()
       
  5135 				&& this._segment2._handleIn.isZero();
       
  5136 	},
       
  5137 
       
  5138 	getIntersections: function(curve) {
       
  5139 		return Curve.getIntersections(this.getValues(), curve.getValues(),
       
  5140 				this, curve, []);
       
  5141 	},
       
  5142 
       
  5143 	reverse: function() {
       
  5144 		return new Curve(this._segment2.reverse(), this._segment1.reverse());
       
  5145 	},
       
  5146 
       
  5147 	_getParameter: function(offset, isParameter) {
       
  5148 		return isParameter
       
  5149 				? offset
       
  5150 				: offset && offset.curve === this
       
  5151 					? offset.parameter
       
  5152 					: offset === undefined && isParameter === undefined
       
  5153 						? 0.5 
       
  5154 						: this.getParameterAt(offset, 0);
       
  5155 	},
       
  5156 
       
  5157 	divide: function(offset, isParameter) {
       
  5158 		var parameter = this._getParameter(offset, isParameter),
       
  5159 			tolerance = 0.00001,
       
  5160 			res = null;
       
  5161 		if (parameter > tolerance && parameter < 1 - tolerance) {
       
  5162 			var parts = Curve.subdivide(this.getValues(), parameter),
       
  5163 				isLinear = this.isLinear(),
       
  5164 				left = parts[0],
       
  5165 				right = parts[1];
       
  5166 
       
  5167 			if (!isLinear) {
       
  5168 				this._segment1._handleOut.set(left[2] - left[0],
       
  5169 						left[3] - left[1]);
       
  5170 				this._segment2._handleIn.set(right[4] - right[6],
       
  5171 						right[5] - right[7]);
       
  5172 			}
       
  5173 
       
  5174 			var x = left[6], y = left[7],
       
  5175 				segment = new Segment(new Point(x, y),
       
  5176 						!isLinear && new Point(left[4] - x, left[5] - y),
       
  5177 						!isLinear && new Point(right[2] - x, right[3] - y));
       
  5178 
       
  5179 			if (this._path) {
       
  5180 				if (this._segment1._index > 0 && this._segment2._index === 0) {
       
  5181 					this._path.add(segment);
       
  5182 				} else {
       
  5183 					this._path.insert(this._segment2._index, segment);
       
  5184 				}
       
  5185 				res = this; 
       
  5186 			} else {
       
  5187 				var end = this._segment2;
       
  5188 				this._segment2 = segment;
       
  5189 				res = new Curve(segment, end);
       
  5190 			}
       
  5191 		}
       
  5192 		return res;
       
  5193 	},
       
  5194 
       
  5195 	split: function(offset, isParameter) {
       
  5196 		return this._path
       
  5197 			? this._path.split(this._segment1._index,
       
  5198 					this._getParameter(offset, isParameter))
       
  5199 			: null;
       
  5200 	},
       
  5201 
       
  5202 	clone: function() {
       
  5203 		return new Curve(this._segment1, this._segment2);
       
  5204 	},
       
  5205 
       
  5206 	toString: function() {
       
  5207 		var parts = [ 'point1: ' + this._segment1._point ];
       
  5208 		if (!this._segment1._handleOut.isZero())
       
  5209 			parts.push('handle1: ' + this._segment1._handleOut);
       
  5210 		if (!this._segment2._handleIn.isZero())
       
  5211 			parts.push('handle2: ' + this._segment2._handleIn);
       
  5212 		parts.push('point2: ' + this._segment2._point);
       
  5213 		return '{ ' + parts.join(', ') + ' }';
       
  5214 	},
       
  5215 
       
  5216 statics: {
       
  5217 	getValues: function(segment1, segment2) {
       
  5218 		var p1 = segment1._point,
       
  5219 			h1 = segment1._handleOut,
       
  5220 			h2 = segment2._handleIn,
       
  5221 			p2 = segment2._point;
       
  5222 		return [
       
  5223 			p1._x, p1._y,
       
  5224 			p1._x + h1._x, p1._y + h1._y,
       
  5225 			p2._x + h2._x, p2._y + h2._y,
       
  5226 			p2._x, p2._y
       
  5227 		];
       
  5228 	},
       
  5229 
       
  5230 	evaluate: function(v, t, type) {
       
  5231 		var p1x = v[0], p1y = v[1],
       
  5232 			c1x = v[2], c1y = v[3],
       
  5233 			c2x = v[4], c2y = v[5],
       
  5234 			p2x = v[6], p2y = v[7],
       
  5235 			x, y;
       
  5236 
       
  5237 		if (type === 0 && (t === 0 || t === 1)) {
       
  5238 			x = t === 0 ? p1x : p2x;
       
  5239 			y = t === 0 ? p1y : p2y;
       
  5240 		} else {
       
  5241 			var cx = 3 * (c1x - p1x),
       
  5242 				bx = 3 * (c2x - c1x) - cx,
       
  5243 				ax = p2x - p1x - cx - bx,
       
  5244 
       
  5245 				cy = 3 * (c1y - p1y),
       
  5246 				by = 3 * (c2y - c1y) - cy,
       
  5247 				ay = p2y - p1y - cy - by;
       
  5248 			if (type === 0) {
       
  5249 				x = ((ax * t + bx) * t + cx) * t + p1x;
       
  5250 				y = ((ay * t + by) * t + cy) * t + p1y;
       
  5251 			} else {
       
  5252 				var tolerance = 0.00001;
       
  5253 				if (t < tolerance && c1x === p1x && c1y === p1y
       
  5254 						|| t > 1 - tolerance && c2x === p2x && c2y === p2y) {
       
  5255 					x = p2x - p1x;
       
  5256 					y = p2y - p1y;
       
  5257 				} else {
       
  5258 					x = (3 * ax * t + 2 * bx) * t + cx;
       
  5259 					y = (3 * ay * t + 2 * by) * t + cy;
       
  5260 				}
       
  5261 				if (type === 3) {
       
  5262 					var x2 = 6 * ax * t + 2 * bx,
       
  5263 						y2 = 6 * ay * t + 2 * by;
       
  5264 					return (x * y2 - y * x2) / Math.pow(x * x + y * y, 3 / 2);
       
  5265 				}
       
  5266 			}
       
  5267 		}
       
  5268 		return type == 2 ? new Point(y, -x) : new Point(x, y);
       
  5269 	},
       
  5270 
       
  5271 	subdivide: function(v, t) {
       
  5272 		var p1x = v[0], p1y = v[1],
       
  5273 			c1x = v[2], c1y = v[3],
       
  5274 			c2x = v[4], c2y = v[5],
       
  5275 			p2x = v[6], p2y = v[7];
       
  5276 		if (t === undefined)
       
  5277 			t = 0.5;
       
  5278 		var u = 1 - t,
       
  5279 			p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y,
       
  5280 			p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y,
       
  5281 			p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y,
       
  5282 			p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y,
       
  5283 			p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
       
  5284 			p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
       
  5285 		return [
       
  5286 			[p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y], 
       
  5287 			[p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y] 
       
  5288 		];
       
  5289 	},
       
  5290 
       
  5291 	solveCubic: function (v, coord, val, roots, min, max) {
       
  5292 		var p1 = v[coord],
       
  5293 			c1 = v[coord + 2],
       
  5294 			c2 = v[coord + 4],
       
  5295 			p2 = v[coord + 6],
       
  5296 			c = 3 * (c1 - p1),
       
  5297 			b = 3 * (c2 - c1) - c,
       
  5298 			a = p2 - p1 - c - b;
       
  5299 		return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
       
  5300 	},
       
  5301 
       
  5302 	getParameterOf: function(v, x, y) {
       
  5303 		if (Math.abs(v[0] - x) < 0.00001
       
  5304 				&& Math.abs(v[1] - y) < 0.00001)
       
  5305 			return 0;
       
  5306 		if (Math.abs(v[6] - x) < 0.00001
       
  5307 				&& Math.abs(v[7] - y) < 0.00001)
       
  5308 			return 1;
       
  5309 		var txs = [],
       
  5310 			tys = [],
       
  5311 			sx = Curve.solveCubic(v, 0, x, txs),
       
  5312 			sy = Curve.solveCubic(v, 1, y, tys),
       
  5313 			tx, ty;
       
  5314 		for (var cx = 0;  sx == -1 || cx < sx;) {
       
  5315 			if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) {
       
  5316 				for (var cy = 0; sy == -1 || cy < sy;) {
       
  5317 					if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) {
       
  5318 						if (sx == -1) tx = ty;
       
  5319 						else if (sy == -1) ty = tx;
       
  5320 						if (Math.abs(tx - ty) < 0.00001)
       
  5321 							return (tx + ty) * 0.5;
       
  5322 					}
       
  5323 				}
       
  5324 				if (sx == -1)
       
  5325 					break;
       
  5326 			}
       
  5327 		}
       
  5328 		return null;
       
  5329 	},
       
  5330 
       
  5331 	getPart: function(v, from, to) {
       
  5332 		if (from > 0)
       
  5333 			v = Curve.subdivide(v, from)[1]; 
       
  5334 		if (to < 1)
       
  5335 			v = Curve.subdivide(v, (to - from) / (1 - from))[0]; 
       
  5336 		return v;
       
  5337 	},
       
  5338 
       
  5339 	isLinear: function(v) {
       
  5340 		var isZero = Numerical.isZero;
       
  5341 		return isZero(v[0] - v[2]) && isZero(v[1] - v[3])
       
  5342 				&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]);
       
  5343 	},
       
  5344 
       
  5345 	isFlatEnough: function(v, tolerance) {
       
  5346 		var p1x = v[0], p1y = v[1],
       
  5347 			c1x = v[2], c1y = v[3],
       
  5348 			c2x = v[4], c2y = v[5],
       
  5349 			p2x = v[6], p2y = v[7],
       
  5350 			ux = 3 * c1x - 2 * p1x - p2x,
       
  5351 			uy = 3 * c1y - 2 * p1y - p2y,
       
  5352 			vx = 3 * c2x - 2 * p2x - p1x,
       
  5353 			vy = 3 * c2y - 2 * p2y - p1y;
       
  5354 		return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
       
  5355 				< 10 * tolerance * tolerance;
       
  5356 	},
       
  5357 
       
  5358 	getArea: function(v) {
       
  5359 		var p1x = v[0], p1y = v[1],
       
  5360 			c1x = v[2], c1y = v[3],
       
  5361 			c2x = v[4], c2y = v[5],
       
  5362 			p2x = v[6], p2y = v[7];
       
  5363 		return (  3.0 * c1y * p1x - 1.5 * c1y * c2x
       
  5364 				- 1.5 * c1y * p2x - 3.0 * p1y * c1x
       
  5365 				- 1.5 * p1y * c2x - 0.5 * p1y * p2x
       
  5366 				+ 1.5 * c2y * p1x + 1.5 * c2y * c1x
       
  5367 				- 3.0 * c2y * p2x + 0.5 * p2y * p1x
       
  5368 				+ 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10;
       
  5369 	},
       
  5370 
       
  5371 	getBounds: function(v) {
       
  5372 		var min = v.slice(0, 2), 
       
  5373 			max = min.slice(), 
       
  5374 			roots = [0, 0];
       
  5375 		for (var i = 0; i < 2; i++)
       
  5376 			Curve._addBounds(v[i], v[i + 2], v[i + 4], v[i + 6],
       
  5377 					i, 0, min, max, roots);
       
  5378 		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
       
  5379 	},
       
  5380 
       
  5381 	_addBounds: function(v0, v1, v2, v3, coord, padding, min, max, roots) {
       
  5382 		function add(value, padding) {
       
  5383 			var left = value - padding,
       
  5384 				right = value + padding;
       
  5385 			if (left < min[coord])
       
  5386 				min[coord] = left;
       
  5387 			if (right > max[coord])
       
  5388 				max[coord] = right;
       
  5389 		}
       
  5390 		var a = 3 * (v1 - v2) - v0 + v3,
       
  5391 			b = 2 * (v0 + v2) - 4 * v1,
       
  5392 			c = v1 - v0,
       
  5393 			count = Numerical.solveQuadratic(a, b, c, roots),
       
  5394 			tMin = 0.00001,
       
  5395 			tMax = 1 - tMin;
       
  5396 		add(v3, 0);
       
  5397 		for (var i = 0; i < count; i++) {
       
  5398 			var t = roots[i],
       
  5399 				u = 1 - t;
       
  5400 			if (tMin < t && t < tMax)
       
  5401 				add(u * u * u * v0
       
  5402 					+ 3 * u * u * t * v1
       
  5403 					+ 3 * u * t * t * v2
       
  5404 					+ t * t * t * v3,
       
  5405 					padding);
       
  5406 		}
       
  5407 	},
       
  5408 
       
  5409 	_getWinding: function(v, prev, x, y, roots1, roots2) {
       
  5410 		var tolerance = 0.00001,
       
  5411 			abs = Math.abs;
       
  5412 
       
  5413 		function getDirection(v) {
       
  5414 			var y0 = v[1],
       
  5415 				y1 = v[7],
       
  5416 				dir = y0 > y1 ? -1 : 1;
       
  5417 			return dir === 1 && (y < y0 || y > y1)
       
  5418 					|| dir === -1 && (y < y1 || y > y0)
       
  5419 					? 0
       
  5420 					: dir;
       
  5421 		}
       
  5422 
       
  5423 		if (Curve.isLinear(v)) {
       
  5424 			var dir = getDirection(v);
       
  5425 			if (!dir)
       
  5426 				return 0;
       
  5427 			var cross = (v[6] - v[0]) * (y - v[1]) - (v[7] - v[1]) * (x - v[0]);
       
  5428 			return (cross < -tolerance ? -1 : 1) == dir ? 0 : dir;
       
  5429 		}
       
  5430 
       
  5431 		var y0 = v[1],
       
  5432 			y1 = v[3],
       
  5433 			y2 = v[5],
       
  5434 			y3 = v[7];
       
  5435 		var a = 3 * (y1 - y2) - y0 + y3,
       
  5436 			b = 2 * (y0 + y2) - 4 * y1,
       
  5437 			c = y1 - y0;
       
  5438 		var count = Numerical.solveQuadratic(a, b, c, roots1, tolerance,
       
  5439 				1 - tolerance),
       
  5440 			part, 
       
  5441 			rest = v, 
       
  5442 			t1 = roots1[0], 
       
  5443 			winding = 0;
       
  5444 		for (var i = 0; i <= count; i++) {
       
  5445 			if (i === count) {
       
  5446 				part = rest;
       
  5447 			} else {
       
  5448 				var curves = Curve.subdivide(rest, t1);
       
  5449 				part = curves[0];
       
  5450 				rest = curves[1];
       
  5451 				t1 = roots1[i];
       
  5452 				t1 = (roots1[i + 1] - t1) / (1 - t1);
       
  5453 			}
       
  5454 			if (i > 0)
       
  5455 				part[3] = part[1]; 
       
  5456 			if (i < count)
       
  5457 				part[5] = rest[1]; 
       
  5458 			var dir = getDirection(part);
       
  5459 			if (!dir)
       
  5460 			    continue;
       
  5461 			var t2,
       
  5462 				px;
       
  5463 			if (Curve.solveCubic(part, 1, y, roots2, -tolerance, 1 + -tolerance)
       
  5464 					=== 1) {
       
  5465 				t2 = roots2[0];
       
  5466 				px = Curve.evaluate(part, t2, 0).x;
       
  5467 			} else {
       
  5468 				var mid = (part[1] + part[7]) / 2;
       
  5469 				t2 = y < mid && dir > 0 ? 0 : 1;
       
  5470 				if (t2 === 1 && y == part[7])
       
  5471 					continue;
       
  5472 				px = t2 === 0 ? part[0] : part[6];
       
  5473 			}
       
  5474 			var slope = Curve.evaluate(part, t2, 1).y,
       
  5475 				stationary = abs(slope) < tolerance || t2 < tolerance
       
  5476 						&& Curve.evaluate(prev, 1, 1).y * slope < 0;
       
  5477 			if (x >= px + (stationary ? -tolerance : tolerance * dir)
       
  5478 					&& !(stationary && (abs(t2) < tolerance
       
  5479 							&& abs(x - part[0]) > tolerance
       
  5480 						|| abs(t2 - 1) < tolerance
       
  5481 							&& abs(x - part[6]) > tolerance))) {
       
  5482 				winding += stationary && abs(t2 - (dir > 0 ? 1 : 0)) < tolerance
       
  5483 						? -dir : dir;
       
  5484 			}
       
  5485 			prev = part;
       
  5486 		}
       
  5487 		return winding;
       
  5488 	}
       
  5489 }}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
       
  5490 	function(name) {
       
  5491 		this[name] = function() {
       
  5492 			if (!this._bounds)
       
  5493 				this._bounds = {};
       
  5494 			var bounds = this._bounds[name];
       
  5495 			if (!bounds) {
       
  5496 				bounds = this._bounds[name] = Path[name]([this._segment1,
       
  5497 						this._segment2], false, this._path.getStyle());
       
  5498 			}
       
  5499 			return bounds.clone();
       
  5500 		};
       
  5501 	},
       
  5502 {
       
  5503 
       
  5504 }), Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'],
       
  5505 	function(name, index) {
       
  5506 		this[name + 'At'] = function(offset, isParameter) {
       
  5507 			var values = this.getValues();
       
  5508 			return Curve.evaluate(values, isParameter
       
  5509 					? offset : Curve.getParameterAt(values, offset, 0), index);
       
  5510 		};
       
  5511 		this[name] = function(parameter) {
       
  5512 			return Curve.evaluate(this.getValues(), parameter, index);
       
  5513 		};
       
  5514 	},
       
  5515 {
       
  5516 	getParameterAt: function(offset, start) {
       
  5517 		return Curve.getParameterAt(this.getValues(), offset,
       
  5518 				start !== undefined ? start : offset < 0 ? 1 : 0);
       
  5519 	},
       
  5520 
       
  5521 	getParameterOf: function(point) {
       
  5522 		point = Point.read(arguments);
       
  5523 		return Curve.getParameterOf(this.getValues(), point.x, point.y);
       
  5524 	},
       
  5525 
       
  5526 	getLocationAt: function(offset, isParameter) {
       
  5527 		if (!isParameter)
       
  5528 			offset = this.getParameterAt(offset);
       
  5529 		return new CurveLocation(this, offset);
       
  5530 	},
       
  5531 
       
  5532 	getLocationOf: function(point) {
       
  5533 		point = Point.read(arguments);
       
  5534 		var t = this.getParameterOf(point);
       
  5535 		return t != null ? new CurveLocation(this, t) : null;
       
  5536 	},
       
  5537 
       
  5538 	getNearestLocation: function(point) {
       
  5539 		point = Point.read(arguments);
       
  5540 		var values = this.getValues(),
       
  5541 			count = 100,
       
  5542 			tolerance = Numerical.TOLERANCE,
       
  5543 			minDist = Infinity,
       
  5544 			minT = 0;
       
  5545 
       
  5546 		function refine(t) {
       
  5547 			if (t >= 0 && t <= 1) {
       
  5548 				var dist = point.getDistance(
       
  5549 						Curve.evaluate(values, t, 0), true);
       
  5550 				if (dist < minDist) {
       
  5551 					minDist = dist;
       
  5552 					minT = t;
       
  5553 					return true;
       
  5554 				}
       
  5555 			}
       
  5556 		}
       
  5557 
       
  5558 		for (var i = 0; i <= count; i++)
       
  5559 			refine(i / count);
       
  5560 
       
  5561 		var step = 1 / (count * 2);
       
  5562 		while (step > tolerance) {
       
  5563 			if (!refine(minT - step) && !refine(minT + step))
       
  5564 				step /= 2;
       
  5565 		}
       
  5566 		var pt = Curve.evaluate(values, minT, 0);
       
  5567 		return new CurveLocation(this, minT, pt, null, null, null,
       
  5568 				point.getDistance(pt));
       
  5569 	},
       
  5570 
       
  5571 	getNearestPoint: function(point) {
       
  5572 		point = Point.read(arguments);
       
  5573 		return this.getNearestLocation(point).getPoint();
       
  5574 	}
       
  5575 
       
  5576 }),
       
  5577 new function() { 
       
  5578 
       
  5579 	function getLengthIntegrand(v) {
       
  5580 		var p1x = v[0], p1y = v[1],
       
  5581 			c1x = v[2], c1y = v[3],
       
  5582 			c2x = v[4], c2y = v[5],
       
  5583 			p2x = v[6], p2y = v[7],
       
  5584 
       
  5585 			ax = 9 * (c1x - c2x) + 3 * (p2x - p1x),
       
  5586 			bx = 6 * (p1x + c2x) - 12 * c1x,
       
  5587 			cx = 3 * (c1x - p1x),
       
  5588 
       
  5589 			ay = 9 * (c1y - c2y) + 3 * (p2y - p1y),
       
  5590 			by = 6 * (p1y + c2y) - 12 * c1y,
       
  5591 			cy = 3 * (c1y - p1y);
       
  5592 
       
  5593 		return function(t) {
       
  5594 			var dx = (ax * t + bx) * t + cx,
       
  5595 				dy = (ay * t + by) * t + cy;
       
  5596 			return Math.sqrt(dx * dx + dy * dy);
       
  5597 		};
       
  5598 	}
       
  5599 
       
  5600 	function getIterations(a, b) {
       
  5601 		return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));
       
  5602 	}
       
  5603 
       
  5604 	return {
       
  5605 		statics: true,
       
  5606 
       
  5607 		getLength: function(v, a, b) {
       
  5608 			if (a === undefined)
       
  5609 				a = 0;
       
  5610 			if (b === undefined)
       
  5611 				b = 1;
       
  5612 			var isZero = Numerical.isZero;
       
  5613 			if (isZero(v[0] - v[2]) && isZero(v[1] - v[3])
       
  5614 					&& isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
       
  5615 				var dx = v[6] - v[0], 
       
  5616 					dy = v[7] - v[1]; 
       
  5617 				return (b - a) * Math.sqrt(dx * dx + dy * dy);
       
  5618 			}
       
  5619 			var ds = getLengthIntegrand(v);
       
  5620 			return Numerical.integrate(ds, a, b, getIterations(a, b));
       
  5621 		},
       
  5622 
       
  5623 		getParameterAt: function(v, offset, start) {
       
  5624 			if (offset === 0)
       
  5625 				return start;
       
  5626 			var forward = offset > 0,
       
  5627 				a = forward ? start : 0,
       
  5628 				b = forward ? 1 : start,
       
  5629 				offset = Math.abs(offset),
       
  5630 				ds = getLengthIntegrand(v),
       
  5631 				rangeLength = Numerical.integrate(ds, a, b,
       
  5632 						getIterations(a, b));
       
  5633 			if (offset >= rangeLength)
       
  5634 				return forward ? b : a;
       
  5635 			var guess = offset / rangeLength,
       
  5636 				length = 0;
       
  5637 			function f(t) {
       
  5638 				var count = getIterations(start, t);
       
  5639 				length += start < t
       
  5640 						? Numerical.integrate(ds, start, t, count)
       
  5641 						: -Numerical.integrate(ds, t, start, count);
       
  5642 				start = t;
       
  5643 				return length - offset;
       
  5644 			}
       
  5645 			return Numerical.findRoot(f, ds,
       
  5646 					forward ? a + guess : b - guess, 
       
  5647 					a, b, 16, 0.00001);
       
  5648 		}
       
  5649 	};
       
  5650 }, new function() { 
       
  5651 	function addLocation(locations, curve1, t1, point1, curve2, t2, point2) {
       
  5652 		var first = locations[0],
       
  5653 			last = locations[locations.length - 1];
       
  5654 		if ((!first || !point1.isClose(first._point, Numerical.EPSILON))
       
  5655 				&& (!last || !point1.isClose(last._point, Numerical.EPSILON)))
       
  5656 			locations.push(
       
  5657 					new CurveLocation(curve1, t1, point1, curve2, t2, point2));
       
  5658 	}
       
  5659 
       
  5660 	function addCurveIntersections(v1, v2, curve1, curve2, locations,
       
  5661 			range1, range2, recursion) {
       
  5662 		recursion = (recursion || 0) + 1;
       
  5663 		if (recursion > 20)
       
  5664 			return;
       
  5665 		range1 = range1 || [ 0, 1 ];
       
  5666 		range2 = range2 || [ 0, 1 ];
       
  5667 		var part1 = Curve.getPart(v1, range1[0], range1[1]),
       
  5668 			part2 = Curve.getPart(v2, range2[0], range2[1]),
       
  5669 			iteration = 0;
       
  5670 		while (iteration++ < 20) {
       
  5671 			var range,
       
  5672 				intersects1 = clipFatLine(part1, part2, range = range2.slice()),
       
  5673 				intersects2 = 0;
       
  5674 			if (intersects1 === 0)
       
  5675 				break;
       
  5676 			if (intersects1 > 0) {
       
  5677 				range2 = range;
       
  5678 				part2 = Curve.getPart(v2, range2[0], range2[1]);
       
  5679 				intersects2 = clipFatLine(part2, part1, range = range1.slice());
       
  5680 				if (intersects2 === 0)
       
  5681 					break;
       
  5682 				if (intersects1 > 0) {
       
  5683 					range1 = range;
       
  5684 					part1 = Curve.getPart(v1, range1[0], range1[1]);
       
  5685 				}
       
  5686 			}
       
  5687 			if (intersects1 < 0 || intersects2 < 0) {
       
  5688 				if (range1[1] - range1[0] > range2[1] - range2[0]) {
       
  5689 					var t = (range1[0] + range1[1]) / 2;
       
  5690 					addCurveIntersections(v1, v2, curve1, curve2, locations,
       
  5691 							[ range1[0], t ], range2, recursion);
       
  5692 					addCurveIntersections(v1, v2, curve1, curve2, locations,
       
  5693 							[ t, range1[1] ], range2, recursion);
       
  5694 					break;
       
  5695 				} else {
       
  5696 					var t = (range2[0] + range2[1]) / 2;
       
  5697 					addCurveIntersections(v1, v2, curve1, curve2, locations,
       
  5698 							range1, [ range2[0], t ], recursion);
       
  5699 					addCurveIntersections(v1, v2, curve1, curve2, locations,
       
  5700 							range1, [ t, range2[1] ], recursion);
       
  5701 					break;
       
  5702 				}
       
  5703 			}
       
  5704 			if (Math.abs(range1[1] - range1[0]) <= 0.00001 &&
       
  5705 				Math.abs(range2[1] - range2[0]) <= 0.00001) {
       
  5706 				var t1 = (range1[0] + range1[1]) / 2,
       
  5707 					t2 = (range2[0] + range2[1]) / 2;
       
  5708 				addLocation(locations,
       
  5709 						curve1, t1, Curve.evaluate(v1, t1, 0),
       
  5710 						curve2, t2, Curve.evaluate(v2, t2, 0));
       
  5711 				break;
       
  5712 			}
       
  5713 		}
       
  5714 	}
       
  5715 
       
  5716 	function clipFatLine(v1, v2, range2) {
       
  5717 		var p0x = v1[0], p0y = v1[1], p1x = v1[2], p1y = v1[3],
       
  5718 			p2x = v1[4], p2y = v1[5], p3x = v1[6], p3y = v1[7],
       
  5719 			q0x = v2[0], q0y = v2[1], q1x = v2[2], q1y = v2[3],
       
  5720 			q2x = v2[4], q2y = v2[5], q3x = v2[6], q3y = v2[7],
       
  5721 			getSignedDistance = Line.getSignedDistance,
       
  5722 			d1 = getSignedDistance(p0x, p0y, p3x, p3y, p1x, p1y) || 0,
       
  5723 			d2 = getSignedDistance(p0x, p0y, p3x, p3y, p2x, p2y) || 0,
       
  5724 			factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
       
  5725 			dmin = factor * Math.min(0, d1, d2),
       
  5726 			dmax = factor * Math.max(0, d1, d2),
       
  5727 			dq0 = getSignedDistance(p0x, p0y, p3x, p3y, q0x, q0y),
       
  5728 			dq1 = getSignedDistance(p0x, p0y, p3x, p3y, q1x, q1y),
       
  5729 			dq2 = getSignedDistance(p0x, p0y, p3x, p3y, q2x, q2y),
       
  5730 			dq3 = getSignedDistance(p0x, p0y, p3x, p3y, q3x, q3y);
       
  5731 		if (dmin > Math.max(dq0, dq1, dq2, dq3)
       
  5732 				|| dmax < Math.min(dq0, dq1, dq2, dq3))
       
  5733 			return 0;
       
  5734 		var hull = getConvexHull(dq0, dq1, dq2, dq3),
       
  5735 			swap;
       
  5736 		if (dq3 < dq0) {
       
  5737 			swap = dmin;
       
  5738 			dmin = dmax;
       
  5739 			dmax = swap;
       
  5740 		}
       
  5741 		var tmaxdmin = -Infinity,
       
  5742 			tmin = Infinity,
       
  5743 			tmax = -Infinity;
       
  5744 		for (var i = 0, l = hull.length; i < l; i++) {
       
  5745 			var p1 = hull[i],
       
  5746 				p2 = hull[(i + 1) % l];
       
  5747 			if (p2[1] < p1[1]) {
       
  5748 				swap = p2;
       
  5749 				p2 = p1;
       
  5750 				p1 = swap;
       
  5751 			}
       
  5752 			var	x1 = p1[0],
       
  5753 				y1 = p1[1],
       
  5754 				x2 = p2[0],
       
  5755 				y2 = p2[1];
       
  5756 			var inv = (y2 - y1) / (x2 - x1);
       
  5757 			if (dmin >= y1 && dmin <= y2) {
       
  5758 				var ixdx = x1 + (dmin - y1) / inv;
       
  5759 				if (ixdx < tmin)
       
  5760 					tmin = ixdx;
       
  5761 				if (ixdx > tmaxdmin)
       
  5762 					tmaxdmin = ixdx;
       
  5763 			}
       
  5764 			if (dmax >= y1 && dmax <= y2) {
       
  5765 				var ixdx = x1 + (dmax - y1) / inv;
       
  5766 				if (ixdx > tmax)
       
  5767 					tmax = ixdx;
       
  5768 				if (ixdx < tmin)
       
  5769 					tmin = 0;
       
  5770 			}
       
  5771 		}
       
  5772 		if (tmin !== Infinity && tmax !== -Infinity) {
       
  5773 			var min = Math.min(dmin, dmax),
       
  5774 				max = Math.max(dmin, dmax);
       
  5775 			if (dq3 > min && dq3 < max)
       
  5776 				tmax = 1;
       
  5777 			if (dq0 > min && dq0 < max)
       
  5778 				tmin = 0;
       
  5779 			if (tmaxdmin > tmax)
       
  5780 				tmax = 1;
       
  5781 			var v2tmin = range2[0],
       
  5782 				tdiff = range2[1] - v2tmin;
       
  5783 			range2[0] = v2tmin + tmin * tdiff;
       
  5784 			range2[1] = v2tmin + tmax * tdiff;
       
  5785 			if ((tdiff - (range2[1] - range2[0])) / tdiff >= 0.2)
       
  5786 				return 1;
       
  5787 		}
       
  5788 		if (Curve.getBounds(v1).touches(Curve.getBounds(v2)))
       
  5789 			return -1;
       
  5790 		return 0;
       
  5791 	}
       
  5792 
       
  5793 	function getConvexHull(dq0, dq1, dq2, dq3) {
       
  5794 		var p0 = [ 0, dq0 ],
       
  5795 			p1 = [ 1 / 3, dq1 ],
       
  5796 			p2 = [ 2 / 3, dq2 ],
       
  5797 			p3 = [ 1, dq3 ],
       
  5798 			getSignedDistance = Line.getSignedDistance,
       
  5799 			dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
       
  5800 			dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2);
       
  5801 		if (dist1 * dist2 < 0) {
       
  5802 			return [ p0, p1, p3, p2 ];
       
  5803 		}
       
  5804 		var pmax, cross;
       
  5805 		if (Math.abs(dist1) > Math.abs(dist2)) {
       
  5806 			pmax = p1;
       
  5807 			cross = (dq3 - dq2 - (dq3 - dq0) / 3)
       
  5808 					* (2 * (dq3 - dq2) - dq3 + dq1) / 3;
       
  5809 		} else {
       
  5810 			pmax = p2;
       
  5811 			cross = (dq1 - dq0 + (dq0 - dq3) / 3)
       
  5812 					* (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
       
  5813 		}
       
  5814 		return cross < 0
       
  5815 				? [ p0, pmax, p3 ]
       
  5816 				: [ p0, p1, p2, p3 ];
       
  5817 	}
       
  5818 
       
  5819 	function addCurveLineIntersections(v1, v2, curve1, curve2, locations) {
       
  5820 		var flip = Curve.isLinear(v1),
       
  5821 			vc = flip ? v2 : v1,
       
  5822 			vl = flip ? v1 : v2,
       
  5823 			lx1 = vl[0], ly1 = vl[1],
       
  5824 			lx2 = vl[6], ly2 = vl[7],
       
  5825 			ldx = lx2 - lx1,
       
  5826 			ldy = ly2 - ly1,
       
  5827 			angle = Math.atan2(-ldy, ldx),
       
  5828 			sin = Math.sin(angle),
       
  5829 			cos = Math.cos(angle),
       
  5830 			rlx2 = ldx * cos - ldy * sin,
       
  5831 			rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0],
       
  5832 			rvc = [];
       
  5833 		for(var i = 0; i < 8; i += 2) {
       
  5834 			var x = vc[i] - lx1,
       
  5835 				y = vc[i + 1] - ly1;
       
  5836 			rvc.push(
       
  5837 				x * cos - y * sin,
       
  5838 				y * cos + x * sin);
       
  5839 		}
       
  5840 		var roots = [],
       
  5841 			count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1);
       
  5842 		for (var i = 0; i < count; i++) {
       
  5843 			var tc = roots[i],
       
  5844 				x = Curve.evaluate(rvc, tc, 0).x;
       
  5845 			if (x >= 0 && x <= rlx2) {
       
  5846 				var tl = Curve.getParameterOf(rvl, x, 0),
       
  5847 					t1 = flip ? tl : tc,
       
  5848 					t2 = flip ? tc : tl;
       
  5849 				addLocation(locations,
       
  5850 						curve1, t1, Curve.evaluate(v1, t1, 0),
       
  5851 						curve2, t2, Curve.evaluate(v2, t2, 0));
       
  5852 			}
       
  5853 		}
       
  5854 	}
       
  5855 
       
  5856 	function addLineIntersection(v1, v2, curve1, curve2, locations) {
       
  5857 		var point = Line.intersect(
       
  5858 				v1[0], v1[1], v1[6], v1[7],
       
  5859 				v2[0], v2[1], v2[6], v2[7]);
       
  5860 		if (point)
       
  5861 			addLocation(locations, curve1, null, point, curve2);
       
  5862 	}
       
  5863 
       
  5864 	return { statics: {
       
  5865 		getIntersections: function(v1, v2, curve1, curve2, locations) {
       
  5866 			var linear1 = Curve.isLinear(v1),
       
  5867 				linear2 = Curve.isLinear(v2),
       
  5868 				c1p1 = curve1.getPoint1(),
       
  5869 				c1p2 = curve1.getPoint2(),
       
  5870 				c2p1 = curve2.getPoint1(),
       
  5871 				c2p2 = curve2.getPoint2(),
       
  5872 				tolerance = 0.00001;
       
  5873 			if (c1p1.isClose(c2p1, tolerance))
       
  5874 				addLocation(locations, curve1, 0, c1p1, curve2, 0, c1p1);
       
  5875 			if (c1p1.isClose(c2p2, tolerance))
       
  5876 				addLocation(locations, curve1, 0, c1p1, curve2, 1, c1p1);
       
  5877 			(linear1 && linear2
       
  5878 				? addLineIntersection
       
  5879 				: linear1 || linear2
       
  5880 					? addCurveLineIntersections
       
  5881 					: addCurveIntersections)(v1, v2, curve1, curve2, locations);
       
  5882 			if (c1p2.isClose(c2p1, tolerance))
       
  5883 				addLocation(locations, curve1, 1, c1p2, curve2, 0, c1p2);
       
  5884 			if (c1p2.isClose(c2p2, tolerance))
       
  5885 				addLocation(locations, curve1, 1, c1p2, curve2, 1, c1p2);
       
  5886 			return locations;
       
  5887 		}
       
  5888 	}};
       
  5889 });
       
  5890 
       
  5891 var CurveLocation = Base.extend({
       
  5892 	_class: 'CurveLocation',
       
  5893 	initialize: function CurveLocation(curve, parameter, point, _curve2,
       
  5894 			_parameter2, _point2, _distance) {
       
  5895 		this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
       
  5896 		this._curve = curve;
       
  5897 		this._segment1 = curve._segment1;
       
  5898 		this._segment2 = curve._segment2;
       
  5899 		this._parameter = parameter;
       
  5900 		this._point = point;
       
  5901 		this._curve2 = _curve2;
       
  5902 		this._parameter2 = _parameter2;
       
  5903 		this._point2 = _point2;
       
  5904 		this._distance = _distance;
       
  5905 	},
       
  5906 
       
  5907 	getSegment: function() {
       
  5908 		if (!this._segment) {
       
  5909 			var curve = this.getCurve(),
       
  5910 				parameter = this.getParameter();
       
  5911 			if (parameter === 1) {
       
  5912 				this._segment = curve._segment2;
       
  5913 			} else if (parameter === 0 || arguments[0]) {
       
  5914 				this._segment = curve._segment1;
       
  5915 			} else if (parameter == null) {
       
  5916 				return null;
       
  5917 			} else {
       
  5918 				this._segment = curve.getLength(0, parameter)
       
  5919 					< curve.getLength(parameter, 1)
       
  5920 						? curve._segment1
       
  5921 						: curve._segment2;
       
  5922 			}
       
  5923 		}
       
  5924 		return this._segment;
       
  5925 	},
       
  5926 
       
  5927 	getCurve: function() {
       
  5928 		if (!this._curve || arguments[0]) {
       
  5929 			this._curve = this._segment1.getCurve();
       
  5930 			if (this._curve.getParameterOf(this._point) == null)
       
  5931 				this._curve = this._segment2.getPrevious().getCurve();
       
  5932 		}
       
  5933 		return this._curve;
       
  5934 	},
       
  5935 
       
  5936 	getIntersection: function() {
       
  5937 		var intersection = this._intersection;
       
  5938 		if (!intersection && this._curve2) {
       
  5939 			var param = this._parameter2;
       
  5940 			this._intersection = intersection = new CurveLocation(
       
  5941 					this._curve2, param, this._point2 || this._point, this);
       
  5942 			intersection._intersection = this;
       
  5943 		}
       
  5944 		return intersection;
       
  5945 	},
       
  5946 
       
  5947 	getPath: function() {
       
  5948 		var curve = this.getCurve();
       
  5949 		return curve && curve._path;
       
  5950 	},
       
  5951 
       
  5952 	getIndex: function() {
       
  5953 		var curve = this.getCurve();
       
  5954 		return curve && curve.getIndex();
       
  5955 	},
       
  5956 
       
  5957 	getOffset: function() {
       
  5958 		var path = this.getPath();
       
  5959 		return path && path._getOffset(this);
       
  5960 	},
       
  5961 
       
  5962 	getCurveOffset: function() {
       
  5963 		var curve = this.getCurve(),
       
  5964 			parameter = this.getParameter();
       
  5965 		return parameter != null && curve && curve.getLength(0, parameter);
       
  5966 	},
       
  5967 
       
  5968 	getParameter: function() {
       
  5969 		if ((this._parameter == null || arguments[0]) && this._point) {
       
  5970 			var curve = this.getCurve(arguments[0] && this._point);
       
  5971 			this._parameter = curve && curve.getParameterOf(this._point);
       
  5972 		}
       
  5973 		return this._parameter;
       
  5974 	},
       
  5975 
       
  5976 	getPoint: function() {
       
  5977 		if ((!this._point || arguments[0]) && this._parameter != null) {
       
  5978 			var curve = this.getCurve();
       
  5979 			this._point = curve && curve.getPointAt(this._parameter, true);
       
  5980 		}
       
  5981 		return this._point;
       
  5982 	},
       
  5983 
       
  5984 	getTangent: function() {
       
  5985 		var parameter = this.getParameter(),
       
  5986 			curve = this.getCurve();
       
  5987 		return parameter != null && curve && curve.getTangentAt(parameter, true);
       
  5988 	},
       
  5989 
       
  5990 	getNormal: function() {
       
  5991 		var parameter = this.getParameter(),
       
  5992 			curve = this.getCurve();
       
  5993 		return parameter != null && curve && curve.getNormalAt(parameter, true);
       
  5994 	},
       
  5995 
       
  5996 	getDistance: function() {
       
  5997 		return this._distance;
       
  5998 	},
       
  5999 
       
  6000 	divide: function() {
       
  6001 		var curve = this.getCurve(true);
       
  6002 		return curve && curve.divide(this.getParameter(true), true);
       
  6003 	},
       
  6004 
       
  6005 	split: function() {
       
  6006 		var curve = this.getCurve(true);
       
  6007 		return curve && curve.split(this.getParameter(true), true);
       
  6008 	},
       
  6009 
       
  6010 	toString: function() {
       
  6011 		var parts = [],
       
  6012 			point = this.getPoint(),
       
  6013 			f = Formatter.instance;
       
  6014 		if (point)
       
  6015 			parts.push('point: ' + point);
       
  6016 		var index = this.getIndex();
       
  6017 		if (index != null)
       
  6018 			parts.push('index: ' + index);
       
  6019 		var parameter = this.getParameter();
       
  6020 		if (parameter != null)
       
  6021 			parts.push('parameter: ' + f.number(parameter));
       
  6022 		if (this._distance != null)
       
  6023 			parts.push('distance: ' + f.number(this._distance));
       
  6024 		return '{ ' + parts.join(', ') + ' }';
       
  6025 	}
       
  6026 });
       
  6027 
       
  6028 var PathItem = Item.extend({
       
  6029 	_class: 'PathItem',
       
  6030 
       
  6031 	initialize: function PathItem() {
       
  6032 	},
       
  6033 
       
  6034 	getIntersections: function(path) {
       
  6035 		if (!this.getBounds().touches(path.getBounds()))
       
  6036 			return [];
       
  6037 		var locations = [],
       
  6038 			curves1 = this.getCurves(),
       
  6039 			curves2 = path.getCurves(),
       
  6040 			length2 = curves2.length,
       
  6041 			values2 = [];
       
  6042 		for (var i = 0; i < length2; i++)
       
  6043 			values2[i] = curves2[i].getValues();
       
  6044 		for (var i = 0, l = curves1.length; i < l; i++) {
       
  6045 			var curve1 = curves1[i],
       
  6046 				values1 = curve1.getValues();
       
  6047 			for (var j = 0; j < length2; j++)
       
  6048 				Curve.getIntersections(values1, values2[j], curve1, curves2[j],
       
  6049 						locations);
       
  6050 		}
       
  6051 		return locations;
       
  6052 	},
       
  6053 
       
  6054 	setPathData: function(data) {
       
  6055 
       
  6056 		var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
       
  6057 			coords,
       
  6058 			relative = false,
       
  6059 			control,
       
  6060 			current = new Point(); 
       
  6061 
       
  6062 		function getCoord(index, coord, isCurrent) {
       
  6063 			var val = parseFloat(coords[index]);
       
  6064 			if (relative)
       
  6065 				val += current[coord];
       
  6066 			if (isCurrent)
       
  6067 				current[coord] = val;
       
  6068 			return val;
       
  6069 		}
       
  6070 
       
  6071 		function getPoint(index, isCurrent) {
       
  6072 			return new Point(
       
  6073 				getCoord(index, 'x', isCurrent),
       
  6074 				getCoord(index + 1, 'y', isCurrent)
       
  6075 			);
       
  6076 		}
       
  6077 
       
  6078 		this.clear();
       
  6079 
       
  6080 		for (var i = 0, l = parts.length; i < l; i++) {
       
  6081 			var part = parts[i],
       
  6082 				cmd = part[0],
       
  6083 				lower = cmd.toLowerCase();
       
  6084 			coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
       
  6085 			var length = coords && coords.length;
       
  6086 			relative = cmd === lower;
       
  6087 			switch (lower) {
       
  6088 			case 'm':
       
  6089 			case 'l':
       
  6090 				for (var j = 0; j < length; j += 2)
       
  6091 					this[j === 0 && lower === 'm' ? 'moveTo' : 'lineTo'](
       
  6092 							getPoint(j, true));
       
  6093 				control = current;
       
  6094 				break;
       
  6095 			case 'h':
       
  6096 			case 'v':
       
  6097 				var coord = lower == 'h' ? 'x' : 'y';
       
  6098 				for (var j = 0; j < length; j++) {
       
  6099 					getCoord(j, coord, true);
       
  6100 					this.lineTo(current);
       
  6101 				}
       
  6102 				control = current;
       
  6103 				break;
       
  6104 			case 'c':
       
  6105 				for (var j = 0; j < length; j += 6) {
       
  6106 					this.cubicCurveTo(
       
  6107 							getPoint(j),
       
  6108 							control = getPoint(j + 2),
       
  6109 							getPoint(j + 4, true));
       
  6110 				}
       
  6111 				break;
       
  6112 			case 's':
       
  6113 				for (var j = 0; j < length; j += 4) {
       
  6114 					this.cubicCurveTo(
       
  6115 							current.multiply(2).subtract(control),
       
  6116 							control = getPoint(j),
       
  6117 							getPoint(j + 2, true));
       
  6118 				}
       
  6119 				break;
       
  6120 			case 'q':
       
  6121 				for (var j = 0; j < length; j += 4) {
       
  6122 					this.quadraticCurveTo(
       
  6123 							control = getPoint(j),
       
  6124 							getPoint(j + 2, true));
       
  6125 				}
       
  6126 				break;
       
  6127 			case 't':
       
  6128 				for (var j = 0; j < length; j += 2) {
       
  6129 					this.quadraticCurveTo(
       
  6130 							control = current.multiply(2).subtract(control),
       
  6131 							getPoint(j, true));
       
  6132 				}
       
  6133 				break;
       
  6134 			case 'a':
       
  6135 				break;
       
  6136 			case 'z':
       
  6137 				this.closePath();
       
  6138 				break;
       
  6139 			}
       
  6140 		}
       
  6141 	},
       
  6142 
       
  6143 	_canComposite: function() {
       
  6144 		return !(this.hasFill() && this.hasStroke());
       
  6145 	},
       
  6146 
       
  6147 	_contains: function(point) {
       
  6148 		var winding = this._getWinding(point);
       
  6149 		return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
       
  6150 	}
       
  6151 
       
  6152 });
       
  6153 
       
  6154 var Path = PathItem.extend({
       
  6155 	_class: 'Path',
       
  6156 	_serializeFields: {
       
  6157 		segments: [],
       
  6158 		closed: false
       
  6159 	},
       
  6160 
       
  6161 	initialize: function Path(arg) {
       
  6162 		this._closed = false;
       
  6163 		this._segments = [];
       
  6164 		var segments = Array.isArray(arg)
       
  6165 			? typeof arg[0] === 'object'
       
  6166 				? arg
       
  6167 				: arguments
       
  6168 			: arg && (arg.point !== undefined && arg.size === undefined
       
  6169 					|| arg.x !== undefined)
       
  6170 				? arguments
       
  6171 				: null;
       
  6172 		this.setSegments(segments || []);
       
  6173 		this._initialize(!segments && arg);
       
  6174 	},
       
  6175 
       
  6176 	_equals: function(item) {
       
  6177 		return Base.equals(this._segments, item._segments);
       
  6178 	},
       
  6179 
       
  6180 	clone: function(insert) {
       
  6181 		var copy = this._clone(new Path({
       
  6182 			segments: this._segments,
       
  6183 			insert: false
       
  6184 		}), insert);
       
  6185 		copy._closed = this._closed;
       
  6186 		if (this._clockwise !== undefined)
       
  6187 			copy._clockwise = this._clockwise;
       
  6188 		return copy;
       
  6189 	},
       
  6190 
       
  6191 	_changed: function _changed(flags) {
       
  6192 		_changed.base.call(this, flags);
       
  6193 		if (flags & 4) {
       
  6194 			delete this._length;
       
  6195 			delete this._clockwise;
       
  6196 			if (this._curves) {
       
  6197 				for (var i = 0, l = this._curves.length; i < l; i++)
       
  6198 					this._curves[i]._changed(5);
       
  6199 			}
       
  6200 		} else if (flags & 8) {
       
  6201 			delete this._bounds;
       
  6202 		}
       
  6203 	},
       
  6204 
       
  6205 	getSegments: function() {
       
  6206 		return this._segments;
       
  6207 	},
       
  6208 
       
  6209 	setSegments: function(segments) {
       
  6210 		var fullySelected = this.isFullySelected();
       
  6211 		this._segments.length = 0;
       
  6212 		this._selectedSegmentState = 0;
       
  6213 		delete this._curves;
       
  6214 		this._add(Segment.readAll(segments));
       
  6215 		if (fullySelected)
       
  6216 			this.setFullySelected(true);
       
  6217 	},
       
  6218 
       
  6219 	getFirstSegment: function() {
       
  6220 		return this._segments[0];
       
  6221 	},
       
  6222 
       
  6223 	getLastSegment: function() {
       
  6224 		return this._segments[this._segments.length - 1];
       
  6225 	},
       
  6226 
       
  6227 	getCurves: function() {
       
  6228 		var curves = this._curves,
       
  6229 			segments = this._segments;
       
  6230 		if (!curves) {
       
  6231 			var length = this._countCurves();
       
  6232 			curves = this._curves = new Array(length);
       
  6233 			for (var i = 0; i < length; i++)
       
  6234 				curves[i] = new Curve(this, segments[i],
       
  6235 					segments[i + 1] || segments[0]);
       
  6236 		}
       
  6237 		return curves;
       
  6238 	},
       
  6239 
       
  6240 	getFirstCurve: function() {
       
  6241 		return this.getCurves()[0];
       
  6242 	},
       
  6243 
       
  6244 	getLastCurve: function() {
       
  6245 		var curves = this.getCurves();
       
  6246 		return curves[curves.length - 1];
       
  6247 	},
       
  6248 
       
  6249 	getPathData: function() {
       
  6250 		var segments = this._segments,
       
  6251 			precision = arguments[0],
       
  6252 			f = Formatter.instance,
       
  6253 			parts = [];
       
  6254 
       
  6255 		function addCurve(seg1, seg2, skipLine) {
       
  6256 			var point1 = seg1._point,
       
  6257 				point2 = seg2._point,
       
  6258 				handle1 = seg1._handleOut,
       
  6259 				handle2 = seg2._handleIn;
       
  6260 			if (handle1.isZero() && handle2.isZero()) {
       
  6261 				if (!skipLine) {
       
  6262 					parts.push('L' + f.point(point2, precision));
       
  6263 				}
       
  6264 			} else {
       
  6265 				var end = point2.subtract(point1);
       
  6266 				parts.push('c' + f.point(handle1, precision)
       
  6267 						+ ' ' + f.point(end.add(handle2), precision)
       
  6268 						+ ' ' + f.point(end, precision));
       
  6269 			}
       
  6270 		}
       
  6271 
       
  6272 		if (segments.length === 0)
       
  6273 			return '';
       
  6274 		parts.push('M' + f.point(segments[0]._point));
       
  6275 		for (var i = 0, l = segments.length  - 1; i < l; i++)
       
  6276 			addCurve(segments[i], segments[i + 1], false);
       
  6277 		if (this._closed) {
       
  6278 			addCurve(segments[segments.length - 1], segments[0], true);
       
  6279 			parts.push('z');
       
  6280 		}
       
  6281 		return parts.join('');
       
  6282 	},
       
  6283 
       
  6284 	isClosed: function() {
       
  6285 		return this._closed;
       
  6286 	},
       
  6287 
       
  6288 	setClosed: function(closed) {
       
  6289 		if (this._closed != (closed = !!closed)) {
       
  6290 			this._closed = closed;
       
  6291 			if (this._curves) {
       
  6292 				var length = this._curves.length = this._countCurves();
       
  6293 				if (closed)
       
  6294 					this._curves[length - 1] = new Curve(this,
       
  6295 						this._segments[length - 1], this._segments[0]);
       
  6296 			}
       
  6297 			this._changed(5);
       
  6298 		}
       
  6299 	},
       
  6300 
       
  6301 	isEmpty: function() {
       
  6302 		return this._segments.length === 0;
       
  6303 	},
       
  6304 
       
  6305 	isPolygon: function() {
       
  6306 		for (var i = 0, l = this._segments.length; i < l; i++) {
       
  6307 			if (!this._segments[i].isLinear())
       
  6308 				return false;
       
  6309 		}
       
  6310 		return true;
       
  6311 	},
       
  6312 
       
  6313 	_applyMatrix: function(matrix) {
       
  6314 		var coords = new Array(6);
       
  6315 		for (var i = 0, l = this._segments.length; i < l; i++)
       
  6316 			this._segments[i]._transformCoordinates(matrix, coords, true);
       
  6317 		return true;
       
  6318 	},
       
  6319 
       
  6320 	_add: function(segs, index) {
       
  6321 		var segments = this._segments,
       
  6322 			curves = this._curves,
       
  6323 			amount = segs.length,
       
  6324 			append = index == null,
       
  6325 			index = append ? segments.length : index;
       
  6326 		for (var i = 0; i < amount; i++) {
       
  6327 			var segment = segs[i];
       
  6328 			if (segment._path)
       
  6329 				segment = segs[i] = segment.clone();
       
  6330 			segment._path = this;
       
  6331 			segment._index = index + i;
       
  6332 			if (segment._selectionState)
       
  6333 				this._updateSelection(segment, 0, segment._selectionState);
       
  6334 		}
       
  6335 		if (append) {
       
  6336 			segments.push.apply(segments, segs);
       
  6337 		} else {
       
  6338 			segments.splice.apply(segments, [index, 0].concat(segs));
       
  6339 			for (var i = index + amount, l = segments.length; i < l; i++)
       
  6340 				segments[i]._index = i;
       
  6341 		}
       
  6342 		if (curves || segs._curves) {
       
  6343 			if (!curves)
       
  6344 				curves = this._curves = [];
       
  6345 			var from = index > 0 ? index - 1 : index,
       
  6346 				start = from,
       
  6347 				to = Math.min(from + amount, this._countCurves());
       
  6348 			if (segs._curves) {
       
  6349 				curves.splice.apply(curves, [from, 0].concat(segs._curves));
       
  6350 				start += segs._curves.length;
       
  6351 			}
       
  6352 			for (var i = start; i < to; i++)
       
  6353 				curves.splice(i, 0, new Curve(this, null, null));
       
  6354 			this._adjustCurves(from, to);
       
  6355 		}
       
  6356 		this._changed(5);
       
  6357 		return segs;
       
  6358 	},
       
  6359 
       
  6360 	_adjustCurves: function(from, to) {
       
  6361 		var segments = this._segments,
       
  6362 			curves = this._curves,
       
  6363 			curve;
       
  6364 		for (var i = from; i < to; i++) {
       
  6365 			curve = curves[i];
       
  6366 			curve._path = this;
       
  6367 			curve._segment1 = segments[i];
       
  6368 			curve._segment2 = segments[i + 1] || segments[0];
       
  6369 		}
       
  6370 		if (curve = curves[this._closed && from === 0 ? segments.length - 1
       
  6371 				: from - 1])
       
  6372 			curve._segment2 = segments[from] || segments[0];
       
  6373 		if (curve = curves[to])
       
  6374 			curve._segment1 = segments[to];
       
  6375 	},
       
  6376 
       
  6377 	_countCurves: function() {
       
  6378 		var length = this._segments.length;
       
  6379 		return !this._closed && length > 0 ? length - 1 : length;
       
  6380 	},
       
  6381 
       
  6382 	add: function(segment1 ) {
       
  6383 		return arguments.length > 1 && typeof segment1 !== 'number'
       
  6384 			? this._add(Segment.readAll(arguments))
       
  6385 			: this._add([ Segment.read(arguments) ])[0];
       
  6386 	},
       
  6387 
       
  6388 	insert: function(index, segment1 ) {
       
  6389 		return arguments.length > 2 && typeof segment1 !== 'number'
       
  6390 			? this._add(Segment.readAll(arguments, 1), index)
       
  6391 			: this._add([ Segment.read(arguments, 1) ], index)[0];
       
  6392 	},
       
  6393 
       
  6394 	addSegment: function() {
       
  6395 		return this._add([ Segment.read(arguments) ])[0];
       
  6396 	},
       
  6397 
       
  6398 	insertSegment: function(index ) {
       
  6399 		return this._add([ Segment.read(arguments, 1) ], index)[0];
       
  6400 	},
       
  6401 
       
  6402 	addSegments: function(segments) {
       
  6403 		return this._add(Segment.readAll(segments));
       
  6404 	},
       
  6405 
       
  6406 	insertSegments: function(index, segments) {
       
  6407 		return this._add(Segment.readAll(segments), index);
       
  6408 	},
       
  6409 
       
  6410 	removeSegment: function(index) {
       
  6411 		return this.removeSegments(index, index + 1)[0] || null;
       
  6412 	},
       
  6413 
       
  6414 	removeSegments: function(from, to) {
       
  6415 		from = from || 0;
       
  6416 		to = Base.pick(to, this._segments.length);
       
  6417 		var segments = this._segments,
       
  6418 			curves = this._curves,
       
  6419 			count = segments.length, 
       
  6420 			removed = segments.splice(from, to - from),
       
  6421 			amount = removed.length;
       
  6422 		if (!amount)
       
  6423 			return removed;
       
  6424 		for (var i = 0; i < amount; i++) {
       
  6425 			var segment = removed[i];
       
  6426 			if (segment._selectionState)
       
  6427 				this._updateSelection(segment, segment._selectionState, 0);
       
  6428 			delete segment._index;
       
  6429 			delete segment._path;
       
  6430 		}
       
  6431 		for (var i = from, l = segments.length; i < l; i++)
       
  6432 			segments[i]._index = i;
       
  6433 		if (curves) {
       
  6434 			var index = from > 0 && to === count + (this._closed ? 1 : 0)
       
  6435 					? from - 1
       
  6436 					: from,
       
  6437 				curves = curves.splice(index, amount);
       
  6438 			if (arguments[2])
       
  6439 				removed._curves = curves.slice(1);
       
  6440 			this._adjustCurves(index, index);
       
  6441 		}
       
  6442 		this._changed(5);
       
  6443 		return removed;
       
  6444 	},
       
  6445 
       
  6446 	clear: '#removeSegments',
       
  6447 
       
  6448 	isFullySelected: function() {
       
  6449 		var length = this._segments.length;
       
  6450 		return this._selected && length > 0 && this._selectedSegmentState
       
  6451 				=== length * 4;
       
  6452 	},
       
  6453 
       
  6454 	setFullySelected: function(selected) {
       
  6455 		if (selected)
       
  6456 			this._selectSegments(true);
       
  6457 		this.setSelected(selected);
       
  6458 	},
       
  6459 
       
  6460 	setSelected: function setSelected(selected) {
       
  6461 		if (!selected)
       
  6462 			this._selectSegments(false);
       
  6463 		setSelected.base.call(this, selected);
       
  6464 	},
       
  6465 
       
  6466 	_selectSegments: function(selected) {
       
  6467 		var length = this._segments.length;
       
  6468 		this._selectedSegmentState = selected
       
  6469 				? length * 4 : 0;
       
  6470 		for (var i = 0; i < length; i++)
       
  6471 			this._segments[i]._selectionState = selected
       
  6472 					? 4 : 0;
       
  6473 	},
       
  6474 
       
  6475 	_updateSelection: function(segment, oldState, newState) {
       
  6476 		segment._selectionState = newState;
       
  6477 		var total = this._selectedSegmentState += newState - oldState;
       
  6478 		if (total > 0)
       
  6479 			this.setSelected(true);
       
  6480 	},
       
  6481 
       
  6482 	flatten: function(maxDistance) {
       
  6483 		var flattener = new PathFlattener(this),
       
  6484 			pos = 0,
       
  6485 			step = flattener.length / Math.ceil(flattener.length / maxDistance),
       
  6486 			end = flattener.length + (this._closed ? -step : step) / 2;
       
  6487 		var segments = [];
       
  6488 		while (pos <= end) {
       
  6489 			segments.push(new Segment(flattener.evaluate(pos, 0)));
       
  6490 			pos += step;
       
  6491 		}
       
  6492 		this.setSegments(segments);
       
  6493 	},
       
  6494 
       
  6495 	simplify: function(tolerance) {
       
  6496 		if (this._segments.length > 2) {
       
  6497 			var fitter = new PathFitter(this, tolerance || 2.5);
       
  6498 			this.setSegments(fitter.fit());
       
  6499 		}
       
  6500 	},
       
  6501 
       
  6502 	split: function(index, parameter) {
       
  6503 		if (parameter === null)
       
  6504 			return;
       
  6505 		if (arguments.length === 1) {
       
  6506 			var arg = index;
       
  6507 			if (typeof arg === 'number')
       
  6508 				arg = this.getLocationAt(arg);
       
  6509 			index = arg.index;
       
  6510 			parameter = arg.parameter;
       
  6511 		}
       
  6512 		if (parameter >= 1) {
       
  6513 			index++;
       
  6514 			parameter--;
       
  6515 		}
       
  6516 		var curves = this.getCurves();
       
  6517 		if (index >= 0 && index < curves.length) {
       
  6518 			if (parameter > 0) {
       
  6519 				curves[index++].divide(parameter, true);
       
  6520 			}
       
  6521 			var segs = this.removeSegments(index, this._segments.length, true),
       
  6522 				path;
       
  6523 			if (this._closed) {
       
  6524 				this.setClosed(false);
       
  6525 				path = this;
       
  6526 			} else if (index > 0) {
       
  6527 				path = this._clone(new Path().insertAbove(this, true));
       
  6528 			}
       
  6529 			path._add(segs, 0);
       
  6530 			this.addSegment(segs[0]);
       
  6531 			return path;
       
  6532 		}
       
  6533 		return null;
       
  6534 	},
       
  6535 
       
  6536 	isClockwise: function() {
       
  6537 		if (this._clockwise !== undefined)
       
  6538 			return this._clockwise;
       
  6539 		return Path.isClockwise(this._segments);
       
  6540 	},
       
  6541 
       
  6542 	setClockwise: function(clockwise) {
       
  6543 		if (this.isClockwise() != (clockwise = !!clockwise))
       
  6544 			this.reverse();
       
  6545 		this._clockwise = clockwise;
       
  6546 	},
       
  6547 
       
  6548 	reverse: function() {
       
  6549 		this._segments.reverse();
       
  6550 		for (var i = 0, l = this._segments.length; i < l; i++) {
       
  6551 			var segment = this._segments[i];
       
  6552 			var handleIn = segment._handleIn;
       
  6553 			segment._handleIn = segment._handleOut;
       
  6554 			segment._handleOut = handleIn;
       
  6555 			segment._index = i;
       
  6556 		}
       
  6557 		delete this._curves;
       
  6558 		if (this._clockwise !== undefined)
       
  6559 			this._clockwise = !this._clockwise;
       
  6560 	},
       
  6561 
       
  6562 	join: function(path) {
       
  6563 		if (path) {
       
  6564 			var segments = path._segments,
       
  6565 				last1 = this.getLastSegment(),
       
  6566 				last2 = path.getLastSegment();
       
  6567 			if (last1._point.equals(last2._point))
       
  6568 				path.reverse();
       
  6569 			var first1,
       
  6570 				first2 = path.getFirstSegment();
       
  6571 			if (last1._point.equals(first2._point)) {
       
  6572 				last1.setHandleOut(first2._handleOut);
       
  6573 				this._add(segments.slice(1));
       
  6574 			} else {
       
  6575 				first1 = this.getFirstSegment();
       
  6576 				if (first1._point.equals(first2._point))
       
  6577 					path.reverse();
       
  6578 				last2 = path.getLastSegment();
       
  6579 				if (first1._point.equals(last2._point)) {
       
  6580 					first1.setHandleIn(last2._handleIn);
       
  6581 					this._add(segments.slice(0, segments.length - 1), 0);
       
  6582 				} else {
       
  6583 					this._add(segments.slice());
       
  6584 				}
       
  6585 			}
       
  6586 			if (path.closed)
       
  6587 				this._add([segments[0]]);
       
  6588 			path.remove();
       
  6589 			first1 = this.getFirstSegment();
       
  6590 			last1 = this.getLastSegment();
       
  6591 			if (last1._point.equals(first1._point)) {
       
  6592 				first1.setHandleIn(last1._handleIn);
       
  6593 				last1.remove();
       
  6594 				this.setClosed(true);
       
  6595 			}
       
  6596 			this._changed(5);
       
  6597 			return true;
       
  6598 		}
       
  6599 		return false;
       
  6600 	},
       
  6601 
       
  6602 	getLength: function() {
       
  6603 		if (this._length == null) {
       
  6604 			var curves = this.getCurves();
       
  6605 			this._length = 0;
       
  6606 			for (var i = 0, l = curves.length; i < l; i++)
       
  6607 				this._length += curves[i].getLength();
       
  6608 		}
       
  6609 		return this._length;
       
  6610 	},
       
  6611 
       
  6612 	getArea: function() {
       
  6613 		var curves = this.getCurves();
       
  6614 		var area = 0;
       
  6615 		for (var i = 0, l = curves.length; i < l; i++)
       
  6616 			area += curves[i].getArea();
       
  6617 		return area;
       
  6618 	},
       
  6619 
       
  6620 	_getOffset: function(location) {
       
  6621 		var index = location && location.getIndex();
       
  6622 		if (index != null) {
       
  6623 			var curves = this.getCurves(),
       
  6624 				offset = 0;
       
  6625 			for (var i = 0; i < index; i++)
       
  6626 				offset += curves[i].getLength();
       
  6627 			var curve = curves[index];
       
  6628 			return offset + curve.getLength(0, location.getParameter());
       
  6629 		}
       
  6630 		return null;
       
  6631 	},
       
  6632 
       
  6633 	getLocationOf: function(point) {
       
  6634 		point = Point.read(arguments);
       
  6635 		var curves = this.getCurves();
       
  6636 		for (var i = 0, l = curves.length; i < l; i++) {
       
  6637 			var loc = curves[i].getLocationOf(point);
       
  6638 			if (loc)
       
  6639 				return loc;
       
  6640 		}
       
  6641 		return null;
       
  6642 	},
       
  6643 
       
  6644 	getLocationAt: function(offset, isParameter) {
       
  6645 		var curves = this.getCurves(),
       
  6646 			length = 0;
       
  6647 		if (isParameter) {
       
  6648 			var index = ~~offset; 
       
  6649 			return curves[index].getLocationAt(offset - index, true);
       
  6650 		}
       
  6651 		for (var i = 0, l = curves.length; i < l; i++) {
       
  6652 			var start = length,
       
  6653 				curve = curves[i];
       
  6654 			length += curve.getLength();
       
  6655 			if (length >= offset) {
       
  6656 				return curve.getLocationAt(offset - start);
       
  6657 			}
       
  6658 		}
       
  6659 		if (offset <= this.getLength())
       
  6660 			return new CurveLocation(curves[curves.length - 1], 1);
       
  6661 		return null;
       
  6662 	},
       
  6663 
       
  6664 	getPointAt: function(offset, isParameter) {
       
  6665 		var loc = this.getLocationAt(offset, isParameter);
       
  6666 		return loc && loc.getPoint();
       
  6667 	},
       
  6668 
       
  6669 	getTangentAt: function(offset, isParameter) {
       
  6670 		var loc = this.getLocationAt(offset, isParameter);
       
  6671 		return loc && loc.getTangent();
       
  6672 	},
       
  6673 
       
  6674 	getNormalAt: function(offset, isParameter) {
       
  6675 		var loc = this.getLocationAt(offset, isParameter);
       
  6676 		return loc && loc.getNormal();
       
  6677 	},
       
  6678 
       
  6679 	getNearestLocation: function(point) {
       
  6680 		point = Point.read(arguments);
       
  6681 		var curves = this.getCurves(),
       
  6682 			minDist = Infinity,
       
  6683 			minLoc = null;
       
  6684 		for (var i = 0, l = curves.length; i < l; i++) {
       
  6685 			var loc = curves[i].getNearestLocation(point);
       
  6686 			if (loc._distance < minDist) {
       
  6687 				minDist = loc._distance;
       
  6688 				minLoc = loc;
       
  6689 			}
       
  6690 		}
       
  6691 		return minLoc;
       
  6692 	},
       
  6693 
       
  6694 	getNearestPoint: function(point) {
       
  6695 		point = Point.read(arguments);
       
  6696 		return this.getNearestLocation(point).getPoint();
       
  6697 	},
       
  6698 
       
  6699 	getStyle: function() {
       
  6700 		var parent = this._parent;
       
  6701 		return (parent && parent._type === 'compound-path'
       
  6702 				? parent : this)._style;
       
  6703 	},
       
  6704 
       
  6705 	toShape: function(insert) {
       
  6706 		if (!this._closed)
       
  6707 			return null;
       
  6708 
       
  6709 		var segments = this._segments,
       
  6710 			type,
       
  6711 			size,
       
  6712 			radius,
       
  6713 			topCenter;
       
  6714 
       
  6715 		function isColinear(i, j) {
       
  6716 			return segments[i].isColinear(segments[j]);
       
  6717 		}
       
  6718 
       
  6719 		function isOrthogonal(i) {
       
  6720 			return segments[i].isOrthogonal();
       
  6721 		}
       
  6722 
       
  6723 		function isArc(i) {
       
  6724 			return segments[i].isArc();
       
  6725 		}
       
  6726 
       
  6727 		function getDistance(i, j) {
       
  6728 			return segments[i]._point.getDistance(segments[j]._point);
       
  6729 		}
       
  6730 
       
  6731 		if (this.isPolygon() && segments.length === 4
       
  6732 				&& isColinear(0, 2) && isColinear(1, 3) && isOrthogonal(1)) {
       
  6733 			type = Shape.Rectangle;
       
  6734 			size = new Size(getDistance(0, 3), getDistance(0, 1));
       
  6735 			topCenter = segments[1]._point.add(segments[2]._point).divide(2);
       
  6736 		} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)
       
  6737 				&& isArc(6) && isColinear(1, 5) && isColinear(3, 7)) {
       
  6738 			type = Shape.Rectangle;
       
  6739 			size = new Size(getDistance(1, 6), getDistance(0, 3));
       
  6740 			radius = size.subtract(new Size(getDistance(0, 7),
       
  6741 					getDistance(1, 2))).divide(2);
       
  6742 			topCenter = segments[3]._point.add(segments[4]._point).divide(2);
       
  6743 		} else if (segments.length === 4
       
  6744 				&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
       
  6745 			if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {
       
  6746 				type = Shape.Circle;
       
  6747 				radius = getDistance(0, 2) / 2;
       
  6748 			} else {
       
  6749 				type = Shape.Ellipse;
       
  6750 				radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);
       
  6751 			}
       
  6752 			topCenter = segments[1]._point;
       
  6753 		}
       
  6754 
       
  6755 		if (type) {
       
  6756 			var center = this.getPosition(true),
       
  6757 				shape = new type({
       
  6758 					center: center,
       
  6759 					size: size,
       
  6760 					radius: radius,
       
  6761 					insert: false
       
  6762 				});
       
  6763 			shape.rotate(topCenter.subtract(center).getAngle() + 90);
       
  6764 			shape.setStyle(this._style);
       
  6765 			if (insert || insert === undefined)
       
  6766 				shape.insertAbove(this);
       
  6767 			return shape;
       
  6768 		}
       
  6769 		return null;
       
  6770 	},
       
  6771 
       
  6772 	_getWinding: function(point) {
       
  6773 		var closed = this._closed;
       
  6774 		if (!closed && !this.hasFill()
       
  6775 				|| !this._getBounds('getRoughBounds')._containsPoint(point))
       
  6776 			return 0;
       
  6777 		var curves = this.getCurves(),
       
  6778 			segments = this._segments,
       
  6779 			winding = 0,
       
  6780 			roots1 = [],
       
  6781 			roots2 = [],
       
  6782 			last = (closed
       
  6783 					? curves[curves.length - 1]
       
  6784 					: new Curve(segments[segments.length - 1]._point,
       
  6785 						segments[0]._point)).getValues(),
       
  6786 			previous = last;
       
  6787 		for (var i = 0, l = curves.length; i < l; i++) {
       
  6788 			var curve = curves[i].getValues(),
       
  6789 				x = curve[0],
       
  6790 				y = curve[1];
       
  6791 			if (!(x === curve[2] && y === curve[3] && x === curve[4]
       
  6792 					&& y === curve[5] && x === curve[6] && y === curve[7])) {
       
  6793 				winding += Curve._getWinding(curve, previous, point.x, point.y,
       
  6794 						roots1, roots2);
       
  6795 				previous = curve;
       
  6796 			}
       
  6797 		}
       
  6798 		if (!closed) {
       
  6799 			winding += Curve._getWinding(last, previous, point.x, point.y,
       
  6800 					roots1, roots2);
       
  6801 		}
       
  6802 		return winding;
       
  6803 	},
       
  6804 
       
  6805 	_hitTest: function(point, options) {
       
  6806 		var style = this.getStyle(),
       
  6807 			segments = this._segments,
       
  6808 			closed = this._closed,
       
  6809 			tolerance = options.tolerance,
       
  6810 			radius = 0, join, cap, miterLimit,
       
  6811 			that = this,
       
  6812 			area, loc, res;
       
  6813 
       
  6814 		if (options.stroke) {
       
  6815 			radius = style.getStrokeWidth() / 2;
       
  6816 			if (radius > 0) {
       
  6817 				join = style.getStrokeJoin();
       
  6818 				cap = style.getStrokeCap();
       
  6819 				miterLimit = radius * style.getMiterLimit();
       
  6820 			} else {
       
  6821 				join = cap = 'round';
       
  6822 			}
       
  6823 			radius += tolerance;
       
  6824 		}
       
  6825 
       
  6826 		function checkPoint(seg, pt, name) {
       
  6827 			if (point.getDistance(pt) < tolerance)
       
  6828 				return new HitResult(name, that, { segment: seg, point: pt });
       
  6829 		}
       
  6830 
       
  6831 		function checkSegmentPoints(seg, ends) {
       
  6832 			var pt = seg._point;
       
  6833 			return (ends || options.segments) && checkPoint(seg, pt, 'segment')
       
  6834 				|| (!ends && options.handles) && (
       
  6835 					checkPoint(seg, pt.add(seg._handleIn), 'handle-in') ||
       
  6836 					checkPoint(seg, pt.add(seg._handleOut), 'handle-out'));
       
  6837 		}
       
  6838 
       
  6839 		function addAreaPoint(point) {
       
  6840 			area.push(point);
       
  6841 		}
       
  6842 
       
  6843 		function getAreaCurve(index) {
       
  6844 			var p1 = area[index],
       
  6845 				p2 = area[(index + 1) % area.length];
       
  6846 			return [p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p2.x ,p2.y];
       
  6847 		}
       
  6848 
       
  6849 		function isInArea(point) {
       
  6850 			var length = area.length,
       
  6851 				previous = getAreaCurve(length - 1),
       
  6852 				roots1 = [],
       
  6853 				roots2 = [],
       
  6854 				winding = 0;
       
  6855 			for (var i = 0; i < length; i++) {
       
  6856 				var curve = getAreaCurve(i);
       
  6857 				winding += Curve._getWinding(curve, previous, point.x, point.y,
       
  6858 						roots1, roots2);
       
  6859 				previous = curve;
       
  6860 			}
       
  6861 			return !!winding;
       
  6862 		}
       
  6863 
       
  6864 		function checkSegmentStroke(segment) {
       
  6865 			if (join !== 'round' || cap !== 'round') {
       
  6866 				area = [];
       
  6867 				if (closed || segment._index > 0
       
  6868 						&& segment._index < segments.length - 1) {
       
  6869 					if (join !== 'round' && (segment._handleIn.isZero() 
       
  6870 							|| segment._handleOut.isZero()))
       
  6871 						Path._addSquareJoin(segment, join, radius, miterLimit,
       
  6872 								addAreaPoint, true);
       
  6873 				} else if (cap !== 'round') {
       
  6874 					Path._addSquareCap(segment, cap, radius, addAreaPoint, true);
       
  6875 				}
       
  6876 				if (area.length > 0)
       
  6877 					return isInArea(point);
       
  6878 			}
       
  6879 			return point.getDistance(segment._point) <= radius;
       
  6880 		}
       
  6881 
       
  6882 		if (options.ends && !options.segments && !closed) {
       
  6883 			if (res = checkSegmentPoints(segments[0], true)
       
  6884 					|| checkSegmentPoints(segments[segments.length - 1], true))
       
  6885 				return res;
       
  6886 		} else if (options.segments || options.handles) {
       
  6887 			for (var i = 0, l = segments.length; i < l; i++) {
       
  6888 				if (res = checkSegmentPoints(segments[i]))
       
  6889 					return res;
       
  6890 			}
       
  6891 		}
       
  6892 		if (radius > 0) {
       
  6893 			loc = this.getNearestLocation(point);
       
  6894 			if (loc) {
       
  6895 				var parameter = loc.getParameter();
       
  6896 				if (parameter === 0 || parameter === 1) {
       
  6897 					if (!checkSegmentStroke(loc.getSegment()))
       
  6898 						loc = null;
       
  6899 				} else  if (loc._distance > radius) {
       
  6900 					loc = null;
       
  6901 				}
       
  6902 			}
       
  6903 			if (!loc && join === 'miter') {
       
  6904 				for (var i = 0, l = segments.length; i < l; i++) {
       
  6905 					var segment = segments[i];
       
  6906 					if (point.getDistance(segment._point) <= miterLimit
       
  6907 							&& checkSegmentStroke(segment)) {
       
  6908 						loc = segment.getLocation();
       
  6909 						break;
       
  6910 					}
       
  6911 				}
       
  6912 			}
       
  6913 		}
       
  6914 		return !loc && options.fill && this.hasFill() && this._contains(point)
       
  6915 				? new HitResult('fill', this)
       
  6916 				: loc
       
  6917 					? new HitResult('stroke', this, { location: loc })
       
  6918 					: null;
       
  6919 	}
       
  6920 
       
  6921 }, new function() { 
       
  6922 
       
  6923 	function drawHandles(ctx, segments, matrix, size) {
       
  6924 		var half = size / 2;
       
  6925 
       
  6926 		function drawHandle(index) {
       
  6927 			var hX = coords[index],
       
  6928 				hY = coords[index + 1];
       
  6929 			if (pX != hX || pY != hY) {
       
  6930 				ctx.beginPath();
       
  6931 				ctx.moveTo(pX, pY);
       
  6932 				ctx.lineTo(hX, hY);
       
  6933 				ctx.stroke();
       
  6934 				ctx.beginPath();
       
  6935 				ctx.arc(hX, hY, half, 0, Math.PI * 2, true);
       
  6936 				ctx.fill();
       
  6937 			}
       
  6938 		}
       
  6939 
       
  6940 		var coords = new Array(6);
       
  6941 		for (var i = 0, l = segments.length; i < l; i++) {
       
  6942 			var segment = segments[i];
       
  6943 			segment._transformCoordinates(matrix, coords, false);
       
  6944 			var state = segment._selectionState,
       
  6945 				selected = state & 4,
       
  6946 				pX = coords[0],
       
  6947 				pY = coords[1];
       
  6948 			if (selected || (state & 1))
       
  6949 				drawHandle(2);
       
  6950 			if (selected || (state & 2))
       
  6951 				drawHandle(4);
       
  6952 			ctx.save();
       
  6953 			ctx.beginPath();
       
  6954 			ctx.rect(pX - half, pY - half, size, size);
       
  6955 			ctx.fill();
       
  6956 			if (!selected) {
       
  6957 				ctx.beginPath();
       
  6958 				ctx.rect(pX - half + 1, pY - half + 1, size - 2, size - 2);
       
  6959 				ctx.fillStyle = '#ffffff';
       
  6960 				ctx.fill();
       
  6961 			}
       
  6962 			ctx.restore();
       
  6963 		}
       
  6964 	}
       
  6965 
       
  6966 	function drawSegments(ctx, path, matrix) {
       
  6967 		var segments = path._segments,
       
  6968 			length = segments.length,
       
  6969 			coords = new Array(6),
       
  6970 			first = true,
       
  6971 			curX, curY,
       
  6972 			prevX, prevY,
       
  6973 			inX, inY,
       
  6974 			outX, outY;
       
  6975 
       
  6976 		function drawSegment(i) {
       
  6977 			var segment = segments[i];
       
  6978 			if (matrix) {
       
  6979 				segment._transformCoordinates(matrix, coords, false);
       
  6980 				curX = coords[0];
       
  6981 				curY = coords[1];
       
  6982 			} else {
       
  6983 				var point = segment._point;
       
  6984 				curX = point._x;
       
  6985 				curY = point._y;
       
  6986 			}
       
  6987 			if (first) {
       
  6988 				ctx.moveTo(curX, curY);
       
  6989 				first = false;
       
  6990 			} else {
       
  6991 				if (matrix) {
       
  6992 					inX = coords[2];
       
  6993 					inY = coords[3];
       
  6994 				} else {
       
  6995 					var handle = segment._handleIn;
       
  6996 					inX = curX + handle._x;
       
  6997 					inY = curY + handle._y;
       
  6998 				}
       
  6999 				if (inX == curX && inY == curY && outX == prevX && outY == prevY) {
       
  7000 					ctx.lineTo(curX, curY);
       
  7001 				} else {
       
  7002 					ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
       
  7003 				}
       
  7004 			}
       
  7005 			prevX = curX;
       
  7006 			prevY = curY;
       
  7007 			if (matrix) {
       
  7008 				outX = coords[4];
       
  7009 				outY = coords[5];
       
  7010 			} else {
       
  7011 				var handle = segment._handleOut;
       
  7012 				outX = prevX + handle._x;
       
  7013 				outY = prevY + handle._y;
       
  7014 			}
       
  7015 		}
       
  7016 
       
  7017 		for (var i = 0; i < length; i++)
       
  7018 			drawSegment(i);
       
  7019 		if (path._closed && length > 1)
       
  7020 			drawSegment(0);
       
  7021 	}
       
  7022 
       
  7023 	return {
       
  7024 		_draw: function(ctx, param) {
       
  7025 			var clip = param.clip,
       
  7026 				compound = param.compound;
       
  7027 			if (!compound)
       
  7028 				ctx.beginPath();
       
  7029 
       
  7030 			var style = this.getStyle(),
       
  7031 				hasFill = style.hasFill(),
       
  7032 				hasStroke = style.hasStroke(),
       
  7033 				dashArray = style.getDashArray(),
       
  7034 				dashLength = !paper.support.nativeDash && hasStroke
       
  7035 						&& dashArray && dashArray.length;
       
  7036 
       
  7037 			function getOffset(i) {
       
  7038 				return dashArray[((i % dashLength) + dashLength) % dashLength];
       
  7039 			}
       
  7040 
       
  7041 			if (hasFill || hasStroke && !dashLength || compound || clip)
       
  7042 				drawSegments(ctx, this);
       
  7043 			if (this._closed)
       
  7044 				ctx.closePath();
       
  7045 
       
  7046 			if (!clip && !compound && (hasFill || hasStroke)) {
       
  7047 				this._setStyles(ctx);
       
  7048 				if (hasFill) {
       
  7049 					ctx.fill(style.getWindingRule());
       
  7050 					ctx.shadowColor = 'rgba(0,0,0,0)';
       
  7051 				}
       
  7052 				if (hasStroke) {
       
  7053 					if (dashLength) {
       
  7054 						ctx.beginPath();
       
  7055 						var flattener = new PathFlattener(this),
       
  7056 							length = flattener.length,
       
  7057 							from = -style.getDashOffset(), to,
       
  7058 							i = 0;
       
  7059 						from = from % length;
       
  7060 						while (from > 0) {
       
  7061 							from -= getOffset(i--) + getOffset(i--);
       
  7062 						}
       
  7063 						while (from < length) {
       
  7064 							to = from + getOffset(i++);
       
  7065 							if (from > 0 || to > 0)
       
  7066 								flattener.drawPart(ctx,
       
  7067 										Math.max(from, 0), Math.max(to, 0));
       
  7068 							from = to + getOffset(i++);
       
  7069 						}
       
  7070 					}
       
  7071 					ctx.stroke();
       
  7072 				}
       
  7073 			}
       
  7074 		},
       
  7075 
       
  7076 		_drawSelected: function(ctx, matrix) {
       
  7077 			ctx.beginPath();
       
  7078 			drawSegments(ctx, this, matrix);
       
  7079 			ctx.stroke();
       
  7080 			drawHandles(ctx, this._segments, matrix,
       
  7081 					this._project.options.handleSize || 4);
       
  7082 		}
       
  7083 	};
       
  7084 }, new function() { 
       
  7085 
       
  7086 	function getFirstControlPoints(rhs) {
       
  7087 		var n = rhs.length,
       
  7088 			x = [], 
       
  7089 			tmp = [], 
       
  7090 			b = 2;
       
  7091 		x[0] = rhs[0] / b;
       
  7092 		for (var i = 1; i < n; i++) {
       
  7093 			tmp[i] = 1 / b;
       
  7094 			b = (i < n - 1 ? 4 : 2) - tmp[i];
       
  7095 			x[i] = (rhs[i] - x[i - 1]) / b;
       
  7096 		}
       
  7097 		for (var i = 1; i < n; i++) {
       
  7098 			x[n - i - 1] -= tmp[n - i] * x[n - i];
       
  7099 		}
       
  7100 		return x;
       
  7101 	}
       
  7102 
       
  7103 	return {
       
  7104 		smooth: function() {
       
  7105 			var segments = this._segments,
       
  7106 				size = segments.length,
       
  7107 				closed = this._closed,
       
  7108 				n = size,
       
  7109 				overlap = 0;
       
  7110 			if (size <= 2)
       
  7111 				return;
       
  7112 			if (closed) {
       
  7113 				overlap = Math.min(size, 4);
       
  7114 				n += Math.min(size, overlap) * 2;
       
  7115 			}
       
  7116 			var knots = [];
       
  7117 			for (var i = 0; i < size; i++)
       
  7118 				knots[i + overlap] = segments[i]._point;
       
  7119 			if (closed) {
       
  7120 				for (var i = 0; i < overlap; i++) {
       
  7121 					knots[i] = segments[i + size - overlap]._point;
       
  7122 					knots[i + size + overlap] = segments[i]._point;
       
  7123 				}
       
  7124 			} else {
       
  7125 				n--;
       
  7126 			}
       
  7127 			var rhs = [];
       
  7128 
       
  7129 			for (var i = 1; i < n - 1; i++)
       
  7130 				rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
       
  7131 			rhs[0] = knots[0]._x + 2 * knots[1]._x;
       
  7132 			rhs[n - 1] = 3 * knots[n - 1]._x;
       
  7133 			var x = getFirstControlPoints(rhs);
       
  7134 
       
  7135 			for (var i = 1; i < n - 1; i++)
       
  7136 				rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
       
  7137 			rhs[0] = knots[0]._y + 2 * knots[1]._y;
       
  7138 			rhs[n - 1] = 3 * knots[n - 1]._y;
       
  7139 			var y = getFirstControlPoints(rhs);
       
  7140 
       
  7141 			if (closed) {
       
  7142 				for (var i = 0, j = size; i < overlap; i++, j++) {
       
  7143 					var f1 = i / overlap,
       
  7144 						f2 = 1 - f1,
       
  7145 						ie = i + overlap,
       
  7146 						je = j + overlap;
       
  7147 					x[j] = x[i] * f1 + x[j] * f2;
       
  7148 					y[j] = y[i] * f1 + y[j] * f2;
       
  7149 					x[je] = x[ie] * f2 + x[je] * f1;
       
  7150 					y[je] = y[ie] * f2 + y[je] * f1;
       
  7151 				}
       
  7152 				n--;
       
  7153 			}
       
  7154 			var handleIn = null;
       
  7155 			for (var i = overlap; i <= n - overlap; i++) {
       
  7156 				var segment = segments[i - overlap];
       
  7157 				if (handleIn)
       
  7158 					segment.setHandleIn(handleIn.subtract(segment._point));
       
  7159 				if (i < n) {
       
  7160 					segment.setHandleOut(
       
  7161 							new Point(x[i], y[i]).subtract(segment._point));
       
  7162 					handleIn = i < n - 1
       
  7163 							? new Point(
       
  7164 								2 * knots[i + 1]._x - x[i + 1],
       
  7165 								2 * knots[i + 1]._y - y[i + 1])
       
  7166 							: new Point(
       
  7167 								(knots[n]._x + x[n - 1]) / 2,
       
  7168 								(knots[n]._y + y[n - 1]) / 2);
       
  7169 				}
       
  7170 			}
       
  7171 			if (closed && handleIn) {
       
  7172 				var segment = this._segments[0];
       
  7173 				segment.setHandleIn(handleIn.subtract(segment._point));
       
  7174 			}
       
  7175 		}
       
  7176 	};
       
  7177 }, new function() { 
       
  7178 	function getCurrentSegment(that) {
       
  7179 		var segments = that._segments;
       
  7180 		if (segments.length == 0)
       
  7181 			throw new Error('Use a moveTo() command first');
       
  7182 		return segments[segments.length - 1];
       
  7183 	}
       
  7184 
       
  7185 	return {
       
  7186 		moveTo: function() {
       
  7187 			if (this._segments.length === 1)
       
  7188 				this.removeSegment(0);
       
  7189 			if (!this._segments.length)
       
  7190 				this._add([ new Segment(Point.read(arguments)) ]);
       
  7191 		},
       
  7192 
       
  7193 		moveBy: function() {
       
  7194 			throw new Error('moveBy() is unsupported on Path items.');
       
  7195 		},
       
  7196 
       
  7197 		lineTo: function() {
       
  7198 			this._add([ new Segment(Point.read(arguments)) ]);
       
  7199 		},
       
  7200 
       
  7201 		cubicCurveTo: function() {
       
  7202 			var handle1 = Point.read(arguments),
       
  7203 				handle2 = Point.read(arguments),
       
  7204 				to = Point.read(arguments),
       
  7205 				current = getCurrentSegment(this);
       
  7206 			current.setHandleOut(handle1.subtract(current._point));
       
  7207 			this._add([ new Segment(to, handle2.subtract(to)) ]);
       
  7208 		},
       
  7209 
       
  7210 		quadraticCurveTo: function() {
       
  7211 			var handle = Point.read(arguments),
       
  7212 				to = Point.read(arguments),
       
  7213 				current = getCurrentSegment(this)._point;
       
  7214 			this.cubicCurveTo(
       
  7215 				handle.add(current.subtract(handle).multiply(1 / 3)),
       
  7216 				handle.add(to.subtract(handle).multiply(1 / 3)),
       
  7217 				to
       
  7218 			);
       
  7219 		},
       
  7220 
       
  7221 		curveTo: function() {
       
  7222 			var through = Point.read(arguments),
       
  7223 				to = Point.read(arguments),
       
  7224 				t = Base.pick(Base.read(arguments), 0.5),
       
  7225 				t1 = 1 - t,
       
  7226 				current = getCurrentSegment(this)._point,
       
  7227 				handle = through.subtract(current.multiply(t1 * t1))
       
  7228 					.subtract(to.multiply(t * t)).divide(2 * t * t1);
       
  7229 			if (handle.isNaN())
       
  7230 				throw new Error(
       
  7231 					'Cannot put a curve through points with parameter = ' + t);
       
  7232 			this.quadraticCurveTo(handle, to);
       
  7233 		},
       
  7234 
       
  7235 		arcTo: function() {
       
  7236 			var current = getCurrentSegment(this),
       
  7237 				from = current._point,
       
  7238 				through,
       
  7239 				to = Point.read(arguments),
       
  7240 				clockwise = Base.pick(Base.peek(arguments), true);
       
  7241 			if (typeof clockwise === 'boolean') {
       
  7242 				var middle = from.add(to).divide(2),
       
  7243 				through = middle.add(middle.subtract(from).rotate(
       
  7244 						clockwise ? -90 : 90));
       
  7245 			} else {
       
  7246 				through = to;
       
  7247 				to = Point.read(arguments);
       
  7248 			}
       
  7249 			var l1 = new Line(from.add(through).divide(2),
       
  7250 						through.subtract(from).rotate(90), true),
       
  7251 				l2 = new Line(through.add(to).divide(2),
       
  7252 						to.subtract(through).rotate(90), true),
       
  7253 				center = l1.intersect(l2, true),
       
  7254 				line = new Line(from, to),
       
  7255 				throughSide = line.getSide(through);
       
  7256 			if (!center) {
       
  7257 				if (!throughSide)
       
  7258 					return this.lineTo(to);
       
  7259 				throw new Error('Cannot put an arc through the given points: '
       
  7260 					+ [from, through, to]);
       
  7261 			}
       
  7262 			var vector = from.subtract(center),
       
  7263 				extent = vector.getDirectedAngle(to.subtract(center)),
       
  7264 				centerSide = line.getSide(center);
       
  7265 			if (centerSide == 0) {
       
  7266 				extent = throughSide * Math.abs(extent);
       
  7267 			} else if (throughSide == centerSide) {
       
  7268 				extent -= 360 * (extent < 0 ? -1 : 1);
       
  7269 			}
       
  7270 			var ext = Math.abs(extent),
       
  7271 				count =  ext >= 360 ? 4 : Math.ceil(ext / 90),
       
  7272 				inc = extent / count,
       
  7273 				half = inc * Math.PI / 360,
       
  7274 				z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
       
  7275 				segments = [];
       
  7276 			for (var i = 0; i <= count; i++) {
       
  7277 				var pt = i < count ? center.add(vector) : to;
       
  7278 				var out = i < count ? vector.rotate(90).multiply(z) : null;
       
  7279 				if (i == 0) {
       
  7280 					current.setHandleOut(out);
       
  7281 				} else {
       
  7282 					segments.push(
       
  7283 						new Segment(pt, vector.rotate(-90).multiply(z), out));
       
  7284 				}
       
  7285 				vector = vector.rotate(inc);
       
  7286 			}
       
  7287 			this._add(segments);
       
  7288 		},
       
  7289 
       
  7290 		lineBy: function() {
       
  7291 			var to = Point.read(arguments),
       
  7292 				current = getCurrentSegment(this)._point;
       
  7293 			this.lineTo(current.add(to));
       
  7294 		},
       
  7295 
       
  7296 		curveBy: function() {
       
  7297 			var through = Point.read(arguments),
       
  7298 				to = Point.read(arguments),
       
  7299 				parameter = Base.read(arguments),
       
  7300 				current = getCurrentSegment(this)._point;
       
  7301 			this.curveTo(current.add(through), current.add(to), parameter);
       
  7302 		},
       
  7303 
       
  7304 		cubicCurveBy: function() {
       
  7305 			var handle1 = Point.read(arguments),
       
  7306 				handle2 = Point.read(arguments),
       
  7307 				to = Point.read(arguments),
       
  7308 				current = getCurrentSegment(this)._point;
       
  7309 			this.cubicCurveTo(current.add(handle1), current.add(handle2),
       
  7310 					current.add(to));
       
  7311 		},
       
  7312 
       
  7313 		quadraticCurveBy: function() {
       
  7314 			var handle = Point.read(arguments),
       
  7315 				to = Point.read(arguments),
       
  7316 				current = getCurrentSegment(this)._point;
       
  7317 			this.quadraticCurveTo(current.add(handle), current.add(to));
       
  7318 		},
       
  7319 
       
  7320 		arcBy: function() {
       
  7321 			var current = getCurrentSegment(this)._point,
       
  7322 				point = current.add(Point.read(arguments)),
       
  7323 				clockwise = Base.pick(Base.peek(arguments), true);
       
  7324 			if (typeof clockwise === 'boolean') {
       
  7325 				this.arcTo(point, clockwise);
       
  7326 			} else {
       
  7327 				this.arcTo(point, current.add(Point.read(arguments)));
       
  7328 			}
       
  7329 		},
       
  7330 
       
  7331 		closePath: function() {
       
  7332 			var first = this.getFirstSegment(),
       
  7333 				last = this.getLastSegment();
       
  7334 			if (first._point.equals(last._point)) {
       
  7335 				first.setHandleIn(last._handleIn);
       
  7336 				last.remove();
       
  7337 			}
       
  7338 			this.setClosed(true);
       
  7339 		}
       
  7340 	};
       
  7341 }, {  
       
  7342 
       
  7343 	_getBounds: function(getter, matrix) {
       
  7344 		return Path[getter](this._segments, this._closed, this.getStyle(),
       
  7345 				matrix);
       
  7346 	},
       
  7347 
       
  7348 statics: {
       
  7349 	isClockwise: function(segments) {
       
  7350 		var sum = 0;
       
  7351 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7352 			var v = Curve.getValues(
       
  7353 					segments[i], segments[i + 1 < l ? i + 1 : 0]);
       
  7354 			for (var j = 2; j < 8; j += 2)
       
  7355 				sum += (v[j - 2] - v[j]) * (v[j + 1] + v[j - 1]);
       
  7356 		}
       
  7357 		return sum > 0;
       
  7358 	},
       
  7359 
       
  7360 	getBounds: function(segments, closed, style, matrix, strokePadding) {
       
  7361 		var first = segments[0];
       
  7362 		if (!first)
       
  7363 			return new Rectangle();
       
  7364 		var coords = new Array(6),
       
  7365 			prevCoords = first._transformCoordinates(matrix, new Array(6), false),
       
  7366 			min = prevCoords.slice(0, 2), 
       
  7367 			max = min.slice(), 
       
  7368 			roots = new Array(2);
       
  7369 
       
  7370 		function processSegment(segment) {
       
  7371 			segment._transformCoordinates(matrix, coords, false);
       
  7372 			for (var i = 0; i < 2; i++) {
       
  7373 				Curve._addBounds(
       
  7374 					prevCoords[i], 
       
  7375 					prevCoords[i + 4], 
       
  7376 					coords[i + 2], 
       
  7377 					coords[i], 
       
  7378 					i, strokePadding ? strokePadding[i] : 0, min, max, roots);
       
  7379 			}
       
  7380 			var tmp = prevCoords;
       
  7381 			prevCoords = coords;
       
  7382 			coords = tmp;
       
  7383 		}
       
  7384 
       
  7385 		for (var i = 1, l = segments.length; i < l; i++)
       
  7386 			processSegment(segments[i]);
       
  7387 		if (closed)
       
  7388 			processSegment(first);
       
  7389 		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
       
  7390 	},
       
  7391 
       
  7392 	getStrokeBounds: function(segments, closed, style, matrix) {
       
  7393 		function getPenPadding(radius, matrix) {
       
  7394 			if (!matrix)
       
  7395 				return [radius, radius];
       
  7396 			var mx = matrix.shiftless(),
       
  7397 				hor = mx.transform(new Point(radius, 0)),
       
  7398 				ver = mx.transform(new Point(0, radius)),
       
  7399 				phi = hor.getAngleInRadians(),
       
  7400 				a = hor.getLength(),
       
  7401 				b = ver.getLength();
       
  7402 			var sin = Math.sin(phi),
       
  7403 				cos = Math.cos(phi),
       
  7404 				tan = Math.tan(phi),
       
  7405 				tx = -Math.atan(b * tan / a),
       
  7406 				ty = Math.atan(b / (tan * a));
       
  7407 			return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
       
  7408 					Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
       
  7409 		}
       
  7410 
       
  7411 		if (!style.hasStroke())
       
  7412 			return Path.getBounds(segments, closed, style, matrix);
       
  7413 		var length = segments.length - (closed ? 0 : 1),
       
  7414 			radius = style.getStrokeWidth() / 2,
       
  7415 			padding = getPenPadding(radius, matrix),
       
  7416 			bounds = Path.getBounds(segments, closed, style, matrix, padding),
       
  7417 			join = style.getStrokeJoin(),
       
  7418 			cap = style.getStrokeCap(),
       
  7419 			miterLimit = radius * style.getMiterLimit();
       
  7420 		var joinBounds = new Rectangle(new Size(padding).multiply(2));
       
  7421 
       
  7422 		function add(point) {
       
  7423 			bounds = bounds.include(matrix
       
  7424 				? matrix._transformPoint(point, point) : point);
       
  7425 		}
       
  7426 
       
  7427 		function addJoin(segment, join) {
       
  7428 			if (join === 'round' || !segment._handleIn.isZero()
       
  7429 					&& !segment._handleOut.isZero()) {
       
  7430 				bounds = bounds.unite(joinBounds.setCenter(matrix
       
  7431 					? matrix._transformPoint(segment._point) : segment._point));
       
  7432 			} else {
       
  7433 				Path._addSquareJoin(segment, join, radius, miterLimit, add);
       
  7434 			}
       
  7435 		}
       
  7436 
       
  7437 		function addCap(segment, cap) {
       
  7438 			switch (cap) {
       
  7439 			case 'round':
       
  7440 				addJoin(segment, cap);
       
  7441 				break;
       
  7442 			case 'butt':
       
  7443 			case 'square':
       
  7444 				Path._addSquareCap(segment, cap, radius, add); 
       
  7445 				break;
       
  7446 			}
       
  7447 		}
       
  7448 
       
  7449 		for (var i = 1; i < length; i++)
       
  7450 			addJoin(segments[i], join);
       
  7451 		if (closed) {
       
  7452 			addJoin(segments[0], join);
       
  7453 		} else {
       
  7454 			addCap(segments[0], cap);
       
  7455 			addCap(segments[segments.length - 1], cap);
       
  7456 		}
       
  7457 		return bounds;
       
  7458 	},
       
  7459 
       
  7460 	_addSquareJoin: function(segment, join, radius, miterLimit, addPoint, area) {
       
  7461 		var curve2 = segment.getCurve(),
       
  7462 			curve1 = curve2.getPrevious(),
       
  7463 			point = curve2.getPointAt(0, true),
       
  7464 			normal1 = curve1.getNormalAt(1, true),
       
  7465 			normal2 = curve2.getNormalAt(0, true),
       
  7466 			step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
       
  7467 		normal1.setLength(step);
       
  7468 		normal2.setLength(step);
       
  7469 		if (area) {
       
  7470 			addPoint(point);
       
  7471 			addPoint(point.add(normal1));
       
  7472 		}
       
  7473 		if (join === 'miter') {
       
  7474 			var corner = new Line(
       
  7475 					point.add(normal1),
       
  7476 					new Point(-normal1.y, normal1.x), true
       
  7477 				).intersect(new Line(
       
  7478 					point.add(normal2),
       
  7479 					new Point(-normal2.y, normal2.x), true
       
  7480 				), true);
       
  7481 			if (corner && point.getDistance(corner) <= miterLimit) {
       
  7482 				addPoint(corner);
       
  7483 				if (!area)
       
  7484 					return;
       
  7485 			}
       
  7486 		}
       
  7487 		if (!area)
       
  7488 			addPoint(point.add(normal1));
       
  7489 		addPoint(point.add(normal2));
       
  7490 	},
       
  7491 
       
  7492 	_addSquareCap: function(segment, cap, radius, addPoint, area) {
       
  7493 		var point = segment._point,
       
  7494 			loc = segment.getLocation(),
       
  7495 			normal = loc.getNormal().normalize(radius);
       
  7496 		if (area) {
       
  7497 			addPoint(point.subtract(normal));
       
  7498 			addPoint(point.add(normal));
       
  7499 		}
       
  7500 		if (cap === 'square')
       
  7501 			point = point.add(normal.rotate(loc.getParameter() == 0 ? -90 : 90));
       
  7502 		addPoint(point.add(normal));
       
  7503 		addPoint(point.subtract(normal));
       
  7504 	},
       
  7505 
       
  7506 	getHandleBounds: function(segments, closed, style, matrix, strokePadding,
       
  7507 			joinPadding) {
       
  7508 		var coords = new Array(6),
       
  7509 			x1 = Infinity,
       
  7510 			x2 = -x1,
       
  7511 			y1 = x1,
       
  7512 			y2 = x2;
       
  7513 		strokePadding = strokePadding / 2 || 0;
       
  7514 		joinPadding = joinPadding / 2 || 0;
       
  7515 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7516 			var segment = segments[i];
       
  7517 			segment._transformCoordinates(matrix, coords, false);
       
  7518 			for (var j = 0; j < 6; j += 2) {
       
  7519 				var padding = j == 0 ? joinPadding : strokePadding,
       
  7520 					x = coords[j],
       
  7521 					y = coords[j + 1],
       
  7522 					xn = x - padding,
       
  7523 					xx = x + padding,
       
  7524 					yn = y - padding,
       
  7525 					yx = y + padding;
       
  7526 				if (xn < x1) x1 = xn;
       
  7527 				if (xx > x2) x2 = xx;
       
  7528 				if (yn < y1) y1 = yn;
       
  7529 				if (yx > y2) y2 = yx;
       
  7530 			}
       
  7531 		}
       
  7532 		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
       
  7533 	},
       
  7534 
       
  7535 	getRoughBounds: function(segments, closed, style, matrix) {
       
  7536 		var strokeWidth = style.getStrokeColor() ? style.getStrokeWidth() : 0,
       
  7537 			joinWidth = strokeWidth;
       
  7538 		if (strokeWidth === 0) {
       
  7539 			strokeWidth = 0.00001;
       
  7540 		} else {
       
  7541 			if (style.getStrokeJoin() === 'miter')
       
  7542 				joinWidth = strokeWidth * style.getMiterLimit();
       
  7543 			if (style.getStrokeCap() === 'square')
       
  7544 				joinWidth = Math.max(joinWidth, strokeWidth * Math.sqrt(2));
       
  7545 		}
       
  7546 		return Path.getHandleBounds(segments, closed, style, matrix,
       
  7547 				strokeWidth, joinWidth);
       
  7548 	}
       
  7549 }});
       
  7550 
       
  7551 Path.inject({ statics: new function() {
       
  7552 
       
  7553 	var kappa = Numerical.KAPPA,
       
  7554 		ellipseSegments = [
       
  7555 			new Segment([-1, 0], [0, kappa ], [0, -kappa]),
       
  7556 			new Segment([0, -1], [-kappa, 0], [kappa, 0 ]),
       
  7557 			new Segment([1, 0], [0, -kappa], [0, kappa ]),
       
  7558 			new Segment([0, 1], [kappa, 0 ], [-kappa, 0])
       
  7559 		];
       
  7560 
       
  7561 	function createEllipse(center, radius, args) {
       
  7562 		var path = new Path(),
       
  7563 			segments = new Array(4);
       
  7564 		for (var i = 0; i < 4; i++) {
       
  7565 			var segment = ellipseSegments[i];
       
  7566 			segments[i] = new Segment(
       
  7567 				segment._point.multiply(radius).add(center),
       
  7568 				segment._handleIn.multiply(radius),
       
  7569 				segment._handleOut.multiply(radius)
       
  7570 			);
       
  7571 		}
       
  7572 		path._add(segments);
       
  7573 		path._closed = true;
       
  7574 		return path.set(Base.getNamed(args));
       
  7575 	}
       
  7576 
       
  7577 	return {
       
  7578 		Line: function() {
       
  7579 			return new Path(
       
  7580 				Point.readNamed(arguments, 'from'),
       
  7581 				Point.readNamed(arguments, 'to')
       
  7582 			).set(Base.getNamed(arguments));
       
  7583 		},
       
  7584 
       
  7585 		Circle: function() {
       
  7586 			var center = Point.readNamed(arguments, 'center'),
       
  7587 				radius = Base.readNamed(arguments, 'radius');
       
  7588 			return createEllipse(center, new Size(radius), arguments);
       
  7589 		},
       
  7590 
       
  7591 		Rectangle: function() {
       
  7592 			var rect = Rectangle.readNamed(arguments, 'rectangle'),
       
  7593 				radius = Size.readNamed(arguments, 'radius', 0, 0,
       
  7594 						{ readNull: true }),
       
  7595 				bl = rect.getBottomLeft(true),
       
  7596 				tl = rect.getTopLeft(true),
       
  7597 				tr = rect.getTopRight(true),
       
  7598 				br = rect.getBottomRight(true);
       
  7599 				path = new Path();
       
  7600 			if (!radius || radius.isZero()) {
       
  7601 				path._add([
       
  7602 					new Segment(bl),
       
  7603 					new Segment(tl),
       
  7604 					new Segment(tr),
       
  7605 					new Segment(br)
       
  7606 				]);
       
  7607 			} else {
       
  7608 				radius = Size.min(radius, rect.getSize(true).divide(2));
       
  7609 				var rx = radius.width,
       
  7610 					ry = radius.height,
       
  7611 					hx = rx * kappa,
       
  7612 					hy = ry * kappa;
       
  7613 				path._add([
       
  7614 					new Segment(bl.add(rx, 0), null, [-hx, 0]),
       
  7615 					new Segment(bl.subtract(0, ry), [0, hy]),
       
  7616 					new Segment(tl.add(0, ry), null, [0, -hy]),
       
  7617 					new Segment(tl.add(rx, 0), [-hx, 0], null),
       
  7618 					new Segment(tr.subtract(rx, 0), null, [hx, 0]),
       
  7619 					new Segment(tr.add(0, ry), [0, -hy], null),
       
  7620 					new Segment(br.subtract(0, ry), null, [0, hy]),
       
  7621 					new Segment(br.subtract(rx, 0), [hx, 0])
       
  7622 				]);
       
  7623 			}
       
  7624 			path._closed = true;
       
  7625 			return path.set(Base.getNamed(arguments));
       
  7626 		},
       
  7627 
       
  7628 		RoundRectangle: '#Rectangle',
       
  7629 
       
  7630 		Ellipse: function() {
       
  7631 			var ellipse = Shape._readEllipse(arguments);
       
  7632 			return createEllipse(ellipse.center, ellipse.radius, arguments);
       
  7633 		},
       
  7634 
       
  7635 		Oval: '#Ellipse',
       
  7636 
       
  7637 		Arc: function() {
       
  7638 			var from = Point.readNamed(arguments, 'from'),
       
  7639 				through = Point.readNamed(arguments, 'through'),
       
  7640 				to = Point.readNamed(arguments, 'to'),
       
  7641 				path = new Path();
       
  7642 			path.moveTo(from);
       
  7643 			path.arcTo(through, to);
       
  7644 			return path.set(Base.getNamed(arguments));
       
  7645 		},
       
  7646 
       
  7647 		RegularPolygon: function() {
       
  7648 			var center = Point.readNamed(arguments, 'center'),
       
  7649 				sides = Base.readNamed(arguments, 'sides'),
       
  7650 				radius = Base.readNamed(arguments, 'radius'),
       
  7651 				path = new Path(),
       
  7652 				step = 360 / sides,
       
  7653 				three = !(sides % 3),
       
  7654 				vector = new Point(0, three ? -radius : radius),
       
  7655 				offset = three ? -1 : 0.5,
       
  7656 				segments = new Array(sides);
       
  7657 			for (var i = 0; i < sides; i++) {
       
  7658 				segments[i] = new Segment(center.add(
       
  7659 					vector.rotate((i + offset) * step)));
       
  7660 			}
       
  7661 			path._add(segments);
       
  7662 			path._closed = true;
       
  7663 			return path.set(Base.getNamed(arguments));
       
  7664 		},
       
  7665 
       
  7666 		Star: function() {
       
  7667 			var center = Point.readNamed(arguments, 'center'),
       
  7668 				points = Base.readNamed(arguments, 'points') * 2,
       
  7669 				radius1 = Base.readNamed(arguments, 'radius1'),
       
  7670 				radius2 = Base.readNamed(arguments, 'radius2'),
       
  7671 				path = new Path(),
       
  7672 				step = 360 / points,
       
  7673 				vector = new Point(0, -1),
       
  7674 				segments = new Array(points);
       
  7675 			for (var i = 0; i < points; i++) {
       
  7676 				segments[i] = new Segment(center.add(
       
  7677 					vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1)));
       
  7678 			}
       
  7679 			path._add(segments);
       
  7680 			path._closed = true;
       
  7681 			return path.set(Base.getNamed(arguments));
       
  7682 		}
       
  7683 	};
       
  7684 }});
       
  7685 
       
  7686 var CompoundPath = PathItem.extend({
       
  7687 	_class: 'CompoundPath',
       
  7688 	_serializeFields: {
       
  7689 		children: []
       
  7690 	},
       
  7691 
       
  7692 	initialize: function CompoundPath(arg) {
       
  7693 		this._children = [];
       
  7694 		this._namedChildren = {};
       
  7695 		if (!this._initialize(arg))
       
  7696 			this.addChildren(Array.isArray(arg) ? arg : arguments);
       
  7697 	},
       
  7698 
       
  7699 	insertChildren: function insertChildren(index, items, _preserve) {
       
  7700 		items = insertChildren.base.call(this, index, items, _preserve, 'path');
       
  7701 		for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
       
  7702 			var item = items[i];
       
  7703 			if (item._clockwise === undefined)
       
  7704 				item.setClockwise(item._index === 0);
       
  7705 		}
       
  7706 		return items;
       
  7707 	},
       
  7708 
       
  7709 	reverse: function() {
       
  7710 		var children = this._children;
       
  7711 		for (var i = 0, l = children.length; i < l; i++)
       
  7712 			children[i].reverse();
       
  7713 	},
       
  7714 
       
  7715 	smooth: function() {
       
  7716 		for (var i = 0, l = this._children.length; i < l; i++)
       
  7717 			this._children[i].smooth();
       
  7718 	},
       
  7719 
       
  7720 	isClockwise: function() {
       
  7721 		var child = this.getFirstChild();
       
  7722 		return child && child.isClockwise();
       
  7723 	},
       
  7724 
       
  7725 	setClockwise: function(clockwise) {
       
  7726 		if (this.isClockwise() != !!clockwise)
       
  7727 			this.reverse();
       
  7728 	},
       
  7729 
       
  7730 	getFirstSegment: function() {
       
  7731 		var first = this.getFirstChild();
       
  7732 		return first && first.getFirstSegment();
       
  7733 	},
       
  7734 
       
  7735 	getLastSegment: function() {
       
  7736 		var last = this.getLastChild();
       
  7737 		return last && last.getLastSegment();
       
  7738 	},
       
  7739 
       
  7740 	getCurves: function() {
       
  7741 		var children = this._children,
       
  7742 			curves = [];
       
  7743 		for (var i = 0, l = children.length; i < l; i++)
       
  7744 			curves = curves.concat(children[i].getCurves());
       
  7745 		return curves;
       
  7746 	},
       
  7747 
       
  7748 	getFirstCurve: function() {
       
  7749 		var first = this.getFirstChild();
       
  7750 		return first && first.getFirstCurve();
       
  7751 	},
       
  7752 
       
  7753 	getLastCurve: function() {
       
  7754 		var last = this.getLastChild();
       
  7755 		return last && last.getFirstCurve();
       
  7756 	},
       
  7757 
       
  7758 	getArea: function() {
       
  7759 		var children = this._children,
       
  7760 			area = 0;
       
  7761 		for (var i = 0, l = children.length; i < l; i++)
       
  7762 			area += children[i].getArea();
       
  7763 		return area;
       
  7764 	},
       
  7765 
       
  7766 	getPathData: function() {
       
  7767 		var children = this._children,
       
  7768 			paths = [];
       
  7769 		for (var i = 0, l = children.length; i < l; i++)
       
  7770 			paths.push(children[i].getPathData(arguments[0]));
       
  7771 		return paths.join(' ');
       
  7772 	},
       
  7773 
       
  7774 	_getWinding: function(point) {
       
  7775 		var children =  this._children,
       
  7776 			winding = 0;
       
  7777 		for (var i = 0, l = children.length; i < l; i++)
       
  7778 			winding += children[i]._getWinding(point);
       
  7779 		return winding;
       
  7780 	},
       
  7781 
       
  7782 	_hitTest : function _hitTest(point, options) {
       
  7783 		var res = _hitTest.base.call(this, point,
       
  7784 				new Base(options, { fill: false }));
       
  7785 		if (!res) {
       
  7786 			if (options.compoundChildren) {
       
  7787 				var children =  this._children;
       
  7788 				for (var i = children.length - 1; i >= 0 && !res; i--)
       
  7789 					res = children[i]._hitTest(point, options);
       
  7790 			} else if (options.fill && this.hasFill()
       
  7791 					&& this._contains(point)) {
       
  7792 				res = new HitResult('fill', this);
       
  7793 			}
       
  7794 		} 
       
  7795 		return res;
       
  7796 	},
       
  7797 
       
  7798 	_draw: function(ctx, param) {
       
  7799 		var children = this._children;
       
  7800 		if (children.length === 0)
       
  7801 			return;
       
  7802 
       
  7803 		ctx.beginPath();
       
  7804 		param = param.extend({ compound: true });
       
  7805 		for (var i = 0, l = children.length; i < l; i++)
       
  7806 			children[i].draw(ctx, param);
       
  7807 
       
  7808 		if (!param.clip) {
       
  7809 			this._setStyles(ctx);
       
  7810 			var style = this._style;
       
  7811 			if (style.hasFill()) {
       
  7812 				ctx.fill(style.getWindingRule());
       
  7813 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  7814 			}
       
  7815 			if (style.hasStroke())
       
  7816 				ctx.stroke();
       
  7817 		}
       
  7818 	}
       
  7819 }, new function() { 
       
  7820 	function getCurrentPath(that) {
       
  7821 		if (!that._children.length)
       
  7822 			throw new Error('Use a moveTo() command first');
       
  7823 		return that._children[that._children.length - 1];
       
  7824 	}
       
  7825 
       
  7826 	var fields = {
       
  7827 		moveTo: function() {
       
  7828 			var path = new Path();
       
  7829 			this.addChild(path);
       
  7830 			path.moveTo.apply(path, arguments);
       
  7831 		},
       
  7832 
       
  7833 		moveBy: function() {
       
  7834 			this.moveTo(getCurrentPath(this).getLastSegment()._point.add(
       
  7835 					Point.read(arguments)));
       
  7836 		},
       
  7837 
       
  7838 		closePath: function() {
       
  7839 			getCurrentPath(this).closePath();
       
  7840 		}
       
  7841 	};
       
  7842 
       
  7843 	Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
       
  7844 			'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
       
  7845 			function(key) {
       
  7846 				fields[key] = function() {
       
  7847 					var path = getCurrentPath(this);
       
  7848 					path[key].apply(path, arguments);
       
  7849 				};
       
  7850 			}
       
  7851 	);
       
  7852 
       
  7853 	return fields;
       
  7854 });
       
  7855 
       
  7856 var PathFlattener = Base.extend({
       
  7857 	initialize: function(path) {
       
  7858 		this.curves = []; 
       
  7859 		this.parts = []; 
       
  7860 		this.length = 0; 
       
  7861 		this.index = 0;
       
  7862 
       
  7863 		var segments = path._segments,
       
  7864 			segment1 = segments[0],
       
  7865 			segment2,
       
  7866 			that = this;
       
  7867 
       
  7868 		function addCurve(segment1, segment2) {
       
  7869 			var curve = Curve.getValues(segment1, segment2);
       
  7870 			that.curves.push(curve);
       
  7871 			that._computeParts(curve, segment1._index, 0, 1);
       
  7872 		}
       
  7873 
       
  7874 		for (var i = 1, l = segments.length; i < l; i++) {
       
  7875 			segment2 = segments[i];
       
  7876 			addCurve(segment1, segment2);
       
  7877 			segment1 = segment2;
       
  7878 		}
       
  7879 		if (path._closed)
       
  7880 			addCurve(segment2, segments[0]);
       
  7881 	},
       
  7882 
       
  7883 	_computeParts: function(curve, index, minT, maxT) {
       
  7884 		if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough(curve, 0.25)) {
       
  7885 			var curves = Curve.subdivide(curve);
       
  7886 			var halfT = (minT + maxT) / 2;
       
  7887 			this._computeParts(curves[0], index, minT, halfT);
       
  7888 			this._computeParts(curves[1], index, halfT, maxT);
       
  7889 		} else {
       
  7890 			var x = curve[6] - curve[0],
       
  7891 				y = curve[7] - curve[1],
       
  7892 				dist = Math.sqrt(x * x + y * y);
       
  7893 			if (dist > 0.00001) {
       
  7894 				this.length += dist;
       
  7895 				this.parts.push({
       
  7896 					offset: this.length,
       
  7897 					value: maxT,
       
  7898 					index: index
       
  7899 				});
       
  7900 			}
       
  7901 		}
       
  7902 	},
       
  7903 
       
  7904 	getParameterAt: function(offset) {
       
  7905 		var i, j = this.index;
       
  7906 		for (;;) {
       
  7907 			i = j;
       
  7908 			if (j == 0 || this.parts[--j].offset < offset)
       
  7909 				break;
       
  7910 		}
       
  7911 		for (var l = this.parts.length; i < l; i++) {
       
  7912 			var part = this.parts[i];
       
  7913 			if (part.offset >= offset) {
       
  7914 				this.index = i;
       
  7915 				var prev = this.parts[i - 1];
       
  7916 				var prevVal = prev && prev.index == part.index ? prev.value : 0,
       
  7917 					prevLen = prev ? prev.offset : 0;
       
  7918 				return {
       
  7919 					value: prevVal + (part.value - prevVal)
       
  7920 						* (offset - prevLen) /  (part.offset - prevLen),
       
  7921 					index: part.index
       
  7922 				};
       
  7923 			}
       
  7924 		}
       
  7925 		var part = this.parts[this.parts.length - 1];
       
  7926 		return {
       
  7927 			value: 1,
       
  7928 			index: part.index
       
  7929 		};
       
  7930 	},
       
  7931 
       
  7932 	evaluate: function(offset, type) {
       
  7933 		var param = this.getParameterAt(offset);
       
  7934 		return Curve.evaluate(this.curves[param.index], param.value, type);
       
  7935 	},
       
  7936 
       
  7937 	drawPart: function(ctx, from, to) {
       
  7938 		from = this.getParameterAt(from);
       
  7939 		to = this.getParameterAt(to);
       
  7940 		for (var i = from.index; i <= to.index; i++) {
       
  7941 			var curve = Curve.getPart(this.curves[i],
       
  7942 					i == from.index ? from.value : 0,
       
  7943 					i == to.index ? to.value : 1);
       
  7944 			if (i == from.index)
       
  7945 				ctx.moveTo(curve[0], curve[1]);
       
  7946 			ctx.bezierCurveTo.apply(ctx, curve.slice(2));
       
  7947 		}
       
  7948 	}
       
  7949 });
       
  7950 
       
  7951 var PathFitter = Base.extend({
       
  7952 	initialize: function(path, error) {
       
  7953 		this.points = [];
       
  7954 		var segments = path._segments,
       
  7955 			prev;
       
  7956 		for (var i = 0, l = segments.length; i < l; i++) {
       
  7957 			var point = segments[i].point.clone();
       
  7958 			if (!prev || !prev.equals(point)) {
       
  7959 				this.points.push(point);
       
  7960 				prev = point;
       
  7961 			}
       
  7962 		}
       
  7963 		this.error = error;
       
  7964 	},
       
  7965 
       
  7966 	fit: function() {
       
  7967 		var points = this.points,
       
  7968 			length = points.length;
       
  7969 		this.segments = length > 0 ? [new Segment(points[0])] : [];
       
  7970 		if (length > 1)
       
  7971 			this.fitCubic(0, length - 1,
       
  7972 				points[1].subtract(points[0]).normalize(),
       
  7973 				points[length - 2].subtract(points[length - 1]).normalize());
       
  7974 		return this.segments;
       
  7975 	},
       
  7976 
       
  7977 	fitCubic: function(first, last, tan1, tan2) {
       
  7978 		if (last - first == 1) {
       
  7979 			var pt1 = this.points[first],
       
  7980 				pt2 = this.points[last],
       
  7981 				dist = pt1.getDistance(pt2) / 3;
       
  7982 			this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
       
  7983 					pt2.add(tan2.normalize(dist)), pt2]);
       
  7984 			return;
       
  7985 		}
       
  7986 		var uPrime = this.chordLengthParameterize(first, last),
       
  7987 			maxError = Math.max(this.error, this.error * this.error),
       
  7988 			split;
       
  7989 		for (var i = 0; i <= 4; i++) {
       
  7990 			var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
       
  7991 			var max = this.findMaxError(first, last, curve, uPrime);
       
  7992 			if (max.error < this.error) {
       
  7993 				this.addCurve(curve);
       
  7994 				return;
       
  7995 			}
       
  7996 			split = max.index;
       
  7997 			if (max.error >= maxError)
       
  7998 				break;
       
  7999 			this.reparameterize(first, last, uPrime, curve);
       
  8000 			maxError = max.error;
       
  8001 		}
       
  8002 		var V1 = this.points[split - 1].subtract(this.points[split]),
       
  8003 			V2 = this.points[split].subtract(this.points[split + 1]),
       
  8004 			tanCenter = V1.add(V2).divide(2).normalize();
       
  8005 		this.fitCubic(first, split, tan1, tanCenter);
       
  8006 		this.fitCubic(split, last, tanCenter.negate(), tan2);
       
  8007 	},
       
  8008 
       
  8009 	addCurve: function(curve) {
       
  8010 		var prev = this.segments[this.segments.length - 1];
       
  8011 		prev.setHandleOut(curve[1].subtract(curve[0]));
       
  8012 		this.segments.push(
       
  8013 				new Segment(curve[3], curve[2].subtract(curve[3])));
       
  8014 	},
       
  8015 
       
  8016 	generateBezier: function(first, last, uPrime, tan1, tan2) {
       
  8017 		var epsilon = 1e-11,
       
  8018 			pt1 = this.points[first],
       
  8019 			pt2 = this.points[last],
       
  8020 			C = [[0, 0], [0, 0]],
       
  8021 			X = [0, 0];
       
  8022 
       
  8023 		for (var i = 0, l = last - first + 1; i < l; i++) {
       
  8024 			var u = uPrime[i],
       
  8025 				t = 1 - u,
       
  8026 				b = 3 * u * t,
       
  8027 				b0 = t * t * t,
       
  8028 				b1 = b * t,
       
  8029 				b2 = b * u,
       
  8030 				b3 = u * u * u,
       
  8031 				a1 = tan1.normalize(b1),
       
  8032 				a2 = tan2.normalize(b2),
       
  8033 				tmp = this.points[first + i]
       
  8034 					.subtract(pt1.multiply(b0 + b1))
       
  8035 					.subtract(pt2.multiply(b2 + b3));
       
  8036 			C[0][0] += a1.dot(a1);
       
  8037 			C[0][1] += a1.dot(a2);
       
  8038 			C[1][0] = C[0][1];
       
  8039 			C[1][1] += a2.dot(a2);
       
  8040 			X[0] += a1.dot(tmp);
       
  8041 			X[1] += a2.dot(tmp);
       
  8042 		}
       
  8043 
       
  8044 		var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
       
  8045 			alpha1, alpha2;
       
  8046 		if (Math.abs(detC0C1) > epsilon) {
       
  8047 			var detC0X  = C[0][0] * X[1]    - C[1][0] * X[0],
       
  8048 				detXC1  = X[0]    * C[1][1] - X[1]    * C[0][1];
       
  8049 			alpha1 = detXC1 / detC0C1;
       
  8050 			alpha2 = detC0X / detC0C1;
       
  8051 		} else {
       
  8052 			var c0 = C[0][0] + C[0][1],
       
  8053 				c1 = C[1][0] + C[1][1];
       
  8054 			if (Math.abs(c0) > epsilon) {
       
  8055 				alpha1 = alpha2 = X[0] / c0;
       
  8056 			} else if (Math.abs(c1) > epsilon) {
       
  8057 				alpha1 = alpha2 = X[1] / c1;
       
  8058 			} else {
       
  8059 				alpha1 = alpha2 = 0;
       
  8060 			}
       
  8061 		}
       
  8062 
       
  8063 		var segLength = pt2.getDistance(pt1);
       
  8064 		epsilon *= segLength;
       
  8065 		if (alpha1 < epsilon || alpha2 < epsilon) {
       
  8066 			alpha1 = alpha2 = segLength / 3;
       
  8067 		}
       
  8068 
       
  8069 		return [pt1, pt1.add(tan1.normalize(alpha1)),
       
  8070 				pt2.add(tan2.normalize(alpha2)), pt2];
       
  8071 	},
       
  8072 
       
  8073 	reparameterize: function(first, last, u, curve) {
       
  8074 		for (var i = first; i <= last; i++) {
       
  8075 			u[i - first] = this.findRoot(curve, this.points[i], u[i - first]);
       
  8076 		}
       
  8077 	},
       
  8078 
       
  8079 	findRoot: function(curve, point, u) {
       
  8080 		var curve1 = [],
       
  8081 			curve2 = [];
       
  8082 		for (var i = 0; i <= 2; i++) {
       
  8083 			curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3);
       
  8084 		}
       
  8085 		for (var i = 0; i <= 1; i++) {
       
  8086 			curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2);
       
  8087 		}
       
  8088 		var pt = this.evaluate(3, curve, u),
       
  8089 			pt1 = this.evaluate(2, curve1, u),
       
  8090 			pt2 = this.evaluate(1, curve2, u),
       
  8091 			diff = pt.subtract(point),
       
  8092 			df = pt1.dot(pt1) + diff.dot(pt2);
       
  8093 		if (Math.abs(df) < 0.00001)
       
  8094 			return u;
       
  8095 		return u - diff.dot(pt1) / df;
       
  8096 	},
       
  8097 
       
  8098 	evaluate: function(degree, curve, t) {
       
  8099 		var tmp = curve.slice();
       
  8100 		for (var i = 1; i <= degree; i++) {
       
  8101 			for (var j = 0; j <= degree - i; j++) {
       
  8102 				tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t));
       
  8103 			}
       
  8104 		}
       
  8105 		return tmp[0];
       
  8106 	},
       
  8107 
       
  8108 	chordLengthParameterize: function(first, last) {
       
  8109 		var u = [0];
       
  8110 		for (var i = first + 1; i <= last; i++) {
       
  8111 			u[i - first] = u[i - first - 1]
       
  8112 					+ this.points[i].getDistance(this.points[i - 1]);
       
  8113 		}
       
  8114 		for (var i = 1, m = last - first; i <= m; i++) {
       
  8115 			u[i] /= u[m];
       
  8116 		}
       
  8117 		return u;
       
  8118 	},
       
  8119 
       
  8120 	findMaxError: function(first, last, curve, u) {
       
  8121 		var index = Math.floor((last - first + 1) / 2),
       
  8122 			maxDist = 0;
       
  8123 		for (var i = first + 1; i < last; i++) {
       
  8124 			var P = this.evaluate(3, curve, u[i - first]);
       
  8125 			var v = P.subtract(this.points[i]);
       
  8126 			var dist = v.x * v.x + v.y * v.y; 
       
  8127 			if (dist >= maxDist) {
       
  8128 				maxDist = dist;
       
  8129 				index = i;
       
  8130 			}
       
  8131 		}
       
  8132 		return {
       
  8133 			error: maxDist,
       
  8134 			index: index
       
  8135 		};
       
  8136 	}
       
  8137 });
       
  8138 
       
  8139 PathItem.inject(new function() {
       
  8140 
       
  8141 	function splitPath(intersections, collectOthers) {
       
  8142 		intersections.sort(function(loc1, loc2) {
       
  8143 			var path1 = loc1.getPath(),
       
  8144 				path2 = loc2.getPath();
       
  8145 			return path1 === path2
       
  8146 					? (loc1.getIndex() + loc1.getParameter())
       
  8147 						- (loc2.getIndex() + loc2.getParameter())
       
  8148 					: path1._id - path2._id;
       
  8149 		});
       
  8150 		var others = collectOthers && [];
       
  8151 		for (var i = intersections.length - 1; i >= 0; i--) {
       
  8152 			var loc = intersections[i],
       
  8153 				other = loc.getIntersection(),
       
  8154 				curve = loc.divide(),
       
  8155 				segment = curve && curve.getSegment1() || loc.getSegment();
       
  8156 			if (others)
       
  8157 				others.push(other);
       
  8158 			segment._intersection = other;
       
  8159 			loc._segment = segment;
       
  8160 		}
       
  8161 		return others;
       
  8162 	}
       
  8163 
       
  8164 	function reorientPath(path) {
       
  8165 		if (path instanceof CompoundPath) {
       
  8166 			var children = path.removeChildren(),
       
  8167 				length = children.length,
       
  8168 				bounds = new Array(length),
       
  8169 				counters = new Array(length),
       
  8170 				clockwise;
       
  8171 			children.sort(function(a, b){
       
  8172 				var b1 = a.getBounds(), b2 = b.getBounds();
       
  8173 				return b1._width * b1._height < b2._width * b2._height;
       
  8174 			});
       
  8175 			path.addChildren(children);
       
  8176 			clockwise = children[0].isClockwise();
       
  8177 			for (var i = 0; i < length; i++) {
       
  8178 				bounds[i] = children[i].getBounds();
       
  8179 				counters[i] = 0;
       
  8180 			}
       
  8181 			for (var i = 0; i < length; i++) {
       
  8182 				for (var j = 1; j < length; j++) {
       
  8183 					if (i !== j && bounds[i].contains(bounds[j]))
       
  8184 						counters[j]++;
       
  8185 				}
       
  8186 				if (i > 0 && counters[i] % 2 === 0)
       
  8187 					children[i].setClockwise(clockwise);
       
  8188 			}
       
  8189 		}
       
  8190 		return path;
       
  8191 	}
       
  8192 
       
  8193 	function computeBoolean(path1, path2, operator, subtract) {
       
  8194 		path1 = reorientPath(path1.clone(false));
       
  8195 		path2 = reorientPath(path2.clone(false));
       
  8196 		var path1Clockwise = path1.isClockwise(),
       
  8197 			path2Clockwise = path2.isClockwise(),
       
  8198 			intersections = path1.getIntersections(path2);
       
  8199 		splitPath(splitPath(intersections, true));
       
  8200 		if (!path1Clockwise)
       
  8201 			path1.reverse();
       
  8202 		if (!(subtract ^ path2Clockwise))
       
  8203 			path2.reverse();
       
  8204 		path1Clockwise = true;
       
  8205 		path2Clockwise = !subtract;
       
  8206 		var paths = []
       
  8207 				.concat(path1._children || [path1])
       
  8208 				.concat(path2._children || [path2]),
       
  8209 			segments = [],
       
  8210 			result = new CompoundPath();
       
  8211 		for (var i = 0, l = paths.length; i < l; i++) {
       
  8212 			var path = paths[i],
       
  8213 				parent = path._parent,
       
  8214 				clockwise = path.isClockwise(),
       
  8215 				segs = path._segments;
       
  8216 			path = parent instanceof CompoundPath ? parent : path;
       
  8217 			for (var j = segs.length - 1; j >= 0; j--) {
       
  8218 				var segment = segs[j],
       
  8219 					midPoint = segment.getCurve().getPoint(0.5),
       
  8220 					insidePath1 = path !== path1 && path1.contains(midPoint)
       
  8221 							&& (clockwise === path1Clockwise || subtract
       
  8222 									|| !testOnCurve(path1, midPoint)),
       
  8223 					insidePath2 = path !== path2 && path2.contains(midPoint)
       
  8224 							&& (clockwise === path2Clockwise
       
  8225 									|| !testOnCurve(path2, midPoint));
       
  8226 				if (operator(path === path1, insidePath1, insidePath2)) {
       
  8227 					segment._invalid = true;
       
  8228 				} else {
       
  8229 					segments.push(segment);
       
  8230 				}
       
  8231 			}
       
  8232 		}
       
  8233 		for (var i = 0, l = segments.length; i < l; i++) {
       
  8234 			var segment = segments[i];
       
  8235 			if (segment._visited)
       
  8236 				continue;
       
  8237 			var path = new Path(),
       
  8238 				loc = segment._intersection,
       
  8239 				intersection = loc && loc.getSegment(true);
       
  8240 			if (segment.getPrevious()._invalid)
       
  8241 				segment.setHandleIn(intersection
       
  8242 						? intersection._handleIn
       
  8243 						: new Point(0, 0));
       
  8244 			do {
       
  8245 				segment._visited = true;
       
  8246 				if (segment._invalid && segment._intersection) {
       
  8247 					var inter = segment._intersection.getSegment(true);
       
  8248 					path.add(new Segment(segment._point, segment._handleIn,
       
  8249 							inter._handleOut));
       
  8250 					inter._visited = true;
       
  8251 					segment = inter;
       
  8252 				} else {
       
  8253 					path.add(segment.clone());
       
  8254 				}
       
  8255 				segment = segment.getNext();
       
  8256 			} while (segment && !segment._visited && segment !== intersection);
       
  8257 			var amount = path._segments.length;
       
  8258 			if (amount > 1 && (amount > 2 || !path.isPolygon())) {
       
  8259 				path.setClosed(true);
       
  8260 				result.addChild(path, true);
       
  8261 			} else {
       
  8262 				path.remove();
       
  8263 			}
       
  8264 		}
       
  8265 		path1.remove();
       
  8266 		path2.remove();
       
  8267 		return result.reduce();
       
  8268 	}
       
  8269 
       
  8270 	function testOnCurve(path, point) {
       
  8271 		var curves = path.getCurves(),
       
  8272 			bounds = path.getBounds();
       
  8273 		if (bounds.contains(point)) {
       
  8274 			for (var i = 0, l = curves.length; i < l; i++) {
       
  8275 				var curve = curves[i];
       
  8276 				if (curve.getBounds().contains(point)
       
  8277 						&& curve.getParameterOf(point))
       
  8278 					return true;
       
  8279 			}
       
  8280 		}
       
  8281 		return false;
       
  8282 	}
       
  8283 
       
  8284 	return {
       
  8285 		unite: function(path) {
       
  8286 			return computeBoolean(this, path,
       
  8287 					function(isPath1, isInPath1, isInPath2) {
       
  8288 						return isInPath1 || isInPath2;
       
  8289 					});
       
  8290 		},
       
  8291 
       
  8292 		intersect: function(path) {
       
  8293 			return computeBoolean(this, path,
       
  8294 					function(isPath1, isInPath1, isInPath2) {
       
  8295 						return !(isInPath1 || isInPath2);
       
  8296 					});
       
  8297 		},
       
  8298 
       
  8299 		subtract: function(path) {
       
  8300 			return computeBoolean(this, path,
       
  8301 					function(isPath1, isInPath1, isInPath2) {
       
  8302 						return isPath1 && isInPath2 || !isPath1 && !isInPath1;
       
  8303 					}, true);
       
  8304 		},
       
  8305 
       
  8306 		exclude: function(path) {
       
  8307 			return new Group([this.subtract(path), path.subtract(this)]);
       
  8308 		},
       
  8309 
       
  8310 		divide: function(path) {
       
  8311 			return new Group([this.subtract(path), this.intersect(path)]);
       
  8312 		}
       
  8313 	};
       
  8314 });
       
  8315 
       
  8316 var TextItem = Item.extend({
       
  8317 	_class: 'TextItem',
       
  8318 	_boundsSelected: true,
       
  8319 	_serializeFields: {
       
  8320 		content: null
       
  8321 	},
       
  8322 	_boundsGetter: 'getBounds',
       
  8323 
       
  8324 	initialize: function TextItem(arg) {
       
  8325 		this._content = '';
       
  8326 		this._lines = [];
       
  8327 		var hasProps = arg && Base.isPlainObject(arg)
       
  8328 				&& arg.x === undefined && arg.y === undefined;
       
  8329 		this._initialize(hasProps && arg, !hasProps && Point.read(arguments));
       
  8330 	},
       
  8331 
       
  8332 	_equals: function(item) {
       
  8333 		return this._content === item._content;
       
  8334 	},
       
  8335 
       
  8336 	_clone: function _clone(copy) {
       
  8337 		copy.setContent(this._content);
       
  8338 		return _clone.base.call(this, copy);
       
  8339 	},
       
  8340 
       
  8341 	getContent: function() {
       
  8342 		return this._content;
       
  8343 	},
       
  8344 
       
  8345 	setContent: function(content) {
       
  8346 		this._content = '' + content;
       
  8347 		this._lines = this._content.split(/\r\n|\n|\r/mg);
       
  8348 		this._changed(69);
       
  8349 	},
       
  8350 
       
  8351 	isEmpty: function() {
       
  8352 		return !this._content;
       
  8353 	},
       
  8354 
       
  8355 	getCharacterStyle: '#getStyle',
       
  8356 	setCharacterStyle: '#setStyle',
       
  8357 
       
  8358 	getParagraphStyle: '#getStyle',
       
  8359 	setParagraphStyle: '#setStyle'
       
  8360 });
       
  8361 
       
  8362 var PointText = TextItem.extend({
       
  8363 	_class: 'PointText',
       
  8364 
       
  8365 	initialize: function PointText() {
       
  8366 		TextItem.apply(this, arguments);
       
  8367 	},
       
  8368 
       
  8369 	clone: function(insert) {
       
  8370 		return this._clone(new PointText({ insert: false }), insert);
       
  8371 	},
       
  8372 
       
  8373 	getPoint: function() {
       
  8374 		var point = this._matrix.getTranslation();
       
  8375 		return new LinkedPoint(point.x, point.y, this, 'setPoint');
       
  8376 	},
       
  8377 
       
  8378 	setPoint: function(point) {
       
  8379 		point = Point.read(arguments);
       
  8380 		this.translate(point.subtract(this._matrix.getTranslation()));
       
  8381 	},
       
  8382 
       
  8383 	_draw: function(ctx) {
       
  8384 		if (!this._content)
       
  8385 			return;
       
  8386 		this._setStyles(ctx);
       
  8387 		var style = this._style,
       
  8388 			lines = this._lines,
       
  8389 			leading = style.getLeading(),
       
  8390 			shadowColor = ctx.shadowColor;
       
  8391 
       
  8392 		ctx.font = style.getFontStyle();
       
  8393 		ctx.textAlign = style.getJustification();
       
  8394 		for (var i = 0, l = lines.length; i < l; i++) {
       
  8395 			ctx.shadowColor = shadowColor;
       
  8396 			var line = lines[i];
       
  8397 			if (style.hasFill()) {
       
  8398 				ctx.fillText(line, 0, 0);
       
  8399 				ctx.shadowColor = 'rgba(0,0,0,0)';
       
  8400 			}
       
  8401 			if (style.hasStroke())
       
  8402 				ctx.strokeText(line, 0, 0);
       
  8403 			ctx.translate(0, leading);
       
  8404 		}
       
  8405 	}
       
  8406 }, new function() {
       
  8407 	var measureCtx = null;
       
  8408 
       
  8409 	return {
       
  8410 		_getBounds: function(getter, matrix) {
       
  8411 			if (!measureCtx)
       
  8412 				measureCtx = CanvasProvider.getContext(1, 1);
       
  8413 			var style = this._style,
       
  8414 				lines = this._lines,
       
  8415 				count = lines.length,
       
  8416 				justification = style.getJustification(),
       
  8417 				leading = style.getLeading(),
       
  8418 				x = 0;
       
  8419 			measureCtx.font = style.getFontStyle();
       
  8420 			var width = 0;
       
  8421 			for (var i = 0; i < count; i++)
       
  8422 				width = Math.max(width, measureCtx.measureText(lines[i]).width);
       
  8423 			if (justification !== 'left')
       
  8424 				x -= width / (justification === 'center' ? 2: 1);
       
  8425 			var bounds = new Rectangle(x,
       
  8426 						count ? - 0.75 * leading : 0,
       
  8427 						width, count * leading);
       
  8428 			return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
       
  8429 		}
       
  8430 	};
       
  8431 });
       
  8432 
       
  8433 var Color = Base.extend(new function() {
       
  8434 
       
  8435 	var types = {
       
  8436 		gray: ['gray'],
       
  8437 		rgb: ['red', 'green', 'blue'],
       
  8438 		hsb: ['hue', 'saturation', 'brightness'],
       
  8439 		hsl: ['hue', 'saturation', 'lightness'],
       
  8440 		gradient: ['gradient', 'origin', 'destination', 'highlight']
       
  8441 	};
       
  8442 
       
  8443 	var componentParsers = {},
       
  8444 		colorCache = {},
       
  8445 		colorCtx;
       
  8446 
       
  8447 	function fromCSS(string) {
       
  8448 		var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
       
  8449 			components;
       
  8450 		if (match) {
       
  8451 			components = [0, 0, 0];
       
  8452 			for (var i = 0; i < 3; i++) {
       
  8453 				var value = match[i + 1];
       
  8454 				components[i] = parseInt(value.length == 1
       
  8455 						? value + value : value, 16) / 255;
       
  8456 			}
       
  8457 		} else if (match = string.match(/^rgba?\((.*)\)$/)) {
       
  8458 			components = match[1].split(',');
       
  8459 			for (var i = 0, l = components.length; i < l; i++) {
       
  8460 				var value = parseFloat(components[i]);
       
  8461 				components[i] = i < 3 ? value / 255 : value;
       
  8462 			}
       
  8463 		} else {
       
  8464 			var cached = colorCache[string];
       
  8465 			if (!cached) {
       
  8466 				if (!colorCtx) {
       
  8467 					colorCtx = CanvasProvider.getContext(1, 1);
       
  8468 					colorCtx.globalCompositeOperation = 'copy';
       
  8469 				}
       
  8470 				colorCtx.fillStyle = 'rgba(0,0,0,0)';
       
  8471 				colorCtx.fillStyle = string;
       
  8472 				colorCtx.fillRect(0, 0, 1, 1);
       
  8473 				var data = colorCtx.getImageData(0, 0, 1, 1).data;
       
  8474 				cached = colorCache[string] = [
       
  8475 					data[0] / 255,
       
  8476 					data[1] / 255,
       
  8477 					data[2] / 255				
       
  8478 				];
       
  8479 			}
       
  8480 			components = cached.slice();
       
  8481 		}
       
  8482 		return components;
       
  8483 	}
       
  8484 
       
  8485 	var hsbIndices = [
       
  8486 		[0, 3, 1], 
       
  8487 		[2, 0, 1], 
       
  8488 		[1, 0, 3], 
       
  8489 		[1, 2, 0], 
       
  8490 		[3, 1, 0], 
       
  8491 		[0, 1, 2]  
       
  8492 	];
       
  8493 
       
  8494 	var converters = {
       
  8495 		'rgb-hsb': function(r, g, b) {
       
  8496 			var max = Math.max(r, g, b),
       
  8497 				min = Math.min(r, g, b),
       
  8498 				delta = max - min,
       
  8499 				h = delta === 0 ? 0
       
  8500 					:   ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
       
  8501 						: max == g ? (b - r) / delta + 2
       
  8502 						:            (r - g) / delta + 4) * 60; 
       
  8503 			return [h, max === 0 ? 0 : delta / max, max];
       
  8504 		},
       
  8505 
       
  8506 		'hsb-rgb': function(h, s, b) {
       
  8507 			var h = (h / 60) % 6, 
       
  8508 				i = Math.floor(h), 
       
  8509 				f = h - i,
       
  8510 				i = hsbIndices[i],
       
  8511 				v = [
       
  8512 					b,						
       
  8513 					b * (1 - s),			
       
  8514 					b * (1 - s * f),		
       
  8515 					b * (1 - s * (1 - f))	
       
  8516 				];
       
  8517 			return [v[i[0]], v[i[1]], v[i[2]]];
       
  8518 		},
       
  8519 
       
  8520 		'rgb-hsl': function(r, g, b) {
       
  8521 			var max = Math.max(r, g, b),
       
  8522 				min = Math.min(r, g, b),
       
  8523 				delta = max - min,
       
  8524 				achromatic = delta === 0,
       
  8525 				h = achromatic ? 0
       
  8526 					:   ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
       
  8527 						: max == g ? (b - r) / delta + 2
       
  8528 						:            (r - g) / delta + 4) * 60, 
       
  8529 				l = (max + min) / 2,
       
  8530 				s = achromatic ? 0 : l < 0.5
       
  8531 						? delta / (max + min)
       
  8532 						: delta / (2 - max - min);
       
  8533 			return [h, s, l];
       
  8534 		},
       
  8535 
       
  8536 		'hsl-rgb': function(h, s, l) {
       
  8537 			h /= 360;
       
  8538 			if (s === 0)
       
  8539 				return [l, l, l];
       
  8540 			var t3s = [ h + 1 / 3, h, h - 1 / 3 ],
       
  8541 				t2 = l < 0.5 ? l * (1 + s) : l + s - l * s,
       
  8542 				t1 = 2 * l - t2,
       
  8543 				c = [];
       
  8544 			for (var i = 0; i < 3; i++) {
       
  8545 				var t3 = t3s[i];
       
  8546 				if (t3 < 0) t3 += 1;
       
  8547 				if (t3 > 1) t3 -= 1;
       
  8548 				c[i] = 6 * t3 < 1
       
  8549 					? t1 + (t2 - t1) * 6 * t3
       
  8550 					: 2 * t3 < 1
       
  8551 						? t2
       
  8552 						: 3 * t3 < 2
       
  8553 							? t1 + (t2 - t1) * ((2 / 3) - t3) * 6
       
  8554 							: t1;
       
  8555 			}
       
  8556 			return c;
       
  8557 		},
       
  8558 
       
  8559 		'rgb-gray': function(r, g, b) {
       
  8560 			return [r * 0.2989 + g * 0.587 + b * 0.114];
       
  8561 		},
       
  8562 
       
  8563 		'gray-rgb': function(g) {
       
  8564 			return [g, g, g];
       
  8565 		},
       
  8566 
       
  8567 		'gray-hsb': function(g) {
       
  8568 			return [0, 0, g];
       
  8569 		},
       
  8570 
       
  8571 		'gray-hsl': function(g) {
       
  8572 			return [0, 0, g];
       
  8573 		},
       
  8574 
       
  8575 		'gradient-rgb': function() {
       
  8576 			return [];
       
  8577 		},
       
  8578 
       
  8579 		'rgb-gradient': function() {
       
  8580 			return [];
       
  8581 		}
       
  8582 
       
  8583 	};
       
  8584 
       
  8585 	return Base.each(types, function(properties, type) {
       
  8586 		componentParsers[type] = [];
       
  8587 		Base.each(properties, function(name, index) {
       
  8588 			var part = Base.capitalize(name),
       
  8589 				hasOverlap = /^(hue|saturation)$/.test(name),
       
  8590 				parser = componentParsers[type][index] = name === 'gradient'
       
  8591 					? function(value) {
       
  8592 						var current = this._components[0];
       
  8593 						value = Gradient.read(
       
  8594 								Array.isArray(value) ? value : arguments,
       
  8595 								0, 0, { readNull: true });
       
  8596 						if (current !== value) {
       
  8597 							if (current)
       
  8598 								current._removeOwner(this);
       
  8599 							if (value)
       
  8600 								value._addOwner(this);
       
  8601 						}
       
  8602 						return value;
       
  8603 					}
       
  8604 					: name === 'hue'
       
  8605 						? function(value) {
       
  8606 							return isNaN(value) ? 0
       
  8607 									: ((value % 360) + 360) % 360;
       
  8608 						}
       
  8609 						: type === 'gradient'
       
  8610 							? function() {
       
  8611 								return Point.read(arguments, 0, 0, {
       
  8612 										readNull: name === 'highlight',
       
  8613 										clone: true
       
  8614 								});
       
  8615 							}
       
  8616 							: function(value) {
       
  8617 								return isNaN(value) ? 0
       
  8618 										: Math.min(Math.max(value, 0), 1);
       
  8619 							};
       
  8620 
       
  8621 			this['get' + part] = function() {
       
  8622 				return this._type === type
       
  8623 					|| hasOverlap && /^hs[bl]$/.test(this._type)
       
  8624 						? this._components[index]
       
  8625 						: this._convert(type)[index];
       
  8626 			};
       
  8627 
       
  8628 			this['set' + part] = function(value) {
       
  8629 				if (this._type !== type
       
  8630 						&& !(hasOverlap && /^hs[bl]$/.test(this._type))) {
       
  8631 					this._components = this._convert(type);
       
  8632 					this._properties = types[type];
       
  8633 					this._type = type;
       
  8634 				}
       
  8635 				value = parser.call(this, value);
       
  8636 				if (value != null) {
       
  8637 					this._components[index] = value;
       
  8638 					this._changed();
       
  8639 				}
       
  8640 			};
       
  8641 		}, this);
       
  8642 	}, {
       
  8643 		_class: 'Color',
       
  8644 		_readIndex: true,
       
  8645 
       
  8646 		initialize: function Color(arg) {
       
  8647 			var slice = Array.prototype.slice,
       
  8648 				args = arguments,
       
  8649 				read = 0,
       
  8650 				parse = true,
       
  8651 				type,
       
  8652 				components,
       
  8653 				alpha,
       
  8654 				values;
       
  8655 			if (Array.isArray(arg)) {
       
  8656 				args = arg;
       
  8657 				arg = args[0];
       
  8658 			}
       
  8659 			var argType = arg != null && typeof arg;
       
  8660 			if (argType === 'string' && arg in types) {
       
  8661 				type = arg;
       
  8662 				arg = args[1];
       
  8663 				if (Array.isArray(arg)) {
       
  8664 					components = arg;
       
  8665 					alpha = args[2];
       
  8666 				} else {
       
  8667 					if (this.__read)
       
  8668 						read = 1; 
       
  8669 					args = slice.call(args, 1);
       
  8670 					argType = typeof arg;
       
  8671 				}
       
  8672 			}
       
  8673 			if (!components) {
       
  8674 				parse = !(this.__options && this.__options.dontParse);
       
  8675 				values = argType === 'number'
       
  8676 						? args
       
  8677 						: argType === 'object' && arg.length != null
       
  8678 							? arg
       
  8679 							: null;
       
  8680 				if (values) {
       
  8681 					if (!type)
       
  8682 						type = values.length >= 3
       
  8683 								? 'rgb'
       
  8684 								: 'gray';
       
  8685 					var length = types[type].length;
       
  8686 					alpha = values[length];
       
  8687 					if (this.__read)
       
  8688 						read += values === arguments
       
  8689 							? length + (alpha != null ? 1 : 0)
       
  8690 							: 1;
       
  8691 					if (values.length > length)
       
  8692 						values = slice.call(values, 0, length);
       
  8693 				} else if (argType === 'string') {
       
  8694 					type = 'rgb';
       
  8695 					components = fromCSS(arg);
       
  8696 					if (components.length === 4) {
       
  8697 						alpha = components[3];
       
  8698 						components.length--;
       
  8699 					}
       
  8700 				} else if (argType === 'object') {
       
  8701 					if (arg.constructor === Color) {
       
  8702 						type = arg._type;
       
  8703 						components = arg._components.slice();
       
  8704 						alpha = arg._alpha;
       
  8705 						if (type === 'gradient') {
       
  8706 							for (var i = 1, l = components.length; i < l; i++) {
       
  8707 								var point = components[i];
       
  8708 								if (point)
       
  8709 									components[i] = point.clone();
       
  8710 							}
       
  8711 						}
       
  8712 					} else if (arg.constructor === Gradient) {
       
  8713 						type = 'gradient';
       
  8714 						values = args;
       
  8715 					} else {
       
  8716 						type = 'hue' in arg
       
  8717 							? 'lightness' in arg
       
  8718 								? 'hsl'
       
  8719 								: 'hsb'
       
  8720 							: 'gradient' in arg || 'stops' in arg
       
  8721 									|| 'radial' in arg
       
  8722 								? 'gradient'
       
  8723 								: 'gray' in arg
       
  8724 									? 'gray'
       
  8725 									: 'rgb';
       
  8726 						var properties = types[type];
       
  8727 							parsers = parse && componentParsers[type];
       
  8728 						this._components = components = [];
       
  8729 						for (var i = 0, l = properties.length; i < l; i++) {
       
  8730 							var value = arg[properties[i]];
       
  8731 							if (value == null && i === 0 && type === 'gradient'
       
  8732 									&& 'stops' in arg) {
       
  8733 								value = {
       
  8734 									stops: arg.stops,
       
  8735 									radial: arg.radial
       
  8736 								};
       
  8737 							}
       
  8738 							if (parse)
       
  8739 								value = parsers[i].call(this, value);
       
  8740 							if (value != null)
       
  8741 								components[i] = value;
       
  8742 						}
       
  8743 						alpha = arg.alpha;
       
  8744 					}
       
  8745 				}
       
  8746 				if (this.__read && type)
       
  8747 					read = 1;
       
  8748 			}
       
  8749 			this._type = type || 'rgb';
       
  8750 			if (type === 'gradient')
       
  8751 				this._id = Color._id = (Color._id || 0) + 1;
       
  8752 			if (!components) {
       
  8753 				this._components = components = [];
       
  8754 				var parsers = componentParsers[this._type];
       
  8755 				for (var i = 0, l = parsers.length; i < l; i++) {
       
  8756 					var value = values && values[i];
       
  8757 					if (parse)
       
  8758 						value = parsers[i].call(this, value);
       
  8759 					if (value != null)
       
  8760 						components[i] = value;
       
  8761 				}
       
  8762 			}
       
  8763 			this._components = components;
       
  8764 			this._properties = types[this._type];
       
  8765 			this._alpha = alpha;
       
  8766 			if (this.__read)
       
  8767 				this.__read = read;
       
  8768 		},
       
  8769 
       
  8770 		_serialize: function(options, dictionary) {
       
  8771 			var components = this.getComponents();
       
  8772 			return Base.serialize(
       
  8773 					/^(gray|rgb)$/.test(this._type)
       
  8774 						? components
       
  8775 						: [this._type].concat(components),
       
  8776 					options, true, dictionary);
       
  8777 		},
       
  8778 
       
  8779 		_changed: function() {
       
  8780 			this._canvasStyle = null;
       
  8781 			if (this._owner)
       
  8782 				this._owner._changed(17);
       
  8783 		},
       
  8784 
       
  8785 		_convert: function(type) {
       
  8786 			var converter;
       
  8787 			return this._type === type
       
  8788 					? this._components.slice()
       
  8789 					: (converter = converters[this._type + '-' + type])
       
  8790 						? converter.apply(this, this._components)
       
  8791 						: converters['rgb-' + type].apply(this,
       
  8792 							converters[this._type + '-rgb'].apply(this,
       
  8793 									this._components));
       
  8794 		},
       
  8795 
       
  8796 		convert: function(type) {
       
  8797 			return new Color(type, this._convert(type), this._alpha);
       
  8798 		},
       
  8799 
       
  8800 		getType: function() {
       
  8801 			return this._type;
       
  8802 		},
       
  8803 
       
  8804 		setType: function(type) {
       
  8805 			this._components = this._convert(type);
       
  8806 			this._properties = types[type];
       
  8807 			this._type = type;
       
  8808 		},
       
  8809 
       
  8810 		getComponents: function() {
       
  8811 			var components = this._components.slice();
       
  8812 			if (this._alpha != null)
       
  8813 				components.push(this._alpha);
       
  8814 			return components;
       
  8815 		},
       
  8816 
       
  8817 		getAlpha: function() {
       
  8818 			return this._alpha != null ? this._alpha : 1;
       
  8819 		},
       
  8820 
       
  8821 		setAlpha: function(alpha) {
       
  8822 			this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1);
       
  8823 			this._changed();
       
  8824 		},
       
  8825 
       
  8826 		hasAlpha: function() {
       
  8827 			return this._alpha != null;
       
  8828 		},
       
  8829 
       
  8830 		equals: function(color) {
       
  8831 			if (Base.isPlainValue(color))
       
  8832 				color = Color.read(arguments);
       
  8833 			return color === this || color && this._class === color._class
       
  8834 					&& this._type === color._type
       
  8835 					&& this._alpha === color._alpha
       
  8836 					&& Base.equals(this._components, color._components)
       
  8837 					|| false;
       
  8838 		},
       
  8839 
       
  8840 		toString: function() {
       
  8841 			var properties = this._properties,
       
  8842 				parts = [],
       
  8843 				isGradient = this._type === 'gradient',
       
  8844 				f = Formatter.instance;
       
  8845 			for (var i = 0, l = properties.length; i < l; i++) {
       
  8846 				var value = this._components[i];
       
  8847 				if (value != null)
       
  8848 					parts.push(properties[i] + ': '
       
  8849 							+ (isGradient ? value : f.number(value)));
       
  8850 			}
       
  8851 			if (this._alpha != null)
       
  8852 				parts.push('alpha: ' + f.number(this._alpha));
       
  8853 			return '{ ' + parts.join(', ') + ' }';
       
  8854 		},
       
  8855 
       
  8856 		toCSS: function(hex) {
       
  8857 			var components = this._convert('rgb'),
       
  8858 				alpha = hex || this._alpha == null ? 1 : this._alpha;
       
  8859 			components = [
       
  8860 				Math.round(components[0] * 255),
       
  8861 				Math.round(components[1] * 255),
       
  8862 				Math.round(components[2] * 255)
       
  8863 			];
       
  8864 			if (alpha < 1)
       
  8865 				components.push(alpha);
       
  8866 			return hex
       
  8867 					? '#' + ((1 << 24) + (components[0] << 16)
       
  8868 						+ (components[1] << 8)
       
  8869 						+ components[2]).toString(16).slice(1)
       
  8870 					: (components.length == 4 ? 'rgba(' : 'rgb(')
       
  8871 						+ components.join(',') + ')';
       
  8872 		},
       
  8873 
       
  8874 		toCanvasStyle: function(ctx) {
       
  8875 			if (this._canvasStyle)
       
  8876 				return this._canvasStyle;
       
  8877 			if (this._type !== 'gradient')
       
  8878 				return this._canvasStyle = this.toCSS();
       
  8879 			var components = this._components,
       
  8880 				gradient = components[0],
       
  8881 				stops = gradient._stops,
       
  8882 				origin = components[1],
       
  8883 				destination = components[2],
       
  8884 				canvasGradient;
       
  8885 			if (gradient._radial) {
       
  8886 				var radius = destination.getDistance(origin),
       
  8887 					highlight = components[3];
       
  8888 				if (highlight) {
       
  8889 					var vector = highlight.subtract(origin);
       
  8890 					if (vector.getLength() > radius)
       
  8891 						highlight = origin.add(vector.normalize(radius - 0.1));
       
  8892 				}
       
  8893 				var start = highlight || origin;
       
  8894 				canvasGradient = ctx.createRadialGradient(start.x, start.y,
       
  8895 						0, origin.x, origin.y, radius);
       
  8896 			} else {
       
  8897 				canvasGradient = ctx.createLinearGradient(origin.x, origin.y,
       
  8898 						destination.x, destination.y);
       
  8899 			}
       
  8900 			for (var i = 0, l = stops.length; i < l; i++) {
       
  8901 				var stop = stops[i];
       
  8902 				canvasGradient.addColorStop(stop._rampPoint,
       
  8903 						stop._color.toCanvasStyle());
       
  8904 			}
       
  8905 			return this._canvasStyle = canvasGradient;
       
  8906 		},
       
  8907 
       
  8908 		transform: function(matrix) {
       
  8909 			if (this._type === 'gradient') {
       
  8910 				var components = this._components;
       
  8911 				for (var i = 1, l = components.length; i < l; i++) {
       
  8912 					var point = components[i];
       
  8913 					matrix._transformPoint(point, point, true);
       
  8914 				}
       
  8915 				this._changed();
       
  8916 			}
       
  8917 		},
       
  8918 
       
  8919 		statics: {
       
  8920 			_types: types,
       
  8921 
       
  8922 			random: function() {
       
  8923 				var random = Math.random;
       
  8924 				return new Color(random(), random(), random());
       
  8925 			}
       
  8926 		}
       
  8927 	});
       
  8928 }, new function() {
       
  8929 	function clamp(value, hue) {
       
  8930 		return value < 0
       
  8931 				? 0
       
  8932 				: hue && value > 360
       
  8933 					? 360
       
  8934 					: !hue && value > 1
       
  8935 						? 1
       
  8936 						: value;
       
  8937 	}
       
  8938 
       
  8939 	var operators = {
       
  8940 		add: function(a, b, hue) {
       
  8941 			return clamp(a + b, hue);
       
  8942 		},
       
  8943 
       
  8944 		subtract: function(a, b, hue) {
       
  8945 			return clamp(a - b, hue);
       
  8946 		},
       
  8947 
       
  8948 		multiply: function(a, b, hue) {
       
  8949 			return clamp(a * b, hue);
       
  8950 		},
       
  8951 
       
  8952 		divide: function(a, b, hue) {
       
  8953 			return clamp(a / b, hue);
       
  8954 		}
       
  8955 	};
       
  8956 
       
  8957 	return Base.each(operators, function(operator, name) {
       
  8958 		var options = { dontParse: /^(multiply|divide)$/.test(name) };
       
  8959 
       
  8960 		this[name] = function(color) {
       
  8961 			color = Color.read(arguments, 0, 0, options);
       
  8962 			var type = this._type,
       
  8963 				properties = this._properties,
       
  8964 				components1 = this._components,
       
  8965 				components2 = color._convert(type);
       
  8966 			for (var i = 0, l = components1.length; i < l; i++)
       
  8967 				components2[i] = operator(components1[i], components2[i],
       
  8968 						properties[i] === 'hue');
       
  8969 			return new Color(type, components2,
       
  8970 					this._alpha != null
       
  8971 							? operator(this._alpha, color.getAlpha())
       
  8972 							: null);
       
  8973 		};
       
  8974 	}, {
       
  8975 	});
       
  8976 });
       
  8977 
       
  8978 Base.each(Color._types, function(properties, type) {
       
  8979 	var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) {
       
  8980 			var argType = arg != null && typeof arg,
       
  8981 				components = argType === 'object' && arg.length != null
       
  8982 					? arg
       
  8983 					: argType === 'string'
       
  8984 						? null
       
  8985 						: arguments;
       
  8986 			return components
       
  8987 					? new Color(type, components)
       
  8988 					: new Color(arg);
       
  8989 		};
       
  8990 	if (type.length == 3) {
       
  8991 		var acronym = type.toUpperCase();
       
  8992 		Color[acronym] = this[acronym + 'Color'] = ctor;
       
  8993 	}
       
  8994 }, Base.exports);
       
  8995 
       
  8996 var Gradient = Base.extend({
       
  8997 	_class: 'Gradient',
       
  8998 
       
  8999 	initialize: function Gradient(stops, radial) {
       
  9000 		this._id = Gradient._id = (Gradient._id || 0) + 1;
       
  9001 		if (stops && this._set(stops))
       
  9002 			stops = radial = null;
       
  9003 		if (!this._stops)
       
  9004 			this.setStops(stops || ['white', 'black']);
       
  9005 		if (this._radial == null)
       
  9006 			this.setRadial(typeof radial === 'string' && radial === 'radial'
       
  9007 					|| radial || false);
       
  9008 	},
       
  9009 
       
  9010 	_serialize: function(options, dictionary) {
       
  9011 		return dictionary.add(this, function() {
       
  9012 			return Base.serialize([this._stops, this._radial],
       
  9013 					options, true, dictionary);
       
  9014 		});
       
  9015 	},
       
  9016 
       
  9017 	_changed: function() {
       
  9018 		for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
       
  9019 			this._owners[i]._changed();
       
  9020 	},
       
  9021 
       
  9022 	_addOwner: function(color) {
       
  9023 		if (!this._owners)
       
  9024 			this._owners = [];
       
  9025 		this._owners.push(color);
       
  9026 	},
       
  9027 
       
  9028 	_removeOwner: function(color) {
       
  9029 		var index = this._owners ? this._owners.indexOf(color) : -1;
       
  9030 		if (index != -1) {
       
  9031 			this._owners.splice(index, 1);
       
  9032 			if (this._owners.length === 0)
       
  9033 				delete this._owners;
       
  9034 		}
       
  9035 	},
       
  9036 
       
  9037 	clone: function() {
       
  9038 		var stops = [];
       
  9039 		for (var i = 0, l = this._stops.length; i < l; i++)
       
  9040 			stops[i] = this._stops[i].clone();
       
  9041 		return new Gradient(stops);
       
  9042 	},
       
  9043 
       
  9044 	getStops: function() {
       
  9045 		return this._stops;
       
  9046 	},
       
  9047 
       
  9048 	setStops: function(stops) {
       
  9049 		if (this.stops) {
       
  9050 			for (var i = 0, l = this._stops.length; i < l; i++)
       
  9051 				delete this._stops[i]._owner;
       
  9052 		}
       
  9053 		if (stops.length < 2)
       
  9054 			throw new Error(
       
  9055 					'Gradient stop list needs to contain at least two stops.');
       
  9056 		this._stops = GradientStop.readAll(stops, 0, false, true); 
       
  9057 		for (var i = 0, l = this._stops.length; i < l; i++) {
       
  9058 			var stop = this._stops[i];
       
  9059 			stop._owner = this;
       
  9060 			if (stop._defaultRamp)
       
  9061 				stop.setRampPoint(i / (l - 1));
       
  9062 		}
       
  9063 		this._changed();
       
  9064 	},
       
  9065 
       
  9066 	getRadial: function() {
       
  9067 		return this._radial;
       
  9068 	},
       
  9069 
       
  9070 	setRadial: function(radial) {
       
  9071 		this._radial = radial;
       
  9072 		this._changed();
       
  9073 	},
       
  9074 
       
  9075 	equals: function(gradient) {
       
  9076 		if (gradient === this)
       
  9077 			return true;
       
  9078 		if (gradient &&  this._class === gradient._class
       
  9079 				&& this._stops.length === gradient._stops.length) {
       
  9080 			for (var i = 0, l = this._stops.length; i < l; i++) {
       
  9081 				if (!this._stops[i].equals(gradient._stops[i]))
       
  9082 					return false;
       
  9083 			}
       
  9084 			return true;
       
  9085 		}
       
  9086 		return false;
       
  9087 	}
       
  9088 });
       
  9089 
       
  9090 var GradientStop = Base.extend({
       
  9091 	_class: 'GradientStop',
       
  9092 
       
  9093 	initialize: function GradientStop(arg0, arg1) {
       
  9094 		if (arg0) {
       
  9095 			var color, rampPoint;
       
  9096 			if (arg1 === undefined && Array.isArray(arg0)) {
       
  9097 				color = arg0[0];
       
  9098 				rampPoint = arg0[1];
       
  9099 			} else if (arg0.color) {
       
  9100 				color = arg0.color;
       
  9101 				rampPoint = arg0.rampPoint;
       
  9102 			} else {
       
  9103 				color = arg0;
       
  9104 				rampPoint = arg1;
       
  9105 			}
       
  9106 			this.setColor(color);
       
  9107 			this.setRampPoint(rampPoint);
       
  9108 		}
       
  9109 	},
       
  9110 
       
  9111 	clone: function() {
       
  9112 		return new GradientStop(this._color.clone(), this._rampPoint);
       
  9113 	},
       
  9114 
       
  9115 	_serialize: function(options, dictionary) {
       
  9116 		return Base.serialize([this._color, this._rampPoint], options, true, 
       
  9117 				dictionary);
       
  9118 	},
       
  9119 
       
  9120 	_changed: function() {
       
  9121 		if (this._owner)
       
  9122 			this._owner._changed(17);
       
  9123 	},
       
  9124 
       
  9125 	getRampPoint: function() {
       
  9126 		return this._rampPoint;
       
  9127 	},
       
  9128 
       
  9129 	setRampPoint: function(rampPoint) {
       
  9130 		this._defaultRamp = rampPoint == null;
       
  9131 		this._rampPoint = rampPoint || 0;
       
  9132 		this._changed();
       
  9133 	},
       
  9134 
       
  9135 	getColor: function() {
       
  9136 		return this._color;
       
  9137 	},
       
  9138 
       
  9139 	setColor: function(color) {
       
  9140 		this._color = Color.read(arguments);
       
  9141 		if (this._color === color)
       
  9142 			this._color = color.clone();
       
  9143 		this._color._owner = this;
       
  9144 		this._changed();
       
  9145 	},
       
  9146 
       
  9147 	equals: function(stop) {
       
  9148 		return stop === this || stop && this._class === stop._class
       
  9149 				&& this._color.equals(stop._color)
       
  9150 				&& this._rampPoint == stop._rampPoint
       
  9151 				|| false;
       
  9152 	}
       
  9153 });
       
  9154 
       
  9155 var Style = Base.extend(new function() {
       
  9156 	var defaults = {
       
  9157 		fillColor: undefined,
       
  9158 		strokeColor: undefined,
       
  9159 		strokeWidth: 1,
       
  9160 		strokeCap: 'butt',
       
  9161 		strokeJoin: 'miter',
       
  9162 		miterLimit: 10,
       
  9163 		dashOffset: 0,
       
  9164 		dashArray: [],
       
  9165 		windingRule: 'nonzero',
       
  9166 		shadowColor: undefined,
       
  9167 		shadowBlur: 0,
       
  9168 		shadowOffset: new Point(),
       
  9169 		selectedColor: undefined,
       
  9170 		font: 'sans-serif',
       
  9171 		fontSize: 12,
       
  9172 		leading: null,
       
  9173 		justification: 'left'
       
  9174 	};
       
  9175 
       
  9176 	var flags = {
       
  9177 		strokeWidth: 25,
       
  9178 		strokeCap: 25,
       
  9179 		strokeJoin: 25,
       
  9180 		miterLimit: 25,
       
  9181 		font: 5,
       
  9182 		fontSize: 5,
       
  9183 		leading: 5,
       
  9184 		justification: 5
       
  9185 	};
       
  9186 
       
  9187 	var item = {},
       
  9188 		fields = {
       
  9189 			_defaults: defaults,
       
  9190 			_textDefaults: new Base(defaults, {
       
  9191 				fillColor: new Color() 
       
  9192 			})
       
  9193 		};
       
  9194 
       
  9195 	Base.each(defaults, function(value, key) {
       
  9196 		var isColor = /Color$/.test(key),
       
  9197 			part = Base.capitalize(key),
       
  9198 			flag = flags[key],
       
  9199 			set = 'set' + part,
       
  9200 			get = 'get' + part;
       
  9201 
       
  9202 		fields[set] = function(value) {
       
  9203 			var children = this._item && this._item._children;
       
  9204 			if (children && children.length > 0
       
  9205 					&& this._item._type !== 'compound-path') {
       
  9206 				for (var i = 0, l = children.length; i < l; i++)
       
  9207 					children[i]._style[set](value);
       
  9208 			} else {
       
  9209 				var old = this._values[key];
       
  9210 				if (old != value) {
       
  9211 					if (isColor) {
       
  9212 						if (old)
       
  9213 							delete old._owner;
       
  9214 						if (value && value.constructor === Color) {
       
  9215 							if (value._owner)
       
  9216 								value = value.clone();
       
  9217 							value._owner = this._item;
       
  9218 						}
       
  9219 					}
       
  9220 					this._values[key] = value;
       
  9221 					if (this._item)
       
  9222 						this._item._changed(flag || 17);
       
  9223 				}
       
  9224 			}
       
  9225 		};
       
  9226 
       
  9227 		fields[get] = function() {
       
  9228 			var value,
       
  9229 				children = this._item && this._item._children;
       
  9230 			if (!children || children.length === 0 || arguments[0]
       
  9231 					|| this._item._type === 'compound-path') {
       
  9232 				var value = this._values[key];
       
  9233 				if (value === undefined) {
       
  9234 					value = this._defaults[key];
       
  9235 					if (value && value.clone)
       
  9236 						value = value.clone();
       
  9237 					this._values[key] = value;
       
  9238 				} else if (isColor && !(value && value.constructor === Color)) {
       
  9239 					this._values[key] = value = Color.read(
       
  9240 							[value], 0, 0, { readNull: true, clone: true });
       
  9241 					if (value)
       
  9242 						value._owner = this._item;
       
  9243 				}
       
  9244 				return value;
       
  9245 			}
       
  9246 			for (var i = 0, l = children.length; i < l; i++) {
       
  9247 				var childValue = children[i]._style[get]();
       
  9248 				if (i === 0) {
       
  9249 					value = childValue;
       
  9250 				} else if (!Base.equals(value, childValue)) {
       
  9251 					return undefined;
       
  9252 				}
       
  9253 			}
       
  9254 			return value;
       
  9255 		};
       
  9256 
       
  9257 		item[get] = function() {
       
  9258 			return this._style[get]();
       
  9259 		};
       
  9260 
       
  9261 		item[set] = function(value) {
       
  9262 			this._style[set](value);
       
  9263 		};
       
  9264 	});
       
  9265 
       
  9266 	Item.inject(item);
       
  9267 	return fields;
       
  9268 }, {
       
  9269 	_class: 'Style',
       
  9270 
       
  9271 	initialize: function Style(style, _item) {
       
  9272 		this._values = {};
       
  9273 		this._item = _item;
       
  9274 		if (_item instanceof TextItem)
       
  9275 			this._defaults = this._textDefaults;
       
  9276 		if (style)
       
  9277 			this.set(style);
       
  9278 	},
       
  9279 
       
  9280 	set: function(style) {
       
  9281 		var isStyle = style instanceof Style,
       
  9282 			values = isStyle ? style._values : style;
       
  9283 		if (values) {
       
  9284 			for (var key in values) {
       
  9285 				if (key in this._defaults) {
       
  9286 					var value = values[key];
       
  9287 					this[key] = value && isStyle && value.clone
       
  9288 							? value.clone() : value;
       
  9289 				}
       
  9290 			}
       
  9291 		}
       
  9292 	},
       
  9293 
       
  9294 	equals: function(style) {
       
  9295 		return style === this || style && this._class === style._class
       
  9296 				&& Base.equals(this._values, style._values)
       
  9297 				|| false;
       
  9298 	},
       
  9299 
       
  9300 	hasFill: function() {
       
  9301 		return !!this.getFillColor();
       
  9302 	},
       
  9303 
       
  9304 	hasStroke: function() {
       
  9305 		return !!this.getStrokeColor() && this.getStrokeWidth() > 0;
       
  9306 	},
       
  9307 
       
  9308 	hasShadow: function() {
       
  9309 		return !!this.getShadowColor() && this.getShadowBlur() > 0;
       
  9310 	},
       
  9311 
       
  9312 	getLeading: function getLeading() {
       
  9313 		var leading = getLeading.base.call(this);
       
  9314 		return leading != null ? leading : this.getFontSize() * 1.2;
       
  9315 	},
       
  9316 
       
  9317 	getFontStyle: function() {
       
  9318 		var size = this.getFontSize();
       
  9319 		return size + (/[a-z]/i.test(size + '') ? ' ' : 'px ') + this.getFont();
       
  9320 	}
       
  9321 
       
  9322 });
       
  9323 
       
  9324 var DomElement = new function() {
       
  9325 
       
  9326 	var special = /^(checked|value|selected|disabled)$/i,
       
  9327 		translated = { text: 'textContent', html: 'innerHTML' },
       
  9328 		unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1 };
       
  9329 
       
  9330 	function create(nodes, parent) {
       
  9331 		var res = [];
       
  9332 		for (var i =  0, l = nodes && nodes.length; i < l;) {
       
  9333 			var el = nodes[i++];
       
  9334 			if (typeof el === 'string') {
       
  9335 				el = document.createElement(el);
       
  9336 			} else if (!el || !el.nodeType) {
       
  9337 				continue;
       
  9338 			}
       
  9339 			if (Base.isPlainObject(nodes[i]))
       
  9340 				DomElement.set(el, nodes[i++]);
       
  9341 			if (Array.isArray(nodes[i]))
       
  9342 				create(nodes[i++], el);
       
  9343 			if (parent)
       
  9344 				parent.appendChild(el);
       
  9345 			res.push(el);
       
  9346 		}
       
  9347 		return res;
       
  9348 	}
       
  9349 
       
  9350 	return {
       
  9351 		create: function(nodes, parent) {
       
  9352 			var isArray = Array.isArray(nodes),
       
  9353 				res = create(isArray ? nodes : arguments, isArray ? parent : null);
       
  9354 			return res.length == 1 ? res[0] : res;
       
  9355 		},
       
  9356 
       
  9357 		find: function(selector, root) {
       
  9358 			return (root || document).querySelector(selector);
       
  9359 		},
       
  9360 
       
  9361 		findAll: function(selector, root) {
       
  9362 			return (root || document).querySelectorAll(selector);
       
  9363 		},
       
  9364 
       
  9365 		get: function(el, key) {
       
  9366 			return el
       
  9367 				? special.test(key)
       
  9368 					? key === 'value' || typeof el[key] !== 'string'
       
  9369 						? el[key]
       
  9370 						: true
       
  9371 					: key in translated
       
  9372 						? el[translated[key]]
       
  9373 						: el.getAttribute(key)
       
  9374 				: null;
       
  9375 		},
       
  9376 
       
  9377 		set: function(el, key, value) {
       
  9378 			if (typeof key !== 'string') {
       
  9379 				for (var name in key)
       
  9380 					if (key.hasOwnProperty(name))
       
  9381 						this.set(el, name, key[name]);
       
  9382 			} else if (!el || value === undefined) {
       
  9383 				return el;
       
  9384 			} else if (special.test(key)) {
       
  9385 				el[key] = value;
       
  9386 			} else if (key in translated) {
       
  9387 				el[translated[key]] = value;
       
  9388 			} else if (key === 'style') {
       
  9389 				this.setStyle(el, value);
       
  9390 			} else if (key === 'events') {
       
  9391 				DomEvent.add(el, value);
       
  9392 			} else {
       
  9393 				el.setAttribute(key, value);
       
  9394 			}
       
  9395 			return el;
       
  9396 		},
       
  9397 
       
  9398 		getStyles: function(el) {
       
  9399 			var doc = el && el.nodeType !== 9 ? el.ownerDocument : el,
       
  9400 				view = doc && doc.defaultView;
       
  9401 			return view && view.getComputedStyle(el, '');
       
  9402 		},
       
  9403 
       
  9404 		getStyle: function(el, key) {
       
  9405 			return el && el.style[key] || this.getStyles(el)[key] || null;
       
  9406 		},
       
  9407 
       
  9408 		setStyle: function(el, key, value) {
       
  9409 			if (typeof key !== 'string') {
       
  9410 				for (var name in key)
       
  9411 					if (key.hasOwnProperty(name))
       
  9412 						this.setStyle(el, name, key[name]);
       
  9413 			} else {
       
  9414 				if (/^-?[\d\.]+$/.test(value) && !(key in unitless))
       
  9415 					value += 'px';
       
  9416 				el.style[key] = value;
       
  9417 			}
       
  9418 			return el;
       
  9419 		},
       
  9420 
       
  9421 		hasClass: function(el, cls) {
       
  9422 			return new RegExp('\\s*' + cls + '\\s*').test(el.className);
       
  9423 		},
       
  9424 
       
  9425 		addClass: function(el, cls) {
       
  9426 			el.className = (el.className + ' ' + cls).trim();
       
  9427 		},
       
  9428 
       
  9429 		removeClass: function(el, cls) {
       
  9430 			el.className = el.className.replace(
       
  9431 				new RegExp('\\s*' + cls + '\\s*'), ' ').trim();
       
  9432 		},
       
  9433 
       
  9434 		remove: function(el) {
       
  9435 			if (el.parentNode)
       
  9436 				el.parentNode.removeChild(el);
       
  9437 		},
       
  9438 
       
  9439 		removeChildren: function(el) {
       
  9440 			while (el.firstChild)
       
  9441 				el.removeChild(el.firstChild);
       
  9442 		},
       
  9443 
       
  9444 		getBounds: function(el, viewport) {
       
  9445 			var doc = el.ownerDocument,
       
  9446 				body = doc.body,
       
  9447 				html = doc.documentElement,
       
  9448 				rect;
       
  9449 			try {
       
  9450 				rect = el.getBoundingClientRect();
       
  9451 			} catch (e) {
       
  9452 				rect = { left: 0, top: 0, width: 0, height: 0 };
       
  9453 			}
       
  9454 			var x = rect.left - (html.clientLeft || body.clientLeft || 0),
       
  9455 				y = rect.top - (html.clientTop  || body.clientTop  || 0);
       
  9456 			if (!viewport) {
       
  9457 				var view = doc.defaultView;
       
  9458 				x += view.pageXOffset || html.scrollLeft || body.scrollLeft;
       
  9459 				y += view.pageYOffset || html.scrollTop || body.scrollTop;
       
  9460 			}
       
  9461 			return new Rectangle(x, y, rect.width, rect.height);
       
  9462 		},
       
  9463 
       
  9464 		getViewportBounds: function(el) {
       
  9465 			var doc = el.ownerDocument,
       
  9466 				view = doc.defaultView,
       
  9467 				html = doc.documentElement;
       
  9468 			return new Rectangle(0, 0, 
       
  9469 				view.innerWidth || html.clientWidth,
       
  9470 				view.innerHeight || html.clientHeight
       
  9471 			);
       
  9472 		},
       
  9473 
       
  9474 		getOffset: function(el, viewport) {
       
  9475 			return this.getBounds(el, viewport).getPoint();
       
  9476 		},
       
  9477 
       
  9478 		getSize: function(el) {
       
  9479 			return this.getBounds(el, true).getSize();
       
  9480 		},
       
  9481 
       
  9482 		isInvisible: function(el) {
       
  9483 			return this.getSize(el).equals(new Size(0, 0));
       
  9484 		},
       
  9485 
       
  9486 		isInView: function(el) {
       
  9487 			return !this.isInvisible(el) && this.getViewportBounds(el).intersects(
       
  9488 					this.getBounds(el, true));
       
  9489 		},
       
  9490 
       
  9491 		getPrefixValue: function(el, name) {
       
  9492 			var value = el[name],
       
  9493 				prefixes = ['webkit', 'moz', 'ms', 'o'],
       
  9494 				suffix = name[0].toUpperCase() + name.substring(1);
       
  9495 			for (var i = 0; i < 4 && value == null; i++)
       
  9496 				value = el[prefixes[i] + suffix];
       
  9497 			return value;
       
  9498 		}
       
  9499 	};
       
  9500 };
       
  9501 
       
  9502 var DomEvent = {
       
  9503 	add: function(el, events) {
       
  9504 		for (var type in events) {
       
  9505 			var func = events[type];
       
  9506 			if (el.addEventListener) {
       
  9507 				el.addEventListener(type, func, false);
       
  9508 			} else if (el.attachEvent) {
       
  9509 				el.attachEvent('on' + type, func.bound = function() {
       
  9510 					func.call(el, window.event);
       
  9511 				});
       
  9512 			}
       
  9513 		}
       
  9514 	},
       
  9515 
       
  9516 	remove: function(el, events) {
       
  9517 		for (var type in events) {
       
  9518 			var func = events[type];
       
  9519 			if (el.removeEventListener) {
       
  9520 				el.removeEventListener(type, func, false);
       
  9521 			} else if (el.detachEvent) {
       
  9522 				el.detachEvent('on' + type, func.bound);
       
  9523 			}
       
  9524 		}
       
  9525 	},
       
  9526 
       
  9527 	getPoint: function(event) {
       
  9528 		var pos = event.targetTouches
       
  9529 				? event.targetTouches.length
       
  9530 					? event.targetTouches[0]
       
  9531 					: event.changedTouches[0]
       
  9532 				: event;
       
  9533 		return new Point(
       
  9534 			pos.pageX || pos.clientX + document.documentElement.scrollLeft,
       
  9535 			pos.pageY || pos.clientY + document.documentElement.scrollTop
       
  9536 		);
       
  9537 	},
       
  9538 
       
  9539 	getTarget: function(event) {
       
  9540 		return event.target || event.srcElement;
       
  9541 	},
       
  9542 
       
  9543 	getOffset: function(event, target) {
       
  9544 		return DomEvent.getPoint(event).subtract(DomElement.getOffset(
       
  9545 				target || DomEvent.getTarget(event)));
       
  9546 	},
       
  9547 
       
  9548 	preventDefault: function(event) {
       
  9549 		if (event.preventDefault) {
       
  9550 			event.preventDefault();
       
  9551 		} else {
       
  9552 			event.returnValue = false;
       
  9553 		}
       
  9554 	},
       
  9555 
       
  9556 	stopPropagation: function(event) {
       
  9557 		if (event.stopPropagation) {
       
  9558 			event.stopPropagation();
       
  9559 		} else {
       
  9560 			event.cancelBubble = true;
       
  9561 		}
       
  9562 	},
       
  9563 
       
  9564 	stop: function(event) {
       
  9565 		DomEvent.stopPropagation(event);
       
  9566 		DomEvent.preventDefault(event);
       
  9567 	}
       
  9568 };
       
  9569 
       
  9570 DomEvent.requestAnimationFrame = new function() {
       
  9571 	var nativeRequest = DomElement.getPrefixValue(window,
       
  9572 			'requestAnimationFrame'),
       
  9573 		requested = false,
       
  9574 		callbacks = [],
       
  9575 		focused = true,
       
  9576 		timer;
       
  9577 
       
  9578 	DomEvent.add(window, {
       
  9579 		focus: function() {
       
  9580 			focused = true;
       
  9581 		},
       
  9582 		blur: function() {
       
  9583 			focused = false;
       
  9584 		}
       
  9585 	});
       
  9586 
       
  9587 	function handleCallbacks() {
       
  9588 		for (var i = callbacks.length - 1; i >= 0; i--) {
       
  9589 			var entry = callbacks[i],
       
  9590 				func = entry[0],
       
  9591 				el = entry[1];
       
  9592 			if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
       
  9593 					|| focused) && DomElement.isInView(el)) {
       
  9594 				callbacks.splice(i, 1);
       
  9595 				func();
       
  9596 			}
       
  9597 		}
       
  9598 		if (nativeRequest) {
       
  9599 			if (callbacks.length) {
       
  9600 				nativeRequest(handleCallbacks);
       
  9601 			} else {
       
  9602 				requested = false;
       
  9603 			}
       
  9604 		}
       
  9605 	}
       
  9606 
       
  9607 	return function(callback, element) {
       
  9608 		callbacks.push([callback, element]);
       
  9609 		if (nativeRequest) {
       
  9610 			if (!requested) {
       
  9611 				nativeRequest(handleCallbacks);
       
  9612 				requested = true;
       
  9613 			}
       
  9614 		} else if (!timer) {
       
  9615 			timer = setInterval(handleCallbacks, 1000 / 60);
       
  9616 		}
       
  9617 	};
       
  9618 };
       
  9619 
       
  9620 var View = Base.extend(Callback, {
       
  9621 	_class: 'View',
       
  9622 
       
  9623 	initialize: function View(element) {
       
  9624 		this._scope = paper;
       
  9625 		this._project = paper.project;
       
  9626 		this._element = element;
       
  9627 		var size;
       
  9628 		this._id = element.getAttribute('id');
       
  9629 		if (this._id == null)
       
  9630 			element.setAttribute('id', this._id = 'view-' + View._id++);
       
  9631 		DomEvent.add(element, this._viewHandlers);
       
  9632 		if (PaperScope.hasAttribute(element, 'resize')) {
       
  9633 			var offset = DomElement.getOffset(element, true),
       
  9634 				that = this;
       
  9635 			size = DomElement.getViewportBounds(element)
       
  9636 					.getSize().subtract(offset);
       
  9637 			this._windowHandlers = {
       
  9638 				resize: function() {
       
  9639 					if (!DomElement.isInvisible(element))
       
  9640 						offset = DomElement.getOffset(element, true);
       
  9641 					that.setViewSize(DomElement.getViewportBounds(element)
       
  9642 							.getSize().subtract(offset));
       
  9643 				}
       
  9644 			};
       
  9645 			DomEvent.add(window, this._windowHandlers);
       
  9646 		} else {
       
  9647 			size = DomElement.getSize(element);
       
  9648 			if (size.isNaN() || size.isZero())
       
  9649 				size = new Size(parseInt(element.getAttribute('width'), 10),
       
  9650 							parseInt(element.getAttribute('height'), 10));
       
  9651 		}
       
  9652 		this._setViewSize(size);
       
  9653 		if (PaperScope.hasAttribute(element, 'stats')
       
  9654 				&& typeof Stats !== 'undefined') {
       
  9655 			this._stats = new Stats();
       
  9656 			var stats = this._stats.domElement,
       
  9657 				style = stats.style,
       
  9658 				offset = DomElement.getOffset(element);
       
  9659 			style.position = 'absolute';
       
  9660 			style.left = offset.x + 'px';
       
  9661 			style.top = offset.y + 'px';
       
  9662 			document.body.appendChild(stats);
       
  9663 		}
       
  9664 		View._views.push(this);
       
  9665 		View._viewsById[this._id] = this;
       
  9666 		this._viewSize = size;
       
  9667 		(this._matrix = new Matrix())._owner = this;
       
  9668 		this._zoom = 1;
       
  9669 		if (!View._focused)
       
  9670 			View._focused = this;
       
  9671 		this._frameItems = {};
       
  9672 		this._frameItemCount = 0;
       
  9673 	},
       
  9674 
       
  9675 	remove: function() {
       
  9676 		if (!this._project)
       
  9677 			return false;
       
  9678 		if (View._focused == this)
       
  9679 			View._focused = null;
       
  9680 		View._views.splice(View._views.indexOf(this), 1);
       
  9681 		delete View._viewsById[this._id];
       
  9682 		if (this._project.view == this)
       
  9683 			this._project.view = null;
       
  9684 		DomEvent.remove(this._element, this._viewHandlers);
       
  9685 		DomEvent.remove(window, this._windowHandlers);
       
  9686 		this._element = this._project = null;
       
  9687 		this.detach('frame');
       
  9688 		this._frameItems = {};
       
  9689 		return true;
       
  9690 	},
       
  9691 
       
  9692 	_events: {
       
  9693 		onFrame: {
       
  9694 			install: function() {
       
  9695 				this._animate = true;
       
  9696 				if (!this._requested)
       
  9697 					this._requestFrame();
       
  9698 			},
       
  9699 
       
  9700 			uninstall: function() {
       
  9701 				this._animate = false;
       
  9702 			}
       
  9703 		},
       
  9704 
       
  9705 		onResize: {}
       
  9706 	},
       
  9707 
       
  9708 	_animate: false,
       
  9709 	_time: 0,
       
  9710 	_count: 0,
       
  9711 
       
  9712 	_requestFrame: function() {
       
  9713 		var that = this;
       
  9714 		DomEvent.requestAnimationFrame(function() {
       
  9715 			that._requested = false;
       
  9716 			if (!that._animate)
       
  9717 				return;
       
  9718 			that._requestFrame();
       
  9719 			that._handleFrame();
       
  9720 		}, this._element);
       
  9721 		this._requested = true;
       
  9722 	},
       
  9723 
       
  9724 	_handleFrame: function() {
       
  9725 		paper = this._scope;
       
  9726 		var now = Date.now() / 1000,
       
  9727 			delta = this._before ? now - this._before : 0;
       
  9728 		this._before = now;
       
  9729 		this._handlingFrame = true;
       
  9730 		this.fire('frame', new Base({
       
  9731 			delta: delta,
       
  9732 			time: this._time += delta,
       
  9733 			count: this._count++
       
  9734 		}));
       
  9735 		if (this._stats)
       
  9736 			this._stats.update();
       
  9737 		this._handlingFrame = false;
       
  9738 		this.draw(true);
       
  9739 	},
       
  9740 
       
  9741 	_animateItem: function(item, animate) {
       
  9742 		var items = this._frameItems;
       
  9743 		if (animate) {
       
  9744 			items[item._id] = {
       
  9745 				item: item,
       
  9746 				time: 0,
       
  9747 				count: 0
       
  9748 			};
       
  9749 			if (++this._frameItemCount === 1)
       
  9750 				this.attach('frame', this._handleFrameItems);
       
  9751 		} else {
       
  9752 			delete items[item._id];
       
  9753 			if (--this._frameItemCount === 0) {
       
  9754 				this.detach('frame', this._handleFrameItems);
       
  9755 			}
       
  9756 		}
       
  9757 	},
       
  9758 
       
  9759 	_handleFrameItems: function(event) {
       
  9760 		for (var i in this._frameItems) {
       
  9761 			var entry = this._frameItems[i];
       
  9762 			entry.item.fire('frame', new Base(event, {
       
  9763 				time: entry.time += event.delta,
       
  9764 				count: entry.count++
       
  9765 			}));
       
  9766 		}
       
  9767 	},
       
  9768 
       
  9769 	_redraw: function() {
       
  9770 		this._project._needsRedraw = true;
       
  9771 		if (this._handlingFrame)
       
  9772 			return;
       
  9773 		if (this._animate) {
       
  9774 			this._handleFrame();
       
  9775 		} else {
       
  9776 			this.draw();
       
  9777 		}
       
  9778 	},
       
  9779 
       
  9780 	_changed: function(flags) {
       
  9781 		if (flags & 1)
       
  9782 			this._project._needsRedraw = true;
       
  9783 	},
       
  9784 
       
  9785 	_transform: function(matrix) {
       
  9786 		this._matrix.concatenate(matrix);
       
  9787 		this._bounds = null;
       
  9788 		this._redraw();
       
  9789 	},
       
  9790 
       
  9791 	getElement: function() {
       
  9792 		return this._element;
       
  9793 	},
       
  9794 
       
  9795 	getViewSize: function() {
       
  9796 		var size = this._viewSize;
       
  9797 		return new LinkedSize(size.width, size.height, this, 'setViewSize');
       
  9798 	},
       
  9799 
       
  9800 	setViewSize: function(size) {
       
  9801 		size = Size.read(arguments);
       
  9802 		var delta = size.subtract(this._viewSize);
       
  9803 		if (delta.isZero())
       
  9804 			return;
       
  9805 		this._viewSize.set(size.width, size.height);
       
  9806 		this._setViewSize(size);
       
  9807 		this._bounds = null; 
       
  9808 		this.fire('resize', {
       
  9809 			size: size,
       
  9810 			delta: delta
       
  9811 		});
       
  9812 		this._redraw();
       
  9813 	},
       
  9814 
       
  9815 	_setViewSize: function(size) {
       
  9816 		var element = this._element;
       
  9817 		element.width = size.width;
       
  9818 		element.height = size.height;
       
  9819 	},
       
  9820 
       
  9821 	getBounds: function() {
       
  9822 		if (!this._bounds)
       
  9823 			this._bounds = this._matrix.inverted()._transformBounds(
       
  9824 					new Rectangle(new Point(), this._viewSize));
       
  9825 		return this._bounds;
       
  9826 	},
       
  9827 
       
  9828 	getSize: function() {
       
  9829 		return this.getBounds().getSize(arguments[0]);
       
  9830 	},
       
  9831 
       
  9832 	getCenter: function() {
       
  9833 		return this.getBounds().getCenter(arguments[0]);
       
  9834 	},
       
  9835 
       
  9836 	setCenter: function(center) {
       
  9837 		center = Point.read(arguments);
       
  9838 		this.scrollBy(center.subtract(this.getCenter()));
       
  9839 	},
       
  9840 
       
  9841 	getZoom: function() {
       
  9842 		return this._zoom;
       
  9843 	},
       
  9844 
       
  9845 	setZoom: function(zoom) {
       
  9846 		this._transform(new Matrix().scale(zoom / this._zoom,
       
  9847 			this.getCenter()));
       
  9848 		this._zoom = zoom;
       
  9849 	},
       
  9850 
       
  9851 	isVisible: function() {
       
  9852 		return DomElement.isInView(this._element);
       
  9853 	},
       
  9854 
       
  9855 	scrollBy: function() {
       
  9856 		this._transform(new Matrix().translate(Point.read(arguments).negate()));
       
  9857 	},
       
  9858 
       
  9859 	projectToView: function() {
       
  9860 		return this._matrix._transformPoint(Point.read(arguments));
       
  9861 	},
       
  9862 
       
  9863 	viewToProject: function() {
       
  9864 		return this._matrix._inverseTransform(Point.read(arguments));
       
  9865 	},
       
  9866 
       
  9867 }, {
       
  9868 	statics: {
       
  9869 		_views: [],
       
  9870 		_viewsById: {},
       
  9871 		_id: 0,
       
  9872 
       
  9873 		create: function(element) {
       
  9874 			if (typeof element === 'string')
       
  9875 				element = document.getElementById(element);
       
  9876 			return new CanvasView(element);
       
  9877 		}
       
  9878 	}
       
  9879 }, new function() {
       
  9880 	var tool,
       
  9881 		prevFocus,
       
  9882 		tempFocus,
       
  9883 		dragging = false;
       
  9884 
       
  9885 	function getView(event) {
       
  9886 		var target = DomEvent.getTarget(event);
       
  9887 		return target.getAttribute && View._viewsById[target.getAttribute('id')];
       
  9888 	}
       
  9889 
       
  9890 	function viewToProject(view, event) {
       
  9891 		return view.viewToProject(DomEvent.getOffset(event, view._element));
       
  9892 	}
       
  9893 
       
  9894 	function updateFocus() {
       
  9895 		if (!View._focused || !View._focused.isVisible()) {
       
  9896 			for (var i = 0, l = View._views.length; i < l; i++) {
       
  9897 				var view = View._views[i];
       
  9898 				if (view && view.isVisible()) {
       
  9899 					View._focused = tempFocus = view;
       
  9900 					break;
       
  9901 				}
       
  9902 			}
       
  9903 		}
       
  9904 	}
       
  9905 
       
  9906 	function mousedown(event) {
       
  9907 		var view = View._focused = getView(event),
       
  9908 			point = viewToProject(view, event);
       
  9909 		dragging = true;
       
  9910 		if (view._onMouseDown)
       
  9911 			view._onMouseDown(event, point);
       
  9912 		if (tool = view._scope._tool)
       
  9913 			tool._onHandleEvent('mousedown', point, event);
       
  9914 		view.draw(true);
       
  9915 	}
       
  9916 
       
  9917 	function mousemove(event) {
       
  9918 		var view;
       
  9919 		if (!dragging) {
       
  9920 			view = getView(event);
       
  9921 			if (view) {
       
  9922 				prevFocus = View._focused;
       
  9923 				View._focused = tempFocus = view;
       
  9924 			} else if (tempFocus && tempFocus == View._focused) {
       
  9925 				View._focused = prevFocus;
       
  9926 				updateFocus();
       
  9927 			}
       
  9928 		}
       
  9929 		if (!(view = view || View._focused))
       
  9930 			return;
       
  9931 		var point = event && viewToProject(view, event);
       
  9932 		if (view._onMouseMove)
       
  9933 			view._onMouseMove(event, point);
       
  9934 		if (tool = view._scope._tool) {
       
  9935 			if (tool._onHandleEvent(dragging && tool.responds('mousedrag')
       
  9936 					? 'mousedrag' : 'mousemove', point, event))
       
  9937 				DomEvent.stop(event);
       
  9938 		}
       
  9939 		view.draw(true);
       
  9940 	}
       
  9941 
       
  9942 	function mouseup(event) {
       
  9943 		var view = View._focused;
       
  9944 		if (!view || !dragging)
       
  9945 			return;
       
  9946 		var point = viewToProject(view, event);
       
  9947 		curPoint = null;
       
  9948 		dragging = false;
       
  9949 		if (view._onMouseUp)
       
  9950 			view._onMouseUp(event, point);
       
  9951 		if (tool && tool._onHandleEvent('mouseup', point, event))
       
  9952 			DomEvent.stop(event);
       
  9953 		view.draw(true);
       
  9954 	}
       
  9955 
       
  9956 	function selectstart(event) {
       
  9957 		if (dragging)
       
  9958 			DomEvent.stop(event);
       
  9959 	}
       
  9960 
       
  9961 	DomEvent.add(document, {
       
  9962 		mousemove: mousemove,
       
  9963 		mouseup: mouseup,
       
  9964 		touchmove: mousemove,
       
  9965 		touchend: mouseup,
       
  9966 		selectstart: selectstart,
       
  9967 		scroll: updateFocus
       
  9968 	});
       
  9969 
       
  9970 	DomEvent.add(window, {
       
  9971 		load: updateFocus
       
  9972 	});
       
  9973 
       
  9974 	return {
       
  9975 		_viewHandlers: {
       
  9976 			mousedown: mousedown,
       
  9977 			touchstart: mousedown,
       
  9978 			selectstart: selectstart
       
  9979 		},
       
  9980 
       
  9981 		statics: {
       
  9982 			updateFocus: updateFocus
       
  9983 		}
       
  9984 	};
       
  9985 });
       
  9986 
       
  9987 var CanvasView = View.extend({
       
  9988 	_class: 'CanvasView',
       
  9989 
       
  9990 	initialize: function CanvasView(canvas) {
       
  9991 		if (!(canvas instanceof HTMLCanvasElement)) {
       
  9992 			var size = Size.read(arguments);
       
  9993 			if (size.isZero())
       
  9994 				throw new Error(
       
  9995 						'Cannot create CanvasView with the provided argument: '
       
  9996 						+ canvas);
       
  9997 			canvas = CanvasProvider.getCanvas(size);
       
  9998 		}
       
  9999 		this._context = canvas.getContext('2d');
       
 10000 		this._eventCounters = {};
       
 10001 		this._ratio = 1;
       
 10002 		if (PaperScope.getAttribute(canvas, 'hidpi') !== 'off') {
       
 10003 			var deviceRatio = window.devicePixelRatio || 1,
       
 10004 				backingStoreRatio = DomElement.getPrefixValue(this._context,
       
 10005 						'backingStorePixelRatio') || 1;
       
 10006 			this._ratio = deviceRatio / backingStoreRatio;
       
 10007 		}
       
 10008 		View.call(this, canvas);
       
 10009 	},
       
 10010 
       
 10011 	_setViewSize: function(size) {
       
 10012 		var width = size.width,
       
 10013 			height = size.height,
       
 10014 			ratio = this._ratio,
       
 10015 			element = this._element,
       
 10016 			style = element.style;
       
 10017 		element.width = width * ratio;
       
 10018 		element.height = height * ratio;
       
 10019 		if (ratio !== 1) {
       
 10020 			style.width = width + 'px';
       
 10021 			style.height = height + 'px';
       
 10022 			this._context.scale(ratio, ratio);
       
 10023 		}
       
 10024 	},
       
 10025 
       
 10026 	draw: function(checkRedraw) {
       
 10027 		if (checkRedraw && !this._project._needsRedraw)
       
 10028 			return false;
       
 10029 		var ctx = this._context,
       
 10030 			size = this._viewSize;
       
 10031 		ctx.clearRect(0, 0, size.width + 1, size.height + 1);
       
 10032 		this._project.draw(ctx, this._matrix, this._ratio);
       
 10033 		this._project._needsRedraw = false;
       
 10034 		return true;
       
 10035 	}
       
 10036 }, new function() { 
       
 10037 
       
 10038 	var downPoint,
       
 10039 		lastPoint,
       
 10040 		overPoint,
       
 10041 		downItem,
       
 10042 		lastItem,
       
 10043 		overItem,
       
 10044 		hasDrag,
       
 10045 		doubleClick,
       
 10046 		clickTime;
       
 10047 
       
 10048 	function callEvent(type, event, point, target, lastPoint, bubble) {
       
 10049 		var item = target,
       
 10050 			mouseEvent;
       
 10051 		while (item) {
       
 10052 			if (item.responds(type)) {
       
 10053 				if (!mouseEvent)
       
 10054 					mouseEvent = new MouseEvent(type, event, point, target,
       
 10055 							lastPoint ? point.subtract(lastPoint) : null);
       
 10056 				if (item.fire(type, mouseEvent)
       
 10057 						&& (!bubble || mouseEvent._stopped))
       
 10058 					return false;
       
 10059 			}
       
 10060 			item = item.getParent();
       
 10061 		}
       
 10062 		return true;
       
 10063 	}
       
 10064 
       
 10065 	function handleEvent(view, type, event, point, lastPoint) {
       
 10066 		if (view._eventCounters[type]) {
       
 10067 			var project = view._project,
       
 10068 				hit = project.hitTest(point, {
       
 10069 					tolerance: project.options.hitTolerance || 0,
       
 10070 					fill: true,
       
 10071 					stroke: true
       
 10072 				}),
       
 10073 				item = hit && hit.item;
       
 10074 			if (item) {
       
 10075 				if (type === 'mousemove' && item != overItem)
       
 10076 					lastPoint = point;
       
 10077 				if (type !== 'mousemove' || !hasDrag)
       
 10078 					callEvent(type, event, point, item, lastPoint);
       
 10079 				return item;
       
 10080 			}
       
 10081 		}
       
 10082 	}
       
 10083 
       
 10084 	return {
       
 10085 		_onMouseDown: function(event, point) {
       
 10086 			var item = handleEvent(this, 'mousedown', event, point);
       
 10087 			doubleClick = lastItem == item && (Date.now() - clickTime < 300);
       
 10088 			downItem = lastItem = item;
       
 10089 			downPoint = lastPoint = overPoint = point;
       
 10090 			hasDrag = downItem && downItem.responds('mousedrag');
       
 10091 		},
       
 10092 
       
 10093 		_onMouseUp: function(event, point) {
       
 10094 			var item = handleEvent(this, 'mouseup', event, point);
       
 10095 			if (hasDrag) {
       
 10096 				if (lastPoint && !lastPoint.equals(point))
       
 10097 					callEvent('mousedrag', event, point, downItem, lastPoint);
       
 10098 				if (item != downItem) {
       
 10099 					overPoint = point;
       
 10100 					callEvent('mousemove', event, point, item, overPoint);
       
 10101 				}
       
 10102 			}
       
 10103 			if (item === downItem) {
       
 10104 				clickTime = Date.now();
       
 10105 				if (!doubleClick
       
 10106 						|| callEvent('doubleclick', event, downPoint, item))
       
 10107 					callEvent('click', event, downPoint, item);
       
 10108 				doubleClick = false;
       
 10109 			}
       
 10110 			downItem = null;
       
 10111 			hasDrag = false;
       
 10112 		},
       
 10113 
       
 10114 		_onMouseMove: function(event, point) {
       
 10115 			if (downItem)
       
 10116 				callEvent('mousedrag', event, point, downItem, lastPoint);
       
 10117 			var item = handleEvent(this, 'mousemove', event, point, overPoint);
       
 10118 			lastPoint = overPoint = point;
       
 10119 			if (item !== overItem) {
       
 10120 				callEvent('mouseleave', event, point, overItem);
       
 10121 				overItem = item;
       
 10122 				callEvent('mouseenter', event, point, item);
       
 10123 			}
       
 10124 		}
       
 10125 	};
       
 10126 });
       
 10127 
       
 10128 var Event = Base.extend({
       
 10129 	_class: 'Event',
       
 10130 
       
 10131 	initialize: function Event(event) {
       
 10132 		this.event = event;
       
 10133 	},
       
 10134 
       
 10135 	preventDefault: function() {
       
 10136 		this._prevented = true;
       
 10137 		DomEvent.preventDefault(this.event);
       
 10138 	},
       
 10139 
       
 10140 	stopPropagation: function() {
       
 10141 		this._stopped = true;
       
 10142 		DomEvent.stopPropagation(this.event);
       
 10143 	},
       
 10144 
       
 10145 	stop: function() {
       
 10146 		this.stopPropagation();
       
 10147 		this.preventDefault();
       
 10148 	},
       
 10149 
       
 10150 	getModifiers: function() {
       
 10151 		return Key.modifiers;
       
 10152 	}
       
 10153 });
       
 10154 
       
 10155 var KeyEvent = Event.extend({
       
 10156 	_class: 'KeyEvent',
       
 10157 
       
 10158 	initialize: function KeyEvent(down, key, character, event) {
       
 10159 		Event.call(this, event);
       
 10160 		this.type = down ? 'keydown' : 'keyup';
       
 10161 		this.key = key;
       
 10162 		this.character = character;
       
 10163 	},
       
 10164 
       
 10165 	toString: function() {
       
 10166 		return "{ type: '" + this.type
       
 10167 				+ "', key: '" + this.key
       
 10168 				+ "', character: '" + this.character
       
 10169 				+ "', modifiers: " + this.getModifiers()
       
 10170 				+ " }";
       
 10171 	}
       
 10172 });
       
 10173 
       
 10174 var Key = new function() {
       
 10175 
       
 10176 	var specialKeys = {
       
 10177 		8: 'backspace',
       
 10178 		9: 'tab',
       
 10179 		13: 'enter',
       
 10180 		16: 'shift',
       
 10181 		17: 'control',
       
 10182 		18: 'option',
       
 10183 		19: 'pause',
       
 10184 		20: 'caps-lock',
       
 10185 		27: 'escape',
       
 10186 		32: 'space',
       
 10187 		35: 'end',
       
 10188 		36: 'home',
       
 10189 		37: 'left',
       
 10190 		38: 'up',
       
 10191 		39: 'right',
       
 10192 		40: 'down',
       
 10193 		46: 'delete',
       
 10194 		91: 'command',
       
 10195 		93: 'command', 
       
 10196 		224: 'command'  
       
 10197 	},
       
 10198 
       
 10199 	specialChars = {
       
 10200 		9: true, 
       
 10201 		13: true, 
       
 10202 		32: true 
       
 10203 	},
       
 10204 
       
 10205 	modifiers = new Base({
       
 10206 		shift: false,
       
 10207 		control: false,
       
 10208 		option: false,
       
 10209 		command: false,
       
 10210 		capsLock: false,
       
 10211 		space: false
       
 10212 	}),
       
 10213 
       
 10214 	charCodeMap = {}, 
       
 10215 	keyMap = {}, 
       
 10216 	downCode; 
       
 10217 
       
 10218 	function handleKey(down, keyCode, charCode, event) {
       
 10219 		var character = charCode ? String.fromCharCode(charCode) : '',
       
 10220 			specialKey = specialKeys[keyCode],
       
 10221 			key = specialKey || character.toLowerCase(),
       
 10222 			type = down ? 'keydown' : 'keyup',
       
 10223 			view = View._focused,
       
 10224 			scope = view && view.isVisible() && view._scope,
       
 10225 			tool = scope && scope._tool,
       
 10226 			name;
       
 10227 		keyMap[key] = down;
       
 10228 		if (specialKey && (name = Base.camelize(specialKey)) in modifiers)
       
 10229 			modifiers[name] = down;
       
 10230 		if (down) {
       
 10231 			charCodeMap[keyCode] = charCode;
       
 10232 		} else {
       
 10233 			delete charCodeMap[keyCode];
       
 10234 		}
       
 10235 		if (tool && tool.responds(type)) {
       
 10236 			paper = scope;
       
 10237 			tool.fire(type, new KeyEvent(down, key, character, event));
       
 10238 			if (view)
       
 10239 				view.draw(true);
       
 10240 		}
       
 10241 	}
       
 10242 
       
 10243 	DomEvent.add(document, {
       
 10244 		keydown: function(event) {
       
 10245 			var code = event.which || event.keyCode;
       
 10246 			if (code in specialKeys) {
       
 10247 				handleKey(true, code, code in specialChars ? code : 0, event);
       
 10248 			} else {
       
 10249 				downCode = code;
       
 10250 			}
       
 10251 		},
       
 10252 
       
 10253 		keypress: function(event) {
       
 10254 			if (downCode != null) {
       
 10255 				handleKey(true, downCode, event.which || event.keyCode, event);
       
 10256 				downCode = null;
       
 10257 			}
       
 10258 		},
       
 10259 
       
 10260 		keyup: function(event) {
       
 10261 			var code = event.which || event.keyCode;
       
 10262 			if (code in charCodeMap)
       
 10263 				handleKey(false, code, charCodeMap[code], event);
       
 10264 		}
       
 10265 	});
       
 10266 
       
 10267 	DomEvent.add(window, {
       
 10268 		blur: function(event) {
       
 10269 			for (var code in charCodeMap)
       
 10270 				handleKey(false, code, charCodeMap[code], event);
       
 10271 		}
       
 10272 	});
       
 10273 
       
 10274 	return {
       
 10275 		modifiers: modifiers,
       
 10276 
       
 10277 		isDown: function(key) {
       
 10278 			return !!keyMap[key];
       
 10279 		}
       
 10280 	};
       
 10281 };
       
 10282 
       
 10283 var MouseEvent = Event.extend({
       
 10284 	_class: 'MouseEvent',
       
 10285 
       
 10286 	initialize: function MouseEvent(type, event, point, target, delta) {
       
 10287 		Event.call(this, event);
       
 10288 		this.type = type;
       
 10289 		this.point = point;
       
 10290 		this.target = target;
       
 10291 		this.delta = delta;
       
 10292 	},
       
 10293 
       
 10294 	toString: function() {
       
 10295 		return "{ type: '" + this.type
       
 10296 				+ "', point: " + this.point
       
 10297 				+ ', target: ' + this.target
       
 10298 				+ (this.delta ? ', delta: ' + this.delta : '')
       
 10299 				+ ', modifiers: ' + this.getModifiers()
       
 10300 				+ ' }';
       
 10301 	}
       
 10302 });
       
 10303 
       
 10304  Base.extend(Callback, {
       
 10305 	_class: 'Palette',
       
 10306 	_events: [ 'onChange' ],
       
 10307 
       
 10308 	initialize: function Palette(title, components, values) {
       
 10309 		var parent = DomElement.find('.palettejs-panel')
       
 10310 			|| DomElement.find('body').appendChild(
       
 10311 				DomElement.create('div', { 'class': 'palettejs-panel' }));
       
 10312 		this._element = parent.appendChild(
       
 10313 			DomElement.create('table', { 'class': 'palettejs-pane' }));
       
 10314 		this._title = title;
       
 10315 		if (!values)
       
 10316 			values = {};
       
 10317 		for (var name in (this.components = components)) {
       
 10318 			var component = components[name];
       
 10319 			if (!(component instanceof Component)) {
       
 10320 				if (component.value == null)
       
 10321 					component.value = values[name];
       
 10322 				component.name = name;
       
 10323 				component = components[name] = new Component(component);
       
 10324 			}
       
 10325 			this._element.appendChild(component._element);
       
 10326 			component._palette = this;
       
 10327 			if (values[name] === undefined)
       
 10328 				values[name] = component.value;
       
 10329 		}
       
 10330 		this.values = Base.each(values, function(value, name) {
       
 10331 			var component = components[name];
       
 10332 			if (component) {
       
 10333 				Base.define(values, name, {
       
 10334 					enumerable: true,
       
 10335 					configurable: true,
       
 10336 					get: function() {
       
 10337 						return component._value;
       
 10338 					},
       
 10339 					set: function(val) {
       
 10340 						component.setValue(val);
       
 10341 					}
       
 10342 				});
       
 10343 			}
       
 10344 		});
       
 10345 		if (window.paper)
       
 10346 			paper.palettes.push(this);
       
 10347 	},
       
 10348 
       
 10349 	reset: function() {
       
 10350 		for (var i in this.components)
       
 10351 			this.components[i].reset();
       
 10352 	},
       
 10353 
       
 10354 	remove: function() {
       
 10355 		DomElement.remove(this._element);
       
 10356 	}
       
 10357 });
       
 10358 
       
 10359 var Component = Base.extend(Callback, {
       
 10360 	_class: 'Component',
       
 10361 	_events: [ 'onChange', 'onClick' ],
       
 10362 
       
 10363 	_types: {
       
 10364 		'boolean': {
       
 10365 			type: 'checkbox',
       
 10366 			value: 'checked'
       
 10367 		},
       
 10368 
       
 10369 		string: {
       
 10370 			type: 'text'
       
 10371 		},
       
 10372 
       
 10373 		number: {
       
 10374 			type: 'number',
       
 10375 			number: true
       
 10376 		},
       
 10377 
       
 10378 		button: {
       
 10379 			type: 'button'
       
 10380 		},
       
 10381 
       
 10382 		text: {
       
 10383 			tag: 'div',
       
 10384 			value: 'text'
       
 10385 		},
       
 10386 
       
 10387 		slider: {
       
 10388 			type: 'range',
       
 10389 			number: true
       
 10390 		},
       
 10391 
       
 10392 		list: {
       
 10393 			tag: 'select',
       
 10394 
       
 10395 			setOptions: function() {
       
 10396 				DomElement.removeChildren(this._input);
       
 10397 				DomElement.create(Base.each(this._options, function(option) {
       
 10398 					this.push('option', { value: option, text: option });
       
 10399 				}, []), this._input);
       
 10400 			}
       
 10401 		},
       
 10402 
       
 10403 		color: {
       
 10404 			type: 'color',
       
 10405 
       
 10406 			getValue: function(value) {
       
 10407 				return new Color(value);
       
 10408 			},
       
 10409 
       
 10410 			setValue: function(value) {
       
 10411 				return new Color(value).toCSS(
       
 10412 						DomElement.get(this._input, 'type') === 'color');
       
 10413 			}
       
 10414 		}
       
 10415 	},
       
 10416 
       
 10417 	initialize: function Component(obj) {
       
 10418 		this._id = Component._id = (Component._id || 0) + 1;
       
 10419 		this._type = obj.type in this._types
       
 10420 			? obj.type
       
 10421 			: 'options' in obj
       
 10422 				? 'list'
       
 10423 				: 'onClick' in obj
       
 10424 					? 'button'
       
 10425 					: typeof obj.value;
       
 10426 		this._meta = this._types[this._type] || { type: this._type };
       
 10427 		var that = this,
       
 10428 			id = 'component-' + this._id;
       
 10429 		this._dontFire = true;
       
 10430 		this._input = DomElement.create(this._meta.tag || 'input', {
       
 10431 			id: id,
       
 10432 			type: this._meta.type,
       
 10433 			events: {
       
 10434 				change: function() {
       
 10435 					that.setValue(
       
 10436 						DomElement.get(this, that._meta.value || 'value'));
       
 10437 				},
       
 10438 				click: function() {
       
 10439 					that.fire('click');
       
 10440 				}
       
 10441 			}
       
 10442 		});
       
 10443 		this.attach('change', function(value) {
       
 10444 			if (!this._dontFire)
       
 10445 				this._palette.fire('change', this, this.name, value);
       
 10446 		});
       
 10447 		this._element = DomElement.create('tr', [
       
 10448 			'td', [this._label = DomElement.create('label', { 'for': id })],
       
 10449 			'td', [this._input]
       
 10450 		]);
       
 10451 		Base.each(obj, function(value, key) {
       
 10452 			this[key] = value;
       
 10453 		}, this);
       
 10454 		this._defaultValue = this._value;
       
 10455 		this._dontFire = false;
       
 10456 	},
       
 10457 
       
 10458 	getType: function() {
       
 10459 		return this._type;
       
 10460 	},
       
 10461 
       
 10462 	getLabel: function() {
       
 10463 		return this.__label;
       
 10464 	},
       
 10465 
       
 10466 	setLabel: function(label) {
       
 10467 		this.__label = label;
       
 10468 		DomElement.set(this._label, 'text', label + ':');
       
 10469 	},
       
 10470 
       
 10471 	getOptions: function() {
       
 10472 		return this._options;
       
 10473 	},
       
 10474 
       
 10475 	setOptions: function(options) {
       
 10476 		this._options = options;
       
 10477 		var setOptions = this._meta.setOptions;
       
 10478 		if (setOptions)
       
 10479 			setOptions.call(this);
       
 10480 	},
       
 10481 
       
 10482 	getValue: function() {
       
 10483 		var value = this._value,
       
 10484 			getValue = this._meta.getValue;
       
 10485 		return getValue ? getValue.call(this, value) : value;
       
 10486 	},
       
 10487 
       
 10488 	setValue: function(value) {
       
 10489 		var key = this._meta.value || 'value',
       
 10490 			setValue = this._meta.setValue;
       
 10491 		if (setValue)
       
 10492 			value = setValue.call(this, value);
       
 10493 		DomElement.set(this._input, key, value);
       
 10494 		value = DomElement.get(this._input, key);
       
 10495 		if (this._meta.number)
       
 10496 			value = parseFloat(value, 10);
       
 10497 		if (this._value !== value) {
       
 10498 			this._value = value;
       
 10499 			if (!this._dontFire)
       
 10500 				this.fire('change', this.getValue());
       
 10501 		}
       
 10502 	},
       
 10503 
       
 10504 	getRange: function() {
       
 10505 		return [parseFloat(DomElement.get(this._input, 'min')),
       
 10506 				parseFloat(DomElement.get(this._input, 'max'))];
       
 10507 	},
       
 10508 
       
 10509 	setRange: function(min, max) {
       
 10510 		var range = Array.isArray(min) ? min : [min, max];
       
 10511 		DomElement.set(this._input, { min: range[0], max: range[1] });
       
 10512 	},
       
 10513 
       
 10514 	getMin: function() {
       
 10515 		return this.getRange()[0];
       
 10516 	},
       
 10517 
       
 10518 	setMin: function(min) {
       
 10519 		this.setRange(min, this.getMax());
       
 10520 	},
       
 10521 
       
 10522 	getMax: function() {
       
 10523 		return this.getRange()[1];
       
 10524 	},
       
 10525 
       
 10526 	setMax: function(max) {
       
 10527 		this.setRange(this.getMin(), max);
       
 10528 	},
       
 10529 
       
 10530 	getStep: function() {
       
 10531 		return parseFloat(DomElement.get(this._input, 'step'));
       
 10532 	},
       
 10533 
       
 10534 	setStep: function(step) {
       
 10535 		DomElement.set(this._input, 'step', step);
       
 10536 	},
       
 10537 
       
 10538 	reset: function() {
       
 10539 		this.setValue(this._defaultValue);
       
 10540 	}
       
 10541 });
       
 10542 
       
 10543 var ToolEvent = Event.extend({
       
 10544 	_class: 'ToolEvent',
       
 10545 	_item: null,
       
 10546 
       
 10547 	initialize: function ToolEvent(tool, type, event) {
       
 10548 		this.tool = tool;
       
 10549 		this.type = type;
       
 10550 		this.event = event;
       
 10551 	},
       
 10552 
       
 10553 	_choosePoint: function(point, toolPoint) {
       
 10554 		return point ? point : toolPoint ? toolPoint.clone() : null;
       
 10555 	},
       
 10556 
       
 10557 	getPoint: function() {
       
 10558 		return this._choosePoint(this._point, this.tool._point);
       
 10559 	},
       
 10560 
       
 10561 	setPoint: function(point) {
       
 10562 		this._point = point;
       
 10563 	},
       
 10564 
       
 10565 	getLastPoint: function() {
       
 10566 		return this._choosePoint(this._lastPoint, this.tool._lastPoint);
       
 10567 	},
       
 10568 
       
 10569 	setLastPoint: function(lastPoint) {
       
 10570 		this._lastPoint = lastPoint;
       
 10571 	},
       
 10572 
       
 10573 	getDownPoint: function() {
       
 10574 		return this._choosePoint(this._downPoint, this.tool._downPoint);
       
 10575 	},
       
 10576 
       
 10577 	setDownPoint: function(downPoint) {
       
 10578 		this._downPoint = downPoint;
       
 10579 	},
       
 10580 
       
 10581 	getMiddlePoint: function() {
       
 10582 		if (!this._middlePoint && this.tool._lastPoint) {
       
 10583 			return this.tool._point.add(this.tool._lastPoint).divide(2);
       
 10584 		}
       
 10585 		return this._middlePoint;
       
 10586 	},
       
 10587 
       
 10588 	setMiddlePoint: function(middlePoint) {
       
 10589 		this._middlePoint = middlePoint;
       
 10590 	},
       
 10591 
       
 10592 	getDelta: function() {
       
 10593 		return !this._delta && this.tool._lastPoint
       
 10594 		 		? this.tool._point.subtract(this.tool._lastPoint)
       
 10595 				: this._delta;
       
 10596 	},
       
 10597 
       
 10598 	setDelta: function(delta) {
       
 10599 		this._delta = delta;
       
 10600 	},
       
 10601 
       
 10602 	getCount: function() {
       
 10603 		return /^mouse(down|up)$/.test(this.type)
       
 10604 				? this.tool._downCount
       
 10605 				: this.tool._count;
       
 10606 	},
       
 10607 
       
 10608 	setCount: function(count) {
       
 10609 		this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
       
 10610 			= count;
       
 10611 	},
       
 10612 
       
 10613 	getItem: function() {
       
 10614 		if (!this._item) {
       
 10615 			var result = this.tool._scope.project.hitTest(this.getPoint());
       
 10616 			if (result) {
       
 10617 				var item = result.item,
       
 10618 					parent = item._parent;
       
 10619 				while (/^(group|compound-path)$/.test(parent._type)) {
       
 10620 					item = parent;
       
 10621 					parent = parent._parent;
       
 10622 				}
       
 10623 				this._item = item;
       
 10624 			}
       
 10625 		}
       
 10626 		return this._item;
       
 10627 	},
       
 10628 	setItem: function(item) {
       
 10629 		this._item = item;
       
 10630 	},
       
 10631 
       
 10632 	toString: function() {
       
 10633 		return '{ type: ' + this.type
       
 10634 				+ ', point: ' + this.getPoint()
       
 10635 				+ ', count: ' + this.getCount()
       
 10636 				+ ', modifiers: ' + this.getModifiers()
       
 10637 				+ ' }';
       
 10638 	}
       
 10639 });
       
 10640 
       
 10641 var Tool = PaperScopeItem.extend({
       
 10642 	_class: 'Tool',
       
 10643 	_list: 'tools',
       
 10644 	_reference: '_tool', 
       
 10645 	_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
       
 10646 			'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
       
 10647 			'onKeyDown', 'onKeyUp' ],
       
 10648 
       
 10649 	initialize: function Tool(props) {
       
 10650 		PaperScopeItem.call(this);
       
 10651 		this._firstMove = true;
       
 10652 		this._count = 0;
       
 10653 		this._downCount = 0;
       
 10654 		this._set(props);
       
 10655 	},
       
 10656 
       
 10657 	getMinDistance: function() {
       
 10658 		return this._minDistance;
       
 10659 	},
       
 10660 
       
 10661 	setMinDistance: function(minDistance) {
       
 10662 		this._minDistance = minDistance;
       
 10663 		if (this._minDistance != null && this._maxDistance != null
       
 10664 				&& this._minDistance > this._maxDistance) {
       
 10665 			this._maxDistance = this._minDistance;
       
 10666 		}
       
 10667 	},
       
 10668 
       
 10669 	getMaxDistance: function() {
       
 10670 		return this._maxDistance;
       
 10671 	},
       
 10672 
       
 10673 	setMaxDistance: function(maxDistance) {
       
 10674 		this._maxDistance = maxDistance;
       
 10675 		if (this._minDistance != null && this._maxDistance != null
       
 10676 				&& this._maxDistance < this._minDistance) {
       
 10677 			this._minDistance = maxDistance;
       
 10678 		}
       
 10679 	},
       
 10680 
       
 10681 	getFixedDistance: function() {
       
 10682 		return this._minDistance == this._maxDistance
       
 10683 			? this._minDistance : null;
       
 10684 	},
       
 10685 
       
 10686 	setFixedDistance: function(distance) {
       
 10687 		this._minDistance = distance;
       
 10688 		this._maxDistance = distance;
       
 10689 	},
       
 10690 
       
 10691 	_updateEvent: function(type, point, minDistance, maxDistance, start,
       
 10692 			needsChange, matchMaxDistance) {
       
 10693 		if (!start) {
       
 10694 			if (minDistance != null || maxDistance != null) {
       
 10695 				var minDist = minDistance != null ? minDistance : 0,
       
 10696 					vector = point.subtract(this._point),
       
 10697 					distance = vector.getLength();
       
 10698 				if (distance < minDist)
       
 10699 					return false;
       
 10700 				var maxDist = maxDistance != null ? maxDistance : 0;
       
 10701 				if (maxDist != 0) {
       
 10702 					if (distance > maxDist) {
       
 10703 						point = this._point.add(vector.normalize(maxDist));
       
 10704 					} else if (matchMaxDistance) {
       
 10705 						return false;
       
 10706 					}
       
 10707 				}
       
 10708 			}
       
 10709 			if (needsChange && point.equals(this._point))
       
 10710 				return false;
       
 10711 		}
       
 10712 		this._lastPoint = start && type == 'mousemove' ? point : this._point;
       
 10713 		this._point = point;
       
 10714 		switch (type) {
       
 10715 		case 'mousedown':
       
 10716 			this._lastPoint = this._downPoint;
       
 10717 			this._downPoint = this._point;
       
 10718 			this._downCount++;
       
 10719 			break;
       
 10720 		case 'mouseup':
       
 10721 			this._lastPoint = this._downPoint;
       
 10722 			break;
       
 10723 		}
       
 10724 		this._count = start ? 0 : this._count + 1;
       
 10725 		return true;
       
 10726 	},
       
 10727 
       
 10728 	_fireEvent: function(type, event) {
       
 10729 		var sets = paper.project._removeSets;
       
 10730 		if (sets) {
       
 10731 			if (type === 'mouseup')
       
 10732 				sets.mousedrag = null;
       
 10733 			var set = sets[type];
       
 10734 			if (set) {
       
 10735 				for (var id in set) {
       
 10736 					var item = set[id];
       
 10737 					for (var key in sets) {
       
 10738 						var other = sets[key];
       
 10739 						if (other && other != set)
       
 10740 							delete other[item._id];
       
 10741 					}
       
 10742 					item.remove();
       
 10743 				}
       
 10744 				sets[type] = null;
       
 10745 			}
       
 10746 		}
       
 10747 		return this.responds(type)
       
 10748 				&& this.fire(type, new ToolEvent(this, type, event));
       
 10749 	},
       
 10750 
       
 10751 	_onHandleEvent: function(type, point, event) {
       
 10752 		paper = this._scope;
       
 10753 		var called = false;
       
 10754 		switch (type) {
       
 10755 		case 'mousedown':
       
 10756 			this._updateEvent(type, point, null, null, true, false, false);
       
 10757 			called = this._fireEvent(type, event);
       
 10758 			break;
       
 10759 		case 'mousedrag':
       
 10760 			var needsChange = false,
       
 10761 				matchMaxDistance = false;
       
 10762 			while (this._updateEvent(type, point, this.minDistance,
       
 10763 					this.maxDistance, false, needsChange, matchMaxDistance)) {
       
 10764 				called = this._fireEvent(type, event) || called;
       
 10765 				needsChange = true;
       
 10766 				matchMaxDistance = true;
       
 10767 			}
       
 10768 			break;
       
 10769 		case 'mouseup':
       
 10770 			if (!point.equals(this._point)
       
 10771 					&& this._updateEvent('mousedrag', point, this.minDistance,
       
 10772 							this.maxDistance, false, false, false)) {
       
 10773 				called = this._fireEvent('mousedrag', event);
       
 10774 			}
       
 10775 			this._updateEvent(type, point, null, this.maxDistance, false,
       
 10776 					false, false);
       
 10777 			called = this._fireEvent(type, event) || called;
       
 10778 			this._updateEvent(type, point, null, null, true, false, false);
       
 10779 			this._firstMove = true;
       
 10780 			break;
       
 10781 		case 'mousemove':
       
 10782 			while (this._updateEvent(type, point, this.minDistance,
       
 10783 					this.maxDistance, this._firstMove, true, false)) {
       
 10784 				called = this._fireEvent(type, event) || called;
       
 10785 				this._firstMove = false;
       
 10786 			}
       
 10787 			break;
       
 10788 		}
       
 10789 		return called;
       
 10790 	}
       
 10791 
       
 10792 });
       
 10793 
       
 10794 var Http = {
       
 10795 	request: function(method, url, callback) {
       
 10796 		var xhr = new (window.ActiveXObject || XMLHttpRequest)(
       
 10797 					'Microsoft.XMLHTTP');
       
 10798 		xhr.open(method.toUpperCase(), url, true);
       
 10799 		if ('overrideMimeType' in xhr)
       
 10800 			xhr.overrideMimeType('text/plain');
       
 10801 		xhr.onreadystatechange = function() {
       
 10802 			if (xhr.readyState === 4) {
       
 10803 				var status = xhr.status;
       
 10804 				if (status === 0 || status === 200) {
       
 10805 					callback.call(xhr, xhr.responseText);
       
 10806 				} else {
       
 10807 					throw new Error('Could not load ' + url + ' (Error '
       
 10808 							+ status + ')');
       
 10809 				}
       
 10810 			}
       
 10811 		};
       
 10812 		return xhr.send(null);
       
 10813 	}
       
 10814 };
       
 10815 
       
 10816 var CanvasProvider = {
       
 10817 	canvases: [],
       
 10818 
       
 10819 	getCanvas: function(width, height, ratio) {
       
 10820 		var canvas,
       
 10821 			init = true;
       
 10822 		if (typeof width === 'object') {
       
 10823 			ratio = height;
       
 10824 			height = width.height;
       
 10825 			width = width.width;
       
 10826 		}
       
 10827 		if (!ratio) {
       
 10828 			ratio = 1;
       
 10829 		} else if (ratio !== 1) {
       
 10830 			width *= ratio;
       
 10831 			height *= ratio;
       
 10832 		}
       
 10833 		if (this.canvases.length) {
       
 10834 			canvas = this.canvases.pop();
       
 10835 		} else {
       
 10836 			canvas = document.createElement('canvas');
       
 10837 		}
       
 10838 		var ctx = canvas.getContext('2d');
       
 10839 		if (canvas.width === width && canvas.height === height) {
       
 10840 			if (init)
       
 10841 				ctx.clearRect(0, 0, width + 1, height + 1);
       
 10842 		} else {
       
 10843 			canvas.width = width;
       
 10844 			canvas.height = height;
       
 10845 		}
       
 10846 		ctx.save();
       
 10847 		if (ratio !== 1)
       
 10848 			ctx.scale(ratio, ratio);
       
 10849 		return canvas;
       
 10850 	},
       
 10851 
       
 10852 	getContext: function(width, height) {
       
 10853 		return this.getCanvas(width, height).getContext('2d');
       
 10854 	},
       
 10855 
       
 10856 	release: function(obj) {
       
 10857 		var canvas = obj.canvas ? obj.canvas : obj;
       
 10858 		canvas.getContext('2d').restore();
       
 10859 		this.canvases.push(canvas);
       
 10860 	}
       
 10861 };
       
 10862 
       
 10863 var BlendMode = new function() {
       
 10864 	var min = Math.min,
       
 10865 		max = Math.max,
       
 10866 		abs = Math.abs,
       
 10867 		sr, sg, sb, sa, 
       
 10868 		br, bg, bb, ba, 
       
 10869 		dr, dg, db;     
       
 10870 
       
 10871 	function getLum(r, g, b) {
       
 10872 		return 0.2989 * r + 0.587 * g + 0.114 * b;
       
 10873 	}
       
 10874 
       
 10875 	function setLum(r, g, b, l) {
       
 10876 		var d = l - getLum(r, g, b);
       
 10877 		dr = r + d;
       
 10878 		dg = g + d;
       
 10879 		db = b + d;
       
 10880 		var l = getLum(dr, dg, db),
       
 10881 			mn = min(dr, dg, db),
       
 10882 			mx = max(dr, dg, db);
       
 10883 		if (mn < 0) {
       
 10884 			var lmn = l - mn;
       
 10885 			dr = l + (dr - l) * l / lmn;
       
 10886 			dg = l + (dg - l) * l / lmn;
       
 10887 			db = l + (db - l) * l / lmn;
       
 10888 		}
       
 10889 		if (mx > 255) {
       
 10890 			var ln = 255 - l,
       
 10891 				mxl = mx - l;
       
 10892 			dr = l + (dr - l) * ln / mxl;
       
 10893 			dg = l + (dg - l) * ln / mxl;
       
 10894 			db = l + (db - l) * ln / mxl;
       
 10895 		}
       
 10896 	}
       
 10897 
       
 10898 	function getSat(r, g, b) {
       
 10899 		return max(r, g, b) - min(r, g, b);
       
 10900 	}
       
 10901 
       
 10902 	function setSat(r, g, b, s) {
       
 10903 		var col = [r, g, b],
       
 10904 			mx = max(r, g, b), 
       
 10905 			mn = min(r, g, b), 
       
 10906 			md; 
       
 10907 		mn = mn === r ? 0 : mn === g ? 1 : 2;
       
 10908 		mx = mx === r ? 0 : mx === g ? 1 : 2;
       
 10909 		md = min(mn, mx) === 0 ? max(mn, mx) === 1 ? 2 : 1 : 0;
       
 10910 		if (col[mx] > col[mn]) {
       
 10911 			col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]);
       
 10912 			col[mx] = s;
       
 10913 		} else {
       
 10914 			col[md] = col[mx] = 0;
       
 10915 		}
       
 10916 		col[mn] = 0;
       
 10917 		dr = col[0];
       
 10918 		dg = col[1];
       
 10919 		db = col[2];
       
 10920 	}
       
 10921 
       
 10922 	var modes = {
       
 10923 		multiply: function() {
       
 10924 			dr = br * sr / 255;
       
 10925 			dg = bg * sg / 255;
       
 10926 			db = bb * sb / 255;
       
 10927 		},
       
 10928 
       
 10929 		screen: function() {
       
 10930 			dr = br + sr - (br * sr / 255);
       
 10931 			dg = bg + sg - (bg * sg / 255);
       
 10932 			db = bb + sb - (bb * sb / 255);
       
 10933 		},
       
 10934 
       
 10935 		overlay: function() {
       
 10936 			dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255;
       
 10937 			dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255;
       
 10938 			db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255;
       
 10939 		},
       
 10940 
       
 10941 		'soft-light': function() {
       
 10942 			var t = sr * br / 255;
       
 10943 			dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255;
       
 10944 			t = sg * bg / 255;
       
 10945 			dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255;
       
 10946 			t = sb * bb / 255;
       
 10947 			db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255;
       
 10948 		},
       
 10949 
       
 10950 		'hard-light': function() {
       
 10951 			dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255;
       
 10952 			dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255;
       
 10953 			db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255;
       
 10954 		},
       
 10955 
       
 10956 		'color-dodge': function() {
       
 10957 			dr = br === 0 ? 0 : sr === 255 ? 255 : min(255, 255 * br / (255 - sr));
       
 10958 			dg = bg === 0 ? 0 : sg === 255 ? 255 : min(255, 255 * bg / (255 - sg));
       
 10959 			db = bb === 0 ? 0 : sb === 255 ? 255 : min(255, 255 * bb / (255 - sb));
       
 10960 		},
       
 10961 
       
 10962 		'color-burn': function() {
       
 10963 			dr = br === 255 ? 255 : sr === 0 ? 0 : max(0, 255 - (255 - br) * 255 / sr);
       
 10964 			dg = bg === 255 ? 255 : sg === 0 ? 0 : max(0, 255 - (255 - bg) * 255 / sg);
       
 10965 			db = bb === 255 ? 255 : sb === 0 ? 0 : max(0, 255 - (255 - bb) * 255 / sb);
       
 10966 		},
       
 10967 
       
 10968 		darken: function() {
       
 10969 			dr = br < sr ? br : sr;
       
 10970 			dg = bg < sg ? bg : sg;
       
 10971 			db = bb < sb ? bb : sb;
       
 10972 		},
       
 10973 
       
 10974 		lighten: function() {
       
 10975 			dr = br > sr ? br : sr;
       
 10976 			dg = bg > sg ? bg : sg;
       
 10977 			db = bb > sb ? bb : sb;
       
 10978 		},
       
 10979 
       
 10980 		difference: function() {
       
 10981 			dr = br - sr;
       
 10982 			if (dr < 0)
       
 10983 				dr = -dr;
       
 10984 			dg = bg - sg;
       
 10985 			if (dg < 0)
       
 10986 				dg = -dg;
       
 10987 			db = bb - sb;
       
 10988 			if (db < 0)
       
 10989 				db = -db;
       
 10990 		},
       
 10991 
       
 10992 		exclusion: function() {
       
 10993 			dr = br + sr * (255 - br - br) / 255;
       
 10994 			dg = bg + sg * (255 - bg - bg) / 255;
       
 10995 			db = bb + sb * (255 - bb - bb) / 255;
       
 10996 		},
       
 10997 
       
 10998 		hue: function() {
       
 10999 			setSat(sr, sg, sb, getSat(br, bg, bb));
       
 11000 			setLum(dr, dg, db, getLum(br, bg, bb));
       
 11001 		},
       
 11002 
       
 11003 		saturation: function() {
       
 11004 			setSat(br, bg, bb, getSat(sr, sg, sb));
       
 11005 			setLum(dr, dg, db, getLum(br, bg, bb));
       
 11006 		},
       
 11007 
       
 11008 		luminosity: function() {
       
 11009 			setLum(br, bg, bb, getLum(sr, sg, sb));
       
 11010 		},
       
 11011 
       
 11012 		color: function() {
       
 11013 			setLum(sr, sg, sb, getLum(br, bg, bb));
       
 11014 		},
       
 11015 
       
 11016 		add: function() {
       
 11017 			dr = min(br + sr, 255);
       
 11018 			dg = min(bg + sg, 255);
       
 11019 			db = min(bb + sb, 255);
       
 11020 		},
       
 11021 
       
 11022 		subtract: function() {
       
 11023 			dr = max(br - sr, 0);
       
 11024 			dg = max(bg - sg, 0);
       
 11025 			db = max(bb - sb, 0);
       
 11026 		},
       
 11027 
       
 11028 		average: function() {
       
 11029 			dr = (br + sr) / 2;
       
 11030 			dg = (bg + sg) / 2;
       
 11031 			db = (bb + sb) / 2;
       
 11032 		},
       
 11033 
       
 11034 		negation: function() {
       
 11035 			dr = 255 - abs(255 - sr - br);
       
 11036 			dg = 255 - abs(255 - sg - bg);
       
 11037 			db = 255 - abs(255 - sb - bb);
       
 11038 		}
       
 11039 	};
       
 11040 
       
 11041 	var nativeModes = this.nativeModes = Base.each([
       
 11042 		'source-over', 'source-in', 'source-out', 'source-atop',
       
 11043 		'destination-over', 'destination-in', 'destination-out',
       
 11044 		'destination-atop', 'lighter', 'darker', 'copy', 'xor'
       
 11045 	], function(mode) {
       
 11046 		this[mode] = true;
       
 11047 	}, {});
       
 11048 
       
 11049 	var ctx = CanvasProvider.getContext(1, 1);
       
 11050 	Base.each(modes, function(func, mode) {
       
 11051 		ctx.save();
       
 11052 		var darken = mode === 'darken',
       
 11053 			ok = false;
       
 11054 		ctx.fillStyle = darken ? '#300' : '#a00';
       
 11055 		ctx.fillRect(0, 0, 1, 1);
       
 11056 		ctx.globalCompositeOperation = mode;
       
 11057 		if (ctx.globalCompositeOperation === mode) {
       
 11058 			ctx.fillStyle = darken ? '#a00' : '#300';
       
 11059 			ctx.fillRect(0, 0, 1, 1);
       
 11060 			ok = ctx.getImageData(0, 0, 1, 1).data[0] !== (darken ? 170 : 51);
       
 11061 		}
       
 11062 		nativeModes[mode] = ok; 
       
 11063 		ctx.restore();
       
 11064 	});
       
 11065 	CanvasProvider.release(ctx);
       
 11066 
       
 11067 	this.process = function(mode, srcContext, dstContext, alpha, offset) {
       
 11068 		var srcCanvas = srcContext.canvas,
       
 11069 			normal = mode === 'normal';
       
 11070 		if (normal || nativeModes[mode]) {
       
 11071 			dstContext.save();
       
 11072 			dstContext.setTransform(1, 0, 0, 1, 0, 0);
       
 11073 			dstContext.globalAlpha = alpha;
       
 11074 			if (!normal)
       
 11075 				dstContext.globalCompositeOperation = mode;
       
 11076 			dstContext.drawImage(srcCanvas, offset.x, offset.y);
       
 11077 			dstContext.restore();	
       
 11078 		} else {
       
 11079 			var process = modes[mode];
       
 11080 			if (!process)
       
 11081 				return;
       
 11082 			var dstData = dstContext.getImageData(offset.x, offset.y,
       
 11083 					srcCanvas.width, srcCanvas.height),
       
 11084 				dst  = dstData.data,
       
 11085 				src  = srcContext.getImageData(0, 0,
       
 11086 					srcCanvas.width, srcCanvas.height).data;
       
 11087 			for (var i = 0, l = dst.length; i < l; i += 4) {
       
 11088 				sr = src[i];
       
 11089 				br = dst[i];
       
 11090 				sg = src[i + 1];
       
 11091 				bg = dst[i + 1];
       
 11092 				sb = src[i + 2];
       
 11093 				bb = dst[i + 2];
       
 11094 				sa = src[i + 3];
       
 11095 				ba = dst[i + 3];
       
 11096 				process();
       
 11097 				var a1 = sa * alpha / 255,
       
 11098 					a2 = 1 - a1;
       
 11099 				dst[i] = a1 * dr + a2 * br;
       
 11100 				dst[i + 1] = a1 * dg + a2 * bg;
       
 11101 				dst[i + 2] = a1 * db + a2 * bb;
       
 11102 				dst[i + 3] = sa * alpha + a2 * ba;
       
 11103 			}
       
 11104 			dstContext.putImageData(dstData, offset.x, offset.y);
       
 11105 		}
       
 11106 	};
       
 11107 };
       
 11108 
       
 11109 var SVGStyles = Base.each({
       
 11110 	fillColor: ['fill', 'color'],
       
 11111 	strokeColor: ['stroke', 'color'],
       
 11112 	strokeWidth: ['stroke-width', 'number'],
       
 11113 	strokeCap: ['stroke-linecap', 'string'],
       
 11114 	strokeJoin: ['stroke-linejoin', 'string'],
       
 11115 	miterLimit: ['stroke-miterlimit', 'number'],
       
 11116 	dashArray: ['stroke-dasharray', 'array'],
       
 11117 	dashOffset: ['stroke-dashoffset', 'number'],
       
 11118 	font: ['font-family', 'string'],
       
 11119 	fontSize: ['font-size', 'number'],
       
 11120 	justification: ['text-anchor', 'lookup', {
       
 11121 		left: 'start',
       
 11122 		center: 'middle',
       
 11123 		right: 'end'
       
 11124 	}],
       
 11125 	opacity: ['opacity', 'number'],
       
 11126 	blendMode: ['mix-blend-mode', 'string']
       
 11127 }, function(entry, key) {
       
 11128 	var part = Base.capitalize(key),
       
 11129 		lookup = entry[2];
       
 11130 	this[key] = {
       
 11131 		type: entry[1],
       
 11132 		property: key,
       
 11133 		attribute: entry[0],
       
 11134 		toSVG: lookup,
       
 11135 		fromSVG: lookup && Base.each(lookup, function(value, name) {
       
 11136 			this[value] = name;
       
 11137 		}, {}),
       
 11138 		get: 'get' + part,
       
 11139 		set: 'set' + part
       
 11140 	};
       
 11141 }, {});
       
 11142 
       
 11143 var SVGNamespaces = {
       
 11144 	href: 'http://www.w3.org/1999/xlink',
       
 11145 	xlink: 'http://www.w3.org/2000/xmlns'
       
 11146 };
       
 11147 
       
 11148 new function() {
       
 11149 	var formatter;
       
 11150 
       
 11151 	function setAttributes(node, attrs) {
       
 11152 		for (var key in attrs) {
       
 11153 			var val = attrs[key],
       
 11154 				namespace = SVGNamespaces[key];
       
 11155 			if (typeof val === 'number')
       
 11156 				val = formatter.number(val);
       
 11157 			if (namespace) {
       
 11158 				node.setAttributeNS(namespace, key, val);
       
 11159 			} else {
       
 11160 				node.setAttribute(key, val);
       
 11161 			}
       
 11162 		}
       
 11163 		return node;
       
 11164 	}
       
 11165 
       
 11166 	function createElement(tag, attrs) {
       
 11167 		return setAttributes(
       
 11168 			document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
       
 11169 	}
       
 11170 
       
 11171 	function getTransform(item, coordinates, center) {
       
 11172 		var matrix = item._matrix,
       
 11173 			trans = matrix.getTranslation(),
       
 11174 			attrs = {};
       
 11175 		if (coordinates) {
       
 11176 			matrix = matrix.shiftless();
       
 11177 			var point = matrix._inverseTransform(trans);
       
 11178 			attrs[center ? 'cx' : 'x'] = point.x;
       
 11179 			attrs[center ? 'cy' : 'y'] = point.y;
       
 11180 			trans = null;
       
 11181 		}
       
 11182 		if (matrix.isIdentity())
       
 11183 			return attrs;
       
 11184 		var decomposed = matrix.decompose();
       
 11185 		if (decomposed && !decomposed.shearing) {
       
 11186 			var parts = [],
       
 11187 				angle = decomposed.rotation,
       
 11188 				scale = decomposed.scaling;
       
 11189 			if (trans && !trans.isZero())
       
 11190 				parts.push('translate(' + formatter.point(trans) + ')');
       
 11191 			if (!Numerical.isZero(scale.x - 1) || !Numerical.isZero(scale.y - 1))
       
 11192 				parts.push('scale(' + formatter.point(scale) +')');
       
 11193 			if (angle)
       
 11194 				parts.push('rotate(' + formatter.number(angle) + ')');
       
 11195 			attrs.transform = parts.join(' ');
       
 11196 		} else {
       
 11197 			attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
       
 11198 		}
       
 11199 		return attrs;
       
 11200 	}
       
 11201 
       
 11202 	function exportGroup(item, options) {
       
 11203 		var attrs = getTransform(item),
       
 11204 			children = item._children;
       
 11205 		var node = createElement('g', attrs);
       
 11206 		for (var i = 0, l = children.length; i < l; i++) {
       
 11207 			var child = children[i];
       
 11208 			var childNode = exportSVG(child, options);
       
 11209 			if (childNode) {
       
 11210 				if (child.isClipMask()) {
       
 11211 					var clip = createElement('clipPath');
       
 11212 					clip.appendChild(childNode);
       
 11213 					setDefinition(child, clip, 'clip');
       
 11214 					setAttributes(node, {
       
 11215 						'clip-path': 'url(#' + clip.id + ')'
       
 11216 					});
       
 11217 				} else {
       
 11218 					node.appendChild(childNode);
       
 11219 				}
       
 11220 			}
       
 11221 		}
       
 11222 		return node;
       
 11223 	}
       
 11224 
       
 11225 	function exportRaster(item) {
       
 11226 		var attrs = getTransform(item, true),
       
 11227 			size = item.getSize();
       
 11228 		attrs.x -= size.width / 2;
       
 11229 		attrs.y -= size.height / 2;
       
 11230 		attrs.width = size.width;
       
 11231 		attrs.height = size.height;
       
 11232 		attrs.href = item.toDataURL();
       
 11233 		return createElement('image', attrs);
       
 11234 	}
       
 11235 
       
 11236 	function exportPath(item, options) {
       
 11237 		if (options.matchShapes) {
       
 11238 			var shape = item.toShape(false);
       
 11239 			if (shape)
       
 11240 				return exportShape(shape, options);
       
 11241 		}
       
 11242 		var segments = item._segments,
       
 11243 			type,
       
 11244 			attrs;
       
 11245 		if (segments.length === 0)
       
 11246 			return null;
       
 11247 		if (item.isPolygon()) {
       
 11248 			if (segments.length >= 3) {
       
 11249 				type = item._closed ? 'polygon' : 'polyline';
       
 11250 				var parts = [];
       
 11251 				for(i = 0, l = segments.length; i < l; i++)
       
 11252 					parts.push(formatter.point(segments[i]._point));
       
 11253 				attrs = {
       
 11254 					points: parts.join(' ')
       
 11255 				};
       
 11256 			} else {
       
 11257 				type = 'line';
       
 11258 				var first = segments[0]._point,
       
 11259 					last = segments[segments.length - 1]._point;
       
 11260 				attrs = {
       
 11261 					x1: first.x,
       
 11262 					y1: first.y,
       
 11263 					x2: last.x,
       
 11264 					y2: last.y
       
 11265 				};
       
 11266 			}
       
 11267 		} else {
       
 11268 			type = 'path';
       
 11269 			var data = item.getPathData();
       
 11270 			attrs = data && { d: data };
       
 11271 		}
       
 11272 		return createElement(type, attrs);
       
 11273 	}
       
 11274 
       
 11275 	function exportShape(item) {
       
 11276 		var shape = item._shape,
       
 11277 			radius = item._radius,
       
 11278 			attrs = getTransform(item, true, shape !== 'rectangle');
       
 11279 		if (shape === 'rectangle') {
       
 11280 			shape = 'rect'; 
       
 11281 			var size = item._size,
       
 11282 				width = size.width,
       
 11283 				height = size.height;
       
 11284 			attrs.x -= width / 2;
       
 11285 			attrs.y -= height / 2;
       
 11286 			attrs.width = width;
       
 11287 			attrs.height = height;
       
 11288 			if (radius.isZero())
       
 11289 				radius = null;
       
 11290 		}
       
 11291 		if (radius) {
       
 11292 			if (shape === 'circle') {
       
 11293 				attrs.r = radius;
       
 11294 			} else {
       
 11295 				attrs.rx = radius.width;
       
 11296 				attrs.ry = radius.height;
       
 11297 			}
       
 11298 		}
       
 11299 		return createElement(shape, attrs);
       
 11300 	}
       
 11301 
       
 11302 	function exportCompoundPath(item) {
       
 11303 		var attrs = getTransform(item, true);
       
 11304 		var data = item.getPathData();
       
 11305 		if (data)
       
 11306 			attrs.d = data;
       
 11307 		return createElement('path', attrs);
       
 11308 	}
       
 11309 
       
 11310 	function exportPlacedSymbol(item, options) {
       
 11311 		var attrs = getTransform(item, true),
       
 11312 			symbol = item.getSymbol(),
       
 11313 			symbolNode = getDefinition(symbol, 'symbol'),
       
 11314 			definition = symbol.getDefinition(),
       
 11315 			bounds = definition.getBounds();
       
 11316 		if (!symbolNode) {
       
 11317 			symbolNode = createElement('symbol', {
       
 11318 				viewBox: formatter.rectangle(bounds)
       
 11319 			});
       
 11320 			symbolNode.appendChild(exportSVG(definition, options));
       
 11321 			setDefinition(symbol, symbolNode, 'symbol');
       
 11322 		}
       
 11323 		attrs.href = '#' + symbolNode.id;
       
 11324 		attrs.x += bounds.x;
       
 11325 		attrs.y += bounds.y;
       
 11326 		attrs.width = formatter.number(bounds.width);
       
 11327 		attrs.height = formatter.number(bounds.height);
       
 11328 		return createElement('use', attrs);
       
 11329 	}
       
 11330 
       
 11331 	function exportGradient(color) {
       
 11332 		var gradientNode = getDefinition(color, 'color');
       
 11333 		if (!gradientNode) {
       
 11334 			var gradient = color.getGradient(),
       
 11335 				radial = gradient._radial,
       
 11336 				origin = color.getOrigin().transform(),
       
 11337 				destination = color.getDestination().transform(),
       
 11338 				attrs;
       
 11339 			if (radial) {
       
 11340 				attrs = {
       
 11341 					cx: origin.x,
       
 11342 					cy: origin.y,
       
 11343 					r: origin.getDistance(destination)
       
 11344 				};
       
 11345 				var highlight = color.getHighlight();
       
 11346 				if (highlight) {
       
 11347 					highlight = highlight.transform();
       
 11348 					attrs.fx = highlight.x;
       
 11349 					attrs.fy = highlight.y;
       
 11350 				}
       
 11351 			} else {
       
 11352 				attrs = {
       
 11353 					x1: origin.x,
       
 11354 					y1: origin.y,
       
 11355 					x2: destination.x,
       
 11356 					y2: destination.y
       
 11357 				};
       
 11358 			}
       
 11359 			attrs.gradientUnits = 'userSpaceOnUse';
       
 11360 			gradientNode = createElement(
       
 11361 					(radial ? 'radial' : 'linear') + 'Gradient', attrs);
       
 11362 			var stops = gradient._stops;
       
 11363 			for (var i = 0, l = stops.length; i < l; i++) {
       
 11364 				var stop = stops[i],
       
 11365 					stopColor = stop._color,
       
 11366 					alpha = stopColor.getAlpha();
       
 11367 				attrs = {
       
 11368 					offset: stop._rampPoint,
       
 11369 					'stop-color': stopColor.toCSS(true)
       
 11370 				};
       
 11371 				if (alpha < 1)
       
 11372 					attrs['stop-opacity'] = alpha;
       
 11373 				gradientNode.appendChild(createElement('stop', attrs));
       
 11374 			}
       
 11375 			setDefinition(color, gradientNode, 'color');
       
 11376 		}
       
 11377 		return 'url(#' + gradientNode.id + ')';
       
 11378 	}
       
 11379 
       
 11380 	function exportText(item) {
       
 11381 		var node = createElement('text', getTransform(item, true));
       
 11382 		node.textContent = item._content;
       
 11383 		return node;
       
 11384 	}
       
 11385 
       
 11386 	var exporters = {
       
 11387 		group: exportGroup,
       
 11388 		layer: exportGroup,
       
 11389 		raster: exportRaster,
       
 11390 		path: exportPath,
       
 11391 		shape: exportShape,
       
 11392 		'compound-path': exportCompoundPath,
       
 11393 		'placed-symbol': exportPlacedSymbol,
       
 11394 		'point-text': exportText
       
 11395 	};
       
 11396 
       
 11397 	function applyStyle(item, node) {
       
 11398 		var attrs = {},
       
 11399 			parent = item.getParent();
       
 11400 
       
 11401 		if (item._name != null)
       
 11402 			attrs.id = item._name;
       
 11403 
       
 11404 		Base.each(SVGStyles, function(entry) {
       
 11405 			var get = entry.get,
       
 11406 				type = entry.type,
       
 11407 				value = item[get]();
       
 11408 			if (!parent || !Base.equals(parent[get](), value)) {
       
 11409 				if (type === 'color' && value != null) {
       
 11410 					var alpha = value.getAlpha();
       
 11411 					if (alpha < 1)
       
 11412 						attrs[entry.attribute + '-opacity'] = alpha;
       
 11413 				}
       
 11414 				attrs[entry.attribute] = value == null
       
 11415 					? 'none'
       
 11416 					: type === 'number'
       
 11417 						? formatter.number(value)
       
 11418 						: type === 'color'
       
 11419 							? value.gradient
       
 11420 								? exportGradient(value, item)
       
 11421 								: value.toCSS(true)
       
 11422 							: type === 'array'
       
 11423 								? value.join(',')
       
 11424 								: type === 'lookup'
       
 11425 									? entry.toSVG[value]
       
 11426 									: value;
       
 11427 			}
       
 11428 		});
       
 11429 
       
 11430 		if (attrs.opacity === 1)
       
 11431 			delete attrs.opacity;
       
 11432 
       
 11433 		if (item._visibility != null && !item._visibility)
       
 11434 			attrs.visibility = 'hidden';
       
 11435 
       
 11436 		return setAttributes(node, attrs);
       
 11437 	}
       
 11438 
       
 11439 	var definitions;
       
 11440 	function getDefinition(item, type) {
       
 11441 		if (!definitions)
       
 11442 			definitions = { ids: {}, svgs: {} };
       
 11443 		return item && definitions.svgs[type + '-' + item._id];
       
 11444 	}
       
 11445 
       
 11446 	function setDefinition(item, node, type) {
       
 11447 		if (!definitions)
       
 11448 			getDefinition();
       
 11449 		var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
       
 11450 		node.id = type + '-' + id;
       
 11451 		definitions.svgs[type + '-' + item._id] = node;
       
 11452 	}
       
 11453 
       
 11454 	function exportDefinitions(node, options) {
       
 11455 		var svg = node,
       
 11456 			defs = null;
       
 11457 		if (definitions) {
       
 11458 			svg = node.nodeName.toLowerCase() === 'svg' && node;
       
 11459 			for (var i in definitions.svgs) {
       
 11460 				if (!defs) {
       
 11461 					if (!svg) {
       
 11462 						svg = createElement('svg');
       
 11463 						svg.appendChild(node);
       
 11464 					}
       
 11465 					defs = svg.insertBefore(createElement('defs'),
       
 11466 							svg.firstChild);
       
 11467 				}
       
 11468 				defs.appendChild(definitions.svgs[i]);
       
 11469 			}
       
 11470 			definitions = null;
       
 11471 		}
       
 11472 		return options.asString
       
 11473 				? new XMLSerializer().serializeToString(svg)
       
 11474 				: svg;
       
 11475 	}
       
 11476 
       
 11477 	function exportSVG(item, options) {
       
 11478 		var exporter = exporters[item._type],
       
 11479 			node = exporter && exporter(item, options);
       
 11480 		if (node && item._data)
       
 11481 			node.setAttribute('data-paper-data', JSON.stringify(item._data));
       
 11482 		return node && applyStyle(item, node);
       
 11483 	}
       
 11484 
       
 11485 	function setOptions(options) {
       
 11486 		if (!options)
       
 11487 			options = {};
       
 11488 		formatter = new Formatter(options.precision);
       
 11489 		return options;
       
 11490 	}
       
 11491 
       
 11492 	Item.inject({
       
 11493 		exportSVG: function(options) {
       
 11494 			options = setOptions(options);
       
 11495 			return exportDefinitions(exportSVG(this, options), options);
       
 11496 		}
       
 11497 	});
       
 11498 
       
 11499 	Project.inject({
       
 11500 		exportSVG: function(options) {
       
 11501 			options = setOptions(options);
       
 11502 			var layers = this.layers,
       
 11503 				size = this.view.getSize(),
       
 11504 				node = createElement('svg', {
       
 11505 					x: 0,
       
 11506 					y: 0,
       
 11507 					width: size.width,
       
 11508 					height: size.height,
       
 11509 					version: '1.1',
       
 11510 					xmlns: 'http://www.w3.org/2000/svg',
       
 11511 					'xmlns:xlink': 'http://www.w3.org/1999/xlink'
       
 11512 				});
       
 11513 			for (var i = 0, l = layers.length; i < l; i++)
       
 11514 				node.appendChild(exportSVG(layers[i], options));
       
 11515 			return exportDefinitions(node, options);
       
 11516 		}
       
 11517 	});
       
 11518 };
       
 11519 
       
 11520 new function() {
       
 11521 
       
 11522 	function getValue(node, name, isString, allowNull) {
       
 11523 		var namespace = SVGNamespaces[name],
       
 11524 			value = namespace
       
 11525 				? node.getAttributeNS(namespace, name)
       
 11526 				: node.getAttribute(name);
       
 11527 		if (value === 'null')
       
 11528 			value = null;
       
 11529 		return value == null
       
 11530 				? allowNull
       
 11531 					? null
       
 11532 					: isString
       
 11533 						? ''
       
 11534 						: 0
       
 11535 				: isString
       
 11536 					? value
       
 11537 					: parseFloat(value);
       
 11538 	}
       
 11539 
       
 11540 	function getPoint(node, x, y, allowNull) {
       
 11541 		x = getValue(node, x, false, allowNull);
       
 11542 		y = getValue(node, y, false, allowNull);
       
 11543 		return allowNull && (x == null || y == null) ? null
       
 11544 				: new Point(x, y);
       
 11545 	}
       
 11546 
       
 11547 	function getSize(node, w, h, allowNull) {
       
 11548 		w = getValue(node, w, false, allowNull);
       
 11549 		h = getValue(node, h, false, allowNull);
       
 11550 		return allowNull && (w == null || h == null) ? null
       
 11551 				: new Size(w, h);
       
 11552 	}
       
 11553 
       
 11554 	function convertValue(value, type, lookup) {
       
 11555 		return value === 'none'
       
 11556 				? null
       
 11557 				: type === 'number'
       
 11558 					? parseFloat(value)
       
 11559 					: type === 'array'
       
 11560 						? value ? value.split(/[\s,]+/g).map(parseFloat) : []
       
 11561 						: type === 'color'
       
 11562 							? getDefinition(value) || value
       
 11563 							: type === 'lookup'
       
 11564 								? lookup[value]
       
 11565 								: value;
       
 11566 	}
       
 11567 
       
 11568 	function importGroup(node, type, isRoot, options) {
       
 11569 		var nodes = node.childNodes,
       
 11570 			isClip = type === 'clippath',
       
 11571 			item = new Group(),
       
 11572 			project = item._project,
       
 11573 			currentStyle = project._currentStyle,
       
 11574 			children = [];
       
 11575 		if (!isClip) {
       
 11576 			item._transformContent = false;
       
 11577 			item = applyAttributes(item, node, isRoot);
       
 11578 			project._currentStyle = item._style.clone();
       
 11579 		}
       
 11580 		for (var i = 0, l = nodes.length; i < l; i++) {
       
 11581 			var childNode = nodes[i],
       
 11582 				child;
       
 11583 			if (childNode.nodeType === 1
       
 11584 					&& (child = importSVG(childNode, false, options))
       
 11585 					&& !(child instanceof Symbol))
       
 11586 				children.push(child);
       
 11587 		}
       
 11588 		item.addChildren(children);
       
 11589 		if (isClip)
       
 11590 			item = applyAttributes(item.reduce(), node, isRoot);
       
 11591 		project._currentStyle = currentStyle;
       
 11592 		if (isClip || type === 'defs') {
       
 11593 			item.remove();
       
 11594 			item = null;
       
 11595 		}
       
 11596 		return item;
       
 11597 	}
       
 11598 
       
 11599 	function importPoly(node, type) {
       
 11600 		var path = new Path(),
       
 11601 			points = node.points;
       
 11602 		path.moveTo(points.getItem(0));
       
 11603 		for (var i = 1, l = points.numberOfItems; i < l; i++)
       
 11604 			path.lineTo(points.getItem(i));
       
 11605 		if (type === 'polygon')
       
 11606 			path.closePath();
       
 11607 		return path;
       
 11608 	}
       
 11609 
       
 11610 	function importPath(node) {
       
 11611 		var data = node.getAttribute('d'),
       
 11612 			path = data.match(/m/gi).length > 1
       
 11613 					? new CompoundPath()
       
 11614 					: new Path();
       
 11615 		path.setPathData(data);
       
 11616 		return path;
       
 11617 	}
       
 11618 
       
 11619 	function importGradient(node, type) {
       
 11620 		var nodes = node.childNodes,
       
 11621 			stops = [];
       
 11622 		for (var i = 0, l = nodes.length; i < l; i++) {
       
 11623 			var child = nodes[i];
       
 11624 			if (child.nodeType === 1)
       
 11625 				stops.push(applyAttributes(new GradientStop(), child));
       
 11626 		}
       
 11627 		var isRadial = type === 'radialgradient',
       
 11628 			gradient = new Gradient(stops, isRadial),
       
 11629 			origin, destination, highlight;
       
 11630 		if (isRadial) {
       
 11631 			origin = getPoint(node, 'cx', 'cy');
       
 11632 			destination = origin.add(getValue(node, 'r'), 0);
       
 11633 			highlight = getPoint(node, 'fx', 'fy', true);
       
 11634 		} else {
       
 11635 			origin = getPoint(node, 'x1', 'y1');
       
 11636 			destination = getPoint(node, 'x2', 'y2');
       
 11637 		}
       
 11638 		applyAttributes(
       
 11639 			new Color(gradient, origin, destination, highlight), node);
       
 11640 		return null;
       
 11641 	}
       
 11642 
       
 11643 	var importers = {
       
 11644 		'#document': function (node, type, isRoot, options) {
       
 11645 			var nodes = node.childNodes;
       
 11646 			for (var i = 0, l = nodes.length; i < l; i++) {
       
 11647 				var child = nodes[i];
       
 11648 				if (child.nodeType === 1) {
       
 11649 					var next = child.nextSibling;
       
 11650 					document.body.appendChild(child);
       
 11651 					var item = importSVG(child, isRoot, options);
       
 11652 					if (next) {
       
 11653 						node.insertBefore(child, next);
       
 11654 					} else {
       
 11655 						node.appendChild(child);
       
 11656 					}
       
 11657 					return item;
       
 11658 				}
       
 11659 			}
       
 11660 		},
       
 11661 		g: importGroup,
       
 11662 		svg: importGroup,
       
 11663 		clippath: importGroup,
       
 11664 		polygon: importPoly,
       
 11665 		polyline: importPoly,
       
 11666 		path: importPath,
       
 11667 		lineargradient: importGradient,
       
 11668 		radialgradient: importGradient,
       
 11669 
       
 11670 		image: function (node) {
       
 11671 			var raster = new Raster(getValue(node, 'href', true));
       
 11672 			raster.attach('load', function() {
       
 11673 				var size = getSize(node, 'width', 'height');
       
 11674 				this.setSize(size);
       
 11675 				var center = this._matrix._transformPoint(
       
 11676 						getPoint(node, 'x', 'y').add(size.divide(2)));
       
 11677 				this.translate(center);
       
 11678 			});
       
 11679 			return raster;
       
 11680 		},
       
 11681 
       
 11682 		symbol: function(node, type, isRoot, options) {
       
 11683 			return new Symbol(importGroup(node, type, isRoot, options), true);
       
 11684 		},
       
 11685 
       
 11686 		defs: importGroup,
       
 11687 
       
 11688 		use: function(node) {
       
 11689 			var id = (getValue(node, 'href', true) || '').substring(1),
       
 11690 				definition = definitions[id],
       
 11691 				point = getPoint(node, 'x', 'y');
       
 11692 			return definition
       
 11693 					? definition instanceof Symbol
       
 11694 						? definition.place(point)
       
 11695 						: definition.clone().translate(point)
       
 11696 					: null;
       
 11697 		},
       
 11698 
       
 11699 		circle: function(node) {
       
 11700 			return new Shape.Circle(getPoint(node, 'cx', 'cy'),
       
 11701 					getValue(node, 'r'));
       
 11702 		},
       
 11703 
       
 11704 		ellipse: function(node) {
       
 11705 			return new Shape.Ellipse({
       
 11706 				center: getPoint(node, 'cx', 'cy'),
       
 11707 				radius: getSize(node, 'rx', 'ry')
       
 11708 			});
       
 11709 		},
       
 11710 
       
 11711 		rect: function(node) {
       
 11712 			var point = getPoint(node, 'x', 'y'),
       
 11713 				size = getSize(node, 'width', 'height'),
       
 11714 				radius = getSize(node, 'rx', 'ry');
       
 11715 			return new Shape.Rectangle(new Rectangle(point, size), radius);
       
 11716 		},
       
 11717 
       
 11718 		line: function(node) {
       
 11719 			return new Path.Line(getPoint(node, 'x1', 'y1'),
       
 11720 					getPoint(node, 'x2', 'y2'));
       
 11721 		},
       
 11722 
       
 11723 		text: function(node) {
       
 11724 			var text = new PointText(getPoint(node, 'x', 'y')
       
 11725 					.add(getPoint(node, 'dx', 'dy')));
       
 11726 			text.setContent(node.textContent.trim() || '');
       
 11727 			return text;
       
 11728 		}
       
 11729 	};
       
 11730 
       
 11731 	function applyTransform(item, value, name, node) {
       
 11732 		var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
       
 11733 			matrix = new Matrix();
       
 11734 		for (var i = 0, l = transforms.length; i < l; i++) {
       
 11735 			var transform = transforms[i];
       
 11736 			if (!transform)
       
 11737 				break;
       
 11738 			var parts = transform.split('('),
       
 11739 				command = parts[0],
       
 11740 				v = parts[1].split(/[\s,]+/g);
       
 11741 			for (var j = 0, m = v.length; j < m; j++)
       
 11742 				v[j] = parseFloat(v[j]);
       
 11743 			switch (command) {
       
 11744 			case 'matrix':
       
 11745 				matrix.concatenate(
       
 11746 						new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
       
 11747 				break;
       
 11748 			case 'rotate':
       
 11749 				matrix.rotate(v[0], v[1], v[2]);
       
 11750 				break;
       
 11751 			case 'translate':
       
 11752 				matrix.translate(v[0], v[1]);
       
 11753 				break;
       
 11754 			case 'scale':
       
 11755 				matrix.scale(v);
       
 11756 				break;
       
 11757 			case 'skewX':
       
 11758 			case 'skewY':
       
 11759 				var value = Math.tan(v[0] * Math.PI / 180),
       
 11760 					isX = command == 'skewX';
       
 11761 				matrix.shear(isX ? value : 0, isX ? 0 : value);
       
 11762 				break;
       
 11763 			}
       
 11764 		}
       
 11765 		item.transform(matrix);
       
 11766 	}
       
 11767 
       
 11768 	function applyOpacity(item, value, name) {
       
 11769 		var color = item[name === 'fill-opacity' ? 'getFillColor'
       
 11770 				: 'getStrokeColor']();
       
 11771 		if (color)
       
 11772 			color.setAlpha(parseFloat(value));
       
 11773 	}
       
 11774 
       
 11775 	var attributes = Base.each(SVGStyles, function(entry) {
       
 11776 		this[entry.attribute] = function(item, value) {
       
 11777 			item[entry.set](convertValue(value, entry.type, entry.fromSVG));
       
 11778 			if (entry.type === 'color' && item instanceof Shape) {
       
 11779 				var color = item[entry.get]();
       
 11780 				if (color)
       
 11781 					color.transform(new Matrix().translate(
       
 11782 							item.getPosition(true).negate()));
       
 11783 			}
       
 11784 		};
       
 11785 	}, {
       
 11786 		id: function(item, value) {
       
 11787 			definitions[value] = item;
       
 11788 			if (item.setName)
       
 11789 				item.setName(value);
       
 11790 		},
       
 11791 
       
 11792 		'clip-path': function(item, value) {
       
 11793 			var clip = getDefinition(value);
       
 11794 			if (clip) {
       
 11795 				clip = clip.clone();
       
 11796 				clip.setClipMask(true);
       
 11797 				if (item instanceof Group) {
       
 11798 					item.insertChild(0, clip);
       
 11799 				} else {
       
 11800 					return new Group(clip, item);
       
 11801 				}
       
 11802 			}
       
 11803 		},
       
 11804 
       
 11805 		gradientTransform: applyTransform,
       
 11806 		transform: applyTransform,
       
 11807 
       
 11808 		'fill-opacity': applyOpacity,
       
 11809 		'stroke-opacity': applyOpacity,
       
 11810 
       
 11811 		visibility: function(item, value) {
       
 11812 			item.setVisible(value === 'visible');
       
 11813 		},
       
 11814 
       
 11815 		'stop-color': function(item, value) {
       
 11816 			if (item.setColor)
       
 11817 				item.setColor(value);
       
 11818 		},
       
 11819 
       
 11820 		'stop-opacity': function(item, value) {
       
 11821 			if (item._color)
       
 11822 				item._color.setAlpha(parseFloat(value));
       
 11823 		},
       
 11824 
       
 11825 		offset: function(item, value) {
       
 11826 			var percentage = value.match(/(.*)%$/);
       
 11827 			item.setRampPoint(percentage
       
 11828 					? percentage[1] / 100
       
 11829 					: parseFloat(value));
       
 11830 		},
       
 11831 
       
 11832 		viewBox: function(item, value, name, node, styles) {
       
 11833 			var rect = new Rectangle(convertValue(value, 'array')),
       
 11834 				size = getSize(node, 'width', 'height', true);
       
 11835 			if (item instanceof Group) {
       
 11836 				var scale = size ? rect.getSize().divide(size) : 1,
       
 11837 					matrix = new Matrix().translate(rect.getPoint()).scale(scale);
       
 11838 				item.transform(matrix.inverted());
       
 11839 			} else if (item instanceof Symbol) {
       
 11840 				if (size)
       
 11841 					rect.setSize(size);
       
 11842 				var clip = getAttribute(node, 'overflow', styles) != 'visible',
       
 11843 					group = item._definition;
       
 11844 				if (clip && !rect.contains(group.getBounds())) {
       
 11845 					clip = new Shape.Rectangle(rect).transform(group._matrix);
       
 11846 					clip.setClipMask(true);
       
 11847 					group.addChild(clip);
       
 11848 				}
       
 11849 			}
       
 11850 		}
       
 11851 	});
       
 11852 
       
 11853 	function getAttribute(node, name, styles) {
       
 11854 		var attr = node.attributes[name],
       
 11855 			value = attr && attr.value;
       
 11856 		if (!value) {
       
 11857 			var style = Base.camelize(name);
       
 11858 			value = node.style[style];
       
 11859 			if (!value && styles.node[style] !== styles.parent[style])
       
 11860 				value = styles.node[style];
       
 11861 		}
       
 11862 		return !value
       
 11863 				? undefined
       
 11864 				: value === 'none'
       
 11865 					? null
       
 11866 					: value;
       
 11867 	}
       
 11868 
       
 11869 	function applyAttributes(item, node, isRoot) {
       
 11870 		var styles = {
       
 11871 			node: DomElement.getStyles(node) || {},
       
 11872 			parent: !isRoot && DomElement.getStyles(node.parentNode) || {}
       
 11873 		};
       
 11874 		Base.each(attributes, function(apply, name) {
       
 11875 			var value = getAttribute(node, name, styles);
       
 11876 			if (value !== undefined)
       
 11877 				item = Base.pick(apply(item, value, name, node, styles), item);
       
 11878 		});
       
 11879 		return item;
       
 11880 	}
       
 11881 
       
 11882 	var definitions = {};
       
 11883 	function getDefinition(value) {
       
 11884 		var match = value && value.match(/\((?:#|)([^)']+)/);
       
 11885 		return match && definitions[match[1]];
       
 11886 	}
       
 11887 
       
 11888 	function importSVG(source, isRoot, options) {
       
 11889 		if (!source)
       
 11890 			return null;
       
 11891 		if (!options) {
       
 11892 			options = {};
       
 11893 		} else if (typeof options === 'function') {
       
 11894 			options = { onLoad: options };
       
 11895 		}
       
 11896 
       
 11897 		var node = source,
       
 11898 			scope = paper;
       
 11899 
       
 11900 		function onLoadCallback(svg) {
       
 11901 			paper = scope;
       
 11902 			var item = importSVG(svg, isRoot, options),
       
 11903 				onLoad = options.onLoad,
       
 11904 				view = scope.project && scope.project.view;
       
 11905 			if (onLoad)
       
 11906 				onLoad.call(this, item);
       
 11907 			view.draw(true);
       
 11908 		}
       
 11909 
       
 11910 		if (isRoot) {
       
 11911 			if (typeof source === 'string' && !/^.*</.test(source)) {
       
 11912 				node = document.getElementById(source);
       
 11913 				if (node) {
       
 11914 					source = null;
       
 11915 				} else {
       
 11916 					return Http.request('get', source, onLoadCallback);
       
 11917 				}
       
 11918 			} else if (typeof File !== 'undefined' && source instanceof File) {
       
 11919 				var reader = new FileReader();
       
 11920 				reader.onload = function() {
       
 11921 					onLoadCallback(reader.result);
       
 11922 				};
       
 11923 				return reader.readAsText(source);
       
 11924 			}
       
 11925 		}
       
 11926 
       
 11927 		if (typeof source === 'string')
       
 11928 			node = new DOMParser().parseFromString(source, 'image/svg+xml');
       
 11929 		if (!node.nodeName)
       
 11930 			throw new Error('Unsupported SVG source: ' + source);
       
 11931 		var type = node.nodeName.toLowerCase(),
       
 11932 			importer = importers[type],
       
 11933 			item = importer && importer(node, type, isRoot, options) || null,
       
 11934 			data = node.getAttribute && node.getAttribute('data-paper-data');
       
 11935 		if (item) {
       
 11936 			if (!(item instanceof Group))
       
 11937 				item = applyAttributes(item, node, isRoot);
       
 11938 			if (options.expandShapes && item instanceof Shape) {
       
 11939 				item.remove();
       
 11940 				item = item.toPath();
       
 11941 			}
       
 11942 			if (data)
       
 11943 				item._data = JSON.parse(data);
       
 11944 		}
       
 11945 		if (isRoot)
       
 11946 			definitions = {};
       
 11947 		return item;
       
 11948 	}
       
 11949 
       
 11950 	Item.inject({
       
 11951 		importSVG: function(node, options) {
       
 11952 			return this.addChild(importSVG(node, true, options));
       
 11953 		}
       
 11954 	});
       
 11955 
       
 11956 	Project.inject({
       
 11957 		importSVG: function(node, options) {
       
 11958 			this.activate();
       
 11959 			return importSVG(node, true, options);
       
 11960 		}
       
 11961 	});
       
 11962 };
       
 11963 
       
 11964 paper = new (PaperScope.inject(new Base(Base.exports, {
       
 11965 	enumerable: true,
       
 11966 	Base: Base,
       
 11967 	Numerical: Numerical,
       
 11968 	DomElement: DomElement,
       
 11969 	DomEvent: DomEvent,
       
 11970 	Http: Http,
       
 11971 	Key: Key
       
 11972 })))();
       
 11973 
       
 11974 if (typeof define === 'function' && define.amd)
       
 11975 	define('paper', paper);
       
 11976 
       
 11977 return paper;
       
 11978 };
       
 11979 
       
 11980 paper.PaperScope.prototype.PaperScript = (function(root) {
       
 11981 	var Base = paper.Base,
       
 11982 		PaperScope = paper.PaperScope,
       
 11983 		PaperScript,
       
 11984 		exports, define,
       
 11985 		scope = this;
       
 11986 !function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in hr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=hr[r]);mr=fr.sourceFile||null}function t(e,r){var t=vr(pr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=pr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(pr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,pr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=pr.charCodeAt(br+=2);dr>br&&10!==t&&13!==t&&8232!==t&&8329!==t;)++br,t=pr.charCodeAt(br);fr.onComment&&fr.onComment(!1,pr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;dr>br;){var e=pr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=pr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e)++br,++Ar,Sr=br;else if(14>e&&e>8)++br;else if(47===e){var r=pr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=pr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=pr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function p(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Ft,1)}function d(e){var r=pr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Vt,1)}function m(){var e=pr.charCodeAt(br+1);return 61===e?x(Et,2):x(Tt,1)}function h(e){var r=pr.charCodeAt(br+1);return r===e?x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=pr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===pr.charCodeAt(br+2)?3:2,61===pr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):(61===r&&(t=61===pr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=pr.charCodeAt(br+1);return 61===r?x(qt,61===pr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(ht);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(pt);case 123:return++br,i(dt);case 125:return++br,i(mt);case 58:return++br,i(gt);case 63:return++br,i(kt);case 48:var r=pr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return p();case 124:case 38:return d(e);case 94:return m();case 43:case 45:return h(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=dr)return i(Br);var r=pr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=pr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=dr&&t(a,"Unterminated regular expression");var o=pr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=pr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=pr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===pr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===pr.charCodeAt(br)&&(++br,w(10),n=!0);var o=pr.charCodeAt(br);(69===o||101===o)&&(o=pr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(pr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=pr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Vr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=dr&&t(yr,"Unterminated string constant");var n=pr.charCodeAt(br);if(n===e)return++br,i(Fr,r);if(92===n){n=pr.charCodeAt(++br);var a=/^[0-7]+/.exec(pr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Vr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+="	";break;case 98:r+="\b";break;case 118:r+="";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===pr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8329===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=pr.charCodeAt(br);if(Yt(a))Bt&&(e+=pr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=pr.slice(n,br)),Bt=!0,117!=pr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:pr.slice(n,br)}function L(){var e=I(),r=Dr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Vr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){for(Vr=e,br=Lr;Sr>br;)Sr=pr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function T(){this.type=null,this.start=yr,this.end=null}function V(){this.start=xr,this.end=null,null!==mr&&(this.source=mr)}function q(){var e=new T;return fr.locations&&(e.loc=new V),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new T;return r.start=e.start,fr.locations&&(r.loc=new V,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function F(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function D(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===mt||Gt.test(pr.slice(Lr,yr)))}function M(){D(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Vr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Vr=null,Tr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&F(n)&&R(!0),t=!1}return j(r,"Program")}function J(){wr===wt&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;D(yt)||B()?r.label=null:wr!==Dr?X():(r.label=lr(),M());for(var a=0;a<Tr.length;++a){var o=Tr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Tr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Tr.push(Zt),r.body=J(),Tr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Tr.push(Zt),z(ht),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),1===i.declarations.length&&D(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return D(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=D(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),D(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(dt),Tr.push(en);for(var s,c;wr!=mt;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Tr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(pr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(ht),l.param=lr(),Vr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=D($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),r=G(r),M(),r;case tt:return U(),r.test=P(),Tr.push(Zt),r.body=J(),Tr.pop(),j(r,"WhileStatement");case nt:return Vr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case dt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,p=K();if(e===Dr&&"Identifier"===p.type&&D(gt)){for(var a=0;a<Tr.length;++a)Tr[a].name===f&&t(p.start,"Label '"+f+"' is already declared");var d=wr.isLoop?"loop":wr===Yr?"switch":null;return Tr.push({name:f,kind:d}),r.body=J(),Tr.pop(),r.label=p,j(r,"LabeledStatement")}return r.expression=p,M(),j(r,"ExpressionStatement")}}function P(){z(ht);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(dt);!D(mt);){var o=J();t.body.push(o),n&&e&&F(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Tr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Vr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=D(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!D(bt))break}return j(e,"VariableDeclaration")}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];D(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(D(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var a=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(a,r,t)}return e}function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,U(),e.argument=rr(),r?N(e.argument):Vr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(D(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(D(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(pt),nr(j(t,"MemberExpression"),r)}if(!r&&D(ht)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Dr:return lr();case Or:case Fr:case jr:var e=q();return e.value=Cr,e.raw=pr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case ht:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(pt,!0,!0),j(e,"ArrayExpression");case dt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=D(ht)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!D(mt);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&D(mt))break;var a,o={key:sr()},i=!1;if(D(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==ht&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Vr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Vr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Fr?ar():lr(!0)}function cr(e,r){wr===Dr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(ht);!D(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Tr;if(Rr=!0,Tr=[],e.body=H(!0),Rr=a,Tr=o,Vr||e.body.body.length&&F(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!D(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&D(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Dr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),U(),j(r,"Identifier")}e.version="0.3.2";var fr,pr,dr,mr;e.parse=function(e,t){return pr=String(e),dr=pr.length,r(t),o(),W(fr.program)};var hr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}pr=String(e),dr=pr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(pr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Tr,Vr,qr=[],Or={type:"num"},jr={type:"regexp"},Fr={type:"string"},Dr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"},tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},pt={type:"]"},dt={type:"{",beforeExpr:!0},mt={type:"}"},ht={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Tt={binop:4,beforeExpr:!0},Vt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Ft={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:pt,braceL:dt,braceR:mt,parenL:ht,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Dr,eof:Br,num:Or,regexp:jr,string:Fr};for(var Dt in lt)e.tokTypes["_"+Dt]=lt[Dt];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc",Ht="\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f",$t=new RegExp("["+Pt+"]"),_t=new RegExp("["+Pt+Ht+"]"),Gt=/[\n\r\u2028\u2029]/,Kt=/\r\n|[\n\r\u2028\u2029]/g,Qt=e.isIdentifierStart=function(e){return 65>e?36===e:91>e?!0:97>e?95===e:123>e?!0:e>=170&&$t.test(String.fromCharCode(e))},Yt=e.isIdentifierChar=function(e){return 48>e?36===e:58>e?!0:65>e?!1:91>e?!0:97>e?95===e:123>e?!0:e>=170&&_t.test(String.fromCharCode(e))},Zt={kind:"loop"},en={kind:"switch"}});
       
 11987 
       
 11988 	var binaryOperators = {
       
 11989 		'+': '_add',
       
 11990 		'-': '_subtract',
       
 11991 		'*': '_multiply',
       
 11992 		'/': '_divide',
       
 11993 		'%': '_modulo',
       
 11994 		'==': 'equals',
       
 11995 		'!=': 'equals'
       
 11996 	};
       
 11997 
       
 11998 	var unaryOperators = {
       
 11999 		'-': '_negate',
       
 12000 		'+': null
       
 12001 	};
       
 12002 
       
 12003 	var fields = Base.each(
       
 12004 		['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
       
 12005 		function(name) {
       
 12006 			this['_' + name] = '#' + name;
       
 12007 		}, 
       
 12008 		{}
       
 12009 	);
       
 12010 	paper.Point.inject(fields);
       
 12011 	paper.Size.inject(fields);
       
 12012 	paper.Color.inject(fields);
       
 12013 
       
 12014 	function _$_(left, operator, right) {
       
 12015 		var handler = binaryOperators[operator];
       
 12016 		if (left && left[handler]) {
       
 12017 			var res = left[handler](right);
       
 12018 			return operator === '!=' ? !res : res;
       
 12019 		}
       
 12020 		switch (operator) {
       
 12021 		case '+': return left + right;
       
 12022 		case '-': return left - right;
       
 12023 		case '*': return left * right;
       
 12024 		case '/': return left / right;
       
 12025 		case '%': return left % right;
       
 12026 		case '==': return left == right;
       
 12027 		case '!=': return left != right;
       
 12028 		}
       
 12029 	}
       
 12030 
       
 12031 	function $_(operator, value) {
       
 12032 		var handler = unaryOperators[operator];
       
 12033 		if (handler && value && value[handler])
       
 12034 			return value[handler]();
       
 12035 		switch (operator) {
       
 12036 		case '+': return +value;
       
 12037 		case '-': return -value;
       
 12038 		}
       
 12039 	}
       
 12040 
       
 12041 	function compile(code) {
       
 12042 
       
 12043 		var insertions = [];
       
 12044 
       
 12045 		function getOffset(offset) {
       
 12046 			for (var i = 0, l = insertions.length; i < l; i++) {
       
 12047 				var insertion = insertions[i];
       
 12048 				if (insertion[0] >= offset)
       
 12049 					break;
       
 12050 				offset += insertion[1];
       
 12051 			}
       
 12052 			return offset;
       
 12053 		}
       
 12054 
       
 12055 		function getCode(node) {
       
 12056 			return code.substring(getOffset(node.range[0]),
       
 12057 					getOffset(node.range[1]));
       
 12058 		}
       
 12059 
       
 12060 		function replaceCode(node, str) {
       
 12061 			var start = getOffset(node.range[0]),
       
 12062 				end = getOffset(node.range[1]);
       
 12063 			var insert = 0;
       
 12064 			for (var i = insertions.length - 1; i >= 0; i--) {
       
 12065 				if (start > insertions[i][0]) {
       
 12066 					insert = i + 1;
       
 12067 					break;
       
 12068 				}
       
 12069 			}
       
 12070 			insertions.splice(insert, 0, [start, str.length - end + start]);
       
 12071 			code = code.substring(0, start) + str + code.substring(end);
       
 12072 		}
       
 12073 
       
 12074 		function walkAST(node, parent) {
       
 12075 			if (!node)
       
 12076 				return;
       
 12077 			for (var key in node) {
       
 12078 				if (key === 'range')
       
 12079 					continue;
       
 12080 				var value = node[key];
       
 12081 				if (Array.isArray(value)) {
       
 12082 					for (var i = 0, l = value.length; i < l; i++)
       
 12083 						walkAST(value[i], node);
       
 12084 				} else if (value && typeof value === 'object') {
       
 12085 					walkAST(value, node);
       
 12086 				}
       
 12087 			}
       
 12088 			switch (node && node.type) {
       
 12089 			case 'BinaryExpression':
       
 12090 				if (node.operator in binaryOperators
       
 12091 						&& node.left.type !== 'Literal') {
       
 12092 					var left = getCode(node.left),
       
 12093 						right = getCode(node.right);
       
 12094 					replaceCode(node, '_$_(' + left + ', "' + node.operator
       
 12095 							+ '", ' + right + ')');
       
 12096 				}
       
 12097 				break;
       
 12098 			case 'AssignmentExpression':
       
 12099 				if (/^.=$/.test(node.operator)
       
 12100 						&& node.left.type !== 'Literal') {
       
 12101 					var left = getCode(node.left),
       
 12102 						right = getCode(node.right);
       
 12103 					replaceCode(node, left + ' = _$_(' + left + ', "'
       
 12104 							+ node.operator[0] + '", ' + right + ')');
       
 12105 				}
       
 12106 				break;
       
 12107 			case 'UpdateExpression':
       
 12108 				if (!node.prefix && !(parent && (
       
 12109 						parent.type === 'BinaryExpression'
       
 12110 							&& /^[=!<>]/.test(parent.operator)
       
 12111 						|| parent.type === 'MemberExpression'
       
 12112 							&& parent.computed))) {
       
 12113 					var arg = getCode(node.argument);
       
 12114 					replaceCode(node, arg + ' = _$_(' + arg + ', "'
       
 12115 							+ node.operator[0] + '", 1)');
       
 12116 				}
       
 12117 				break;
       
 12118 			case 'UnaryExpression':
       
 12119 				if (node.operator in unaryOperators
       
 12120 						&& node.argument.type !== 'Literal') {
       
 12121 					var arg = getCode(node.argument);
       
 12122 					replaceCode(node, '$_("' + node.operator + '", '
       
 12123 							+ arg + ')');
       
 12124 				}
       
 12125 				break;
       
 12126 			}
       
 12127 		}
       
 12128 		walkAST(scope.acorn.parse(code, { ranges: true }));
       
 12129 		return code;
       
 12130 	}
       
 12131 
       
 12132 	function evaluate(code, scope) {
       
 12133 		paper = scope;
       
 12134 		var view = scope.project && scope.project.view,
       
 12135 			res;
       
 12136 		with (scope) {
       
 12137 			(function() {
       
 12138 				var onActivate, onDeactivate, onEditOptions,
       
 12139 					onMouseDown, onMouseUp, onMouseDrag, onMouseMove,
       
 12140 					onKeyDown, onKeyUp, onFrame, onResize;
       
 12141 				code = compile(code);
       
 12142 				if (root.InstallTrigger) { 
       
 12143 					var handle = PaperScript.handleException;
       
 12144 					if (!handle) {
       
 12145 						handle = PaperScript.handleException = function(e) {
       
 12146 							throw e.lineNumber >= lineNumber
       
 12147 									? new Error(e.message, e.fileName,
       
 12148 										e.lineNumber - lineNumber)
       
 12149 									: e;
       
 12150 						}
       
 12151 						var lineNumber = new Error().lineNumber;
       
 12152 						lineNumber += (new Error().lineNumber - lineNumber) * 3;
       
 12153 					}
       
 12154 					try {
       
 12155 						res = eval(';' + code);
       
 12156 					} catch (e) {
       
 12157 						handle(e);
       
 12158 					}
       
 12159 				} else {
       
 12160 					res = eval(code);
       
 12161 				}
       
 12162 				if (/on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)) {
       
 12163 					Base.each(paper.Tool.prototype._events, function(key) {
       
 12164 						var value = eval(key);
       
 12165 						if (value) {
       
 12166 							scope.getTool()[key] = value;
       
 12167 						}
       
 12168 					});
       
 12169 				}
       
 12170 				if (view) {
       
 12171 					view.setOnResize(onResize);
       
 12172 					view.fire('resize', {
       
 12173 						size: view.size,
       
 12174 						delta: new Point()
       
 12175 					});
       
 12176 					if (onFrame)
       
 12177 						view.setOnFrame(onFrame);
       
 12178 					view.draw();
       
 12179 				}
       
 12180 			}).call(scope);
       
 12181 		}
       
 12182 		return res;
       
 12183 	}
       
 12184 
       
 12185 	function load() {
       
 12186 		Base.each(document.getElementsByTagName('script'), function(script) {
       
 12187 			if (/^text\/(?:x-|)paperscript$/.test(script.type)
       
 12188 					&& !script.getAttribute('data-paper-ignore')) {
       
 12189 				var canvas = PaperScope.getAttribute(script, 'canvas'),
       
 12190 					scope = PaperScope.get(canvas)
       
 12191 							|| new PaperScope(script).setup(canvas),
       
 12192 					src = script.src;
       
 12193 				if (src) {
       
 12194 					paper.Http.request('get', src, function(code) {
       
 12195 						evaluate(code, scope);
       
 12196 					});
       
 12197 				} else {
       
 12198 					evaluate(script.innerHTML, scope);
       
 12199 				}
       
 12200 				script.setAttribute('data-paper-ignore', true);
       
 12201 			}
       
 12202 		}, this);
       
 12203 	}
       
 12204 
       
 12205 	if (document.readyState === 'complete') {
       
 12206 		setTimeout(load);
       
 12207 	} else {
       
 12208 		paper.DomEvent.add(window, { load: load });
       
 12209 	}
       
 12210 
       
 12211 	return PaperScript = {
       
 12212 		compile: compile,
       
 12213 		evaluate: evaluate,
       
 12214 		load: load,
       
 12215 		lineNumberBase: 0
       
 12216 	};
       
 12217 
       
 12218 })(this);