thywill
Version:
A Node.js clustered framework for single page web applications based on asynchronous messaging.
2,114 lines (1,859 loc) • 219 kB
JavaScript
/*!
* 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