UNPKG

thywill

Version:

A Node.js clustered framework for single page web applications based on asynchronous messaging.

2,114 lines (1,859 loc) 219 kB
/*! * Paper.js v0.22 * * This file is part of Paper.js, a JavaScript Vector Graphics Library, * based on Scriptographer.org and designed to be largely API compatible. * http://paperjs.org/ * http://scriptographer.org/ * * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey * http://lehni.org/ & http://jonathanpuckey.com/ * * Distributed under the MIT license. See LICENSE file for details. * * All rights reserved. * * Date: Thu Nov 10 19:19:25 2011 +0100 * *** * * Bootstrap.js JavaScript Framework. * http://bootstrapjs.org/ * * Copyright (c) 2006 - 2011 Juerg Lehni * http://lehni.org/ * * Distributed under the MIT license. * *** * * Parse-js * * A JavaScript tokenizer / parser / generator, originally written in Lisp. * Copyright (c) Marijn Haverbeke <marijnh@gmail.com> * http://marijn.haverbeke.nl/parse-js/ * * Ported by to JavaScript by Mihai Bazon * Copyright (c) 2010, Mihai Bazon <mihai.bazon@gmail.com> * http://mihai.bazon.net/blog/ * * Modifications and adaptions to browser (c) 2011, Juerg Lehni * http://lehni.org/ * * Distributed under the BSD license. */ var paper = new function() { var Base = new function() { var fix = !this.__proto__, hidden = /^(statics|generics|preserve|enumerable|prototype|__proto__|toString|valueOf)$/, proto = Object.prototype, has = fix ? function(name) { return name !== '__proto__' && this.hasOwnProperty(name); } : proto.hasOwnProperty, toString = proto.toString, proto = Array.prototype, isArray = Array.isArray = Array.isArray || function(obj) { return toString.call(obj) === '[object Array]'; }, slice = proto.slice, forEach = proto.forEach = proto.forEach || function(iter, bind) { for (var i = 0, l = this.length; i < l; i++) iter.call(bind, this[i], i, this); }, forIn = function(iter, bind) { for (var i in this) if (this.hasOwnProperty(i)) iter.call(bind, this[i], i, this); }, _define = Object.defineProperty, _describe = Object.getOwnPropertyDescriptor; function define(obj, name, desc) { if (_define) { try { delete obj[name]; return _define(obj, name, desc); } catch (e) {} } if ((desc.get || desc.set) && obj.__defineGetter__) { desc.get && obj.__defineGetter__(name, desc.get); desc.set && obj.__defineSetter__(name, desc.set); } else { obj[name] = desc.value; } return obj; } function describe(obj, name) { if (_describe) { try { return _describe(obj, name); } catch (e) {} } var get = obj.__lookupGetter__ && obj.__lookupGetter__(name); return get ? { get: get, set: obj.__lookupSetter__(name), enumerable: true, configurable: true } : has.call(obj, name) ? { value: obj[name], enumerable: true, configurable: true, writable: true } : null; } function inject(dest, src, enumerable, base, preserve, generics) { var beans, bean; function field(name, val, dontCheck, generics) { var val = val || (val = describe(src, name)) && (val.get ? val : val.value), func = typeof val === 'function', res = val, prev = preserve || func ? (val && val.get ? name in dest : dest[name]) : null; if (generics && func && (!preserve || !generics[name])) { generics[name] = function(bind) { return bind && dest[name].apply(bind, slice.call(arguments, 1)); } } if ((dontCheck || val !== undefined && has.call(src, name)) && (!preserve || !prev)) { if (func) { if (prev && /\bthis\.base\b/.test(val)) { var fromBase = base && base[name] == prev; res = function() { var tmp = describe(this, 'base'); define(this, 'base', { value: fromBase ? base[name] : prev, configurable: true }); try { return val.apply(this, arguments); } finally { tmp ? define(this, 'base', tmp) : delete this.base; } }; res.toString = function() { return val.toString(); } res.valueOf = function() { return val.valueOf(); } } if (beans && val.length == 0 && (bean = name.match(/^(get|is)(([A-Z])(.*))$/))) beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]); } if (!res || func || !res.get && !res.set) res = { value: res, writable: true }; if ((describe(dest, name) || { configurable: true }).configurable) { res.configurable = true; res.enumerable = enumerable; } define(dest, name, res); } } if (src) { beans = []; for (var name in src) if (has.call(src, name) && !hidden.test(name)) field(name, null, true, generics); field('toString'); field('valueOf'); for (var i = 0, l = beans && beans.length; i < l; i++) try { var bean = beans[i], part = bean[1]; field(bean[0], { get: dest['get' + part] || dest['is' + part], set: dest['set' + part] }, true); } catch (e) {} } return dest; } function extend(obj) { var ctor = function(dont) { if (fix) define(this, '__proto__', { value: obj }); if (this.initialize && dont !== ctor.dont) return this.initialize.apply(this, arguments); } ctor.prototype = obj; ctor.toString = function() { return (this.prototype.initialize || function() {}).toString(); } return ctor; } function iterator(iter) { return !iter ? function(val) { return val } : typeof iter !== 'function' ? function(val) { return val == iter } : iter; } function each(obj, iter, bind, asArray) { try { if (obj) (asArray || asArray === undefined && isArray(obj) ? forEach : forIn).call(obj, iterator(iter), bind = bind || obj); } catch (e) { if (e !== Base.stop) throw e; } return bind; } function clone(obj) { return each(obj, function(val, i) { this[i] = val; }, new obj.constructor()); } return inject(function() {}, { inject: function(src) { if (src) { var proto = this.prototype, base = proto.__proto__ && proto.__proto__.constructor, statics = src.statics == true ? src : src.statics; if (statics != src) inject(proto, src, src.enumerable, base && base.prototype, src.preserve, src.generics && this); inject(this, statics, true, base, src.preserve); } for (var i = 1, l = arguments.length; i < l; i++) this.inject(arguments[i]); return this; }, extend: function(src) { var proto = new this(this.dont), ctor = extend(proto); define(proto, 'constructor', { value: ctor, writable: true, configurable: true }); ctor.dont = {}; inject(ctor, this, true); return arguments.length ? this.inject.apply(ctor, arguments) : ctor; } }, true).inject({ has: has, each: each, inject: function() { for (var i = 0, l = arguments.length; i < l; i++) inject(this, arguments[i]); return this; }, extend: function() { var res = new (extend(this)); return res.inject.apply(res, arguments); }, each: function(iter, bind) { return each(this, iter, bind); }, clone: function() { return clone(this); }, statics: { each: each, clone: clone, define: define, describe: describe, iterator: iterator, has: function(obj, name) { return has.call(obj, name); }, type: function(obj) { return (obj || obj === 0) && (obj._type || typeof obj) || null; }, check: function(obj) { return !!(obj || obj === 0); }, pick: function() { for (var i = 0, l = arguments.length; i < l; i++) if (arguments[i] !== undefined) return arguments[i]; return null; }, stop: {} } }); } this.Base = Base.inject({ generics: true, clone: function() { return new this.constructor(this); }, toString: function() { return '{ ' + Base.each(this, function(value, key) { if (key.charAt(0) != '_') { var type = typeof value; this.push(key + ': ' + (type === 'number' ? Base.formatNumber(value) : type === 'string' ? "'" + value + "'" : value)); } }, []).join(', ') + ' }'; }, statics: { read: function(list, start, length) { var start = start || 0, length = length || list.length - start; var obj = list[start]; if (obj instanceof this || this.prototype._readNull && obj == null && length <= 1) return obj; obj = new this(this.dont); return obj.initialize.apply(obj, start > 0 || length < list.length ? Array.prototype.slice.call(list, start, start + length) : list) || obj; }, readAll: function(list, start) { var res = [], entry; for (var i = start || 0, l = list.length; i < l; i++) { res.push(Array.isArray(entry = list[i]) ? this.read(entry, 0) : this.read(list, i, 1)); } return res; }, splice: function(list, items, index, remove) { var amount = items && items.length, append = index === undefined; index = append ? list.length : index; for (var i = 0; i < amount; i++) items[i]._index = index + i; if (append) { list.push.apply(list, items); return []; } else { var args = [index, remove]; if (items) args.push.apply(args, items); var removed = list.splice.apply(list, args); for (var i = 0, l = removed.length; i < l; i++) delete removed[i]._index; for (var i = index + amount, l = list.length; i < l; i++) list[i]._index = i; return removed; } }, merge: function() { return Base.each(arguments, function(hash) { Base.each(hash, function(value, key) { this[key] = value; }, this); }, new Base(), true); }, capitalize: function(str) { return str.replace(/\b[a-z]/g, function(match) { return match.toUpperCase(); }); }, camelize: function(str) { return str.replace(/-(\w)/g, function(all, chr) { return chr.toUpperCase(); }); }, hyphenate: function(str) { return str.replace(/[a-z][A-Z0-9]|[0-9][a-zA-Z]|[A-Z]{2}[a-z]/g, function(match) { return match.charAt(0) + '-' + match.substring(1); } ).toLowerCase(); }, formatNumber: function(num) { return (Math.round(num * 100000) / 100000).toString(); } } }); var PaperScope = this.PaperScope = Base.extend({ initialize: function(script) { paper = this; this.view = null; this.views = []; this.project = null; this.projects = []; this.tool = null; this.tools = []; this._id = script && (script.getAttribute('id') || script.src) || ('paperscope-' + (PaperScope._id++)); if (script) script.setAttribute('id', this._id); PaperScope._scopes[this._id] = this; }, version: 0.22, evaluate: function(code) { var res = PaperScript.evaluate(code, this); View.updateFocus(); return res; }, install: function(scope) { var that = this; Base.each(['project', 'view', 'tool'], function(key) { Base.define(scope, key, { configurable: true, writable: true, get: function() { return that[key]; } }); }); for (var key in this) { if (!/^(version|_id|load)/.test(key) && !(key in scope)) scope[key] = this[key]; } }, setup: function(canvas) { paper = this; this.project = new Project(); if (canvas) this.view = new View(canvas); }, clear: function() { for (var i = this.projects.length - 1; i >= 0; i--) this.projects[i].remove(); for (var i = this.views.length - 1; i >= 0; i--) this.views[i].remove(); for (var i = this.tools.length - 1; i >= 0; i--) this.tools[i].remove(); }, remove: function() { this.clear(); delete PaperScope._scopes[this._id]; }, _needsRedraw: function() { if (!this._redrawNotified) { for (var i = this.views.length - 1; i >= 0; i--) this.views[i]._redrawNeeded = true; this._redrawNotified = true; } }, statics: { _scopes: {}, _id: 0, get: function(id) { if (typeof id === 'object') id = id.getAttribute('id'); return this._scopes[id] || null; }, each: function(iter) { Base.each(this._scopes, iter); } } }); var PaperScopeItem = Base.extend({ initialize: function(activate) { this._scope = paper; this._index = this._scope[this._list].push(this) - 1; if (activate || !this._scope[this._reference]) this.activate(); }, activate: function() { if (!this._scope) return false; this._scope[this._reference] = this; return true; }, remove: function() { if (this._index == null) return false; Base.splice(this._scope[this._list], null, this._index, 1); if (this._scope[this._reference] == this) this._scope[this._reference] = null; this._scope = null; return true; } }); var Point = this.Point = Base.extend({ initialize: function(arg0, arg1) { if (arg1 !== undefined) { this.x = arg0; this.y = arg1; } else if (arg0 !== undefined) { if (arg0 == null) { this.x = this.y = 0; } else if (arg0.x !== undefined) { this.x = arg0.x; this.y = arg0.y; } else if (arg0.width !== undefined) { this.x = arg0.width; this.y = arg0.height; } else if (Array.isArray(arg0)) { this.x = arg0[0]; this.y = arg0.length > 1 ? arg0[1] : arg0[0]; } else if (arg0.angle !== undefined) { this.x = arg0.length; this.y = 0; this.setAngle(arg0.angle); } else if (typeof arg0 === 'number') { this.x = this.y = arg0; } else { this.x = this.y = 0; } } else { this.x = this.y = 0; } }, set: function(x, y) { this.x = x; this.y = y; return this; }, clone: function() { return Point.create(this.x, this.y); }, toString: function() { var format = Base.formatNumber; return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ' }'; }, add: function(point) { point = Point.read(arguments); return Point.create(this.x + point.x, this.y + point.y); }, subtract: function(point) { point = Point.read(arguments); return Point.create(this.x - point.x, this.y - point.y); }, multiply: function(point) { point = Point.read(arguments); return Point.create(this.x * point.x, this.y * point.y); }, divide: function(point) { point = Point.read(arguments); return Point.create(this.x / point.x, this.y / point.y); }, modulo: function(point) { point = Point.read(arguments); return Point.create(this.x % point.x, this.y % point.y); }, negate: function() { return Point.create(-this.x, -this.y); }, transform: function(matrix) { return matrix ? matrix._transformPoint(this) : this; }, getDistance: function(point, squared) { point = Point.read(arguments); var x = point.x - this.x, y = point.y - this.y, d = x * x + y * y; return squared ? d : Math.sqrt(d); }, getLength: function() { var l = this.x * this.x + this.y * this.y; return arguments[0] ? l : Math.sqrt(l); }, setLength: function(length) { if (this.isZero()) { var angle = this._angle || 0; this.set( Math.cos(angle) * length, Math.sin(angle) * length ); } else { var scale = length / this.getLength(); if (scale == 0) this.getAngle(); this.set( this.x * scale, this.y * scale ); } return this; }, normalize: function(length) { if (length === undefined) length = 1; var current = this.getLength(), scale = current != 0 ? length / current : 0, point = Point.create(this.x * scale, this.y * scale); point._angle = this._angle; return point; }, getAngle: function() { return this.getAngleInRadians(arguments[0]) * 180 / Math.PI; }, setAngle: function(angle) { angle = this._angle = angle * Math.PI / 180; if (!this.isZero()) { var length = this.getLength(); this.set( Math.cos(angle) * length, Math.sin(angle) * length ); } return this; }, getAngleInRadians: function() { if (arguments[0] === undefined) { if (this._angle == null) this._angle = Math.atan2(this.y, this.x); return this._angle; } else { var point = Point.read(arguments), div = this.getLength() * point.getLength(); if (div == 0) { return NaN; } else { return Math.acos(this.dot(point) / div); } } }, getAngleInDegrees: function() { return this.getAngle(arguments[0]); }, getQuadrant: function() { return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; }, getDirectedAngle: function(point) { point = Point.read(arguments); return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI; }, rotate: function(angle, center) { angle = angle * Math.PI / 180; var point = center ? this.subtract(center) : this, s = Math.sin(angle), c = Math.cos(angle); point = Point.create( point.x * c - point.y * s, point.y * c + point.x * s ); return center ? point.add(center) : point; }, equals: function(point) { point = Point.read(arguments); return this.x == point.x && this.y == point.y; }, isInside: function(rect) { return rect.contains(this); }, isClose: function(point, tolerance) { return this.getDistance(point) < tolerance; }, isColinear: function(point) { return this.cross(point) < Numerical.TOLERANCE; }, isOrthogonal: function(point) { return this.dot(point) < Numerical.TOLERANCE; }, isZero: function() { return this.x == 0 && this.y == 0; }, isNaN: function() { return isNaN(this.x) || isNaN(this.y); }, dot: function(point) { point = Point.read(arguments); return this.x * point.x + this.y * point.y; }, cross: function(point) { point = Point.read(arguments); return this.x * point.y - this.y * point.x; }, project: function(point) { point = Point.read(arguments); if (point.isZero()) { return Point.create(0, 0); } else { var scale = this.dot(point) / point.dot(point); return Point.create( point.x * scale, point.y * scale ); } }, statics: { create: function(x, y) { var point = new Point(Point.dont); point.x = x; point.y = y; return point; }, min: function(point1, point2) { point1 = Point.read(arguments, 0, 1); point2 = Point.read(arguments, 1, 1); return Point.create( Math.min(point1.x, point2.x), Math.min(point1.y, point2.y) ); }, max: function(point1, point2) { point1 = Point.read(arguments, 0, 1); point2 = Point.read(arguments, 1, 1); return Point.create( Math.max(point1.x, point2.x), Math.max(point1.y, point2.y) ); }, random: function() { return Point.create(Math.random(), Math.random()); } } }, new function() { return Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { var op = Math[name]; this[name] = function() { return Point.create(op(this.x), op(this.y)); }; }, {}); }); var LinkedPoint = Point.extend({ set: function(x, y, dontNotify) { this._x = x; this._y = y; if (!dontNotify) this._owner[this._setter](this); return this; }, getX: function() { return this._x; }, setX: function(x) { this._x = x; this._owner[this._setter](this); }, getY: function() { return this._y; }, setY: function(y) { this._y = y; this._owner[this._setter](this); }, statics: { create: function(owner, setter, x, y, dontLink) { if (dontLink) return Point.create(x, y); var point = new LinkedPoint(LinkedPoint.dont); point._x = x; point._y = y; point._owner = owner; point._setter = setter; return point; } } }); var Size = this.Size = Base.extend({ initialize: function(arg0, arg1) { if (arg1 !== undefined) { this.width = arg0; this.height = arg1; } else if (arg0 !== undefined) { if (arg0 == null) { this.width = this.height = 0; } else if (arg0.width !== undefined) { this.width = arg0.width; this.height = arg0.height; } else if (arg0.x !== undefined) { this.width = arg0.x; this.height = arg0.y; } else if (Array.isArray(arg0)) { this.width = arg0[0]; this.height = arg0.length > 1 ? arg0[1] : arg0[0]; } else if (typeof arg0 === 'number') { this.width = this.height = arg0; } else { this.width = this.height = 0; } } else { this.width = this.height = 0; } }, toString: function() { var format = Base.formatNumber; return '{ width: ' + format(this.width) + ', height: ' + format(this.height) + ' }'; }, set: function(width, height) { this.width = width; this.height = height; return this; }, clone: function() { return Size.create(this.width, this.height); }, add: function(size) { size = Size.read(arguments); return Size.create(this.width + size.width, this.height + size.height); }, subtract: function(size) { size = Size.read(arguments); return Size.create(this.width - size.width, this.height - size.height); }, multiply: function(size) { size = Size.read(arguments); return Size.create(this.width * size.width, this.height * size.height); }, divide: function(size) { size = Size.read(arguments); return Size.create(this.width / size.width, this.height / size.height); }, modulo: function(size) { size = Size.read(arguments); return Size.create(this.width % size.width, this.height % size.height); }, negate: function() { return Size.create(-this.width, -this.height); }, equals: function(size) { size = Size.read(arguments); return this.width == size.width && this.height == size.height; }, isZero: function() { return this.width == 0 && this.height == 0; }, isNaN: function() { return isNaN(this.width) || isNaN(this.height); }, statics: { create: function(width, height) { return new Size(Size.dont).set(width, height); }, min: function(size1, size2) { return Size.create( Math.min(size1.width, size2.width), Math.min(size1.height, size2.height)); }, max: function(size1, size2) { return Size.create( Math.max(size1.width, size2.width), Math.max(size1.height, size2.height)); }, random: function() { return Size.create(Math.random(), Math.random()); } } }, new function() { return Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { var op = Math[name]; this[name] = function() { return Size.create(op(this.width), op(this.height)); }; }, {}); }); var LinkedSize = Size.extend({ set: function(width, height, dontNotify) { this._width = width; this._height = height; if (!dontNotify) this._owner[this._setter](this); return this; }, getWidth: function() { return this._width; }, setWidth: function(width) { this._width = width; this._owner[this._setter](this); }, getHeight: function() { return this._height; }, setHeight: function(height) { this._height = height; this._owner[this._setter](this); }, statics: { create: function(owner, setter, width, height, dontLink) { if (dontLink) return Size.create(width, height); var size = new LinkedSize(LinkedSize.dont); size._width = width; size._height = height; size._owner = owner; size._setter = setter; return size; } } }); var Rectangle = this.Rectangle = Base.extend({ initialize: function(arg0, arg1, arg2, arg3) { if (arguments.length == 4) { this.x = arg0; this.y = arg1; this.width = arg2; this.height = arg3; } else if (arguments.length == 2) { if (arg1 && arg1.x !== undefined) { var point1 = Point.read(arguments, 0, 1); var point2 = Point.read(arguments, 1, 1); this.x = point1.x; this.y = point1.y; this.width = point2.x - point1.x; this.height = point2.y - point1.y; if (this.width < 0) { this.x = point2.x; this.width = -this.width; } if (this.height < 0) { this.y = point2.y; this.height = -this.height; } } else { var point = Point.read(arguments, 0, 1); var size = Size.read(arguments, 1, 1); this.x = point.x; this.y = point.y; this.width = size.width; this.height = size.height; } } else if (arg0) { this.x = arg0.x || 0; this.y = arg0.y || 0; this.width = arg0.width || 0; this.height = arg0.height || 0; } else { this.x = this.y = this.width = this.height = 0; } }, set: function(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; return this; }, getPoint: function() { return LinkedPoint.create(this, 'setPoint', this.x, this.y, arguments[0]); }, setPoint: function(point) { point = Point.read(arguments); this.x = point.x; this.y = point.y; return this; }, getSize: function() { return LinkedSize.create(this, 'setSize', this.width, this.height, arguments[0]); }, setSize: function(size) { size = Size.read(arguments); this.width = size.width; this.height = size.height; return this; }, getLeft: function() { return this.x; }, setLeft: function(left) { this.width -= left - this.x; this.x = left; return this; }, getTop: function() { return this.y; }, setTop: function(top) { this.height -= top - this.y; this.y = top; return this; }, getRight: function() { return this.x + this.width; }, setRight: function(right) { this.width = right - this.x; return this; }, getBottom: function() { return this.y + this.height; }, setBottom: function(bottom) { this.height = bottom - this.y; return this; }, getCenterX: function() { return this.x + this.width * 0.5; }, setCenterX: function(x) { this.x = x - this.width * 0.5; return this; }, getCenterY: function() { return this.y + this.height * 0.5; }, setCenterY: function(y) { this.y = y - this.height * 0.5; return this; }, getCenter: function() { return LinkedPoint.create(this, 'setCenter', this.getCenterX(), this.getCenterY(), arguments[0]); }, setCenter: function(point) { point = Point.read(arguments); return this.setCenterX(point.x).setCenterY(point.y); }, equals: function(rect) { rect = Rectangle.read(arguments); return this.x == rect.x && this.y == rect.y && this.width == rect.width && this.height == rect.height; }, isEmpty: function() { return this.width == 0 || this.height == 0; }, toString: function() { var format = Base.formatNumber; return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ', width: ' + format(this.width) + ', height: ' + format(this.height) + ' }'; }, contains: function(arg) { return arg && arg.width !== undefined || (Array.isArray(arg) ? arg : arguments).length == 4 ? this._containsRectangle(Rectangle.read(arguments)) : this._containsPoint(Point.read(arguments)); }, _containsPoint: function(point) { var x = point.x, y = point.y; return x >= this.x && y >= this.y && x <= this.x + this.width && y <= this.y + this.height; }, _containsRectangle: function(rect) { var x = rect.x, y = rect.y; return x >= this.x && y >= this.y && x + rect.width <= this.x + this.width && y + rect.height <= this.y + this.height; }, intersects: function(rect) { rect = Rectangle.read(arguments); return rect.x + rect.width > this.x && rect.y + rect.height > this.y && rect.x < this.x + this.width && rect.y < this.y + this.height; }, intersect: function(rect) { rect = Rectangle.read(arguments); var x1 = Math.max(this.x, rect.x), y1 = Math.max(this.y, rect.y), x2 = Math.min(this.x + this.width, rect.x + rect.width), y2 = Math.min(this.y + this.height, rect.y + rect.height); return Rectangle.create(x1, y1, x2 - x1, y2 - y1); }, unite: function(rect) { rect = Rectangle.read(arguments); var x1 = Math.min(this.x, rect.x), y1 = Math.min(this.y, rect.y), x2 = Math.max(this.x + this.width, rect.x + rect.width), y2 = Math.max(this.y + this.height, rect.y + rect.height); return Rectangle.create(x1, y1, x2 - x1, y2 - y1); }, include: function(point) { point = Point.read(arguments); var x1 = Math.min(this.x, point.x), y1 = Math.min(this.y, point.y), x2 = Math.max(this.x + this.width, point.x), y2 = Math.max(this.y + this.height, point.y); return Rectangle.create(x1, y1, x2 - x1, y2 - y1); }, expand: function(hor, ver) { if (ver === undefined) ver = hor; return Rectangle.create(this.x - hor / 2, this.y - ver / 2, this.width + hor, this.height + ver); }, scale: function(hor, ver) { return this.expand(this.width * hor - this.width, this.height * (ver === undefined ? hor : ver) - this.height); }, statics: { create: function(x, y, width, height) { return new Rectangle(Rectangle.dont).set(x, y, width, height); } } }, new function() { return Base.each([ ['Top', 'Left'], ['Top', 'Right'], ['Bottom', 'Left'], ['Bottom', 'Right'], ['Left', 'Center'], ['Top', 'Center'], ['Right', 'Center'], ['Bottom', 'Center'] ], function(parts, index) { var part = parts.join(''); var xFirst = /^[RL]/.test(part); if (index >= 4) parts[1] += xFirst ? 'Y' : 'X'; var x = parts[xFirst ? 0 : 1], y = parts[xFirst ? 1 : 0], getX = 'get' + x, getY = 'get' + y, setX = 'set' + x, setY = 'set' + y, get = 'get' + part, set = 'set' + part; this[get] = function() { return LinkedPoint.create(this, set, this[getX](), this[getY](), arguments[0]); }; this[set] = function(point) { point = Point.read(arguments); return this[setX](point.x)[setY](point.y); }; }, {}); }); var LinkedRectangle = Rectangle.extend({ set: function(x, y, width, height, dontNotify) { this._x = x; this._y = y; this._width = width; this._height = height; if (!dontNotify) this._owner[this._setter](this); return this; }, statics: { create: function(owner, setter, x, y, width, height) { var rect = new LinkedRectangle(LinkedRectangle.dont).set( x, y, width, height, true); rect._owner = owner; rect._setter = setter; return rect; } } }, new function() { var proto = Rectangle.prototype; return Base.each(['x', 'y', 'width', 'height'], function(key) { var part = Base.capitalize(key); var internal = '_' + key; this['get' + part] = function() { return this[internal]; }; this['set' + part] = function(value) { this[internal] = value; if (!this._dontNotify) this._owner[this._setter](this); }; }, Base.each(['Point', 'Size', 'Center', 'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY', 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], function(key) { var name = 'set' + key; this[name] = function(value) { this._dontNotify = true; proto[name].apply(this, arguments); delete this._dontNotify; this._owner[this._setter](this); return this; }; }, {}) ); }); var Matrix = this.Matrix = Base.extend({ initialize: function(arg) { var count = arguments.length, ok = true; if (count == 6) { this.set.apply(this, arguments); } else if (count == 1) { if (arg instanceof Matrix) { this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty); } else if (Array.isArray(arg)) { this.set.apply(this, arg); } else { ok = false; } } else if (count == 0) { this._a = this._d = 1; this._c = this._b = this._tx = this._ty = 0; } else { ok = false; } if (!ok) throw new Error('Unsupported matrix parameters'); }, clone: function() { return Matrix.create(this._a, this._c, this._b, this._d, this._tx, this._ty); }, set: function(a, c, b, d, tx, ty) { this._a = a; this._c = c; this._b = b; this._d = d; this._tx = tx; this._ty = ty; return this; }, scale: function( hor, ver, center) { if (arguments.length < 2 || typeof ver === 'object') { center = Point.read(arguments, 1); ver = hor; } else { center = Point.read(arguments, 2); } if (center) this.translate(center); this._a *= hor; this._c *= hor; this._b *= ver; this._d *= ver; if (center) this.translate(center.negate()); return this; }, translate: function(point) { point = Point.read(arguments); var x = point.x, y = point.y; this._tx += x * this._a + y * this._b; this._ty += x * this._c + y * this._d; return this; }, rotate: function(angle, center) { return this.concatenate( Matrix.getRotateInstance.apply(Matrix, arguments)); }, shear: function( hor, ver, center) { if (arguments.length < 2 || typeof ver === 'object') { center = Point.read(arguments, 1); ver = hor; } else { center = Point.read(arguments, 2); } if (center) this.translate(center); var a = this._a, c = this._c; this._a += ver * this._b; this._c += ver * this._d; this._b += hor * a; this._d += hor * c; if (center) this.translate(center.negate()); return this; }, toString: function() { var format = Base.formatNumber; return '[[' + [format(this._a), format(this._b), format(this._tx)].join(', ') + '], [' + [format(this._c), format(this._d), format(this._ty)].join(', ') + ']]'; }, getValues: function() { return [ this._a, this._c, this._b, this._d, this._tx, this._ty ]; }, concatenate: function(mx) { var a = this._a, b = this._b, c = this._c, d = this._d; this._a = mx._a * a + mx._c * b; this._b = mx._b * a + mx._d * b; this._tx += mx._tx * a + mx._ty * b; this._c = mx._a * c + mx._c * d; this._d = mx._b * c + mx._d * d; this._ty += mx._tx * c + mx._ty * d; return this; }, preConcatenate: function(mx) { var a = this._a, b = this._b, c = this._c, d = this._d, tx = this._tx, ty = this._ty; this._a = mx._a * a + mx._b * c; this._c = mx._c * a + mx._d * c; this._b = mx._a * b + mx._b * d; this._d = mx._c * b + mx._d * d; this._tx = mx._a * tx + mx._b * ty + mx._tx; this._ty = mx._c * tx + mx._d * ty + mx._ty; return this; }, transform: function( src, srcOff, dst, dstOff, numPts) { return arguments.length < 5 ? this._transformPoint(Point.read(arguments)) : this._transformCoordinates(src, srcOff, dst, dstOff, numPts); }, _transformPoint: function(point, dest, dontNotify) { var x = point.x, y = point.y; if (!dest) dest = new Point(Point.dont); return dest.set( x * this._a + y * this._b + this._tx, x * this._c + y * this._d + this._ty, dontNotify ); }, _transformCoordinates: function(src, srcOff, dst, dstOff, numPts) { var i = srcOff, j = dstOff, srcEnd = srcOff + 2 * numPts; while (i < srcEnd) { var x = src[i++]; var y = src[i++]; dst[j++] = x * this._a + y * this._b + this._tx; dst[j++] = x * this._c + y * this._d + this._ty; } return dst; }, _transformCorners: function(rect) { var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ]; return this._transformCoordinates(coords, 0, coords, 0, 4); }, _transformBounds: function(bounds) { var coords = this._transformCorners(bounds), min = coords.slice(0, 2), max = coords.slice(0); for (var i = 2; i < 8; i++) { var val = coords[i], j = i & 1; if (val < min[j]) min[j] = val; else if (val > max[j]) max[j] = val; } return Rectangle.create(min[0], min[1], max[0] - min[0], max[1] - min[1]); }, inverseTransform: function(point) { return this._inverseTransform(Point.read(arguments)); }, _getDeterminant: function() { var det = this._a * this._d - this._b * this._c; return isFinite(det) && Math.abs(det) > Numerical.EPSILON && isFinite(this._tx) && isFinite(this._ty) ? det : null; }, _inverseTransform: function(point, dest, dontNotify) { var det = this._getDeterminant(); if (!det) return null; var x = point.x - this._tx, y = point.y - this._ty; if (!dest) dest = new Point(Point.dont); return dest.set( (x * this._d - y * this._b) / det, (y * this._a - x * this._c) / det, dontNotify ); }, getTranslation: function() { return new Point(this._tx, this._ty); }, getScaling: function() { var hor = Math.sqrt(this._a * this._a + this._c * this._c), ver = Math.sqrt(this._b * this._b + this._d * this._d); return new Point(this._a < 0 ? -hor : hor, this._b < 0 ? -ver : ver); }, getRotation: function() { var angle1 = -Math.atan2(this._b, this._d), angle2 = Math.atan2(this._c, this._a); return Math.abs(angle1 - angle2) < Numerical.TOLERANCE ? angle1 * 180 / Math.PI : undefined; }, isIdentity: function() { return this._a == 1 && this._c == 0 && this._b == 0 && this._d == 1 && this._tx == 0 && this._ty == 0; }, isInvertible: function() { return !!this._getDeterminant(); }, isSingular: function() { return !this._getDeterminant(); }, createInverse: function() { var det = this._getDeterminant(); return det && Matrix.create( this._d / det, -this._c / det, -this._b / det, this._a / det, (this._b * this._ty - this._d * this._tx) / det, (this._c * this._tx - this._a * this._ty) / det); }, createShiftless: function() { return Matrix.create(this._a, this._c, this._b, this._d, 0, 0); }, setToScale: function(hor, ver) { return this.set(hor, 0, 0, ver, 0, 0); }, setToTranslation: function(delta) { delta = Point.read(arguments); return this.set(1, 0, 0, 1, delta.x, delta.y); }, setToShear: function(hor, ver) { return this.set(1, ver, hor, 1, 0, 0); }, setToRotation: function(angle, center) { center = Point.read(arguments, 1); angle = angle * Math.PI / 180; var x = center.x, y = center.y, cos = Math.cos(angle), sin = Math.sin(angle); return this.set(cos, sin, -sin, cos, x - x * cos + y * sin, y - x * sin - y * cos); }, applyToContext: function(ctx, reset) { ctx[reset ? 'setTransform' : 'transform']( this._a, this._c, this._b, this._d, this._tx, this._ty); return this; }, statics: { create: function(a, c, b, d, tx, ty) { return new Matrix(Matrix.dont).set(a, c, b, d, tx, ty); }, getScaleInstance: function(hor, ver) { var mx = new Matrix(); return mx.setToScale.apply(mx, arguments); }, getTranslateInstance: function(delta) { var mx = new Matrix(); return mx.setToTranslation.apply(mx, arguments); }, getShearInstance: function(hor, ver, center) { var mx = new Matrix(); return mx.setToShear.apply(mx, arguments); }, getRotateInstance: function(angle, center) { var mx = new Matrix(); return mx.setToRotation.apply(mx, arguments); } } }, new function() { return Base.each({ scaleX: '_a', scaleY: '_d', translateX: '_tx', translateY: '_ty', shearX: '_b', shearY: '_c' }, function(prop, name) { name = Base.capitalize(name); this['get' + name] = function() { return this[prop]; }; this['set' + name] = function(value) { this[prop] = value; }; }, {}); }); var Line = this.Line = Base.extend({ initialize: function(point1, point2, infinite) { point1 = Point.read(arguments, 0, 1); point2 = Point.read(arguments, 1, 1); if (arguments.length == 3) { this.point = point1; this.vector = point2.subtract(point1); this.infinite = infinite; } else { this.point = point1; this.vector = point2; this.infinite = true; } }, intersect: function(line) { var cross = this.vector.cross(line.vector); if (Math.abs(cross) <= Numerical.EPSILON) return null; var v = line.point.subtract(this.point), t1 = v.cross(line.vector) / cross, t2 = v.cross(this.vector) / cross; return (this.infinite || 0 <= t1 && t1 <= 1) && (line.infinite || 0 <= t2 && t2 <= 1) ? this.point.add(this.vector.multiply(t1)) : null; }, getSide: function(point) { var v1 = this.vector, v2 = point.subtract(this.point), ccw = v2.cross(v1); if (ccw == 0) { ccw = v2.dot(v1); if (ccw > 0) { ccw = v2.subtract(v1).dot(v1); if (ccw < 0) ccw = 0; } } return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; }, getDistance: function(point) { var m = this.vector.y / this.vector.x, b = this.point.y - (m * this.point.x); var dist = Math.abs(point.y - (m * point.x) - b) / Math.sqrt(m * m + 1); return this.infinite ? dist : Math.min(dist, point.getDistance(this.point), point.getDistance(this.point.add(this.vector))); } }); var Project = this.Project = PaperScopeItem.extend({ _list: 'projects', _reference: 'project', initialize: function() { this.base(true); this._currentStyle = new PathStyle(); this._selectedItems = {}; this._selectedItemCount = 0; this.layers = []; this.symbols = []; this.activeLayer = new Layer(); }, _needsRedraw: function() { if (this._scope) this._scope._needsRedraw(); }, getCurrentStyle: function() { return this._currentStyle; }, setCurrentStyle: function(style) { this._currentStyle.initialize(style); }, getIndex: function() { return this._index; }, getSelectedItems: function() { var items = []; Base.each(this._selectedItems, function(item) { items.push(item); }); return items; }, _updateSelection: function(item) { if (item._selected) { this._selectedItemCount++; this._selectedItems[item.getId()] = item; } else { this._selectedItemCount--; delete this._selectedItems[item.getId()]; } }, selectAll: function() { for (var i = 0, l = this.layers.length; i < l; i++) this.layers[i].setSelected(true); }, deselectAll: function() { for (var i in this._selectedItems) this._selectedItems[i].setSelected(false); }, hitTest: function(point, options) { options = HitResult.getOptions(point, options); point = options.point; for (var i = this.layers.length - 1; i >= 0; i--) { var res = this.layers[i].hitTest(point, options); if (res) return res; } return null; }, draw: function(ctx) { ctx.save(); var param = { offset: new Point(0, 0) }; for (var i = 0, l = this.layers.length; i < l; i++) Item.draw(this.layers[i], ctx, param); ctx.restore(); if (this._selectedItemCount > 0) { ctx.save(); ctx.strokeWidth = 1; ctx.strokeStyle = ctx.fillStyle = '#009dec'; param = { selection: true }; Base.each(this._selectedItems, function(item) { item.draw(ctx, param); }); ctx.restore(); } } }); var Symbol = this.Symbol = Base.extend({ initialize: function(item) { this.project = paper.project; this.project.symbols.push(this); this.setDefinition(item); this._instances = {}; }, _changed: function(flags) { Base.each(this._instances, function(item) { item._changed(flags); }); }, getDefinition: function() { return this._definition; }, setDefinition: function(item) { if (item._parentSymbol) item = item.clone(); if (this._definition) delete this._definition._parentSymbol; this._definition = item; item.remove(); item.setPosition(new Point()); item._parentSymbol = this; this._changed(Change.GEOMETRY); }, place: function(position) { return new PlacedSymbol(this, position); }, clone: function() { return new Symbol(this._definition.clone()); } }); var ChangeFlag = { APPEARANCE: 1, HIERARCHY: 2, GEOMETRY: 4, STROKE: 8, STYLE: 16, ATTRIBUTE: 32, CONTENT: 64, PIXELS: 128, CLIPPING: 256 }; var Change = { HIERARCHY: ChangeFlag.HIERARCHY | ChangeFlag.APPEARANCE, GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE, STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE, ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE, CONTENT: ChangeFlag.CONTENT | ChangeFlag.APPEARANCE, PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE }; var Item = this.Item = Base.extend({ initialize: function() { this._id = ++Item._id; if (!this._project) paper.project.activeLayer.addChild(this); this._style = PathStyle.create(this); this.setStyle(this._project.getCurrentStyle()); }, _changed: function(flags) { if (flags & ChangeFlag.GEOMETRY) { delete this._bounds; delete this._position; } if (flags & ChangeFlag.APPEARANCE) { this._project._needsRedraw(); } if (this._parentSymbol) this._parentSymbol._changed(flags); if (this._project._changes) { var entry = this._project._changesById[this._id]; if (entry) { entry.flags |= flags; } else { entry = { item: this, flags: flags }; this._project._changesById[this._id] = entry; this._project._changes.push(entry); } } }, getId: function() { return this._id; }, getName: function() { return this._name; }, setName: function(name) { if (this._name) this._removeFromNamed(); this._name = name || undefined; if (name) { var children = this._parent._children, namedChildren = this._parent._namedChildren; (namedChildren[name] = namedChildren[name] || []).push(this); children[name] = this; } this._changed(ChangeFlag.ATTRIBUTE); }, getPosition: function() { var pos = this._position || (this._position = this.getBounds().getCenter()); return LinkedPoint.create(this, 'setPosition', pos._x, pos._y); }, setPosition: function(point) { this.translate(Point.read(arguments).subtract(this.getPosition())); }, getStyle: function() { return this._style; }, setStyle: function(style) { this._style.initialize(style); }, statics: { _id: 0 } }, new function() { return Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'], function(name) { var part = Base.capitalize(name), name = '_' + name; this['get' + part] = function() { return this[name]; }; this['set' + part] = function(value) { if (value != this[name]) { this[name] = value; this._changed(name === '_locked' ? ChangeFlag.ATTRIBUTE : Change.ATTRIBUTE); } }; }, {}); }, { _locked: false, _visible: true, _blendMode: 'normal', _opacity: 1, _guide: false, isSelected: function() { if (this._children) { for (var i = 0, l = this._children.length; i < l; i++) if (this._children[i].isSelected()) return true; } return this._selected; }, setSelected: function(selected) { if (this._children) { for (var i = 0, l = this._children.length; i < l; i++) { this._children[i].setSelected(selected); } } else if ((selected = !!selected) != this._selected) { this._selected = selected; this._project._updateSelection(this); this._changed(Change.ATTRIBUTE); } }, _selected: false, isFullySelected: function() { if (this._children && this._selected) { for (var i = 0, l = this._children.length; i < l; i++) if (!this._children[i].isFullySelected()) return false; return true; } return this._selected; }, setFullySelected: function(selected) { if (this._children) { for (var i = 0, l = this._children.length; i < l; i++) { this._children[i].setFullySelected(selected); } } this.setSelected(selected); }, isClipMask: function() { return this._clipMask; }, setClipMask: function(clipMask) { if (this._clipMask != (clipMask = !!clipMask)) { this._clipMask = clipMask; if (clipMask) { this.setFillColor(null); this.setStrokeColor(null); } this._changed(Change.ATTRIBUTE); if (this._parent) this._parent._changed(ChangeFlag.CLIPPING); } }, _clipMask: false, getProject: function() { return this._project; }, _setProject: function(project) { if (this._project != project) { this._project = project; if (this._children) { for (var i = 0, l = this._children.length; i < l; i++) { this._children[i]._setProject(project); } } } }, getLayer: function() { var parent = this; while (parent = parent._parent) { if (parent instanceof Layer) return parent; } return null; }, getParent: function() { return this._parent; }, getChildren: function() { return this._children; }, setChildren: function(items) { this.removeChildren(); this.addChildren(items); }, getFirstChild: function() { return this._children && this._children[0] || null; }, getLastChild: function() { return this._children && this._children[this._children.length - 1] || null; }, getNextSibling: function() { return this._parent && this._parent._children[this._index + 1] || null; }, getPreviousSibling: function() { return this._parent && this._parent._children[this._index - 1] || null; }, getIndex: function() { return this._index; }, clone: function() { return this._clone(new this.constructor()); }, _clone: function(copy) { copy.setStyle(this._style); if (this._children) { for (var i = 0, l = this._children.length; i < l; i++) copy.addChild(this._children[i].clone()); } var keys = ['_locked', '_visible', '_blendMode', '_opacity', '_clipMask', '_guide']; for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; if (this.hasOwnProperty(key)) copy[key] = this[key]; } copy.setSelected(this._selected); if (this._name) copy.setName(this._name); return copy; }, copyTo: function(itemOrProject) { var copy = this.clone(); if (itemOrProject.layers) { itemOrProject.activeLayer.addChild(copy); } else { itemOrProject.addChild(copy); } return copy; }, rasterize: function(resolution) { var bounds = this.getStrokeBounds(), scale = (resolution || 72) / 72, canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)), ctx = canvas.getContext('2d'), matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); matrix.applyToContext(ctx); this.draw(ctx, {}); var raster = new Raster(canvas); raster.setBounds(bounds); return raster; }, hitTest: function(point, options, matrix) { options = HitResult.getOptions(point, options); point = options.point; if (!this._children && !this.getRoughBounds(matrix) .expand(options.tolerance)._containsPoint(point)) return null; if ((options.center || options.bounds) && !(this instanceof Layer && !this._parent)) { var bounds = this.getBounds(), that = this, points = ['TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], res; function checkBounds(type, part) { var pt = bounds['get' + part]().transform(matrix); if (point.getDistance(pt) < options.tolerance) return new HitResult(type, that, { name: Base.hyphenate(part), point: pt }); } if (options.center && (res = checkBounds('cente