--- a/web/res/js/paper.js Wed Dec 11 11:04:27 2019 +0100
+++ b/web/res/js/paper.js Wed Dec 11 14:30:18 2019 +0100
@@ -1,21 +1,21 @@
/*!
- * Paper.js v0.9.24 - The Swiss Army Knife of Vector Graphics Scripting.
+ * Paper.js v0.12.3 - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
- * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
- * http://scratchdisk.com/ & http://jonathanpuckey.com/
+ * Copyright (c) 2011 - 2019, Juerg Lehni & Jonathan Puckey
+ * http://scratchdisk.com/ & https://puckey.studio/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*
- * Date: Fri Aug 21 16:39:41 2015 +0200
+ * Date: Sat Jun 22 14:16:49 2019 +0200
*
***
*
* Straps.js - Class inheritance library with support for bean-style accessors
*
- * Copyright (c) 2006 - 2013 Juerg Lehni
+ * Copyright (c) 2006 - 2019 Juerg Lehni
* http://scratchdisk.com/
*
* Distributed under the MIT license.
@@ -23,59 +23,58 @@
***
*
* Acorn.js
- * http://marijnhaverbeke.nl/acorn/
+ * https://marijnhaverbeke.nl/acorn/
*
* Acorn is a tiny, fast JavaScript parser written in JavaScript,
* created by Marijn Haverbeke and released under an MIT license.
*
*/
-var paper = new function(undefined) {
+var paper = function(self, undefined) {
+
+self = self || require('./node/self.js');
+var window = self.window,
+ document = self.document;
var Base = new function() {
var hidden = /^(statics|enumerable|beans|preserve)$/,
-
- forEach = [].forEach || function(iter, bind) {
- for (var i = 0, l = this.length; i < l; i++)
+ array = [],
+ slice = array.slice,
+ create = Object.create,
+ describe = Object.getOwnPropertyDescriptor,
+ define = Object.defineProperty,
+
+ forEach = array.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)
+ for (var i in this) {
if (this.hasOwnProperty(i))
iter.call(bind, this[i], i, this);
- },
-
- create = Object.create || function(proto) {
- return { __proto__: proto };
- },
-
- describe = Object.getOwnPropertyDescriptor || function(obj, name) {
- var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
- return get
- ? { get: get, set: obj.__lookupSetter__(name),
- enumerable: true, configurable: true }
- : obj.hasOwnProperty(name)
- ? { value: obj[name], enumerable: true,
- configurable: true, writable: true }
- : null;
- },
-
- _define = Object.defineProperty || function(obj, name, desc) {
- if ((desc.get || desc.set) && obj.__defineGetter__) {
- if (desc.get)
- obj.__defineGetter__(name, desc.get);
- if (desc.set)
- obj.__defineSetter__(name, desc.set);
- } else {
- obj[name] = desc.value;
- }
- return obj;
- },
-
- define = function(obj, name, desc) {
- delete obj[name];
- return _define(obj, name, desc);
+ }
+ },
+
+ set = Object.assign || function(dst) {
+ for (var i = 1, l = arguments.length; i < l; i++) {
+ var src = arguments[i];
+ for (var key in src) {
+ if (src.hasOwnProperty(key))
+ dst[key] = src[key];
+ }
+ }
+ return dst;
+ },
+
+ each = function(obj, iter, bind) {
+ if (obj) {
+ var desc = describe(obj, 'length');
+ (desc && typeof desc.value === 'number' ? forEach : forIn)
+ .call(obj, iter, bind = bind || obj);
+ }
+ return bind;
};
function inject(dest, src, enumerable, beans, preserve) {
@@ -99,12 +98,13 @@
&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
beansNames[bean[3].toLowerCase() + bean[4]] = bean[2];
if (!res || isFunc || !res.get || typeof res.get !== 'function'
- || !Base.isPlainObject(res))
+ || !Base.isPlainObject(res)) {
res = { value: res, writable: true };
+ }
if ((describe(dest, name)
|| { configurable: true }).configurable) {
res.configurable = true;
- res.enumerable = enumerable;
+ res.enumerable = enumerable != null ? enumerable : !bean;
}
define(dest, name, res);
}
@@ -125,26 +125,16 @@
return dest;
}
- function each(obj, iter, bind) {
- if (obj)
- ('length' in obj && !obj.getLength
- && typeof obj.length === 'number'
- ? forEach
- : forIn).call(obj, iter, bind = bind || obj);
- return bind;
- }
-
- function set(obj, props, exclude) {
- for (var key in props)
- if (props.hasOwnProperty(key) && !(exclude && exclude[key]))
- obj[key] = props[key];
- return obj;
- }
-
- return inject(function Base() {
- for (var i = 0, l = arguments.length; i < l; i++)
- set(this, arguments[i]);
- }, {
+ function Base() {
+ for (var i = 0, l = arguments.length; i < l; i++) {
+ var src = arguments[i];
+ if (src)
+ set(this, src);
+ }
+ return this;
+ }
+
+ return inject(Base, {
inject: function(src) {
if (src) {
var statics = src.statics === true ? src : src.statics,
@@ -152,7 +142,7 @@
preserve = src.preserve;
if (statics !== src)
inject(this.prototype, src, src.enumerable, beans, preserve);
- inject(this, statics, true, beans, preserve);
+ inject(this, statics, null, beans, preserve);
}
for (var i = 1, l = arguments.length; i < l; i++)
this.inject(arguments[i]);
@@ -163,27 +153,37 @@
var base = this,
ctor,
proto;
- for (var i = 0, l = arguments.length; i < l; i++)
- if (ctor = arguments[i].initialize)
- break;
+ for (var i = 0, obj, l = arguments.length;
+ i < l && !(ctor && proto); i++) {
+ obj = arguments[i];
+ ctor = ctor || obj.initialize;
+ proto = proto || obj.prototype;
+ }
ctor = ctor || function() {
base.apply(this, arguments);
};
- proto = ctor.prototype = create(this.prototype);
+ proto = ctor.prototype = proto || create(this.prototype);
define(proto, 'constructor',
{ value: ctor, writable: true, configurable: true });
- inject(ctor, this, true);
+ inject(ctor, this);
if (arguments.length)
this.inject.apply(ctor, arguments);
ctor.base = base;
return ctor;
}
- }, true).inject({
+ }).inject({
+ enumerable: false,
+
+ initialize: Base,
+
+ set: Base,
+
inject: function() {
for (var i = 0, l = arguments.length; i < l; i++) {
var src = arguments[i];
- if (src)
+ if (src) {
inject(this, src, src.enumerable, src.beans, src.preserve);
+ }
}
return this;
},
@@ -197,20 +197,16 @@
return each(this, iter, bind);
},
- set: function(props) {
- return set(this, props);
- },
-
clone: function() {
return new this.constructor(this);
},
statics: {
+ set: set,
each: each,
create: create,
define: define,
describe: describe,
- set: set,
clone: function(obj) {
return set(new obj.constructor(), obj);
@@ -224,6 +220,10 @@
pick: function(a, b) {
return a !== undefined ? a : b;
+ },
+
+ slice: function(list, begin, end) {
+ return slice.call(list, begin, end);
}
}
});
@@ -233,6 +233,8 @@
module.exports = Base;
Base.inject({
+ enumerable: false,
+
toString: function() {
return this._id != null
? (this._class || 'Object') + (this._name
@@ -252,6 +254,10 @@
return this._class || '';
},
+ importJSON: function(json) {
+ return Base.importJSON(json, this);
+ },
+
exportJSON: function(options) {
return Base.exportJSON(this, options);
},
@@ -260,313 +266,354 @@
return Base.serialize(this);
},
- _set: function(props, exclude, dontCheck) {
- if (props && (dontCheck || Base.isPlainObject(props))) {
- var keys = Object.keys(props._filtering || props);
- for (var i = 0, l = keys.length; i < l; i++) {
- var key = keys[i];
- if (!(exclude && exclude[key])) {
- var value = props[key];
- if (value !== undefined)
- this[key] = value;
+ set: function(props, exclude) {
+ if (props)
+ Base.filter(this, props, exclude, this._prioritize);
+ return this;
+ }
+}, {
+
+beans: false,
+statics: {
+ exports: {},
+
+ extend: function extend() {
+ var res = extend.base.apply(this, arguments),
+ name = res.prototype._class;
+ if (name && !Base.exports[name])
+ Base.exports[name] = res;
+ return res;
+ },
+
+ equals: function(obj1, obj2) {
+ if (obj1 === obj2)
+ return true;
+ if (obj1 && obj1.equals)
+ return obj1.equals(obj2);
+ if (obj2 && obj2.equals)
+ return obj2.equals(obj1);
+ if (obj1 && obj2
+ && typeof obj1 === 'object' && typeof obj2 === 'object') {
+ if (Array.isArray(obj1) && Array.isArray(obj2)) {
+ var length = obj1.length;
+ if (length !== obj2.length)
+ return false;
+ while (length--) {
+ if (!Base.equals(obj1[length], obj2[length]))
+ return false;
+ }
+ } else {
+ var keys = Object.keys(obj1),
+ length = keys.length;
+ if (length !== Object.keys(obj2).length)
+ return false;
+ while (length--) {
+ var key = keys[length];
+ if (!(obj2.hasOwnProperty(key)
+ && Base.equals(obj1[key], obj2[key])))
+ return false;
}
}
return true;
}
- },
-
- statics: {
-
- exports: {
- enumerable: true
- },
-
- extend: function extend() {
- var res = extend.base.apply(this, arguments),
- name = res.prototype._class;
- if (name && !Base.exports[name])
- Base.exports[name] = res;
- return res;
- },
-
- equals: function(obj1, obj2) {
- if (obj1 === obj2)
- return true;
- if (obj1 && obj1.equals)
- return obj1.equals(obj2);
- if (obj2 && obj2.equals)
- return obj2.equals(obj1);
- if (obj1 && obj2
- && typeof obj1 === 'object' && typeof obj2 === 'object') {
- if (Array.isArray(obj1) && Array.isArray(obj2)) {
- var length = obj1.length;
- if (length !== obj2.length)
- return false;
- while (length--) {
- if (!Base.equals(obj1[length], obj2[length]))
- return false;
- }
- } else {
- var keys = Object.keys(obj1),
- length = keys.length;
- if (length !== Object.keys(obj2).length)
- return false;
- while (length--) {
- var key = keys[length];
- if (!(obj2.hasOwnProperty(key)
- && Base.equals(obj1[key], obj2[key])))
- return false;
- }
- }
- return true;
- }
- return false;
- },
-
- read: function(list, start, options, length) {
- if (this === Base) {
- var value = this.peek(list, start);
- list.__index++;
- return value;
- }
- var proto = this.prototype,
- readIndex = proto._readIndex,
- index = start || readIndex && list.__index || 0;
- if (!length)
- length = list.length - index;
- var obj = list[index];
- if (obj instanceof this
- || options && options.readNull && obj == null && length <= 1) {
- if (readIndex)
- list.__index = index + 1;
- return obj && options && options.clone ? obj.clone() : obj;
- }
- obj = Base.create(this.prototype);
+ return false;
+ },
+
+ read: function(list, start, options, amount) {
+ if (this === Base) {
+ var value = this.peek(list, start);
+ list.__index++;
+ return value;
+ }
+ var proto = this.prototype,
+ readIndex = proto._readIndex,
+ begin = start || readIndex && list.__index || 0,
+ length = list.length,
+ obj = list[begin];
+ amount = amount || length - begin;
+ if (obj instanceof this
+ || options && options.readNull && obj == null && amount <= 1) {
if (readIndex)
- obj.__read = true;
- obj = obj.initialize.apply(obj, index > 0 || length < list.length
- ? Array.prototype.slice.call(list, index, index + length)
+ list.__index = begin + 1;
+ return obj && options && options.clone ? obj.clone() : obj;
+ }
+ obj = Base.create(proto);
+ if (readIndex)
+ obj.__read = true;
+ obj = obj.initialize.apply(obj, begin > 0 || begin + amount < length
+ ? Base.slice(list, begin, begin + amount)
: list) || obj;
- if (readIndex) {
- list.__index = index + obj.__read;
- obj.__read = undefined;
- }
- return obj;
- },
-
- peek: function(list, start) {
- return list[list.__index = start || list.__index || 0];
- },
-
- remain: function(list) {
- return list.length - (list.__index || 0);
- },
-
- readAll: function(list, start, options) {
- 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, options)
- : this.read(list, i, options, 1));
- }
- return res;
- },
-
- readNamed: function(list, name, start, options, length) {
- var value = this.getNamed(list, name),
- hasObject = value !== undefined;
- if (hasObject) {
- var filtered = list._filtered;
- if (!filtered) {
- filtered = list._filtered = Base.create(list[0]);
- filtered._filtering = list[0];
- }
- filtered[name] = undefined;
- }
- return this.read(hasObject ? [value] : list, start, options, length);
- },
-
- getNamed: function(list, name) {
- var arg = list[0];
- if (list._hasObject === undefined)
- list._hasObject = list.length === 1 && Base.isPlainObject(arg);
- if (list._hasObject)
- return name ? arg[name] : list._filtered || arg;
- },
-
- hasNamed: function(list, name) {
- return !!this.getNamed(list, name);
- },
-
- isPlainValue: function(obj, asString) {
- return this.isPlainObject(obj) || Array.isArray(obj)
- || asString && typeof obj === 'string';
- },
-
- serialize: function(obj, options, compact, dictionary) {
- options = options || {};
-
- var root = !dictionary,
- res;
- if (root) {
- options.formatter = new Formatter(options.precision);
- dictionary = {
- length: 0,
- definitions: {},
- references: {},
- add: function(item, create) {
- var id = '#' + item._id,
- ref = this.references[id];
- if (!ref) {
- this.length++;
- var res = create.call(item),
- name = item._class;
- if (name && res[0] !== name)
- res.unshift(name);
- this.definitions[id] = res;
- ref = this.references[id] = [id];
+ if (readIndex) {
+ list.__index = begin + obj.__read;
+ var filtered = obj.__filtered;
+ if (filtered) {
+ list.__filtered = filtered;
+ obj.__filtered = undefined;
+ }
+ obj.__read = undefined;
+ }
+ return obj;
+ },
+
+ peek: function(list, start) {
+ return list[list.__index = start || list.__index || 0];
+ },
+
+ remain: function(list) {
+ return list.length - (list.__index || 0);
+ },
+
+ readList: function(list, start, options, amount) {
+ var res = [],
+ entry,
+ begin = start || 0,
+ end = amount ? begin + amount : list.length;
+ for (var i = begin; i < end; i++) {
+ res.push(Array.isArray(entry = list[i])
+ ? this.read(entry, 0, options)
+ : this.read(list, i, options, 1));
+ }
+ return res;
+ },
+
+ readNamed: function(list, name, start, options, amount) {
+ var value = this.getNamed(list, name),
+ hasObject = value !== undefined;
+ if (hasObject) {
+ var filtered = list.__filtered;
+ if (!filtered) {
+ filtered = list.__filtered = Base.create(list[0]);
+ filtered.__unfiltered = list[0];
+ }
+ filtered[name] = undefined;
+ }
+ var l = hasObject ? [value] : list,
+ res = this.read(l, start, options, amount);
+ return res;
+ },
+
+ getNamed: function(list, name) {
+ var arg = list[0];
+ if (list._hasObject === undefined)
+ list._hasObject = list.length === 1 && Base.isPlainObject(arg);
+ if (list._hasObject)
+ return name ? arg[name] : list.__filtered || arg;
+ },
+
+ hasNamed: function(list, name) {
+ return !!this.getNamed(list, name);
+ },
+
+ filter: function(dest, source, exclude, prioritize) {
+ var processed;
+
+ function handleKey(key) {
+ if (!(exclude && key in exclude) &&
+ !(processed && key in processed)) {
+ var value = source[key];
+ if (value !== undefined)
+ dest[key] = value;
+ }
+ }
+
+ if (prioritize) {
+ var keys = {};
+ for (var i = 0, key, l = prioritize.length; i < l; i++) {
+ if ((key = prioritize[i]) in source) {
+ handleKey(key);
+ keys[key] = true;
+ }
+ }
+ processed = keys;
+ }
+
+ Object.keys(source.__unfiltered || source).forEach(handleKey);
+ return dest;
+ },
+
+ isPlainValue: function(obj, asString) {
+ return Base.isPlainObject(obj) || Array.isArray(obj)
+ || asString && typeof obj === 'string';
+ },
+
+ serialize: function(obj, options, compact, dictionary) {
+ options = options || {};
+
+ var isRoot = !dictionary,
+ res;
+ if (isRoot) {
+ options.formatter = new Formatter(options.precision);
+ dictionary = {
+ length: 0,
+ definitions: {},
+ references: {},
+ add: function(item, create) {
+ var id = '#' + item._id,
+ ref = this.references[id];
+ if (!ref) {
+ this.length++;
+ var res = create.call(item),
+ name = item._class;
+ if (name && res[0] !== name)
+ res.unshift(name);
+ this.definitions[id] = res;
+ ref = this.references[id] = [id];
+ }
+ return ref;
+ }
+ };
+ }
+ if (obj && obj._serialize) {
+ res = obj._serialize(options, dictionary);
+ var name = obj._class;
+ if (name && !obj._compactSerialize && (isRoot || !compact)
+ && res[0] !== name) {
+ res.unshift(name);
+ }
+ } else if (Array.isArray(obj)) {
+ res = [];
+ for (var i = 0, l = obj.length; i < l; i++)
+ res[i] = Base.serialize(obj[i], options, compact, dictionary);
+ } else if (Base.isPlainObject(obj)) {
+ res = {};
+ var keys = Object.keys(obj);
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ res[key] = Base.serialize(obj[key], options, compact,
+ dictionary);
+ }
+ } else if (typeof obj === 'number') {
+ res = options.formatter.number(obj, options.precision);
+ } else {
+ res = obj;
+ }
+ return isRoot && dictionary.length > 0
+ ? [['dictionary', dictionary.definitions], res]
+ : res;
+ },
+
+ deserialize: function(json, create, _data, _setDictionary, _isRoot) {
+ var res = json,
+ isFirst = !_data,
+ hasDictionary = isFirst && json && json.length
+ && json[0][0] === 'dictionary';
+ _data = _data || {};
+ if (Array.isArray(json)) {
+ var type = json[0],
+ isDictionary = type === 'dictionary';
+ if (json.length == 1 && /^#/.test(type)) {
+ return _data.dictionary[type];
+ }
+ type = Base.exports[type];
+ res = [];
+ for (var i = type ? 1 : 0, l = json.length; i < l; i++) {
+ res.push(Base.deserialize(json[i], create, _data,
+ isDictionary, hasDictionary));
+ }
+ if (type) {
+ var args = res;
+ if (create) {
+ res = create(type, args, isFirst || _isRoot);
+ } else {
+ res = new type(args);
+ }
+ }
+ } else if (Base.isPlainObject(json)) {
+ res = {};
+ if (_setDictionary)
+ _data.dictionary = res;
+ for (var key in json)
+ res[key] = Base.deserialize(json[key], create, _data);
+ }
+ return hasDictionary ? res[1] : res;
+ },
+
+ exportJSON: function(obj, options) {
+ var json = Base.serialize(obj, options);
+ return options && options.asString == false
+ ? json
+ : JSON.stringify(json);
+ },
+
+ importJSON: function(json, target) {
+ return Base.deserialize(
+ typeof json === 'string' ? JSON.parse(json) : json,
+ function(ctor, args, isRoot) {
+ var useTarget = isRoot && target
+ && target.constructor === ctor,
+ obj = useTarget ? target
+ : Base.create(ctor.prototype);
+ if (args.length === 1 && obj instanceof Item
+ && (useTarget || !(obj instanceof Layer))) {
+ var arg = args[0];
+ if (Base.isPlainObject(arg)) {
+ arg.insert = false;
+ if (useTarget) {
+ args = args.concat([{ insert: true }]);
+ }
}
- return ref;
- }
- };
- }
- if (obj && obj._serialize) {
- res = obj._serialize(options, dictionary);
- var name = obj._class;
- if (name && !compact && !res._compact && res[0] !== name)
- res.unshift(name);
- } else if (Array.isArray(obj)) {
- res = [];
- for (var i = 0, l = obj.length; i < l; i++)
- res[i] = Base.serialize(obj[i], options, compact,
- dictionary);
- if (compact)
- res._compact = true;
- } else if (Base.isPlainObject(obj)) {
- res = {};
- var keys = Object.keys(obj);
- for (var i = 0, l = keys.length; i < l; i++) {
- var key = keys[i];
- res[key] = Base.serialize(obj[key], options, compact,
- dictionary);
- }
- } else if (typeof obj === 'number') {
- res = options.formatter.number(obj, options.precision);
- } else {
- res = obj;
- }
- return root && dictionary.length > 0
- ? [['dictionary', dictionary.definitions], res]
- : res;
- },
-
- deserialize: function(json, create, _data, _isDictionary) {
- var res = json,
- isRoot = !_data;
- _data = _data || {};
- if (Array.isArray(json)) {
- var type = json[0],
- isDictionary = type === 'dictionary';
- if (json.length == 1 && /^#/.test(type))
- return _data.dictionary[type];
- type = Base.exports[type];
- res = [];
- if (_isDictionary)
- _data.dictionary = res;
- for (var i = type ? 1 : 0, l = json.length; i < l; i++)
- res.push(Base.deserialize(json[i], create, _data,
- isDictionary));
- if (type) {
- var args = res;
- if (create) {
- res = create(type, args);
- } else {
- res = Base.create(type.prototype);
- type.apply(res, args);
- }
- }
- } else if (Base.isPlainObject(json)) {
- res = {};
- if (_isDictionary)
- _data.dictionary = res;
- for (var key in json)
- res[key] = Base.deserialize(json[key], create, _data);
- }
- return isRoot && json && json.length && json[0][0] === 'dictionary'
- ? res[1]
- : res;
- },
-
- exportJSON: function(obj, options) {
- var json = Base.serialize(obj, options);
- return options && options.asString === false
- ? json
- : JSON.stringify(json);
- },
-
- importJSON: function(json, target) {
- return Base.deserialize(
- typeof json === 'string' ? JSON.parse(json) : json,
- function(type, args) {
- var obj = target && target.constructor === type
- ? target
- : Base.create(type.prototype),
- isTarget = obj === target;
- if (args.length === 1 && obj instanceof Item
- && (isTarget || !(obj instanceof Layer))) {
- var arg = args[0];
- if (Base.isPlainObject(arg))
- arg.insert = false;
- }
- type.apply(obj, args);
- if (isTarget)
- target = null;
- return obj;
- });
- },
-
- splice: function(list, items, index, remove) {
- var amount = items && items.length,
- append = index === undefined;
- index = append ? list.length : index;
- if (index > list.length)
- index = list.length;
- 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++)
- removed[i]._index = undefined;
- for (var i = index + amount, l = list.length; i < l; i++)
- list[i]._index = i;
- return removed;
- }
- },
-
- capitalize: function(str) {
- return str.replace(/\b[a-z]/g, function(match) {
- return match.toUpperCase();
- });
- },
-
- camelize: function(str) {
- return str.replace(/-(.)/g, function(all, chr) {
- return chr.toUpperCase();
- });
- },
-
- hyphenate: function(str) {
- return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
- }
- }
-});
+ }
+ (useTarget ? obj.set : ctor).apply(obj, args);
+ if (useTarget)
+ target = null;
+ return obj;
+ });
+ },
+
+ push: function(list, items) {
+ var itemsLength = items.length;
+ if (itemsLength < 4096) {
+ list.push.apply(list, items);
+ } else {
+ var startLength = list.length;
+ list.length += itemsLength;
+ for (var i = 0; i < itemsLength; i++) {
+ list[startLength + i] = items[i];
+ }
+ }
+ return list;
+ },
+
+ splice: function(list, items, index, remove) {
+ var amount = items && items.length,
+ append = index === undefined;
+ index = append ? list.length : index;
+ if (index > list.length)
+ index = list.length;
+ for (var i = 0; i < amount; i++)
+ items[i]._index = index + i;
+ if (append) {
+ Base.push(list, items);
+ return [];
+ } else {
+ var args = [index, remove];
+ if (items)
+ Base.push(args, items);
+ var removed = list.splice.apply(list, args);
+ for (var i = 0, l = removed.length; i < l; i++)
+ removed[i]._index = undefined;
+ for (var i = index + amount, l = list.length; i < l; i++)
+ list[i]._index = i;
+ return removed;
+ }
+ },
+
+ capitalize: function(str) {
+ return str.replace(/\b[a-z]/g, function(match) {
+ return match.toUpperCase();
+ });
+ },
+
+ camelize: function(str) {
+ return str.replace(/-(.)/g, function(match, chr) {
+ return chr.toUpperCase();
+ });
+ },
+
+ hyphenate: function(str) {
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
+ }
+}});
var Emitter = {
on: function(type, func) {
@@ -581,7 +628,7 @@
handlers = handlers[type] = handlers[type] || [];
if (handlers.indexOf(func) === -1) {
handlers.push(func);
- if (entry && entry.install && handlers.length == 1)
+ if (entry && entry.install && handlers.length === 1)
entry.install.call(this, type);
}
}
@@ -613,9 +660,9 @@
},
once: function(type, func) {
- return this.on(type, function() {
+ return this.on(type, function handler() {
func.apply(this, arguments);
- this.off(type, func);
+ this.off(type, handler);
});
},
@@ -623,15 +670,20 @@
var handlers = this._callbacks && this._callbacks[type];
if (!handlers)
return false;
- var args = [].slice.call(arguments, 1);
+ var args = Base.slice(arguments, 1),
+ setTarget = event && event.target && !event.currentTarget;
handlers = handlers.slice();
+ if (setTarget)
+ event.currentTarget = this;
for (var i = 0, l = handlers.length; i < l; i++) {
- if (handlers[i].apply(this, args) === false) {
+ if (handlers[i].apply(this, args) == false) {
if (event && event.stop)
event.stop();
break;
- }
- }
+ }
+ }
+ if (setTarget)
+ delete event.currentTarget;
return true;
},
@@ -644,15 +696,17 @@
fire: '#emit',
_installEvents: function(install) {
- var handlers = this._callbacks,
+ var types = this._eventTypes,
+ handlers = this._callbacks,
key = install ? 'install' : 'uninstall';
- for (var type in handlers) {
- if (handlers[type].length > 0) {
- var types = this._eventTypes,
- entry = types && types[type],
- func = entry && entry[key];
- if (func)
- func.call(this, type);
+ if (types) {
+ for (var type in handlers) {
+ if (handlers[type].length > 0) {
+ var entry = types[type],
+ func = entry && entry[key];
+ if (func)
+ func.call(this, type);
+ }
}
}
},
@@ -695,70 +749,67 @@
paper = this;
this.settings = new Base({
applyMatrix: true,
+ insertItems: true,
handleSize: 4,
hitTolerance: 0
});
this.project = null;
this.projects = [];
this.tools = [];
- this.palettes = [];
this._id = PaperScope._id++;
PaperScope._scopes[this._id] = this;
var proto = PaperScope.prototype;
if (!this.support) {
- var ctx = CanvasProvider.getContext(1, 1);
+ var ctx = CanvasProvider.getContext(1, 1) || {};
proto.support = {
nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
nativeBlendModes: BlendMode.nativeModes
};
CanvasProvider.release(ctx);
}
-
- if (!this.browser) {
- var agent = navigator.userAgent.toLowerCase(),
- platform = (/(win)/.exec(agent)
- || /(mac)/.exec(agent)
- || /(linux)/.exec(agent)
- || [])[0],
- browser = proto.browser = { platform: platform };
+ if (!this.agent) {
+ var user = self.navigator.userAgent.toLowerCase(),
+ os = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user)||[])[0],
+ platform = os === 'darwin' ? 'mac' : os,
+ agent = proto.agent = proto.browser = { platform: platform };
if (platform)
- browser[platform] = true;
- agent.replace(
- /(opera|chrome|safari|webkit|firefox|msie|trident|atom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:([.\d]+))?/g,
- function(all, n, v1, v2, rv) {
- if (!browser.chrome) {
- var v = n === 'opera' ? v2 : v1;
- if (n === 'trident') {
- v = rv;
- n = 'msie';
- }
- browser.version = v;
- browser.versionNumber = parseFloat(v);
- browser.name = n;
- browser[n] = true;
+ agent[platform] = true;
+ user.replace(
+ /(opera|chrome|safari|webkit|firefox|msie|trident|atom|node|jsdom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:v?([.\d]+))?/g,
+ function(match, n, v1, v2, rv) {
+ if (!agent.chrome) {
+ var v = n === 'opera' ? v2 :
+ /^(node|trident)$/.test(n) ? rv : v1;
+ agent.version = v;
+ agent.versionNumber = parseFloat(v);
+ n = { trident: 'msie', jsdom: 'node' }[n] || n;
+ agent.name = n;
+ agent[n] = true;
}
}
);
- if (browser.chrome)
- delete browser.webkit;
- if (browser.atom)
- delete browser.chrome;
- }
- },
-
- version: '0.9.24',
+ if (agent.chrome)
+ delete agent.webkit;
+ if (agent.atom)
+ delete agent.chrome;
+ }
+ },
+
+ version: "0.12.3",
getView: function() {
- return this.project && this.project.getView();
+ var project = this.project;
+ return project && project._view;
},
getPaper: function() {
return this;
},
- execute: function(code, url, options) {
- paper.PaperScript.execute(code, this, url, options);
- View.updateFocus();
+ execute: function(code, options) {
+ var exports = paper.PaperScript.execute(code, this, options);
+ View.updateFocus();
+ return exports;
},
install: function(scope) {
@@ -782,17 +833,21 @@
return this;
},
+ createCanvas: function(width, height) {
+ return CanvasProvider.getCanvas(width, height);
+ },
+
activate: function() {
paper = this;
},
clear: function() {
- for (var i = this.projects.length - 1; i >= 0; i--)
- this.projects[i].remove();
- for (var i = this.tools.length - 1; i >= 0; i--)
- this.tools[i].remove();
- for (var i = this.palettes.length - 1; i >= 0; i--)
- this.palettes[i].remove();
+ var projects = this.projects,
+ tools = this.tools;
+ for (var i = projects.length - 1; i >= 0; i--)
+ projects[i].remove();
+ for (var i = tools.length - 1; i >= 0; i--)
+ tools[i].remove();
},
remove: function() {
@@ -854,17 +909,22 @@
this._scope[this._reference] = null;
this._scope = null;
return true;
+ },
+
+ getView: function() {
+ return this._scope.getView();
}
});
var Formatter = Base.extend({
initialize: function(precision) {
- this.precision = precision || 5;
+ this.precision = Base.pick(precision, 5);
this.multiplier = Math.pow(10, this.precision);
},
number: function(val) {
- return Math.round(val * this.multiplier) / this.multiplier;
+ return this.precision < 16
+ ? Math.round(val * this.multiplier) / this.multiplier : val;
},
pair: function(val1, val2, separator) {
@@ -929,19 +989,61 @@
var abs = Math.abs,
sqrt = Math.sqrt,
pow = Math.pow,
- TOLERANCE = 1e-6,
+ log2 = Math.log2 || function(x) {
+ return Math.log(x) * Math.LOG2E;
+ },
EPSILON = 1e-12,
MACHINE_EPSILON = 1.12e-16;
+ function clamp(value, min, max) {
+ return value < min ? min : value > max ? max : value;
+ }
+
+ function getDiscriminant(a, b, c) {
+ function split(v) {
+ var x = v * 134217729,
+ y = v - x,
+ hi = y + x,
+ lo = v - hi;
+ return [hi, lo];
+ }
+
+ var D = b * b - a * c,
+ E = b * b + a * c;
+ if (abs(D) * 3 < E) {
+ var ad = split(a),
+ bd = split(b),
+ cd = split(c),
+ p = b * b,
+ dp = (bd[0] * bd[0] - p + 2 * bd[0] * bd[1]) + bd[1] * bd[1],
+ q = a * c,
+ dq = (ad[0] * cd[0] - q + ad[0] * cd[1] + ad[1] * cd[0])
+ + ad[1] * cd[1];
+ D = (p - q) + (dp - dq);
+ }
+ return D;
+ }
+
+ function getNormalizationFactor() {
+ var norm = Math.max.apply(Math, arguments);
+ return norm && (norm < 1e-8 || norm > 1e8)
+ ? pow(2, -Math.round(log2(norm)))
+ : 0;
+ }
+
return {
- TOLERANCE: TOLERANCE,
EPSILON: EPSILON,
MACHINE_EPSILON: MACHINE_EPSILON,
+ CURVETIME_EPSILON: 1e-8,
+ GEOMETRIC_EPSILON: 1e-7,
+ TRIGONOMETRIC_EPSILON: 1e-8,
KAPPA: 4 * (sqrt(2) - 1) / 3,
isZero: function(val) {
- return abs(val) <= EPSILON;
- },
+ return val >= -EPSILON && val <= EPSILON;
+ },
+
+ clamp: clamp,
integrate: function(f, a, b, n) {
var x = abscissas[n - 2],
@@ -963,8 +1065,10 @@
var fx = f(x),
dx = fx / df(x),
nx = x - dx;
- if (abs(dx) < tolerance)
- return nx;
+ if (abs(dx) < tolerance) {
+ x = nx;
+ break;
+ }
if (fx > 0) {
b = x;
x = nx <= a ? (a + b) * 0.5 : nx;
@@ -973,58 +1077,70 @@
x = nx >= b ? (a + b) * 0.5 : nx;
}
}
- return x;
+ return clamp(x, a, b);
},
solveQuadratic: function(a, b, c, roots, min, max) {
- var count = 0,
- x1, x2 = Infinity,
- B = b,
- D;
- b /= 2;
- D = b * b - a * c;
- if (D !== 0 && abs(D) < MACHINE_EPSILON) {
- var gmC = pow(abs(a * b * c), 1 / 3);
- if (gmC < 1e-8) {
- var mult = pow(10, abs(
- Math.floor(Math.log(gmC) * Math.LOG10E)));
- if (!isFinite(mult))
- mult = 0;
- a *= mult;
- b *= mult;
- c *= mult;
- D = b * b - a * c;
- }
- }
+ var x1, x2 = Infinity;
if (abs(a) < EPSILON) {
- if (abs(B) < EPSILON)
+ if (abs(b) < EPSILON)
return abs(c) < EPSILON ? -1 : 0;
- x1 = -c / B;
- } else {
+ x1 = -c / b;
+ } else {
+ b *= -0.5;
+ var D = getDiscriminant(a, b, c);
+ if (D && abs(D) < MACHINE_EPSILON) {
+ var f = getNormalizationFactor(abs(a), abs(b), abs(c));
+ if (f) {
+ a *= f;
+ b *= f;
+ c *= f;
+ D = getDiscriminant(a, b, c);
+ }
+ }
if (D >= -MACHINE_EPSILON) {
- D = D < 0 ? 0 : D;
- var R = sqrt(D);
- if (b >= MACHINE_EPSILON && b <= MACHINE_EPSILON) {
- x1 = abs(a) >= abs(c) ? R / a : -c / R;
+ var Q = D < 0 ? 0 : sqrt(D),
+ R = b + (b < 0 ? -Q : Q);
+ if (R === 0) {
+ x1 = c / a;
x2 = -x1;
} else {
- var q = -(b + (b < 0 ? -1 : 1) * R);
- x1 = q / a;
- x2 = c / q;
- }
- }
- }
- if (isFinite(x1) && (min == null || x1 >= min && x1 <= max))
- roots[count++] = x1;
+ x1 = R / a;
+ x2 = c / R;
+ }
+ }
+ }
+ var count = 0,
+ boundless = min == null,
+ minB = min - EPSILON,
+ maxB = max + EPSILON;
+ if (isFinite(x1) && (boundless || x1 > minB && x1 < maxB))
+ roots[count++] = boundless ? x1 : clamp(x1, min, max);
if (x2 !== x1
- && isFinite(x2) && (min == null || x2 >= min && x2 <= max))
- roots[count++] = x2;
+ && isFinite(x2) && (boundless || x2 > minB && x2 < maxB))
+ roots[count++] = boundless ? x2 : clamp(x2, min, max);
return count;
},
solveCubic: function(a, b, c, d, roots, min, max) {
- var count = 0,
- x, b1, c2;
+ var f = getNormalizationFactor(abs(a), abs(b), abs(c), abs(d)),
+ x, b1, c2, qd, q;
+ if (f) {
+ a *= f;
+ b *= f;
+ c *= f;
+ d *= f;
+ }
+
+ function evaluate(x0) {
+ x = x0;
+ var tmp = a * x;
+ b1 = tmp + b;
+ c2 = b1 * x + c;
+ qd = (tmp + b1) * x + c2;
+ q = c2 * x + d;
+ }
+
if (abs(a) < EPSILON) {
a = b;
b1 = c;
@@ -1035,33 +1151,17 @@
c2 = c;
x = 0;
} else {
- var ec = 1 + MACHINE_EPSILON,
- x0, q, qd, t, r, s, tmp;
- x = -(b / a) / 3;
- tmp = a * x,
- b1 = tmp + b,
- c2 = b1 * x + c,
- qd = (tmp + b1) * x + c2,
- q = c2 * x + d;
- t = q /a;
- r = pow(abs(t), 1/3);
- s = t < 0 ? -1 : 1;
- t = -qd / a;
- r = t > 0 ? 1.3247179572 * Math.max(r, sqrt(t)) : r;
- x0 = x - s * r;
+ evaluate(-(b / a) / 3);
+ var t = q / a,
+ r = pow(abs(t), 1/3),
+ s = t < 0 ? -1 : 1,
+ td = -qd / a,
+ rd = td > 0 ? 1.324717957244746 * Math.max(r, sqrt(td)) : r,
+ x0 = x - s * rd;
if (x0 !== x) {
do {
- x = x0;
- tmp = a * x,
- b1 = tmp + b,
- c2 = b1 * x + c,
- qd = (tmp + b1) * x + c2,
- q = c2 * x + d;
- x0 = qd === 0 ? x : x - q / qd / ec;
- if (x0 === x) {
- x = x0;
- break;
- }
+ evaluate(x0);
+ x0 = qd === 0 ? x : x - q / qd / (1 + MACHINE_EPSILON);
} while (s * x0 > s * x);
if (abs(a) * x * x > abs(d / x)) {
c2 = -d / x;
@@ -1069,10 +1169,12 @@
}
}
}
- var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max);
- if (isFinite(x) && (count === 0 || x !== roots[count - 1])
- && (min == null || x >= min && x <= max))
- roots[count++] = x;
+ var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max),
+ boundless = min == null;
+ if (isFinite(x) && (count === 0
+ || count > 0 && x !== roots[0] && x !== roots[1])
+ && (boundless || x > min - EPSILON && x < max + EPSILON))
+ roots[count++] = boundless ? x : clamp(x, min, max);
return count;
}
};
@@ -1082,10 +1184,9 @@
_id: 1,
_pools: {},
- get: function(ctor) {
- if (ctor) {
- var name = ctor._class,
- pool = this._pools[name];
+ get: function(name) {
+ if (name) {
+ var pool = this._pools[name];
if (!pool)
pool = this._pools[name] = { _id: 1 };
return pool._id++;
@@ -1100,42 +1201,43 @@
_readIndex: true,
initialize: function Point(arg0, arg1) {
- var type = typeof arg0;
+ var type = typeof arg0,
+ reading = this.__read,
+ read = 0;
if (type === 'number') {
var hasY = typeof arg1 === 'number';
- this.x = arg0;
- this.y = hasY ? arg1 : arg0;
- if (this.__read)
- this.__read = hasY ? 2 : 1;
+ this._set(arg0, hasY ? arg1 : arg0);
+ if (reading)
+ read = hasY ? 2 : 1;
} else if (type === 'undefined' || arg0 === null) {
- this.x = this.y = 0;
- if (this.__read)
- this.__read = arg0 === null ? 1 : 0;
+ this._set(0, 0);
+ if (reading)
+ read = arg0 === null ? 1 : 0;
} else {
- if (Array.isArray(arg0)) {
- this.x = arg0[0];
- this.y = arg0.length > 1 ? arg0[1] : arg0[0];
- } else if (arg0.x != null) {
- this.x = arg0.x;
- this.y = arg0.y;
- } else if (arg0.width != null) {
- this.x = arg0.width;
- this.y = arg0.height;
- } else if (arg0.angle != null) {
- this.x = arg0.length;
- this.y = 0;
- this.setAngle(arg0.angle);
- } else {
- this.x = this.y = 0;
- if (this.__read)
- this.__read = 0;
- }
- if (this.__read)
- this.__read = 1;
- }
- },
-
- set: function(x, y) {
+ var obj = type === 'string' ? arg0.split(/[\s,]+/) || [] : arg0;
+ read = 1;
+ if (Array.isArray(obj)) {
+ this._set(+obj[0], +(obj.length > 1 ? obj[1] : obj[0]));
+ } else if ('x' in obj) {
+ this._set(obj.x || 0, obj.y || 0);
+ } else if ('width' in obj) {
+ this._set(obj.width || 0, obj.height || 0);
+ } else if ('angle' in obj) {
+ this._set(obj.length || 0, 0);
+ this.setAngle(obj.angle || 0);
+ } else {
+ this._set(0, 0);
+ read = 0;
+ }
+ }
+ if (reading)
+ this.__read = read;
+ return this;
+ },
+
+ set: '#initialize',
+
+ _set: function(x, y) {
this.x = x;
this.y = y;
return this;
@@ -1170,7 +1272,7 @@
setLength: function(length) {
if (this.isZero()) {
var angle = this._angle || 0;
- this.set(
+ this._set(
Math.cos(angle) * length,
Math.sin(angle) * length
);
@@ -1178,7 +1280,7 @@
var scale = length / this.getLength();
if (Numerical.isZero(scale))
this.getAngle();
- this.set(
+ this._set(
this.x * scale,
this.y * scale
);
@@ -1216,7 +1318,7 @@
this._angle = angle;
if (!this.isZero()) {
var length = this.getLength();
- this.set(
+ this._set(
Math.cos(angle) * length,
Math.sin(angle) * length
);
@@ -1259,11 +1361,11 @@
return this.clone();
angle = angle * Math.PI / 180;
var point = center ? this.subtract(center) : this,
- s = Math.sin(angle),
- c = Math.cos(angle);
+ sin = Math.sin(angle),
+ cos = Math.cos(angle);
point = new Point(
- point.x * c - point.y * s,
- point.x * s + point.y * c
+ point.x * cos - point.y * sin,
+ point.x * sin + point.y * cos
);
return center ? point.add(center) : point;
},
@@ -1305,28 +1407,38 @@
return Rectangle.read(arguments).contains(this);
},
- isClose: function(point, tolerance) {
- return this.getDistance(point) < tolerance;
- },
-
- isCollinear: function(point) {
- return Math.abs(this.cross(point)) < 0.000001;
+ isClose: function() {
+ var point = Point.read(arguments),
+ tolerance = Base.read(arguments);
+ return this.getDistance(point) <= tolerance;
+ },
+
+ isCollinear: function() {
+ var point = Point.read(arguments);
+ return Point.isCollinear(this.x, this.y, point.x, point.y);
},
isColinear: '#isCollinear',
- isOrthogonal: function(point) {
- return Math.abs(this.dot(point)) < 0.000001;
+ isOrthogonal: function() {
+ var point = Point.read(arguments);
+ return Point.isOrthogonal(this.x, this.y, point.x, point.y);
},
isZero: function() {
- return Numerical.isZero(this.x) && Numerical.isZero(this.y);
+ var isZero = Numerical.isZero;
+ return isZero(this.x) && isZero(this.y);
},
isNaN: function() {
return isNaN(this.x) || isNaN(this.y);
},
+ isInQuadrant: function(q) {
+ return this.x * (q > 1 && q < 4 ? -1 : 1) >= 0
+ && this.y * (q > 2 ? -1 : 1) >= 0;
+ },
+
dot: function() {
var point = Point.read(arguments);
return this.x * point.x + this.y * point.y;
@@ -1338,16 +1450,12 @@
},
project: function() {
- var point = Point.read(arguments);
- if (point.isZero()) {
- return new Point(0, 0);
- } else {
- var scale = this.dot(point) / point.dot(point);
- return new Point(
- point.x * scale,
- point.y * scale
- );
- }
+ var point = Point.read(arguments),
+ scale = point.isZero() ? 0 : this.dot(point) / point.dot(point);
+ return new Point(
+ point.x * scale,
+ point.y * scale
+ );
},
statics: {
@@ -1371,11 +1479,23 @@
random: function() {
return new Point(Math.random(), Math.random());
- }
- }
-}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
- var op = Math[name];
- this[name] = function() {
+ },
+
+ isCollinear: function(x1, y1, x2, y2) {
+ return Math.abs(x1 * y2 - y1 * x2)
+ <= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))
+ * 1e-8;
+ },
+
+ isOrthogonal: function(x1, y1, x2, y2) {
+ return Math.abs(x1 * x2 + y1 * y2)
+ <= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))
+ * 1e-8;
+ }
+ }
+}, Base.each(['round', 'ceil', 'floor', 'abs'], function(key) {
+ var op = Math[key];
+ this[key] = function() {
return new Point(op(this.x), op(this.y));
};
}, {}));
@@ -1388,7 +1508,7 @@
this._setter = setter;
},
- set: function(x, y, _dontNotify) {
+ _set: function(x, y, _dontNotify) {
this._x = x;
this._y = y;
if (!_dontNotify)
@@ -1412,6 +1532,18 @@
setY: function(y) {
this._y = y;
this._owner[this._setter](this);
+ },
+
+ isSelected: function() {
+ return !!(this._owner._selection & this._getSelection());
+ },
+
+ setSelected: function(selected) {
+ this._owner._changeSelection(this._getSelection(), selected);
+ },
+
+ _getSelection: function() {
+ return this._setter === 'setPosition' ? 4 : 0;
}
});
@@ -1420,38 +1552,40 @@
_readIndex: true,
initialize: function Size(arg0, arg1) {
- var type = typeof arg0;
+ var type = typeof arg0,
+ reading = this.__read,
+ read = 0;
if (type === 'number') {
var hasHeight = typeof arg1 === 'number';
- this.width = arg0;
- this.height = hasHeight ? arg1 : arg0;
- if (this.__read)
- this.__read = hasHeight ? 2 : 1;
+ this._set(arg0, hasHeight ? arg1 : arg0);
+ if (reading)
+ read = hasHeight ? 2 : 1;
} else if (type === 'undefined' || arg0 === null) {
- this.width = this.height = 0;
- if (this.__read)
- this.__read = arg0 === null ? 1 : 0;
+ this._set(0, 0);
+ if (reading)
+ read = arg0 === null ? 1 : 0;
} else {
- if (Array.isArray(arg0)) {
- this.width = arg0[0];
- this.height = arg0.length > 1 ? arg0[1] : arg0[0];
- } else if (arg0.width != null) {
- this.width = arg0.width;
- this.height = arg0.height;
- } else if (arg0.x != null) {
- this.width = arg0.x;
- this.height = arg0.y;
- } else {
- this.width = this.height = 0;
- if (this.__read)
- this.__read = 0;
- }
- if (this.__read)
- this.__read = 1;
- }
- },
-
- set: function(width, height) {
+ var obj = type === 'string' ? arg0.split(/[\s,]+/) || [] : arg0;
+ read = 1;
+ if (Array.isArray(obj)) {
+ this._set(+obj[0], +(obj.length > 1 ? obj[1] : obj[0]));
+ } else if ('width' in obj) {
+ this._set(obj.width || 0, obj.height || 0);
+ } else if ('x' in obj) {
+ this._set(obj.x || 0, obj.y || 0);
+ } else {
+ this._set(0, 0);
+ read = 0;
+ }
+ }
+ if (reading)
+ this.__read = read;
+ return this;
+ },
+
+ set: '#initialize',
+
+ _set: function(width, height) {
this.width = width;
this.height = height;
return this;
@@ -1510,7 +1644,8 @@
},
isZero: function() {
- return Numerical.isZero(this.width) && Numerical.isZero(this.height);
+ var isZero = Numerical.isZero;
+ return isZero(this.width) && isZero(this.height);
},
isNaN: function() {
@@ -1534,9 +1669,9 @@
return new Size(Math.random(), Math.random());
}
}
-}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) {
- var op = Math[name];
- this[name] = function() {
+}, Base.each(['round', 'ceil', 'floor', 'abs'], function(key) {
+ var op = Math[key];
+ this[key] = function() {
return new Size(op(this.width), op(this.height));
};
}, {}));
@@ -1549,7 +1684,7 @@
this._setter = setter;
},
- set: function(width, height, _dontNotify) {
+ _set: function(width, height, _dontNotify) {
this._width = width;
this._height = height;
if (!_dontNotify)
@@ -1583,64 +1718,66 @@
initialize: function Rectangle(arg0, arg1, arg2, arg3) {
var type = typeof arg0,
- read = 0;
+ read;
if (type === 'number') {
- this.x = arg0;
- this.y = arg1;
- this.width = arg2;
- this.height = arg3;
+ this._set(arg0, arg1, arg2, arg3);
read = 4;
} else if (type === 'undefined' || arg0 === null) {
- this.x = this.y = this.width = this.height = 0;
+ this._set(0, 0, 0, 0);
read = arg0 === null ? 1 : 0;
} else if (arguments.length === 1) {
if (Array.isArray(arg0)) {
- this.x = arg0[0];
- this.y = arg0[1];
- this.width = arg0[2];
- this.height = arg0[3];
+ this._set.apply(this, arg0);
read = 1;
} else if (arg0.x !== undefined || arg0.width !== undefined) {
- this.x = arg0.x || 0;
- this.y = arg0.y || 0;
- this.width = arg0.width || 0;
- this.height = arg0.height || 0;
+ this._set(arg0.x || 0, arg0.y || 0,
+ arg0.width || 0, arg0.height || 0);
read = 1;
} else if (arg0.from === undefined && arg0.to === undefined) {
- this.x = this.y = this.width = this.height = 0;
- this._set(arg0);
+ this._set(0, 0, 0, 0);
+ Base.filter(this, arg0);
read = 1;
}
}
- if (!read) {
- var point = Point.readNamed(arguments, 'from'),
- next = Base.peek(arguments);
- this.x = point.x;
- this.y = point.y;
- if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
+ if (read === undefined) {
+ var frm = Point.readNamed(arguments, 'from'),
+ next = Base.peek(arguments),
+ x = frm.x,
+ y = frm.y,
+ width,
+ height;
+ if (next && next.x !== undefined
+ || Base.hasNamed(arguments, 'to')) {
var to = Point.readNamed(arguments, 'to');
- this.width = to.x - point.x;
- this.height = to.y - point.y;
- if (this.width < 0) {
- this.x = to.x;
- this.width = -this.width;
- }
- if (this.height < 0) {
- this.y = to.y;
- this.height = -this.height;
+ width = to.x - x;
+ height = to.y - y;
+ if (width < 0) {
+ x = to.x;
+ width = -width;
+ }
+ if (height < 0) {
+ y = to.y;
+ height = -height;
}
} else {
var size = Size.read(arguments);
- this.width = size.width;
- this.height = size.height;
- }
+ width = size.width;
+ height = size.height;
+ }
+ this._set(x, y, width, height);
read = arguments.__index;
+ var filtered = arguments.__filtered;
+ if (filtered)
+ this.__filtered = filtered;
}
if (this.__read)
this.__read = read;
- },
-
- set: function(x, y, width, height) {
+ return this;
+ },
+
+ set: '#initialize',
+
+ _set: function(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
@@ -1695,16 +1832,24 @@
return new ctor(this.width, this.height, this, 'setSize');
},
+ _fw: 1,
+ _fh: 1,
+
setSize: function() {
- var size = Size.read(arguments);
- if (this._fixX)
- this.x += (this.width - size.width) * this._fixX;
- if (this._fixY)
- this.y += (this.height - size.height) * this._fixY;
- this.width = size.width;
- this.height = size.height;
- this._fixW = 1;
- this._fixH = 1;
+ var size = Size.read(arguments),
+ sx = this._sx,
+ sy = this._sy,
+ w = size.width,
+ h = size.height;
+ if (sx) {
+ this.x += (this.width - w) * sx;
+ }
+ if (sy) {
+ this.y += (this.height - h) * sy;
+ }
+ this.width = w;
+ this.height = h;
+ this._fw = this._fh = 1;
},
getLeft: function() {
@@ -1712,10 +1857,12 @@
},
setLeft: function(left) {
- if (!this._fixW)
- this.width -= left - this.x;
+ if (!this._fw) {
+ var amount = left - this.x;
+ this.width -= this._sx === 0.5 ? amount * 2 : amount;
+ }
this.x = left;
- this._fixX = 0;
+ this._sx = this._fw = 0;
},
getTop: function() {
@@ -1723,10 +1870,12 @@
},
setTop: function(top) {
- if (!this._fixH)
- this.height -= top - this.y;
+ if (!this._fh) {
+ var amount = top - this.y;
+ this.height -= this._sy === 0.5 ? amount * 2 : amount;
+ }
this.y = top;
- this._fixY = 0;
+ this._sy = this._fh = 0;
},
getRight: function() {
@@ -1734,13 +1883,13 @@
},
setRight: function(right) {
- if (this._fixX !== undefined && this._fixX !== 1)
- this._fixW = 0;
- if (this._fixW)
- this.x = right - this.width;
- else
- this.width = right - this.x;
- this._fixX = 1;
+ if (!this._fw) {
+ var amount = right - this.x;
+ this.width = this._sx === 0.5 ? amount * 2 : amount;
+ }
+ this.x = right - this.width;
+ this._sx = 1;
+ this._fw = 0;
},
getBottom: function() {
@@ -1748,31 +1897,47 @@
},
setBottom: function(bottom) {
- if (this._fixY !== undefined && this._fixY !== 1)
- this._fixH = 0;
- if (this._fixH)
- this.y = bottom - this.height;
- else
- this.height = bottom - this.y;
- this._fixY = 1;
+ if (!this._fh) {
+ var amount = bottom - this.y;
+ this.height = this._sy === 0.5 ? amount * 2 : amount;
+ }
+ this.y = bottom - this.height;
+ this._sy = 1;
+ this._fh = 0;
},
getCenterX: function() {
- return this.x + this.width * 0.5;
+ return this.x + this.width / 2;
},
setCenterX: function(x) {
- this.x = x - this.width * 0.5;
- this._fixX = 0.5;
+ if (this._fw || this._sx === 0.5) {
+ this.x = x - this.width / 2;
+ } else {
+ if (this._sx) {
+ this.x += (x - this.x) * 2 * this._sx;
+ }
+ this.width = (x - this.x) * 2;
+ }
+ this._sx = 0.5;
+ this._fw = 0;
},
getCenterY: function() {
- return this.y + this.height * 0.5;
+ return this.y + this.height / 2;
},
setCenterY: function(y) {
- this.y = y - this.height * 0.5;
- this._fixY = 0.5;
+ if (this._fh || this._sy === 0.5) {
+ this.y = y - this.height / 2;
+ } else {
+ if (this._sy) {
+ this.y += (y - this.y) * 2 * this._sy;
+ }
+ this.height = (y - this.y) * 2;
+ }
+ this._sy = 0.5;
+ this._fh = 0;
},
getCenter: function(_dontLink) {
@@ -1797,7 +1962,7 @@
contains: function(arg) {
return arg && arg.width !== undefined
- || (Array.isArray(arg) ? arg : arguments).length == 4
+ || (Array.isArray(arg) ? arg : arguments).length === 4
? this._containsRectangle(Rectangle.read(arguments))
: this._containsPoint(Point.read(arguments));
},
@@ -1819,19 +1984,12 @@
},
intersects: function() {
- var 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;
- },
-
- touches: function() {
- var 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;
+ var rect = Rectangle.read(arguments),
+ epsilon = Base.read(arguments) || 0;
+ return rect.x + rect.width > this.x - epsilon
+ && rect.y + rect.height > this.y - epsilon
+ && rect.x < this.x + this.width + epsilon
+ && rect.y < this.y + this.height + epsilon;
},
intersect: function() {
@@ -1880,8 +2038,8 @@
['Right', 'Center'], ['Bottom', 'Center']
],
function(parts, index) {
- var part = parts.join('');
- var xFirst = /^[RL]/.test(part);
+ var part = parts.join(''),
+ xFirst = /^[RL]/.test(part);
if (index >= 4)
parts[1] += xFirst ? 'Y' : 'X';
var x = parts[xFirst ? 0 : 1],
@@ -1908,12 +2066,12 @@
var LinkedRectangle = Rectangle.extend({
initialize: function Rectangle(x, y, width, height, owner, setter) {
- this.set(x, y, width, height, true);
+ this._set(x, y, width, height, true);
this._owner = owner;
this._setter = setter;
},
- set: function(x, y, width, height, _dontNotify) {
+ _set: function(x, y, width, height, _dontNotify) {
this._x = x;
this._y = y;
this._width = width;
@@ -1922,12 +2080,13 @@
this._owner[this._setter](this);
return this;
}
-}, new function() {
+},
+new function() {
var proto = Rectangle.prototype;
return Base.each(['x', 'y', 'width', 'height'], function(key) {
- var part = Base.capitalize(key);
- var internal = '_' + key;
+ var part = Base.capitalize(key),
+ internal = '_' + key;
this['get' + part] = function() {
return this[internal];
};
@@ -1951,14 +2110,13 @@
};
}, {
isSelected: function() {
- return this._owner._boundsSelected;
+ return !!(this._owner._selection & 2);
},
setSelected: function(selected) {
var owner = this._owner;
- if (owner.setSelected) {
- owner._boundsSelected = selected;
- owner.setSelected(selected || owner._selectedSegmentState > 0);
+ if (owner._changeSelection) {
+ owner._changeSelection(2, selected);
}
}
})
@@ -1968,32 +2126,38 @@
var Matrix = Base.extend({
_class: 'Matrix',
- initialize: function Matrix(arg) {
+ initialize: function Matrix(arg, _dontNotify) {
var count = arguments.length,
ok = true;
- if (count === 6) {
- this.set.apply(this, arguments);
- } else if (count === 1) {
+ if (count >= 6) {
+ this._set.apply(this, arguments);
+ } else if (count === 1 || count === 2) {
if (arg instanceof Matrix) {
- this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
+ this._set(arg._a, arg._b, arg._c, arg._d, arg._tx, arg._ty,
+ _dontNotify);
} else if (Array.isArray(arg)) {
- this.set.apply(this, arg);
+ this._set.apply(this,
+ _dontNotify ? arg.concat([_dontNotify]) : arg);
} else {
ok = false;
}
- } else if (count === 0) {
+ } else if (!count) {
this.reset();
} else {
ok = false;
}
- if (!ok)
+ if (!ok) {
throw new Error('Unsupported matrix parameters');
- },
-
- set: function(a, c, b, d, tx, ty, _dontNotify) {
+ }
+ return this;
+ },
+
+ set: '#initialize',
+
+ _set: function(a, b, c, d, tx, ty, _dontNotify) {
this._a = a;
+ this._b = b;
this._c = c;
- this._b = b;
this._d = d;
this._tx = tx;
this._ty = ty;
@@ -2002,8 +2166,8 @@
return this;
},
- _serialize: function(options) {
- return Base.serialize(this.getValues(), options);
+ _serialize: function(options, dictionary) {
+ return Base.serialize(this.getValues(), options, true, dictionary);
},
_changed: function() {
@@ -2012,34 +2176,33 @@
if (owner._applyMatrix) {
owner.transform(null, true);
} else {
- owner._changed(9);
+ owner._changed(25);
}
}
},
clone: function() {
- return new Matrix(this._a, this._c, this._b, this._d,
+ return new Matrix(this._a, this._b, this._c, this._d,
this._tx, this._ty);
},
equals: function(mx) {
return mx === this || mx && this._a === mx._a && this._b === mx._b
&& this._c === mx._c && this._d === mx._d
- && this._tx === mx._tx && this._ty === mx._ty
- || false;
+ && this._tx === mx._tx && this._ty === mx._ty;
},
toString: function() {
var f = Formatter.instance;
- return '[[' + [f.number(this._a), f.number(this._b),
+ return '[[' + [f.number(this._a), f.number(this._c),
f.number(this._tx)].join(', ') + '], ['
- + [f.number(this._c), f.number(this._d),
+ + [f.number(this._b), f.number(this._d),
f.number(this._ty)].join(', ') + ']]';
},
reset: function(_dontNotify) {
this._a = this._d = 1;
- this._c = this._b = this._tx = this._ty = 0;
+ this._b = this._c = this._tx = this._ty = 0;
if (!_dontNotify)
this._changed();
return this;
@@ -2059,8 +2222,8 @@
var point = Point.read(arguments),
x = point.x,
y = point.y;
- this._tx += x * this._a + y * this._b;
- this._ty += x * this._c + y * this._d;
+ this._tx += x * this._a + y * this._c;
+ this._ty += x * this._b + y * this._d;
this._changed();
return this;
},
@@ -2071,8 +2234,8 @@
if (center)
this.translate(center);
this._a *= scale.x;
- this._c *= scale.x;
- this._b *= scale.y;
+ this._b *= scale.x;
+ this._c *= scale.y;
this._d *= scale.y;
if (center)
this.translate(center.negate());
@@ -2093,12 +2256,12 @@
b = this._b,
c = this._c,
d = this._d;
- this._a = cos * a + sin * b;
- this._b = -sin * a + cos * b;
- this._c = cos * c + sin * d;
- this._d = -sin * c + cos * d;
- this._tx += tx * a + ty * b;
- this._ty += tx * c + ty * d;
+ this._a = cos * a + sin * c;
+ this._b = cos * b + sin * d;
+ this._c = -sin * a + cos * c;
+ this._d = -sin * b + cos * d;
+ this._tx += tx * a + ty * c;
+ this._ty += tx * b + ty * d;
this._changed();
return this;
},
@@ -2109,11 +2272,11 @@
if (center)
this.translate(center);
var a = this._a,
- c = this._c;
- this._a += shear.y * this._b;
- this._c += shear.y * this._d;
- this._b += shear.x * a;
- this._d += shear.x * c;
+ b = this._b;
+ this._a += shear.y * this._c;
+ this._b += shear.y * this._d;
+ this._c += shear.x * a;
+ this._d += shear.x * b;
if (center)
this.translate(center.negate());
this._changed();
@@ -2129,87 +2292,113 @@
return this.shear(shear, center);
},
- concatenate: function(mx) {
- var a1 = this._a,
- b1 = this._b,
- c1 = this._c,
- d1 = this._d,
- a2 = mx._a,
- b2 = mx._b,
- c2 = mx._c,
- d2 = mx._d,
- tx2 = mx._tx,
- ty2 = mx._ty;
- this._a = a2 * a1 + c2 * b1;
- this._b = b2 * a1 + d2 * b1;
- this._c = a2 * c1 + c2 * d1;
- this._d = b2 * c1 + d2 * d1;
- this._tx += tx2 * a1 + ty2 * b1;
- this._ty += tx2 * c1 + ty2 * d1;
- this._changed();
+ append: function(mx, _dontNotify) {
+ if (mx) {
+ var a1 = this._a,
+ b1 = this._b,
+ c1 = this._c,
+ d1 = this._d,
+ a2 = mx._a,
+ b2 = mx._c,
+ c2 = mx._b,
+ d2 = mx._d,
+ tx2 = mx._tx,
+ ty2 = mx._ty;
+ this._a = a2 * a1 + c2 * c1;
+ this._c = b2 * a1 + d2 * c1;
+ this._b = a2 * b1 + c2 * d1;
+ this._d = b2 * b1 + d2 * d1;
+ this._tx += tx2 * a1 + ty2 * c1;
+ this._ty += tx2 * b1 + ty2 * d1;
+ if (!_dontNotify)
+ this._changed();
+ }
return this;
},
- preConcatenate: function(mx) {
- var a1 = this._a,
- b1 = this._b,
- c1 = this._c,
- d1 = this._d,
- tx1 = this._tx,
- ty1 = this._ty,
- a2 = mx._a,
- b2 = mx._b,
- c2 = mx._c,
- d2 = mx._d,
- tx2 = mx._tx,
- ty2 = mx._ty;
- this._a = a2 * a1 + b2 * c1;
- this._b = a2 * b1 + b2 * d1;
- this._c = c2 * a1 + d2 * c1;
- this._d = c2 * b1 + d2 * d1;
- this._tx = a2 * tx1 + b2 * ty1 + tx2;
- this._ty = c2 * tx1 + d2 * ty1 + ty2;
- this._changed();
+ prepend: function(mx, _dontNotify) {
+ if (mx) {
+ var a1 = this._a,
+ b1 = this._b,
+ c1 = this._c,
+ d1 = this._d,
+ tx1 = this._tx,
+ ty1 = this._ty,
+ a2 = mx._a,
+ b2 = mx._c,
+ c2 = mx._b,
+ d2 = mx._d,
+ tx2 = mx._tx,
+ ty2 = mx._ty;
+ this._a = a2 * a1 + b2 * b1;
+ this._c = a2 * c1 + b2 * d1;
+ this._b = c2 * a1 + d2 * b1;
+ this._d = c2 * c1 + d2 * d1;
+ this._tx = a2 * tx1 + b2 * ty1 + tx2;
+ this._ty = c2 * tx1 + d2 * ty1 + ty2;
+ if (!_dontNotify)
+ this._changed();
+ }
return this;
},
- chain: function(mx) {
- var a1 = this._a,
- b1 = this._b,
- c1 = this._c,
- d1 = this._d,
- tx1 = this._tx,
- ty1 = this._ty,
- a2 = mx._a,
- b2 = mx._b,
- c2 = mx._c,
- d2 = mx._d,
- tx2 = mx._tx,
- ty2 = mx._ty;
- return new Matrix(
- a2 * a1 + c2 * b1,
- a2 * c1 + c2 * d1,
- b2 * a1 + d2 * b1,
- b2 * c1 + d2 * d1,
- tx1 + tx2 * a1 + ty2 * b1,
- ty1 + tx2 * c1 + ty2 * d1);
+ appended: function(mx) {
+ return this.clone().append(mx);
+ },
+
+ prepended: function(mx) {
+ return this.clone().prepend(mx);
+ },
+
+ invert: function() {
+ var a = this._a,
+ b = this._b,
+ c = this._c,
+ d = this._d,
+ tx = this._tx,
+ ty = this._ty,
+ det = a * d - b * c,
+ res = null;
+ if (det && !isNaN(det) && isFinite(tx) && isFinite(ty)) {
+ this._a = d / det;
+ this._b = -b / det;
+ this._c = -c / det;
+ this._d = a / det;
+ this._tx = (c * ty - d * tx) / det;
+ this._ty = (b * tx - a * ty) / det;
+ res = this;
+ }
+ return res;
+ },
+
+ inverted: function() {
+ return this.clone().invert();
+ },
+
+ concatenate: '#append',
+ preConcatenate: '#prepend',
+ chain: '#appended',
+
+ _shiftless: function() {
+ return new Matrix(this._a, this._b, this._c, this._d, 0, 0);
+ },
+
+ _orNullIfIdentity: function() {
+ return this.isIdentity() ? null : this;
},
isIdentity: function() {
- return this._a === 1 && this._c === 0 && this._b === 0 && this._d === 1
+ return this._a === 1 && this._b === 0 && this._c === 0 && this._d === 1
&& this._tx === 0 && this._ty === 0;
},
- orNullIfIdentity: function() {
- return this.isIdentity() ? null : this;
- },
-
isInvertible: function() {
- return !!this._getDeterminant();
+ var det = this._a * this._d - this._c * this._b;
+ return det && !isNaN(det) && isFinite(this._tx) && isFinite(this._ty);
},
isSingular: function() {
- return !this._getDeterminant();
+ return !this.isInvertible();
},
transform: function( src, dst, count) {
@@ -2223,22 +2412,18 @@
y = point.y;
if (!dest)
dest = new Point();
- return dest.set(
- x * this._a + y * this._b + this._tx,
- x * this._c + y * this._d + this._ty,
- _dontNotify
- );
+ return dest._set(
+ x * this._a + y * this._c + this._tx,
+ x * this._b + y * this._d + this._ty,
+ _dontNotify);
},
_transformCoordinates: function(src, dst, count) {
- var i = 0,
- j = 0,
- max = 2 * count;
- while (i < max) {
- var x = src[i++],
- y = src[i++];
- dst[j++] = x * this._a + y * this._b + this._tx;
- dst[j++] = x * this._c + y * this._d + this._ty;
+ for (var i = 0, max = 2 * count; i < max; i += 2) {
+ var x = src[i],
+ y = src[i + 1];
+ dst[i] = x * this._a + y * this._c + this._tx;
+ dst[i + 1] = x * this._b + y * this._d + this._ty;
}
return dst;
},
@@ -2255,18 +2440,19 @@
_transformBounds: function(bounds, dest, _dontNotify) {
var coords = this._transformCorners(bounds),
min = coords.slice(0, 2),
- max = coords.slice();
+ max = min.slice();
for (var i = 2; i < 8; i++) {
var val = coords[i],
j = i & 1;
- if (val < min[j])
+ if (val < min[j]) {
min[j] = val;
- else if (val > max[j])
+ } else if (val > max[j]) {
max[j] = val;
+ }
}
if (!dest)
dest = new Rectangle();
- return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
+ return dest._set(min[0], min[1], max[0] - min[0], max[1] - min[1],
_dontNotify);
},
@@ -2274,62 +2460,64 @@
return this._inverseTransform(Point.read(arguments));
},
- _getDeterminant: function() {
- var det = this._a * this._d - this._b * this._c;
- return isFinite(det) && !Numerical.isZero(det)
- && 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();
- return dest.set(
- (x * this._d - y * this._b) / det,
- (y * this._a - x * this._c) / det,
- _dontNotify
- );
+ var a = this._a,
+ b = this._b,
+ c = this._c,
+ d = this._d,
+ tx = this._tx,
+ ty = this._ty,
+ det = a * d - b * c,
+ res = null;
+ if (det && !isNaN(det) && isFinite(tx) && isFinite(ty)) {
+ var x = point.x - this._tx,
+ y = point.y - this._ty;
+ if (!dest)
+ dest = new Point();
+ res = dest._set(
+ (x * d - y * c) / det,
+ (y * a - x * b) / det,
+ _dontNotify);
+ }
+ return res;
},
decompose: function() {
- var a = this._a, b = this._b, c = this._c, d = this._d;
- if (Numerical.isZero(a * d - b * c))
- return null;
-
- var scaleX = Math.sqrt(a * a + b * b);
- a /= scaleX;
- b /= scaleX;
-
- var shear = a * c + b * d;
- c -= a * shear;
- d -= b * shear;
-
- var scaleY = Math.sqrt(c * c + d * d);
- c /= scaleY;
- d /= scaleY;
- shear /= scaleY;
-
- if (a * d < b * c) {
- a = -a;
- b = -b;
- shear = -shear;
- scaleX = -scaleX;
- }
-
+ var a = this._a,
+ b = this._b,
+ c = this._c,
+ d = this._d,
+ det = a * d - b * c,
+ sqrt = Math.sqrt,
+ atan2 = Math.atan2,
+ degrees = 180 / Math.PI,
+ rotate,
+ scale,
+ skew;
+ if (a !== 0 || b !== 0) {
+ var r = sqrt(a * a + b * b);
+ rotate = Math.acos(a / r) * (b > 0 ? 1 : -1);
+ scale = [r, det / r];
+ skew = [atan2(a * c + b * d, r * r), 0];
+ } else if (c !== 0 || d !== 0) {
+ var s = sqrt(c * c + d * d);
+ rotate = Math.asin(c / s) * (d > 0 ? 1 : -1);
+ scale = [det / s, s];
+ skew = [0, atan2(a * c + b * d, s * s)];
+ } else {
+ rotate = 0;
+ skew = scale = [0, 0];
+ }
return {
- scaling: new Point(scaleX, scaleY),
- rotation: -Math.atan2(b, a) * 180 / Math.PI,
- shearing: shear
+ translation: this.getTranslation(),
+ rotation: rotate * degrees,
+ scaling: new Point(scale),
+ skewing: new Point(skew[0] * degrees, skew[1] * degrees)
};
},
getValues: function() {
- return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
+ return [ this._a, this._b, this._c, this._d, this._tx, this._ty ];
},
getTranslation: function() {
@@ -2337,34 +2525,22 @@
},
getScaling: function() {
- return (this.decompose() || {}).scaling;
+ return this.decompose().scaling;
},
getRotation: function() {
- return (this.decompose() || {}).rotation;
- },
-
- inverted: function() {
- var det = this._getDeterminant();
- return det && new Matrix(
- 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);
- },
-
- shiftless: function() {
- return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
+ return this.decompose().rotation;
},
applyToContext: function(ctx) {
- ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
- }
-}, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) {
- var part = Base.capitalize(name),
- prop = '_' + name;
+ if (!this.isIdentity()) {
+ ctx.transform(this._a, this._b, this._c, this._d,
+ this._tx, this._ty);
+ }
+ }
+}, Base.each(['a', 'b', 'c', 'd', 'tx', 'ty'], function(key) {
+ var part = Base.capitalize(key),
+ prop = '_' + key;
this['get' + part] = function() {
return this[prop];
};
@@ -2417,41 +2593,60 @@
true, isInfinite);
},
- getSide: function(point) {
+ getSide: function(point, isInfinite) {
return Line.getSide(
this._px, this._py, this._vx, this._vy,
- point.x, point.y, true);
+ point.x, point.y, true, isInfinite);
},
getDistance: function(point) {
- return Math.abs(Line.getSignedDistance(
- this._px, this._py, this._vx, this._vy,
- point.x, point.y, true));
+ return Math.abs(this.getSignedDistance(point));
+ },
+
+ getSignedDistance: function(point) {
+ return Line.getSignedDistance(this._px, this._py, this._vx, this._vy,
+ point.x, point.y, true);
+ },
+
+ isCollinear: function(line) {
+ return Point.isCollinear(this._vx, this._vy, line._vx, line._vy);
+ },
+
+ isOrthogonal: function(line) {
+ return Point.isOrthogonal(this._vx, this._vy, line._vx, line._vy);
},
statics: {
- intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector,
+ intersect: function(p1x, p1y, v1x, v1y, p2x, p2y, v2x, v2y, asVector,
isInfinite) {
if (!asVector) {
- avx -= apx;
- avy -= apy;
- bvx -= bpx;
- bvy -= bpy;
- }
- var cross = avx * bvy - avy * bvx;
+ v1x -= p1x;
+ v1y -= p1y;
+ v2x -= p2x;
+ v2y -= p2y;
+ }
+ var cross = v1x * v2y - v1y * v2x;
if (!Numerical.isZero(cross)) {
- var dx = apx - bpx,
- dy = apy - bpy,
- ta = (bvx * dy - bvy * dx) / cross,
- tb = (avx * dy - avy * dx) / cross;
- if (isInfinite || 0 <= ta && ta <= 1 && 0 <= tb && tb <= 1)
+ var dx = p1x - p2x,
+ dy = p1y - p2y,
+ u1 = (v2x * dy - v2y * dx) / cross,
+ u2 = (v1x * dy - v1y * dx) / cross,
+ epsilon = 1e-12,
+ uMin = -epsilon,
+ uMax = 1 + epsilon;
+ if (isInfinite
+ || uMin < u1 && u1 < uMax && uMin < u2 && u2 < uMax) {
+ if (!isInfinite) {
+ u1 = u1 <= 0 ? 0 : u1 >= 1 ? 1 : u1;
+ }
return new Point(
- apx + ta * avx,
- apy + ta * avy);
- }
- },
-
- getSide: function(px, py, vx, vy, x, y, asVector) {
+ p1x + u1 * v1x,
+ p1y + u1 * v1y);
+ }
+ }
+ },
+
+ getSide: function(px, py, vx, vy, x, y, asVector, isInfinite) {
if (!asVector) {
vx -= px;
vy -= py;
@@ -2459,15 +2654,10 @@
var v2x = x - px,
v2y = y - py,
ccw = v2x * vy - v2y * vx;
- if (ccw === 0) {
- ccw = v2x * vx + v2y * vy;
- if (ccw > 0) {
- v2x -= vx;
- v2y -= vy;
- ccw = v2x * vx + v2y * vy;
- if (ccw < 0)
- ccw = 0;
- }
+ if (!isInfinite && Numerical.isZero(ccw)) {
+ ccw = (v2x * vx + v2x * vx) / (vx * vx + vy * vy);
+ if (ccw >= 0 && ccw <= 1)
+ ccw = 0;
}
return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
},
@@ -2477,11 +2667,14 @@
vx -= px;
vy -= py;
}
- return Numerical.isZero(vx)
- ? vy >= 0 ? px - x : x - px
- : Numerical.isZero(vy)
- ? vx >= 0 ? y - py : py - y
- : (vx * (y - py) - vy * (x - px)) / Math.sqrt(vx * vx + vy * vy);
+ return vx === 0 ? vy > 0 ? x - px : px - x
+ : vy === 0 ? vx < 0 ? y - py : py - y
+ : ((x-px) * vy - (y-py) * vx) / Math.sqrt(vx * vx + vy * vy);
+ },
+
+ getDistance: function(px, py, vx, vy, x, y, asVector) {
+ return Math.abs(
+ Line.getSignedDistance(px, py, vx, vy, x, y, asVector));
}
}
});
@@ -2490,32 +2683,55 @@
_class: 'Project',
_list: 'projects',
_reference: 'project',
+ _compactSerialize: true,
initialize: function Project(element) {
PaperScopeItem.call(this, true);
- this.layers = [];
+ this._children = [];
+ this._namedChildren = {};
this._activeLayer = null;
- this.symbols = [];
this._currentStyle = new Style(null, null, this);
this._view = View.create(this,
element || CanvasProvider.getCanvas(1, 1));
- this._selectedItems = {};
- this._selectedItemCount = 0;
+ this._selectionItems = {};
+ this._selectionCount = 0;
this._updateVersion = 0;
},
_serialize: function(options, dictionary) {
- return Base.serialize(this.layers, options, true, dictionary);
+ return Base.serialize(this._children, options, true, dictionary);
+ },
+
+ _changed: function(flags, item) {
+ if (flags & 1) {
+ var view = this._view;
+ if (view) {
+ view._needsUpdate = true;
+ if (!view._requested && view._autoUpdate)
+ view.requestUpdate();
+ }
+ }
+ var changes = this._changes;
+ if (changes && item) {
+ var changesById = this._changesById,
+ id = item._id,
+ entry = changesById[id];
+ if (entry) {
+ entry.flags |= flags;
+ } else {
+ changes.push(changesById[id] = { item: item, flags: flags });
+ }
+ }
},
clear: function() {
- for (var i = this.layers.length - 1; i >= 0; i--)
- this.layers[i].remove();
- this.symbols = [];
+ var children = this._children;
+ for (var i = children.length - 1; i >= 0; i--)
+ children[i].remove();
},
isEmpty: function() {
- return this.layers.length === 0;
+ return !this._children.length;
},
remove: function remove() {
@@ -2535,7 +2751,7 @@
},
setCurrentStyle: function(style) {
- this._currentStyle.initialize(style);
+ this._currentStyle.set(style);
},
getIndex: function() {
@@ -2546,85 +2762,113 @@
return this._scope.settings;
},
+ getLayers: function() {
+ return this._children;
+ },
+
getActiveLayer: function() {
- return this._activeLayer || new Layer({ project: this });
- },
+ return this._activeLayer || new Layer({ project: this, insert: true });
+ },
+
+ getSymbolDefinitions: function() {
+ var definitions = [],
+ ids = {};
+ this.getItems({
+ class: SymbolItem,
+ match: function(item) {
+ var definition = item._definition,
+ id = definition._id;
+ if (!ids[id]) {
+ ids[id] = true;
+ definitions.push(definition);
+ }
+ return false;
+ }
+ });
+ return definitions;
+ },
+
+ getSymbols: 'getSymbolDefinitions',
getSelectedItems: function() {
- var items = [];
- for (var id in this._selectedItems) {
- var item = this._selectedItems[id];
- if (item.isInserted())
+ var selectionItems = this._selectionItems,
+ items = [];
+ for (var id in selectionItems) {
+ var item = selectionItems[id],
+ selection = item._selection;
+ if ((selection & 1) && item.isInserted()) {
items.push(item);
+ } else if (!selection) {
+ this._updateSelection(item);
+ }
}
return items;
},
- insertChild: function(index, item, _preserve) {
- if (item instanceof Layer) {
- item._remove(false, true);
- Base.splice(this.layers, [item], index, 0);
- item._setProject(this, true);
- if (this._changes)
- item._changed(5);
- if (!this._activeLayer)
- this._activeLayer = item;
- } else if (item instanceof Item) {
- (this._activeLayer
- || this.insertChild(index, new Layer(Item.NO_INSERT)))
- .insertChild(index, item, _preserve);
- } else {
- item = null;
- }
- return item;
- },
-
- addChild: function(item, _preserve) {
- return this.insertChild(undefined, item, _preserve);
- },
-
_updateSelection: function(item) {
var id = item._id,
- selectedItems = this._selectedItems;
- if (item._selected) {
- if (selectedItems[id] !== item) {
- this._selectedItemCount++;
- selectedItems[id] = item;
- }
- } else if (selectedItems[id] === item) {
- this._selectedItemCount--;
- delete selectedItems[id];
+ selectionItems = this._selectionItems;
+ if (item._selection) {
+ if (selectionItems[id] !== item) {
+ this._selectionCount++;
+ selectionItems[id] = item;
+ }
+ } else if (selectionItems[id] === item) {
+ this._selectionCount--;
+ delete selectionItems[id];
}
},
selectAll: function() {
- var layers = this.layers;
- for (var i = 0, l = layers.length; i < l; i++)
- layers[i].setFullySelected(true);
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].setFullySelected(true);
},
deselectAll: function() {
- var selectedItems = this._selectedItems;
- for (var i in selectedItems)
- selectedItems[i].setFullySelected(false);
- },
-
- hitTest: function() {
- var point = Point.read(arguments),
- options = HitResult.getOptions(Base.read(arguments));
- for (var i = this.layers.length - 1; i >= 0; i--) {
- var res = this.layers[i]._hitTest(point, options);
- if (res) return res;
- }
- return null;
- },
-
- getItems: function(match) {
- return Item._getItems(this.layers, match);
- },
-
- getItem: function(match) {
- return Item._getItems(this.layers, match, null, null, true)[0] || null;
+ var selectionItems = this._selectionItems;
+ for (var i in selectionItems)
+ selectionItems[i].setFullySelected(false);
+ },
+
+ addLayer: function(layer) {
+ return this.insertLayer(undefined, layer);
+ },
+
+ insertLayer: function(index, layer) {
+ if (layer instanceof Layer) {
+ layer._remove(false, true);
+ Base.splice(this._children, [layer], index, 0);
+ layer._setProject(this, true);
+ var name = layer._name;
+ if (name)
+ layer.setName(name);
+ if (this._changes)
+ layer._changed(5);
+ if (!this._activeLayer)
+ this._activeLayer = layer;
+ } else {
+ layer = null;
+ }
+ return layer;
+ },
+
+ _insertItem: function(index, item, _created) {
+ item = this.insertLayer(index, item)
+ || (this._activeLayer || this._insertItem(undefined,
+ new Layer(Item.NO_INSERT), true))
+ .insertChild(index, item);
+ if (_created && item.activate)
+ item.activate();
+ return item;
+ },
+
+ getItems: function(options) {
+ return Item._getItems(this, options);
+ },
+
+ getItem: function(options) {
+ return Item._getItems(this, options, null, null, true)[0] || null;
},
importJSON: function(json) {
@@ -2633,100 +2877,64 @@
return Base.importJSON(json, layer && layer.isEmpty() && layer);
},
+ removeOn: function(type) {
+ var sets = this._removeSets;
+ if (sets) {
+ if (type === 'mouseup')
+ sets.mousedrag = null;
+ var set = sets[type];
+ if (set) {
+ for (var id in set) {
+ var item = set[id];
+ for (var key in sets) {
+ var other = sets[key];
+ if (other && other != set)
+ delete other[item._id];
+ }
+ item.remove();
+ }
+ sets[type] = null;
+ }
+ }
+ },
+
draw: function(ctx, matrix, pixelRatio) {
this._updateVersion++;
ctx.save();
matrix.applyToContext(ctx);
- var param = new Base({
- offset: new Point(0, 0),
- pixelRatio: pixelRatio,
- viewMatrix: matrix.isIdentity() ? null : matrix,
- matrices: [new Matrix()],
- updateMatrix: true
- });
- for (var i = 0, layers = this.layers, l = layers.length; i < l; i++)
- layers[i].draw(ctx, param);
+ var children = this._children,
+ param = new Base({
+ offset: new Point(0, 0),
+ pixelRatio: pixelRatio,
+ viewMatrix: matrix.isIdentity() ? null : matrix,
+ matrices: [new Matrix()],
+ updateMatrix: true
+ });
+ for (var i = 0, l = children.length; i < l; i++) {
+ children[i].draw(ctx, param);
+ }
ctx.restore();
- if (this._selectedItemCount > 0) {
+ if (this._selectionCount > 0) {
ctx.save();
ctx.strokeWidth = 1;
- var items = this._selectedItems,
+ var items = this._selectionItems,
size = this._scope.settings.handleSize,
version = this._updateVersion;
- for (var id in items)
+ for (var id in items) {
items[id]._drawSelection(ctx, matrix, size, items, version);
+ }
ctx.restore();
}
}
});
-var Symbol = Base.extend({
- _class: 'Symbol',
-
- initialize: function Symbol(item, dontCenter) {
- this._id = UID.get();
- this.project = paper.project;
- this.project.symbols.push(this);
- if (item)
- this.setDefinition(item, dontCenter);
- },
-
- _serialize: function(options, dictionary) {
- return dictionary.add(this, function() {
- return Base.serialize([this._class, this._definition],
- options, false, dictionary);
- });
- },
-
- _changed: function(flags) {
- if (flags & 8) {
- Item._clearBoundsCache(this);
- }
- if (flags & 1) {
- this.project._needsUpdate = true;
- }
- },
-
- getDefinition: function() {
- return this._definition;
- },
-
- setDefinition: function(item, _dontCenter) {
- if (item._parentSymbol)
- item = item.clone();
- if (this._definition)
- this._definition._parentSymbol = null;
- this._definition = item;
- item.remove();
- item.setSelected(false);
- if (!_dontCenter)
- item.setPosition(new Point());
- item._parentSymbol = this;
- this._changed(9);
- },
-
- place: function(position) {
- return new PlacedSymbol(this, position);
- },
-
- clone: function() {
- return new Symbol(this._definition.clone(false));
- },
-
- equals: function(symbol) {
- return symbol === this
- || symbol && this.definition.equals(symbol.definition)
- || false;
- }
-});
-
var Item = Base.extend(Emitter, {
statics: {
extend: function extend(src) {
if (src._serializeFields)
- src._serializeFields = new Base(
- this.prototype._serializeFields, src._serializeFields);
+ src._serializeFields = Base.set({},
+ this.prototype._serializeFields, src._serializeFields);
return extend.base.apply(this, arguments);
},
@@ -2734,25 +2942,71 @@
},
_class: 'Item',
+ _name: null,
_applyMatrix: true,
_canApplyMatrix: true,
- _boundsSelected: false,
+ _canScaleStroke: false,
+ _pivot: null,
+ _visible: true,
+ _blendMode: 'normal',
+ _opacity: 1,
+ _locked: false,
+ _guide: false,
+ _clipMask: false,
+ _selection: 0,
+ _selectBounds: true,
_selectChildren: false,
_serializeFields: {
name: null,
applyMatrix: null,
matrix: new Matrix(),
pivot: null,
- locked: false,
visible: true,
blendMode: 'normal',
opacity: 1,
+ locked: false,
guide: false,
- selected: false,
clipMask: false,
+ selected: false,
data: {}
},
-
+ _prioritize: ['applyMatrix']
+},
+new function() {
+ var handlers = ['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
+ 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'];
+ return Base.each(handlers,
+ function(name) {
+ this._events[name] = {
+ install: function(type) {
+ this.getView()._countItemEvent(type, 1);
+ },
+
+ uninstall: function(type) {
+ this.getView()._countItemEvent(type, -1);
+ }
+ };
+ }, {
+ _events: {
+ onFrame: {
+ install: function() {
+ this.getView()._animateItem(this, true);
+ },
+
+ uninstall: function() {
+ this.getView()._animateItem(this, false);
+ }
+ },
+
+ onLoad: {},
+ onError: {}
+ },
+ statics: {
+ _itemHandlers: handlers
+ }
+ }
+ );
+}, {
initialize: function Item() {
},
@@ -2760,94 +3014,30 @@
var hasProps = props && Base.isPlainObject(props),
internal = hasProps && props.internal === true,
matrix = this._matrix = new Matrix(),
- project = hasProps && props.project || paper.project;
- if (!internal)
- this._id = UID.get();
- this._applyMatrix = this._canApplyMatrix && paper.settings.applyMatrix;
+ project = hasProps && props.project || paper.project,
+ settings = paper.settings;
+ this._id = internal ? null : UID.get();
+ this._parent = this._index = null;
+ this._applyMatrix = this._canApplyMatrix && settings.applyMatrix;
if (point)
matrix.translate(point);
matrix._owner = this;
this._style = new Style(project._currentStyle, this, project);
- if (!this._project) {
- if (internal || hasProps && props.insert === false) {
- this._setProject(project);
- } else if (hasProps && props.parent) {
- this.setParent(props.parent);
- } else {
- (project._activeLayer || new Layer()).addChild(this);
- }
- }
- if (hasProps && props !== Item.NO_INSERT)
- this._set(props, { insert: true, project: true, parent: true },
- true);
+ if (internal || hasProps && props.insert == false
+ || !settings.insertItems && !(hasProps && props.insert === true)) {
+ this._setProject(project);
+ } else {
+ (hasProps && props.parent || project)
+ ._insertItem(undefined, this, true);
+ }
+ if (hasProps && props !== Item.NO_INSERT) {
+ this.set(props, {
+ internal: true, insert: true, project: true, parent: true
+ });
+ }
return hasProps;
},
- _events: new function() {
-
- var mouseFlags = {
- mousedown: {
- mousedown: 1,
- mousedrag: 1,
- click: 1,
- doubleclick: 1
- },
- mouseup: {
- mouseup: 1,
- mousedrag: 1,
- click: 1,
- doubleclick: 1
- },
- mousemove: {
- mousedrag: 1,
- mousemove: 1,
- mouseenter: 1,
- mouseleave: 1
- }
- };
-
- var mouseEvent = {
- install: function(type) {
- var counters = this.getView()._eventCounters;
- if (counters) {
- for (var key in mouseFlags) {
- counters[key] = (counters[key] || 0)
- + (mouseFlags[key][type] || 0);
- }
- }
- },
- uninstall: function(type) {
- var counters = this.getView()._eventCounters;
- if (counters) {
- for (var key in mouseFlags)
- counters[key] -= mouseFlags[key][type] || 0;
- }
- }
- };
-
- return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
- 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
- function(name) {
- this[name] = mouseEvent;
- }, {
- onFrame: {
- install: function() {
- this._animateItem(true);
- },
- uninstall: function() {
- this._animateItem(false);
- }
- },
-
- onLoad: {}
- }
- );
- },
-
- _animateItem: function(animate) {
- this.getView()._animateItem(this, animate);
- },
-
_serialize: function(options, dictionary) {
var props = {},
that = this;
@@ -2870,45 +3060,28 @@
},
_changed: function(flags) {
- var symbol = this._parentSymbol,
+ var symbol = this._symbol,
cacheParent = this._parent || symbol,
project = this._project;
if (flags & 8) {
- this._bounds = this._position = this._decomposed =
- this._globalMatrix = this._currentPath = undefined;
+ this._bounds = this._position = this._decomposed = undefined;
+ }
+ if (flags & 16) {
+ this._globalMatrix = undefined;
}
if (cacheParent
- && (flags & 40)) {
+ && (flags & 72)) {
Item._clearBoundsCache(cacheParent);
}
if (flags & 2) {
Item._clearBoundsCache(this);
}
- if (project) {
- if (flags & 1) {
- project._needsUpdate = true;
- }
- if (project._changes) {
- var entry = project._changesById[this._id];
- if (entry) {
- entry.flags |= flags;
- } else {
- entry = { item: this, flags: flags };
- project._changesById[this._id] = entry;
- project._changes.push(entry);
- }
- }
- }
+ if (project)
+ project._changed(flags, this);
if (symbol)
symbol._changed(flags);
},
- set: function(props) {
- if (props)
- this._set(props);
- return this;
- },
-
getId: function() {
return this._id;
},
@@ -2917,26 +3090,23 @@
return this._name;
},
- setName: function(name, unique) {
+ setName: function(name) {
if (this._name)
this._removeNamed();
if (name === (+name) + '')
throw new Error(
'Names consisting only of numbers are not supported.');
- var parent = this._parent;
- if (name && parent) {
- var children = parent._children,
- namedChildren = parent._namedChildren,
- orig = name,
- i = 1;
- while (unique && children[name])
- name = orig + ' ' + (i++);
+ var owner = this._getOwner();
+ if (name && owner) {
+ var children = owner._children,
+ namedChildren = owner._namedChildren;
(namedChildren[name] = namedChildren[name] || []).push(this);
- children[name] = this;
+ if (!(name in children))
+ children[name] = this;
}
this._name = name || undefined;
- this._changed(128);
+ this._changed(256);
},
getStyle: function() {
@@ -2949,30 +3119,43 @@
}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
function(name) {
var part = Base.capitalize(name),
- name = '_' + name;
+ key = '_' + name,
+ flags = {
+ locked: 256,
+ visible: 265
+ };
this['get' + part] = function() {
- return this[name];
+ return this[key];
};
this['set' + part] = function(value) {
- if (value != this[name]) {
- this[name] = value;
- this._changed(name === '_locked'
- ? 128 : 129);
+ if (value != this[key]) {
+ this[key] = value;
+ this._changed(flags[name] || 257);
}
};
},
{}), {
beans: true,
- _locked: false,
-
- _visible: true,
-
- _blendMode: 'normal',
-
- _opacity: 1,
-
- _guide: false,
+ getSelection: function() {
+ return this._selection;
+ },
+
+ setSelection: function(selection) {
+ if (selection !== this._selection) {
+ this._selection = selection;
+ var project = this._project;
+ if (project) {
+ project._updateSelection(this);
+ this._changed(257);
+ }
+ }
+ },
+
+ _changeSelection: function(flag, selected) {
+ var selection = this._selection;
+ this.setSelection(selected ? selection | flag : selection & ~flag);
+ },
isSelected: function() {
if (this._selectChildren) {
@@ -2981,33 +3164,28 @@
if (children[i].isSelected())
return true;
}
- return this._selected;
- },
-
- setSelected: function(selected, noChildren) {
- if (!noChildren && this._selectChildren) {
+ return !!(this._selection & 1);
+ },
+
+ setSelected: function(selected) {
+ if (this._selectChildren) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].setSelected(selected);
}
- if ((selected = !!selected) ^ this._selected) {
- this._selected = selected;
- this._project._updateSelection(this);
- this._changed(129);
- }
- },
-
- _selected: false,
+ this._changeSelection(1, selected);
+ },
isFullySelected: function() {
- var children = this._children;
- if (children && this._selected) {
+ var children = this._children,
+ selected = !!(this._selection & 1);
+ if (children && selected) {
for (var i = 0, l = children.length; i < l; i++)
if (!children[i].isFullySelected())
return false;
return true;
}
- return this._selected;
+ return selected;
},
setFullySelected: function(selected) {
@@ -3016,7 +3194,7 @@
for (var i = 0, l = children.length; i < l; i++)
children[i].setFullySelected(selected);
}
- this.setSelected(selected, true);
+ this._changeSelection(1, selected);
},
isClipMask: function() {
@@ -3030,13 +3208,11 @@
this.setFillColor(null);
this.setStrokeColor(null);
}
- this._changed(129);
+ this._changed(257);
if (this._parent)
- this._parent._changed(1024);
- }
- },
-
- _clipMask: false,
+ this._parent._changed(2048);
+ }
+ },
getData: function() {
if (!this._data)
@@ -3049,14 +3225,9 @@
},
getPosition: function(_dontLink) {
- var position = this._position,
- ctor = _dontLink ? Point : LinkedPoint;
- if (!position) {
- var pivot = this._pivot;
- position = this._position = pivot
- ? this._matrix._transformPoint(pivot)
- : this.getBounds().getCenter(true);
- }
+ var ctor = _dontLink ? Point : LinkedPoint;
+ var position = this._position ||
+ (this._position = this._getPositionFromBounds());
return new ctor(position.x, position.y, this, 'setPosition');
},
@@ -3064,105 +3235,135 @@
this.translate(Point.read(arguments).subtract(this.getPosition(true)));
},
- getPivot: function(_dontLink) {
+ _getPositionFromBounds: function(bounds) {
+ return this._pivot
+ ? this._matrix._transformPoint(this._pivot)
+ : (bounds || this.getBounds()).getCenter(true);
+ },
+
+ getPivot: function() {
var pivot = this._pivot;
- if (pivot) {
- var ctor = _dontLink ? Point : LinkedPoint;
- pivot = new ctor(pivot.x, pivot.y, this, 'setPivot');
- }
- return pivot;
+ return pivot
+ ? new LinkedPoint(pivot.x, pivot.y, this, 'setPivot')
+ : null;
},
setPivot: function() {
this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
this._position = undefined;
- },
-
- _pivot: null,
-}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds',
- 'internalBounds', 'internalRoughBounds'],
- function(key) {
- var getter = 'get' + Base.capitalize(key),
- match = key.match(/^internal(.*)$/),
- internalGetter = match ? 'get' + match[1] : null;
- this[getter] = function(_matrix) {
- var boundsGetter = this._boundsGetter,
- name = !internalGetter && (typeof boundsGetter === 'string'
- ? boundsGetter : boundsGetter && boundsGetter[getter])
- || getter,
- bounds = this._getCachedBounds(name, _matrix, this,
- internalGetter);
- return key === 'bounds'
- ? new LinkedRectangle(bounds.x, bounds.y, bounds.width,
- bounds.height, this, 'setBounds')
- : bounds;
+ }
+}, Base.each({
+ getStrokeBounds: { stroke: true },
+ getHandleBounds: { handle: true },
+ getInternalBounds: { internal: true }
+ },
+ function(options, key) {
+ this[key] = function(matrix) {
+ return this.getBounds(matrix, options);
};
},
{
beans: true,
- _getBounds: function(getter, matrix, cacheItem) {
- var children = this._children;
- if (!children || children.length == 0)
- return new Rectangle();
- Item._updateBoundsCache(this, cacheItem);
- var x1 = Infinity,
- x2 = -x1,
- y1 = x1,
- y2 = x2;
- for (var i = 0, l = children.length; i < l; i++) {
- var child = children[i];
- if (child._visible && !child.isEmpty()) {
- var rect = child._getCachedBounds(getter,
- matrix && matrix.chain(child._matrix), cacheItem);
- x1 = Math.min(rect.x, x1);
- y1 = Math.min(rect.y, y1);
- x2 = Math.max(rect.x + rect.width, x2);
- y2 = Math.max(rect.y + rect.height, y2);
- }
- }
- return isFinite(x1)
- ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
- : new Rectangle();
+ getBounds: function(matrix, options) {
+ var hasMatrix = options || matrix instanceof Matrix,
+ opts = Base.set({}, hasMatrix ? options : matrix,
+ this._boundsOptions);
+ if (!opts.stroke || this.getStrokeScaling())
+ opts.cacheItem = this;
+ var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
+ return !arguments.length
+ ? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
+ this, 'setBounds')
+ : rect;
},
setBounds: function() {
var rect = Rectangle.read(arguments),
bounds = this.getBounds(),
+ _matrix = this._matrix,
matrix = new Matrix(),
center = rect.getCenter();
matrix.translate(center);
if (rect.width != bounds.width || rect.height != bounds.height) {
+ if (!_matrix.isInvertible()) {
+ _matrix.set(_matrix._backup
+ || new Matrix().translate(_matrix.getTranslation()));
+ bounds = this.getBounds();
+ }
matrix.scale(
- bounds.width != 0 ? rect.width / bounds.width : 1,
- bounds.height != 0 ? rect.height / bounds.height : 1);
+ bounds.width !== 0 ? rect.width / bounds.width : 0,
+ bounds.height !== 0 ? rect.height / bounds.height : 0);
}
center = bounds.getCenter();
matrix.translate(-center.x, -center.y);
this.transform(matrix);
},
- _getCachedBounds: function(getter, matrix, cacheItem, internalGetter) {
- matrix = matrix && matrix.orNullIfIdentity();
- var _matrix = internalGetter ? null : this._matrix.orNullIfIdentity(),
- cache = (!matrix || matrix.equals(_matrix)) && getter;
- Item._updateBoundsCache(this._parent || this._parentSymbol, cacheItem);
- if (cache && this._bounds && this._bounds[cache])
- return this._bounds[cache].clone();
- var bounds = this._getBounds(internalGetter || getter,
- matrix || _matrix, cacheItem);
- if (cache) {
- if (!this._bounds)
- this._bounds = {};
- var cached = this._bounds[cache] = bounds.clone();
- cached._internal = !!internalGetter;
- }
- return bounds;
+ _getBounds: function(matrix, options) {
+ var children = this._children;
+ if (!children || !children.length)
+ return new Rectangle();
+ Item._updateBoundsCache(this, options.cacheItem);
+ return Item._getBounds(children, matrix, options);
+ },
+
+ _getBoundsCacheKey: function(options, internal) {
+ return [
+ options.stroke ? 1 : 0,
+ options.handle ? 1 : 0,
+ internal ? 1 : 0
+ ].join('');
+ },
+
+ _getCachedBounds: function(matrix, options, noInternal) {
+ matrix = matrix && matrix._orNullIfIdentity();
+ var internal = options.internal && !noInternal,
+ cacheItem = options.cacheItem,
+ _matrix = internal ? null : this._matrix._orNullIfIdentity(),
+ cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
+ && this._getBoundsCacheKey(options, internal),
+ bounds = this._bounds;
+ Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
+ if (cacheKey && bounds && cacheKey in bounds) {
+ var cached = bounds[cacheKey];
+ return {
+ rect: cached.rect.clone(),
+ nonscaling: cached.nonscaling
+ };
+ }
+ var res = this._getBounds(matrix || _matrix, options),
+ rect = res.rect || res,
+ style = this._style,
+ nonscaling = res.nonscaling || style.hasStroke()
+ && !style.getStrokeScaling();
+ if (cacheKey) {
+ if (!bounds) {
+ this._bounds = bounds = {};
+ }
+ var cached = bounds[cacheKey] = {
+ rect: rect.clone(),
+ nonscaling: nonscaling,
+ internal: internal
+ };
+ }
+ return {
+ rect: rect,
+ nonscaling: nonscaling
+ };
+ },
+
+ _getStrokeMatrix: function(matrix, options) {
+ var parent = this.getStrokeScaling() ? null
+ : options && options.internal ? this
+ : this._parent || this._symbol && this._symbol._item,
+ mx = parent ? parent.getViewMatrix().invert() : matrix;
+ return mx && mx._shiftless();
},
statics: {
_updateBoundsCache: function(parent, item) {
- if (parent) {
+ if (parent && item) {
var id = item._id,
ref = parent._boundsCache = parent._boundsCache || {
ids: {},
@@ -3188,6 +3389,35 @@
}
}
}
+ },
+
+ _getBounds: function(items, matrix, options) {
+ var x1 = Infinity,
+ x2 = -x1,
+ y1 = x1,
+ y2 = x2,
+ nonscaling = false;
+ options = options || {};
+ for (var i = 0, l = items.length; i < l; i++) {
+ var item = items[i];
+ if (item._visible && !item.isEmpty(true)) {
+ var bounds = item._getCachedBounds(
+ matrix && matrix.appended(item._matrix), options, true),
+ rect = bounds.rect;
+ x1 = Math.min(rect.x, x1);
+ y1 = Math.min(rect.y, y1);
+ x2 = Math.max(rect.x + rect.width, x2);
+ y2 = Math.max(rect.y + rect.height, y2);
+ if (bounds.nonscaling)
+ nonscaling = true;
+ }
+ }
+ return {
+ rect: isFinite(x1)
+ ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
+ : new Rectangle(),
+ nonscaling: nonscaling
+ };
}
}
@@ -3195,12 +3425,14 @@
beans: true,
_decompose: function() {
- return this._decomposed = this._matrix.decompose();
+ return this._applyMatrix
+ ? null
+ : this._decomposed || (this._decomposed = this._matrix.decompose());
},
getRotation: function() {
- var decomposed = this._decomposed || this._decompose();
- return decomposed && decomposed.rotation;
+ var decomposed = this._decompose();
+ return decomposed ? decomposed.rotation : 0;
},
setRotation: function(rotation) {
@@ -3208,26 +3440,39 @@
if (current != null && rotation != null) {
var decomposed = this._decomposed;
this.rotate(rotation - current);
- decomposed.rotation = rotation;
- this._decomposed = decomposed;
- }
- },
-
- getScaling: function(_dontLink) {
- var decomposed = this._decomposed || this._decompose(),
- scaling = decomposed && decomposed.scaling,
- ctor = _dontLink ? Point : LinkedPoint;
- return scaling && new ctor(scaling.x, scaling.y, this, 'setScaling');
+ if (decomposed) {
+ decomposed.rotation = rotation;
+ this._decomposed = decomposed;
+ }
+ }
+ },
+
+ getScaling: function() {
+ var decomposed = this._decompose(),
+ s = decomposed && decomposed.scaling;
+ return new LinkedPoint(s ? s.x : 1, s ? s.y : 1, this, 'setScaling');
},
setScaling: function() {
- var current = this.getScaling();
- if (current) {
- var scaling = Point.read(arguments, 0, { clone: true }),
- decomposed = this._decomposed;
- this.scale(scaling.x / current.x, scaling.y / current.y);
- decomposed.scaling = scaling;
- this._decomposed = decomposed;
+ var current = this.getScaling(),
+ scaling = Point.read(arguments, 0, { clone: true, readNull: true });
+ if (current && scaling && !current.equals(scaling)) {
+ var rotation = this.getRotation(),
+ decomposed = this._decomposed,
+ matrix = new Matrix(),
+ center = this.getPosition(true);
+ matrix.translate(center);
+ if (rotation)
+ matrix.rotate(rotation);
+ matrix.scale(scaling.x / current.x, scaling.y / current.y);
+ if (rotation)
+ matrix.rotate(-rotation);
+ matrix.translate(center.negate());
+ this.transform(matrix);
+ if (decomposed) {
+ decomposed.scaling = scaling;
+ this._decomposed = decomposed;
+ }
}
},
@@ -3238,28 +3483,38 @@
setMatrix: function() {
var matrix = this._matrix;
matrix.initialize.apply(matrix, arguments);
- if (this._applyMatrix) {
- this.transform(null, true);
- } else {
- this._changed(9);
- }
},
getGlobalMatrix: function(_dontClone) {
- var matrix = this._globalMatrix,
- updateVersion = this._project._updateVersion;
- if (matrix && matrix._updateVersion !== updateVersion)
- matrix = null;
+ var matrix = this._globalMatrix;
+ if (matrix) {
+ var parent = this._parent;
+ var parents = [];
+ while (parent) {
+ if (!parent._globalMatrix) {
+ matrix = null;
+ for (var i = 0, l = parents.length; i < l; i++) {
+ parents[i]._globalMatrix = null;
+ }
+ break;
+ }
+ parents.push(parent);
+ parent = parent._parent;
+ }
+ }
if (!matrix) {
matrix = this._globalMatrix = this._matrix.clone();
var parent = this._parent;
if (parent)
- matrix.preConcatenate(parent.getGlobalMatrix(true));
- matrix._updateVersion = updateVersion;
+ matrix.prepend(parent.getGlobalMatrix(true));
}
return _dontClone ? matrix : matrix.clone();
},
+ getViewMatrix: function() {
+ return this.getGlobalMatrix().prepend(this.getView()._matrix);
+ },
+
getApplyMatrix: function() {
return this._applyMatrix;
},
@@ -3291,7 +3546,7 @@
},
getView: function() {
- return this._project.getView();
+ return this._project._view;
},
_installEvents: function _installEvents(install) {
@@ -3318,6 +3573,8 @@
return item.addChild(this);
},
+ _getOwner: '#getParent',
+
getChildren: function() {
return this._children;
},
@@ -3337,11 +3594,13 @@
},
getNextSibling: function() {
- return this._parent && this._parent._children[this._index + 1] || null;
+ var owner = this._getOwner();
+ return owner && owner._children[this._index + 1] || null;
},
getPreviousSibling: function() {
- return this._parent && this._parent._children[this._index - 1] || null;
+ var owner = this._getOwner();
+ return owner && owner._children[this._index - 1] || null;
},
getIndex: function() {
@@ -3366,70 +3625,99 @@
return Base.equals(this._children, item._children);
},
- clone: function(insert) {
- return this._clone(new this.constructor(Item.NO_INSERT), insert);
- },
-
- _clone: function(copy, insert, includeMatrix) {
+ clone: function(options) {
+ var copy = new this.constructor(Item.NO_INSERT),
+ children = this._children,
+ insert = Base.pick(options ? options.insert : undefined,
+ options === undefined || options === true),
+ deep = Base.pick(options ? options.deep : undefined, true);
+ if (children)
+ copy.copyAttributes(this);
+ if (!children || deep)
+ copy.copyContent(this);
+ if (!children)
+ copy.copyAttributes(this);
+ if (insert)
+ copy.insertAbove(this);
+ var name = this._name,
+ parent = this._parent;
+ if (name && parent) {
+ var children = parent._children,
+ orig = name,
+ i = 1;
+ while (children[name])
+ name = orig + ' ' + (i++);
+ if (name !== orig)
+ copy.setName(name);
+ }
+ return copy;
+ },
+
+ copyContent: function(source) {
+ var children = source._children;
+ for (var i = 0, l = children && children.length; i < l; i++) {
+ this.addChild(children[i].clone(false), true);
+ }
+ },
+
+ copyAttributes: function(source, excludeMatrix) {
+ this.setStyle(source._style);
var keys = ['_locked', '_visible', '_blendMode', '_opacity',
- '_clipMask', '_guide'],
- children = this._children;
- copy.setStyle(this._style);
- for (var i = 0, l = children && children.length; i < l; i++) {
- copy.addChild(children[i].clone(false), true);
- }
+ '_clipMask', '_guide'];
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
- if (this.hasOwnProperty(key))
- copy[key] = this[key];
- }
- if (includeMatrix !== false)
- copy._matrix.initialize(this._matrix);
- copy.setApplyMatrix(this._applyMatrix);
- copy.setPivot(this._pivot);
- copy.setSelected(this._selected);
- copy._data = this._data ? Base.clone(this._data) : null;
- if (insert || insert === undefined)
- copy.insertAbove(this);
- if (this._name)
- copy.setName(this._name, true);
- return copy;
- },
-
- copyTo: function(itemOrProject) {
- return itemOrProject.addChild(this.clone(false));
- },
-
- rasterize: function(resolution) {
+ if (source.hasOwnProperty(key))
+ this[key] = source[key];
+ }
+ if (!excludeMatrix)
+ this._matrix.set(source._matrix, true);
+ this.setApplyMatrix(source._applyMatrix);
+ this.setPivot(source._pivot);
+ this.setSelection(source._selection);
+ var data = source._data,
+ name = source._name;
+ this._data = data ? Base.clone(data) : null;
+ if (name)
+ this.setName(name);
+ },
+
+ rasterize: function(resolution, insert) {
var bounds = this.getStrokeBounds(),
scale = (resolution || this.getView().getResolution()) / 72,
topLeft = bounds.getTopLeft().floor(),
bottomRight = bounds.getBottomRight().ceil(),
size = new Size(bottomRight.subtract(topLeft)),
- canvas = CanvasProvider.getCanvas(size.multiply(scale)),
- ctx = canvas.getContext('2d'),
- matrix = new Matrix().scale(scale).translate(topLeft.negate());
- ctx.save();
- matrix.applyToContext(ctx);
- this.draw(ctx, new Base({ matrices: [matrix] }));
- ctx.restore();
- var raster = new Raster(Item.NO_INSERT);
- raster.setCanvas(canvas);
+ raster = new Raster(Item.NO_INSERT);
+ if (!size.isZero()) {
+ var canvas = CanvasProvider.getCanvas(size.multiply(scale)),
+ ctx = canvas.getContext('2d'),
+ matrix = new Matrix().scale(scale).translate(topLeft.negate());
+ ctx.save();
+ matrix.applyToContext(ctx);
+ this.draw(ctx, new Base({ matrices: [matrix] }));
+ ctx.restore();
+ raster.setCanvas(canvas);
+ }
raster.transform(new Matrix().translate(topLeft.add(size.divide(2)))
.scale(1 / scale));
- raster.insertAbove(this);
+ if (insert === undefined || insert)
+ raster.insertAbove(this);
return raster;
},
contains: function() {
- return !!this._contains(
- this._matrix._inverseTransform(Point.read(arguments)));
+ var matrix = this._matrix;
+ return (
+ matrix.isInvertible() &&
+ !!this._contains(matrix._inverseTransform(Point.read(arguments)))
+ );
},
_contains: function(point) {
- if (this._children) {
- for (var i = this._children.length - 1; i >= 0; i--) {
- if (this._children[i].contains(point))
+ var children = this._children;
+ if (children) {
+ for (var i = children.length - 1; i >= 0; i--) {
+ if (children[i].contains(point))
return true;
}
return false;
@@ -3452,83 +3740,136 @@
intersects: function(item, _matrix) {
if (!(item instanceof Item))
return false;
- return this._asPathItem().getIntersections(item._asPathItem(),
- _matrix || item._matrix).length > 0;
- },
-
- hitTest: function() {
+ return this._asPathItem().getIntersections(item._asPathItem(), null,
+ _matrix, true).length > 0;
+ }
+},
+new function() {
+ function hitTest() {
return this._hitTest(
Point.read(arguments),
- HitResult.getOptions(Base.read(arguments)));
- },
-
- _hitTest: function(point, options) {
+ HitResult.getOptions(arguments));
+ }
+
+ function hitTestAll() {
+ var point = Point.read(arguments),
+ options = HitResult.getOptions(arguments),
+ all = [];
+ this._hitTest(point, Base.set({ all: all }, options));
+ return all;
+ }
+
+ function hitTestChildren(point, options, viewMatrix, _exclude) {
+ var children = this._children;
+ if (children) {
+ for (var i = children.length - 1; i >= 0; i--) {
+ var child = children[i];
+ var res = child !== _exclude && child._hitTest(point, options,
+ viewMatrix);
+ if (res && !options.all)
+ return res;
+ }
+ }
+ return null;
+ }
+
+ Project.inject({
+ hitTest: hitTest,
+ hitTestAll: hitTestAll,
+ _hitTest: hitTestChildren
+ });
+
+ return {
+ hitTest: hitTest,
+ hitTestAll: hitTestAll,
+ _hitTestChildren: hitTestChildren,
+ };
+}, {
+
+ _hitTest: function(point, options, parentViewMatrix) {
if (this._locked || !this._visible || this._guide && !options.guides
- || this.isEmpty())
+ || this.isEmpty()) {
return null;
+ }
var matrix = this._matrix,
- parentTotalMatrix = options._totalMatrix,
- view = this.getView(),
- totalMatrix = options._totalMatrix = parentTotalMatrix
- ? parentTotalMatrix.chain(matrix)
- : this.getGlobalMatrix().preConcatenate(view._matrix),
+ viewMatrix = parentViewMatrix
+ ? parentViewMatrix.appended(matrix)
+ : this.getGlobalMatrix().prepend(this.getView()._matrix),
+ tolerance = Math.max(options.tolerance, 1e-12),
tolerancePadding = options._tolerancePadding = new Size(
- Path._getPenPadding(1, totalMatrix.inverted())
- ).multiply(
- Math.max(options.tolerance, 0.000001)
- );
+ Path._getStrokePadding(tolerance,
+ matrix._shiftless().invert()));
point = matrix._inverseTransform(point);
-
- if (!this._children && !this.getInternalRoughBounds()
- .expand(tolerancePadding.multiply(2))._containsPoint(point))
+ if (!point || !this._children &&
+ !this.getBounds({ internal: true, stroke: true, handle: true })
+ .expand(tolerancePadding.multiply(2))._containsPoint(point)) {
return null;
+ }
+
var checkSelf = !(options.guides && !this._guide
- || options.selected && !this._selected
+ || options.selected && !this.isSelected()
|| options.type && options.type !== Base.hyphenate(this._class)
|| options.class && !(this instanceof options.class)),
+ match = options.match,
that = this,
+ bounds,
res;
- function checkBounds(type, part) {
- var pt = bounds['get' + part]();
- if (point.subtract(pt).divide(tolerancePadding).length <= 1)
- return new HitResult(type, that,
- { name: Base.hyphenate(part), point: pt });
- }
-
- if (checkSelf && (options.center || options.bounds) && this._parent) {
- var bounds = this.getInternalBounds();
- if (options.center)
- res = checkBounds('center', 'Center');
- if (!res && options.bounds) {
+ function filter(hit) {
+ if (hit && match && !match(hit))
+ hit = null;
+ if (hit && options.all)
+ options.all.push(hit);
+ return hit;
+ }
+
+ function checkPoint(type, part) {
+ var pt = part ? bounds['get' + part]() : that.getPosition();
+ if (point.subtract(pt).divide(tolerancePadding).length <= 1) {
+ return new HitResult(type, that, {
+ name: part ? Base.hyphenate(part) : type,
+ point: pt
+ });
+ }
+ }
+
+ var checkPosition = options.position,
+ checkCenter = options.center,
+ checkBounds = options.bounds;
+ if (checkSelf && this._parent
+ && (checkPosition || checkCenter || checkBounds)) {
+ if (checkCenter || checkBounds) {
+ bounds = this.getInternalBounds();
+ }
+ res = checkPosition && checkPoint('position') ||
+ checkCenter && checkPoint('center', 'Center');
+ if (!res && checkBounds) {
var points = [
'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
];
- for (var i = 0; i < 8 && !res; i++)
- res = checkBounds('bounds', points[i]);
- }
- }
-
- var children = !res && this._children;
- if (children) {
- var opts = this._getChildHitTestOptions(options);
- for (var i = children.length - 1; i >= 0 && !res; i--)
- res = children[i]._hitTest(point, opts);
- }
- if (!res && checkSelf)
- res = this._hitTestSelf(point, options);
- if (res && res.point)
+ for (var i = 0; i < 8 && !res; i++) {
+ res = checkPoint('bounds', points[i]);
+ }
+ }
+ res = filter(res);
+ }
+
+ if (!res) {
+ res = this._hitTestChildren(point, options, viewMatrix)
+ || checkSelf
+ && filter(this._hitTestSelf(point, options, viewMatrix,
+ this.getStrokeScaling() ? null
+ : viewMatrix._shiftless().invert()))
+ || null;
+ }
+ if (res && res.point) {
res.point = matrix.transform(res.point);
- options._totalMatrix = parentTotalMatrix;
+ }
return res;
},
- _getChildHitTestOptions: function(options) {
- return options;
- },
-
_hitTestSelf: function(point, options) {
if (options.fill && this.hasFill() && this._contains(point))
return new HitResult('fill', this);
@@ -3556,52 +3897,55 @@
if (name.hasOwnProperty(key) && !this.matches(key, name[key]))
return false;
}
+ return true;
} else if (type === 'function') {
return name(this);
+ } else if (name === 'match') {
+ return compare(this);
} else {
var value = /^(empty|editable)$/.test(name)
? this['is' + Base.capitalize(name)]()
: name === 'type'
? Base.hyphenate(this._class)
: this[name];
- if (/^(constructor|class)$/.test(name)) {
- if (!(this instanceof compare))
- return false;
- } else if (compare instanceof RegExp) {
- if (!compare.test(value))
- return false;
- } else if (typeof compare === 'function') {
- if (!compare(value))
- return false;
- } else if (Base.isPlainObject(compare)) {
- if (!matchObject(compare, value))
- return false;
- } else if (!Base.equals(value, compare)) {
- return false;
- }
- }
- return true;
- },
-
- getItems: function(match) {
- return Item._getItems(this._children, match, this._matrix);
- },
-
- getItem: function(match) {
- return Item._getItems(this._children, match, this._matrix, null, true)
- [0] || null;
+ if (name === 'class') {
+ if (typeof compare === 'function')
+ return this instanceof compare;
+ value = this._class;
+ }
+ if (typeof compare === 'function') {
+ return !!compare(value);
+ } else if (compare) {
+ if (compare.test) {
+ return compare.test(value);
+ } else if (Base.isPlainObject(compare)) {
+ return matchObject(compare, value);
+ }
+ }
+ return Base.equals(value, compare);
+ }
+ },
+
+ getItems: function(options) {
+ return Item._getItems(this, options, this._matrix);
+ },
+
+ getItem: function(options) {
+ return Item._getItems(this, options, this._matrix, null, true)[0]
+ || null;
},
statics: {
- _getItems: function _getItems(children, match, matrix, param,
- firstOnly) {
- if (!param && typeof match === 'object') {
- var overlapping = match.overlapping,
- inside = match.inside,
+ _getItems: function _getItems(item, options, matrix, param, firstOnly) {
+ if (!param) {
+ var obj = typeof options === 'object' && options,
+ overlapping = obj && obj.overlapping,
+ inside = obj && obj.inside,
bounds = overlapping || inside,
- rect = bounds && Rectangle.read([bounds]);
+ rect = bounds && Rectangle.read([bounds]);
param = {
items: [],
+ recursive: obj && obj.recursive !== false,
inside: !!inside,
overlapping: !!overlapping,
rect: rect,
@@ -3610,34 +3954,37 @@
insert: false
})
};
- if (bounds)
- match = Base.set({}, match,
- { inside: true, overlapping: true });
- }
- var items = param && param.items,
- rect = param && param.rect;
+ if (obj) {
+ options = Base.filter({}, options, {
+ recursive: true, inside: true, overlapping: true
+ });
+ }
+ }
+ var children = item._children,
+ items = param.items,
+ rect = param.rect;
matrix = rect && (matrix || new Matrix());
for (var i = 0, l = children && children.length; i < l; i++) {
var child = children[i],
- childMatrix = matrix && matrix.chain(child._matrix),
+ childMatrix = matrix && matrix.appended(child._matrix),
add = true;
if (rect) {
var bounds = child.getBounds(childMatrix);
if (!rect.intersects(bounds))
continue;
- if (!(param.inside && rect.contains(bounds))
- && !(param.overlapping && (bounds.contains(rect)
+ if (!(rect.contains(bounds)
+ || param.overlapping && (bounds.contains(rect)
|| param.path.intersects(child, childMatrix))))
add = false;
}
- if (add && child.matches(match)) {
+ if (add && child.matches(options)) {
items.push(child);
if (firstOnly)
break;
}
- _getItems(child._children, match,
- childMatrix, param,
- firstOnly);
+ if (param.recursive !== false) {
+ _getItems(child, options, childMatrix, param, firstOnly);
+ }
if (firstOnly && items.length > 0)
break;
}
@@ -3648,49 +3995,49 @@
importJSON: function(json) {
var res = Base.importJSON(json, this);
- return res !== this
- ? this.addChild(res)
- : res;
- },
-
- addChild: function(item, _preserve) {
- return this.insertChild(undefined, item, _preserve);
- },
-
- insertChild: function(index, item, _preserve) {
- var res = item ? this.insertChildren(index, [item], _preserve) : null;
+ return res !== this ? this.addChild(res) : res;
+ },
+
+ addChild: function(item) {
+ return this.insertChild(undefined, item);
+ },
+
+ insertChild: function(index, item) {
+ var res = item ? this.insertChildren(index, [item]) : null;
return res && res[0];
},
- addChildren: function(items, _preserve) {
- return this.insertChildren(this._children.length, items, _preserve);
- },
-
- insertChildren: function(index, items, _preserve, _proto) {
+ addChildren: function(items) {
+ return this.insertChildren(this._children.length, items);
+ },
+
+ insertChildren: function(index, items) {
var children = this._children;
if (children && items && items.length > 0) {
- items = Array.prototype.slice.apply(items);
+ items = Base.slice(items);
+ var inserted = {};
for (var i = items.length - 1; i >= 0; i--) {
- var item = items[i];
- if (_proto && !(item instanceof _proto)) {
+ var item = items[i],
+ id = item && item._id;
+ if (!item || inserted[id]) {
items.splice(i, 1);
} else {
- var shift = item._parent === this && item._index < index;
- if (item._remove(false, true) && shift)
- index--;
+ item._remove(false, true);
+ inserted[id] = true;
}
}
Base.splice(children, items, index, 0);
var project = this._project,
- notifySelf = project && project._changes;
+ notifySelf = project._changes;
for (var i = 0, l = items.length; i < l; i++) {
- var item = items[i];
+ var item = items[i],
+ name = item._name;
item._parent = this;
- item._setProject(this._project, true);
- if (item._name)
- item.setName(item._name);
+ item._setProject(project, true);
+ if (name)
+ item.setName(name);
if (notifySelf)
- this._changed(5);
+ item._changed(5);
}
this._changed(11);
} else {
@@ -3699,28 +4046,34 @@
return items;
},
- _insertSibling: function(index, item, _preserve) {
- return this._parent
- ? this._parent.insertChild(index, item, _preserve)
- : null;
- },
-
- insertAbove: function(item, _preserve) {
- return item._insertSibling(item._index + 1, this, _preserve);
- },
-
- insertBelow: function(item, _preserve) {
- return item._insertSibling(item._index, this, _preserve);
+ _insertItem: '#insertChild',
+
+ _insertAt: function(item, offset) {
+ var owner = item && item._getOwner(),
+ res = item !== this && owner ? this : null;
+ if (res) {
+ res._remove(false, true);
+ owner._insertItem(item._index + offset, res);
+ }
+ return res;
+ },
+
+ insertAbove: function(item) {
+ return this._insertAt(item, 1);
+ },
+
+ insertBelow: function(item) {
+ return this._insertAt(item, 0);
},
sendToBack: function() {
- return (this._parent || this instanceof Layer && this._project)
- .insertChild(0, this);
+ var owner = this._getOwner();
+ return owner ? owner._insertItem(0, this) : null;
},
bringToFront: function() {
- return (this._parent || this instanceof Layer && this._project)
- .addChild(this);
+ var owner = this._getOwner();
+ return owner ? owner._insertItem(undefined, this) : null;
},
appendTop: '#addChild',
@@ -3733,22 +4086,34 @@
moveBelow: '#insertBelow',
- reduce: function() {
- if (this._children && this._children.length === 1) {
- var child = this._children[0].reduce();
- child.insertAbove(this);
- child.setStyle(this._style);
- this.remove();
+ addTo: function(owner) {
+ return owner._insertItem(undefined, this);
+ },
+
+ copyTo: function(owner) {
+ return this.clone(false).addTo(owner);
+ },
+
+ reduce: function(options) {
+ var children = this._children;
+ if (children && children.length === 1) {
+ var child = children[0].reduce(options);
+ if (this._parent) {
+ child.insertAbove(this);
+ this.remove();
+ } else {
+ child.remove();
+ }
return child;
}
return this;
},
_removeNamed: function() {
- var parent = this._parent;
- if (parent) {
- var children = parent._children,
- namedChildren = parent._namedChildren,
+ var owner = this._getOwner();
+ if (owner) {
+ var children = owner._children,
+ namedChildren = owner._namedChildren,
name = this._name,
namedArray = namedChildren[name],
index = namedArray ? namedArray.indexOf(this) : -1;
@@ -3757,7 +4122,7 @@
delete children[name];
namedArray.splice(index, 1);
if (namedArray.length) {
- children[name] = namedArray[namedArray.length - 1];
+ children[name] = namedArray[0];
} else {
delete namedChildren[name];
}
@@ -3766,20 +4131,25 @@
},
_remove: function(notifySelf, notifyParent) {
- var parent = this._parent;
- if (parent) {
+ var owner = this._getOwner(),
+ project = this._project,
+ index = this._index;
+ if (this._style)
+ this._style._dispose();
+ if (owner) {
if (this._name)
this._removeNamed();
- if (this._index != null)
- Base.splice(parent._children, null, this._index, 1);
+ if (index != null) {
+ if (project._activeLayer === this)
+ project._activeLayer = this.getNextSibling()
+ || this.getPreviousSibling();
+ Base.splice(owner._children, null, index, 1);
+ }
this._installEvents(false);
- if (notifySelf) {
- var project = this._project;
- if (project && project._changes)
- this._changed(5);
- }
+ if (notifySelf && project._changes)
+ this._changed(5);
if (notifyParent)
- parent._changed(11);
+ owner._changed(11, this);
this._parent = null;
return true;
}
@@ -3797,12 +4167,12 @@
return ok;
},
- removeChildren: function(from, to) {
+ removeChildren: function(start, end) {
if (!this._children)
return null;
- from = from || 0;
- to = Base.pick(to, this._children.length);
- var removed = Base.splice(this._children, null, from, to - from);
+ start = start || 0;
+ end = Base.pick(end, this._children.length);
+ var removed = Base.splice(this._children, null, start, end - start);
for (var i = removed.length - 1; i >= 0; i--) {
removed[i]._remove(true, false);
}
@@ -3822,8 +4192,18 @@
}
},
- isEmpty: function() {
- return !this._children || this._children.length === 0;
+ isEmpty: function(recursively) {
+ var children = this._children;
+ var numChildren = children ? children.length : 0;
+ if (recursively) {
+ for (var i = 0; i < numChildren; i++) {
+ if (!children[i].isEmpty(recursively)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return !numChildren;
},
isEditable: function() {
@@ -3893,7 +4273,7 @@
isDescendant: function(item) {
var parent = this;
while (parent = parent._parent) {
- if (parent == item)
+ if (parent === item)
return true;
}
return false;
@@ -3903,6 +4283,10 @@
return item ? item.isDescendant(this) : false;
},
+ isSibling: function(item) {
+ return this._parent === item._parent;
+ },
+
isGroupedWith: function(item) {
var parent = this._parent;
while (parent) {
@@ -3915,70 +4299,73 @@
return false;
},
- translate: function() {
- var mx = new Matrix();
- return this.transform(mx.translate.apply(mx, arguments));
- },
-
- rotate: function(angle ) {
- return this.transform(new Matrix().rotate(angle,
- Point.read(arguments, 1, { readNull: true })
- || this.getPosition(true)));
- }
-}, Base.each(['scale', 'shear', 'skew'], function(name) {
- this[name] = function() {
- var point = Point.read(arguments),
+}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {
+ var rotate = key === 'rotate';
+ this[key] = function() {
+ var value = (rotate ? Base : Point).read(arguments),
center = Point.read(arguments, 0, { readNull: true });
- return this.transform(new Matrix()[name](point,
+ return this.transform(new Matrix()[key](value,
center || this.getPosition(true)));
};
}, {
-
-}), {
+ translate: function() {
+ var mx = new Matrix();
+ return this.transform(mx.translate.apply(mx, arguments));
+ },
+
transform: function(matrix, _applyMatrix, _applyRecursively,
_setApplyMatrix) {
- if (matrix && matrix.isIdentity())
- matrix = null;
var _matrix = this._matrix,
+ transformMatrix = matrix && !matrix.isIdentity(),
applyMatrix = (_applyMatrix || this._applyMatrix)
- && ((!_matrix.isIdentity() || matrix)
+ && ((!_matrix.isIdentity() || transformMatrix)
|| _applyMatrix && _applyRecursively && this._children);
- if (!matrix && !applyMatrix)
+ if (!transformMatrix && !applyMatrix)
return this;
- if (matrix)
- _matrix.preConcatenate(matrix);
- if (applyMatrix = applyMatrix && this._transformContent(_matrix,
- _applyRecursively, _setApplyMatrix)) {
- var pivot = this._pivot,
- style = this._style,
+ if (transformMatrix) {
+ if (!matrix.isInvertible() && _matrix.isInvertible())
+ _matrix._backup = _matrix.getValues();
+ _matrix.prepend(matrix, true);
+ var style = this._style,
fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true);
+ if (fillColor)
+ fillColor.transform(matrix);
+ if (strokeColor)
+ strokeColor.transform(matrix);
+ }
+ if (applyMatrix && (applyMatrix = this._transformContent(_matrix,
+ _applyRecursively, _setApplyMatrix))) {
+ var pivot = this._pivot;
if (pivot)
_matrix._transformPoint(pivot, pivot, true);
- if (fillColor)
- fillColor.transform(_matrix);
- if (strokeColor)
- strokeColor.transform(_matrix);
_matrix.reset(true);
if (_setApplyMatrix && this._canApplyMatrix)
this._applyMatrix = true;
}
var bounds = this._bounds,
position = this._position;
- this._changed(9);
- var decomp = bounds && matrix && matrix.decompose();
- if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
+ if (transformMatrix || applyMatrix) {
+ this._changed(25);
+ }
+ var decomp = transformMatrix && bounds && matrix.decompose();
+ if (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {
for (var key in bounds) {
- var rect = bounds[key];
- if (applyMatrix || !rect._internal)
+ var cache = bounds[key];
+ if (cache.nonscaling) {
+ delete bounds[key];
+ } else if (applyMatrix || !cache.internal) {
+ var rect = cache.rect;
matrix._transformBounds(rect, rect);
- }
- var getter = this._boundsGetter,
- rect = bounds[getter && getter.getBounds || getter || 'getBounds'];
- if (rect)
- this._position = rect.getCenter(true);
+ }
+ }
this._bounds = bounds;
- } else if (matrix && position) {
+ var cached = bounds[this._getBoundsCacheKey(
+ this._boundsOptions || {})];
+ if (cached) {
+ this._position = this._getPositionFromBounds(cached.rect);
+ }
+ } else if (transformMatrix && position && this._pivot) {
this._position = matrix._transformPoint(position, position);
}
return this;
@@ -4024,53 +4411,51 @@
new Size(bounds.width * scale, bounds.height * scale));
newBounds.setCenter(rectangle.getCenter());
this.setBounds(newBounds);
- },
-
- _setStyles: function(ctx) {
+ }
+}), {
+
+ _setStyles: function(ctx, param, viewMatrix) {
var style = this._style,
- fillColor = style.getFillColor(),
- strokeColor = style.getStrokeColor(),
- shadowColor = style.getShadowColor();
- if (fillColor)
- ctx.fillStyle = fillColor.toCanvasStyle(ctx);
- if (strokeColor) {
- var strokeWidth = style.getStrokeWidth();
- if (strokeWidth > 0) {
- ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
- ctx.lineWidth = strokeWidth;
- var strokeJoin = style.getStrokeJoin(),
- strokeCap = style.getStrokeCap(),
- miterLimit = style.getMiterLimit();
- if (strokeJoin)
- ctx.lineJoin = strokeJoin;
- if (strokeCap)
- ctx.lineCap = strokeCap;
- if (miterLimit)
- ctx.miterLimit = miterLimit;
- if (paper.support.nativeDash) {
- var dashArray = style.getDashArray(),
- dashOffset = style.getDashOffset();
- if (dashArray && dashArray.length) {
- if ('setLineDash' in ctx) {
- ctx.setLineDash(dashArray);
- ctx.lineDashOffset = dashOffset;
- } else {
- ctx.mozDash = dashArray;
- ctx.mozDashOffset = dashOffset;
- }
- }
- }
- }
- }
- if (shadowColor) {
- var shadowBlur = style.getShadowBlur();
- if (shadowBlur > 0) {
- ctx.shadowColor = shadowColor.toCanvasStyle(ctx);
- ctx.shadowBlur = shadowBlur;
- var offset = this.getShadowOffset();
- ctx.shadowOffsetX = offset.x;
- ctx.shadowOffsetY = offset.y;
- }
+ matrix = this._matrix;
+ if (style.hasFill()) {
+ ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix);
+ }
+ if (style.hasStroke()) {
+ ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx, matrix);
+ ctx.lineWidth = style.getStrokeWidth();
+ var strokeJoin = style.getStrokeJoin(),
+ strokeCap = style.getStrokeCap(),
+ miterLimit = style.getMiterLimit();
+ if (strokeJoin)
+ ctx.lineJoin = strokeJoin;
+ if (strokeCap)
+ ctx.lineCap = strokeCap;
+ if (miterLimit)
+ ctx.miterLimit = miterLimit;
+ if (paper.support.nativeDash) {
+ var dashArray = style.getDashArray(),
+ dashOffset = style.getDashOffset();
+ if (dashArray && dashArray.length) {
+ if ('setLineDash' in ctx) {
+ ctx.setLineDash(dashArray);
+ ctx.lineDashOffset = dashOffset;
+ } else {
+ ctx.mozDash = dashArray;
+ ctx.mozDashOffset = dashOffset;
+ }
+ }
+ }
+ }
+ if (style.hasShadow()) {
+ var pixelRatio = param.pixelRatio || 1,
+ mx = viewMatrix._shiftless().prepend(
+ new Matrix().scale(pixelRatio, pixelRatio)),
+ blur = mx.transform(new Point(style.getShadowBlur(), 0)),
+ offset = mx.transform(this.getShadowOffset());
+ ctx.shadowColor = style.getShadowColor().toCanvasStyle(ctx);
+ ctx.shadowBlur = blur.getLength();
+ ctx.shadowOffsetX = offset.x;
+ ctx.shadowOffsetY = offset.y;
}
},
@@ -4081,17 +4466,15 @@
var matrices = param.matrices,
viewMatrix = param.viewMatrix,
matrix = this._matrix,
- globalMatrix = matrices[matrices.length - 1].chain(matrix);
+ globalMatrix = matrices[matrices.length - 1].appended(matrix);
if (!globalMatrix.isInvertible())
return;
- function getViewMatrix(matrix) {
- return viewMatrix ? viewMatrix.chain(matrix) : matrix;
- }
+ viewMatrix = viewMatrix ? viewMatrix.appended(globalMatrix)
+ : globalMatrix;
matrices.push(globalMatrix);
if (param.updateMatrix) {
- globalMatrix._updateVersion = updateVersion;
this._globalMatrix = globalMatrix;
}
@@ -4107,9 +4490,11 @@
pixelRatio = param.pixelRatio || 1,
mainCtx, itemOffset, prevOffset;
if (!direct) {
- var bounds = this.getStrokeBounds(getViewMatrix(globalMatrix));
- if (!bounds.width || !bounds.height)
+ var bounds = this.getStrokeBounds(viewMatrix);
+ if (!bounds.width || !bounds.height) {
+ matrices.pop();
return;
+ }
prevOffset = param.offset;
itemOffset = param.offset = bounds.getTopLeft().floor();
mainCtx = ctx;
@@ -4120,8 +4505,9 @@
}
ctx.save();
var strokeMatrix = parentStrokeMatrix
- ? parentStrokeMatrix.chain(matrix)
- : !this.getStrokeScaling(true) && getViewMatrix(globalMatrix),
+ ? parentStrokeMatrix.appended(matrix)
+ : this._canScaleStroke && !this.getStrokeScaling(true)
+ && viewMatrix,
clip = !direct && param.clipItem,
transform = !strokeMatrix || clip;
if (direct) {
@@ -4131,21 +4517,24 @@
} else if (transform) {
ctx.translate(-itemOffset.x, -itemOffset.y);
}
- if (transform)
- (direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
- if (clip)
+ if (transform) {
+ (direct ? matrix : viewMatrix).applyToContext(ctx);
+ }
+ if (clip) {
param.clipItem.draw(ctx, param.extend({ clip: true }));
+ }
if (strokeMatrix) {
ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
var offset = param.offset;
if (offset)
ctx.translate(-offset.x, -offset.y);
}
- this._draw(ctx, param, strokeMatrix);
+ this._draw(ctx, param, viewMatrix, strokeMatrix);
ctx.restore();
matrices.pop();
- if (param.clip && !param.dontFinish)
- ctx.clip();
+ if (param.clip && !param.dontFinish) {
+ ctx.clip(this.getFillRule());
+ }
if (!direct) {
BlendMode.process(blendMode, ctx, mainCtx, opacity,
itemOffset.subtract(prevOffset).multiply(pixelRatio));
@@ -4167,27 +4556,58 @@
return updated;
},
- _drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
- if ((this._drawSelected || this._boundsSelected)
+ _drawSelection: function(ctx, matrix, size, selectionItems, updateVersion) {
+ var selection = this._selection,
+ itemSelected = selection & 1,
+ boundsSelected = selection & 2
+ || itemSelected && this._selectBounds,
+ positionSelected = selection & 4;
+ if (!this._drawSelected)
+ itemSelected = false;
+ if ((itemSelected || boundsSelected || positionSelected)
&& this._isUpdated(updateVersion)) {
- var color = this.getSelectedColor(true)
- || this.getLayer().getSelectedColor(true),
- mx = matrix.chain(this.getGlobalMatrix(true));
+ var layer,
+ color = this.getSelectedColor(true) || (layer = this.getLayer())
+ && layer.getSelectedColor(true),
+ mx = matrix.appended(this.getGlobalMatrix(true)),
+ half = size / 2;
ctx.strokeStyle = ctx.fillStyle = color
? color.toCanvasStyle(ctx) : '#009dec';
- if (this._drawSelected)
- this._drawSelected(ctx, mx, selectedItems);
- if (this._boundsSelected) {
- var half = size / 2;
- coords = mx._transformCorners(this.getInternalBounds());
+ if (itemSelected)
+ this._drawSelected(ctx, mx, selectionItems);
+ if (positionSelected) {
+ var pos = this.getPosition(true),
+ parent = this._parent,
+ point = parent ? parent.localToGlobal(pos) : pos,
+ x = point.x,
+ y = point.y;
ctx.beginPath();
- for (var i = 0; i < 8; i++)
- ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
+ ctx.arc(x, y, half, 0, Math.PI * 2, true);
+ ctx.stroke();
+ var deltas = [[0, -1], [1, 0], [0, 1], [-1, 0]],
+ start = half,
+ end = size + 1;
+ for (var i = 0; i < 4; i++) {
+ var delta = deltas[i],
+ dx = delta[0],
+ dy = delta[1];
+ ctx.moveTo(x + dx * start, y + dy * start);
+ ctx.lineTo(x + dx * end, y + dy * end);
+ ctx.stroke();
+ }
+ }
+ if (boundsSelected) {
+ var coords = mx._transformCorners(this.getInternalBounds());
+ ctx.beginPath();
+ for (var i = 0; i < 8; i++) {
+ ctx[!i ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
+ }
ctx.closePath();
ctx.stroke();
- for (var i = 0; i < 8; i++)
+ for (var i = 0; i < 8; i++) {
ctx.fillRect(coords[i] - half, coords[++i] - half,
size, size);
+ }
}
}
},
@@ -4195,10 +4615,10 @@
_canComposite: function() {
return false;
}
-}, Base.each(['down', 'drag', 'up', 'move'], function(name) {
- this['removeOn' + Base.capitalize(name)] = function() {
+}, Base.each(['down', 'drag', 'up', 'move'], function(key) {
+ this['removeOn' + Base.capitalize(key)] = function() {
var hash = {};
- hash[name] = true;
+ hash[key] = true;
return this.removeOn(hash);
};
}, {
@@ -4215,10 +4635,47 @@
}
return this;
}
-}));
+}), {
+ tween: function(from, to, options) {
+ if (!options) {
+ options = to;
+ to = from;
+ from = null;
+ if (!options) {
+ options = to;
+ to = null;
+ }
+ }
+ var easing = options && options.easing,
+ start = options && options.start,
+ duration = options != null && (
+ typeof options === 'number' ? options : options.duration
+ ),
+ tween = new Tween(this, from, to, duration, easing, start);
+ function onFrame(event) {
+ tween._handleFrame(event.time * 1000);
+ if (!tween.running) {
+ this.off('frame', onFrame);
+ }
+ }
+ if (duration) {
+ this.on('frame', onFrame);
+ }
+ return tween;
+ },
+
+ tweenTo: function(to, options) {
+ return this.tween(null, to, options);
+ },
+
+ tweenFrom: function(from, options) {
+ return this.tween(from, null, options);
+ }
+});
var Group = Item.extend({
_class: 'Group',
+ _selectBounds: false,
_selectChildren: true,
_serializeFields: {
children: []
@@ -4233,7 +4690,7 @@
_changed: function _changed(flags) {
_changed.base.call(this, flags);
- if (flags & 1026) {
+ if (flags & 2050) {
this._clipItem = undefined;
}
},
@@ -4242,10 +4699,10 @@
var clipItem = this._clipItem;
if (clipItem === undefined) {
clipItem = null;
- for (var i = 0, l = this._children.length; i < l; i++) {
- var child = this._children[i];
- if (child._clipMask) {
- clipItem = child;
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++) {
+ if (children[i]._clipMask) {
+ clipItem = children[i];
break;
}
}
@@ -4264,31 +4721,36 @@
child.setClipMask(clipped);
},
+ _getBounds: function _getBounds(matrix, options) {
+ var clipItem = this._getClipItem();
+ return clipItem
+ ? clipItem._getCachedBounds(clipItem._matrix.prepended(matrix),
+ Base.set({}, options, { stroke: false }))
+ : _getBounds.base.call(this, matrix, options);
+ },
+
+ _hitTestChildren: function _hitTestChildren(point, options, viewMatrix) {
+ var clipItem = this._getClipItem();
+ return (!clipItem || clipItem.contains(point))
+ && _hitTestChildren.base.call(this, point, options, viewMatrix,
+ clipItem);
+ },
+
_draw: function(ctx, param) {
var clip = param.clip,
- clipItem = !clip && this._getClipItem(),
- draw = true;
+ clipItem = !clip && this._getClipItem();
param = param.extend({ clipItem: clipItem, clip: false });
if (clip) {
- if (this._currentPath) {
- ctx.currentPath = this._currentPath;
- draw = false;
- } else {
- ctx.beginPath();
- param.dontStart = param.dontFinish = true;
- }
+ ctx.beginPath();
+ param.dontStart = param.dontFinish = true;
} else if (clipItem) {
clipItem.draw(ctx, param.extend({ clip: true }));
}
- if (draw) {
- for (var i = 0, l = this._children.length; i < l; i++) {
- var item = this._children[i];
- if (item !== clipItem)
- item.draw(ctx, param);
- }
- }
- if (clip) {
- this._currentPath = ctx.currentPath;
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++) {
+ var item = children[i];
+ if (item !== clipItem)
+ item.draw(ctx, param);
}
}
});
@@ -4296,47 +4758,12 @@
var Layer = Group.extend({
_class: 'Layer',
- initialize: function Layer(arg) {
- var props = Base.isPlainObject(arg)
- ? new Base(arg)
- : { children: Array.isArray(arg) ? arg : arguments },
- insert = props.insert;
- props.insert = false;
- Group.call(this, props);
- if (insert || insert === undefined) {
- this._project.addChild(this);
- this.activate();
- }
- },
-
- _remove: function _remove(notifySelf, notifyParent) {
- if (this._parent)
- return _remove.base.call(this, notifySelf, notifyParent);
- if (this._index != null) {
- var project = this._project;
- if (project._activeLayer === this)
- project._activeLayer = this.getNextSibling()
- || this.getPreviousSibling();
- Base.splice(project.layers, null, this._index, 1);
- this._installEvents(false);
- if (notifySelf && project._changes)
- this._changed(5);
- if (notifyParent) {
- project._needsUpdate = true;
- }
- return true;
- }
- return false;
- },
-
- getNextSibling: function getNextSibling() {
- return this._parent ? getNextSibling.base.call(this)
- : this._project.layers[this._index + 1] || null;
- },
-
- getPreviousSibling: function getPreviousSibling() {
- return this._parent ? getPreviousSibling.base.call(this)
- : this._project.layers[this._index - 1] || null;
+ initialize: function Layer() {
+ Group.apply(this, arguments);
+ },
+
+ _getOwner: function() {
+ return this._parent || this._index != null && this._project;
},
isInserted: function isInserted() {
@@ -4347,10 +4774,7 @@
this._project._activeLayer = this;
},
- _insertSibling: function _insertSibling(index, item, _preserve) {
- return !this._parent
- ? this._project.insertChild(index, item, _preserve)
- : _insertSibling.base.call(this, index, item, _preserve);
+ _hitTestSelf: function() {
}
});
@@ -4358,15 +4782,15 @@
_class: 'Shape',
_applyMatrix: false,
_canApplyMatrix: false,
- _boundsSelected: true,
+ _canScaleStroke: true,
_serializeFields: {
type: null,
size: null,
radius: null
},
- initialize: function Shape(props) {
- this._initialize(props);
+ initialize: function Shape(props, point) {
+ this._initialize(props, point);
},
_equals: function(item) {
@@ -4375,12 +4799,10 @@
&& Base.equals(this._radius, item._radius);
},
- clone: function(insert) {
- var copy = new Shape(Item.NO_INSERT);
- copy.setType(this._type);
- copy.setSize(this._size);
- copy.setRadius(this._radius);
- return this._clone(copy, insert);
+ copyContent: function(source) {
+ this.setType(source._type);
+ this.setSize(source._size);
+ this.setRadius(source._radius);
},
getType: function() {
@@ -4408,15 +4830,14 @@
width = size.width,
height = size.height;
if (type === 'rectangle') {
- var radius = Size.min(this._radius, size.divide(2));
- this._radius.set(radius.width, radius.height);
+ this._radius.set(Size.min(this._radius, size.divide(2)));
} else if (type === 'circle') {
width = height = (width + height) / 2;
this._radius = width / 2;
} else if (type === 'ellipse') {
- this._radius.set(width / 2, height / 2);
- }
- this._size.set(width, height);
+ this._radius._set(width / 2, height / 2);
+ }
+ this._size._set(width, height);
this._changed(9);
}
},
@@ -4435,7 +4856,7 @@
return;
var size = radius * 2;
this._radius = radius;
- this._size.set(size, size);
+ this._size._set(size, size);
} else {
radius = Size.read(arguments);
if (!this._radius) {
@@ -4443,12 +4864,12 @@
} else {
if (this._radius.equals(radius))
return;
- this._radius.set(radius.width, radius.height);
+ this._radius.set(radius);
if (type === 'rectangle') {
var size = Size.max(this._size, radius.multiply(2));
- this._size.set(size.width, size.height);
+ this._size.set(size);
} else if (type === 'ellipse') {
- this._size.set(radius.width * 2, radius.height * 2);
+ this._size._set(radius.width * 2, radius.height * 2);
}
}
}
@@ -4460,18 +4881,27 @@
},
toPath: function(insert) {
- var path = this._clone(new Path[Base.capitalize(this._type)]({
+ var path = new Path[Base.capitalize(this._type)]({
center: new Point(),
size: this._size,
radius: this._radius,
insert: false
- }), insert);
+ });
+ path.copyAttributes(this);
if (paper.settings.applyMatrix)
path.setApplyMatrix(true);
+ if (insert === undefined || insert)
+ path.insertAbove(this);
return path;
},
- _draw: function(ctx, param, strokeMatrix) {
+ toShape: '#clone',
+
+ _asPathItem: function() {
+ return this.toPath(false);
+ },
+
+ _draw: function(ctx, param, viewMatrix, strokeMatrix) {
var style = this._style,
hasFill = style.hasFill(),
hasStroke = style.hasStroke(),
@@ -4535,9 +4965,9 @@
ctx.closePath();
}
if (!dontPaint && (hasFill || hasStroke)) {
- this._setStyles(ctx);
+ this._setStyles(ctx, param, viewMatrix);
if (hasFill) {
- ctx.fill(style.getWindingRule());
+ ctx.fill(style.getFillRule());
ctx.shadowColor = 'rgba(0,0,0,0)';
}
if (hasStroke)
@@ -4549,37 +4979,42 @@
return !(this.hasFill() && this.hasStroke());
},
- _getBounds: function(getter, matrix) {
- var rect = new Rectangle(this._size).setCenter(0, 0);
- if (getter !== 'getBounds' && this.hasStroke())
- rect = rect.expand(this.getStrokeWidth());
- return matrix ? matrix._transformBounds(rect) : rect;
+ _getBounds: function(matrix, options) {
+ var rect = new Rectangle(this._size).setCenter(0, 0),
+ style = this._style,
+ strokeWidth = options.stroke && style.hasStroke()
+ && style.getStrokeWidth();
+ if (matrix)
+ rect = matrix._transformBounds(rect);
+ return strokeWidth
+ ? rect.expand(Path._getStrokePadding(strokeWidth,
+ this._getStrokeMatrix(matrix, options)))
+ : rect;
}
},
new function() {
-
function getCornerCenter(that, point, expand) {
var radius = that._radius;
if (!radius.isZero()) {
var halfSize = that._size.divide(2);
- for (var i = 0; i < 4; i++) {
- var dir = new Point(i & 1 ? 1 : -1, i > 1 ? 1 : -1),
+ for (var q = 1; q <= 4; q++) {
+ var dir = new Point(q > 1 && q < 4 ? -1 : 1, q > 2 ? -1 : 1),
corner = dir.multiply(halfSize),
center = corner.subtract(dir.multiply(radius)),
- rect = new Rectangle(corner, center);
- if ((expand ? rect.expand(expand) : rect).contains(point))
- return center;
- }
- }
- }
-
- function getEllipseRadius(point, radius) {
- var angle = point.getAngleInRadians(),
- width = radius.width * 2,
- height = radius.height * 2,
- x = width * Math.sin(angle),
- y = height * Math.cos(angle);
- return width * height / (2 * Math.sqrt(x * x + y * y));
+ rect = new Rectangle(
+ expand ? corner.add(dir.multiply(expand)) : corner,
+ center);
+ if (rect.contains(point))
+ return { point: center, quadrant: q };
+ }
+ }
+ }
+
+ function isOnEllipseStroke(point, radius, padding, quadrant) {
+ var vector = point.divide(radius);
+ return (!quadrant || vector.isInQuadrant(quadrant)) &&
+ vector.subtract(vector.normalize()).multiply(radius)
+ .divide(padding).length <= 1;
}
return {
@@ -4587,7 +5022,7 @@
if (this._type === 'rectangle') {
var center = getCornerCenter(this, point);
return center
- ? point.subtract(center).divide(this._radius)
+ ? point.subtract(center.point).divide(this._radius)
.getLength() <= 1
: _contains.base.call(this, point);
} else {
@@ -4595,34 +5030,37 @@
}
},
- _hitTestSelf: function _hitTestSelf(point, options) {
- var hit = false;
- if (this.hasStroke()) {
+ _hitTestSelf: function _hitTestSelf(point, options, viewMatrix,
+ strokeMatrix) {
+ var hit = false,
+ style = this._style,
+ hitStroke = options.stroke && style.hasStroke(),
+ hitFill = options.fill && style.hasFill();
+ if (hitStroke || hitFill) {
var type = this._type,
radius = this._radius,
- strokeWidth = this.getStrokeWidth() + 2 * options.tolerance;
+ strokeRadius = hitStroke ? style.getStrokeWidth() / 2 : 0,
+ strokePadding = options._tolerancePadding.add(
+ Path._getStrokePadding(strokeRadius,
+ !style.getStrokeScaling() && strokeMatrix));
if (type === 'rectangle') {
- var center = getCornerCenter(this, point, strokeWidth);
+ var padding = strokePadding.multiply(2),
+ center = getCornerCenter(this, point, padding);
if (center) {
- var pt = point.subtract(center);
- hit = 2 * Math.abs(pt.getLength()
- - getEllipseRadius(pt, radius)) <= strokeWidth;
+ hit = isOnEllipseStroke(point.subtract(center.point),
+ radius, strokePadding, center.quadrant);
} else {
var rect = new Rectangle(this._size).setCenter(0, 0),
- outer = rect.expand(strokeWidth),
- inner = rect.expand(-strokeWidth);
+ outer = rect.expand(padding),
+ inner = rect.expand(padding.negate());
hit = outer._containsPoint(point)
&& !inner._containsPoint(point);
}
} else {
- if (type === 'ellipse')
- radius = getEllipseRadius(point, radius);
- hit = 2 * Math.abs(point.getLength() - radius)
- <= strokeWidth;
- }
- }
- return hit
- ? new HitResult('stroke', this)
+ hit = isOnEllipseStroke(point, radius, strokePadding);
+ }
+ }
+ return hit ? new HitResult(hitStroke ? 'stroke' : 'fill', this)
: _hitTestSelf.base.apply(this, arguments);
}
};
@@ -4630,11 +5068,11 @@
statics: new function() {
function createShape(type, point, size, radius, args) {
- var item = new Shape(Base.getNamed(args));
+ var item = new Shape(Base.getNamed(args), point);
item._type = type;
item._size = size;
item._radius = radius;
- return item.translate(point);
+ return item;
}
return {
@@ -4677,23 +5115,43 @@
}});
var Raster = Item.extend({
+}, {
_class: 'Raster',
_applyMatrix: false,
_canApplyMatrix: false,
- _boundsGetter: 'getBounds',
- _boundsSelected: true,
+ _boundsOptions: { stroke: false, handle: false },
_serializeFields: {
crossOrigin: null,
source: null
},
-
- initialize: function Raster(object, position) {
- if (!this._initialize(object,
- position !== undefined && Point.read(arguments, 1))) {
- if (typeof object === 'string') {
- this.setSource(object);
- } else {
- this.setImage(object);
+ _prioritize: ['crossOrigin'],
+ _smoothing: true,
+ beans: true,
+
+ initialize: function Raster(source, position) {
+ if (!this._initialize(source,
+ position !== undefined && Point.read(arguments))) {
+ var image,
+ type = typeof source,
+ object = type === 'string'
+ ? document.getElementById(source)
+ : type === 'object'
+ ? source
+ : null;
+ if (object && object !== Item.NO_INSERT) {
+ if (object.getContent || object.naturalHeight != null) {
+ image = object;
+ } else if (object) {
+ var size = Size.read(arguments);
+ if (!size.isZero()) {
+ image = CanvasProvider.getCanvas(size);
+ }
+ }
+ }
+ if (image) {
+ this.setImage(image);
+ } else {
+ this.setSource(source);
}
}
if (!this._size) {
@@ -4706,19 +5164,17 @@
return this.getSource() === item.getSource();
},
- clone: function(insert) {
- var copy = new Raster(Item.NO_INSERT),
- image = this._image,
- canvas = this._canvas;
+ copyContent: function(source) {
+ var image = source._image,
+ canvas = source._canvas;
if (image) {
- copy.setImage(image);
+ this._setImage(image);
} else if (canvas) {
- var copyCanvas = CanvasProvider.getCanvas(this._size);
+ var copyCanvas = CanvasProvider.getCanvas(source._size);
copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
- copy.setImage(copyCanvas);
- }
- copy._crossOrigin = this._crossOrigin;
- return this._clone(copy, insert);
+ this._setImage(copyCanvas);
+ }
+ this._crossOrigin = source._crossOrigin;
},
getSize: function() {
@@ -4732,7 +5188,7 @@
if (!size.equals(this._size)) {
if (size.width > 0 && size.height > 0) {
var element = this.getElement();
- this.setImage(CanvasProvider.getCanvas(size));
+ this._setImage(CanvasProvider.getCanvas(size));
if (element)
this.getContext(true).drawImage(element, 0, 0,
size.width, size.height);
@@ -4760,6 +5216,10 @@
this.setSize(this.getWidth(), height);
},
+ getLoaded: function() {
+ return this._loaded;
+ },
+
isEmpty: function() {
var size = this._size;
return !size || size.width === 0 && size.height === 0;
@@ -4783,6 +5243,32 @@
},
setImage: function(image) {
+ var that = this;
+
+ function emit(event) {
+ var view = that.getView(),
+ type = event && event.type || 'load';
+ if (view && that.responds(type)) {
+ paper = view._scope;
+ that.emit(type, new Event(event));
+ }
+ }
+
+ this._setImage(image);
+ if (this._loaded) {
+ setTimeout(emit, 0);
+ } else if (image) {
+ DomEvent.add(image, {
+ load: function(event) {
+ that._setImage(image);
+ emit(event);
+ },
+ error: emit
+ });
+ }
+ },
+
+ _setImage: function(image) {
if (this._canvas)
CanvasProvider.release(this._canvas);
if (image && image.getContext) {
@@ -4792,13 +5278,13 @@
} else {
this._image = image;
this._canvas = null;
- this._loaded = image && image.complete;
+ this._loaded = !!(image && image.src && image.complete);
}
this._size = new Size(
image ? image.naturalWidth || image.width : 0,
image ? image.naturalHeight || image.height : 0);
this._context = null;
- this._changed(521);
+ this._changed(1033);
},
getCanvas: function() {
@@ -4817,12 +5303,12 @@
setCanvas: '#setImage',
- getContext: function(modify) {
+ getContext: function(_change) {
if (!this._context)
this._context = this.getCanvas().getContext('2d');
- if (modify) {
+ if (_change) {
this._image = null;
- this._changed(513);
+ this._changed(1025);
}
return this._context;
},
@@ -4832,45 +5318,39 @@
},
getSource: function() {
- return this._image && this._image.src || this.toDataURL();
+ var image = this._image;
+ return image && image.src || this.toDataURL();
},
setSource: function(src) {
- var that = this,
- crossOrigin = this._crossOrigin,
- image;
-
- function loaded() {
- var view = that.getView();
- if (view) {
- paper = view._scope;
- that.setImage(image);
- that.emit('load');
- view.update();
- }
- }
-
- image = document.getElementById(src) || new Image();
+ var image = new self.Image(),
+ crossOrigin = this._crossOrigin;
if (crossOrigin)
image.crossOrigin = crossOrigin;
- if (image.naturalWidth && image.naturalHeight) {
- setTimeout(loaded, 0);
- } else {
- DomEvent.add(image, { load: loaded });
- if (!image.src)
- image.src = src;
- }
+ if (src)
+ image.src = src;
this.setImage(image);
},
getCrossOrigin: function() {
- return this._image && this._image.crossOrigin || this._crossOrigin || '';
+ var image = this._image;
+ return image && image.crossOrigin || this._crossOrigin || '';
},
setCrossOrigin: function(crossOrigin) {
this._crossOrigin = crossOrigin;
- if (this._image)
- this._image.crossOrigin = crossOrigin;
+ var image = this._image;
+ if (image)
+ image.crossOrigin = crossOrigin;
+ },
+
+ getSmoothing: function() {
+ return this._smoothing;
+ },
+
+ setSmoothing: function(smoothing) {
+ this._smoothing = smoothing;
+ this._changed(257);
},
getElement: function() {
@@ -4890,19 +5370,20 @@
getSubRaster: function() {
var rect = Rectangle.read(arguments),
raster = new Raster(Item.NO_INSERT);
- raster.setImage(this.getSubCanvas(rect));
+ raster._setImage(this.getSubCanvas(rect));
raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
- raster._matrix.preConcatenate(this._matrix);
+ raster._matrix.prepend(this._matrix);
raster.insertAbove(this);
return raster;
},
toDataURL: function() {
- var src = this._image && this._image.src;
+ var image = this._image,
+ src = image && image.src;
if (/^data:/.test(src))
return src;
var canvas = this.getCanvas();
- return canvas ? canvas.toDataURL() : null;
+ return canvas ? canvas.toDataURL.apply(canvas, arguments) : null;
},
drawImage: function(image ) {
@@ -4917,11 +5398,15 @@
} else if (object instanceof PathItem) {
path = object;
bounds = object.getBounds();
- } else if (object.width) {
- bounds = new Rectangle(object);
- } else if (object.x) {
- bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
- }
+ } else if (typeof object === 'object') {
+ if ('width' in object) {
+ bounds = new Rectangle(object);
+ } else if ('x' in object) {
+ bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
+ }
+ }
+ if (!bounds)
+ return null;
var sampleSize = 32,
width = Math.min(bounds.width, sampleSize),
height = Math.min(bounds.height, sampleSize);
@@ -4984,6 +5469,11 @@
ctx.putImageData(imageData, point.x, point.y);
},
+ clear: function() {
+ var size = this._size;
+ this.getContext(true).clearRect(0, 0, size.width + 1, size.height + 1);
+ },
+
createImageData: function() {
var size = Size.read(arguments);
return this.getContext().createImageData(size.width, size.height);
@@ -5002,7 +5492,7 @@
this.getContext(true).putImageData(data, point.x, point.y);
},
- _getBounds: function(getter, matrix) {
+ _getBounds: function(matrix, options) {
var rect = new Rectangle(this._size).setCenter(0, 0);
return matrix ? matrix._transformBounds(rect) : rect;
},
@@ -5021,10 +5511,17 @@
}
},
- _draw: function(ctx) {
+ _draw: function(ctx, param, viewMatrix) {
var element = this.getElement();
- if (element) {
+ if (element && element.width > 0 && element.height > 0) {
ctx.globalAlpha = this._opacity;
+
+ this._setStyles(ctx, param, viewMatrix);
+
+ DomElement.setPrefixed(
+ ctx, 'imageSmoothingEnabled', this._smoothing
+ );
+
ctx.drawImage(element,
-this._size.width / 2, -this._size.height / 2);
}
@@ -5035,62 +5532,122 @@
}
});
-var PlacedSymbol = Item.extend({
- _class: 'PlacedSymbol',
+var SymbolItem = Item.extend({
+ _class: 'SymbolItem',
_applyMatrix: false,
_canApplyMatrix: false,
- _boundsGetter: { getBounds: 'getStrokeBounds' },
- _boundsSelected: true,
+ _boundsOptions: { stroke: true },
_serializeFields: {
symbol: null
},
- initialize: function PlacedSymbol(arg0, arg1) {
+ initialize: function SymbolItem(arg0, arg1) {
if (!this._initialize(arg0,
arg1 !== undefined && Point.read(arguments, 1)))
- this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
+ this.setDefinition(arg0 instanceof SymbolDefinition ?
+ arg0 : new SymbolDefinition(arg0));
},
_equals: function(item) {
- return this._symbol === item._symbol;
- },
-
- getSymbol: function() {
- return this._symbol;
- },
-
- setSymbol: function(symbol) {
- this._symbol = symbol;
+ return this._definition === item._definition;
+ },
+
+ copyContent: function(source) {
+ this.setDefinition(source._definition);
+ },
+
+ getDefinition: function() {
+ return this._definition;
+ },
+
+ setDefinition: function(definition) {
+ this._definition = definition;
this._changed(9);
},
- clone: function(insert) {
- var copy = new PlacedSymbol(Item.NO_INSERT);
- copy.setSymbol(this._symbol);
- return this._clone(copy, insert);
- },
+ getSymbol: '#getDefinition',
+ setSymbol: '#setDefinition',
isEmpty: function() {
- return this._symbol._definition.isEmpty();
- },
-
- _getBounds: function(getter, matrix, cacheItem) {
- var definition = this.symbol._definition;
- return definition._getCachedBounds(getter,
- matrix && matrix.chain(definition._matrix), cacheItem);
- },
-
- _hitTestSelf: function(point, options) {
- var res = this._symbol._definition._hitTest(point, options);
+ return this._definition._item.isEmpty();
+ },
+
+ _getBounds: function(matrix, options) {
+ var item = this._definition._item;
+ return item._getCachedBounds(item._matrix.prepended(matrix), options);
+ },
+
+ _hitTestSelf: function(point, options, viewMatrix) {
+ var res = this._definition._item._hitTest(point, options, viewMatrix);
if (res)
res.item = this;
return res;
},
_draw: function(ctx, param) {
- this.symbol._definition.draw(ctx, param);
- }
-
+ this._definition._item.draw(ctx, param);
+ }
+
+});
+
+var SymbolDefinition = Base.extend({
+ _class: 'SymbolDefinition',
+
+ initialize: function SymbolDefinition(item, dontCenter) {
+ this._id = UID.get();
+ this.project = paper.project;
+ if (item)
+ this.setItem(item, dontCenter);
+ },
+
+ _serialize: function(options, dictionary) {
+ return dictionary.add(this, function() {
+ return Base.serialize([this._class, this._item],
+ options, false, dictionary);
+ });
+ },
+
+ _changed: function(flags) {
+ if (flags & 8)
+ Item._clearBoundsCache(this);
+ if (flags & 1)
+ this.project._changed(flags);
+ },
+
+ getItem: function() {
+ return this._item;
+ },
+
+ setItem: function(item, _dontCenter) {
+ if (item._symbol)
+ item = item.clone();
+ if (this._item)
+ this._item._symbol = null;
+ this._item = item;
+ item.remove();
+ item.setSelected(false);
+ if (!_dontCenter)
+ item.setPosition(new Point());
+ item._symbol = this;
+ this._changed(9);
+ },
+
+ getDefinition: '#getItem',
+ setDefinition: '#setItem',
+
+ place: function(position) {
+ return new SymbolItem(this, position);
+ },
+
+ clone: function() {
+ return new SymbolDefinition(this._item.clone(false));
+ },
+
+ equals: function(symbol) {
+ return symbol === this
+ || symbol && this._item.equals(symbol._item)
+ || false;
+ }
});
var HitResult = Base.extend({
@@ -5099,15 +5656,14 @@
initialize: function HitResult(type, item, values) {
this.type = type;
this.item = item;
- if (values) {
- values.enumerable = true;
+ if (values)
this.inject(values);
- }
},
statics: {
- getOptions: function(options) {
- return new Base({
+ getOptions: function(args) {
+ var options = args && Base.read(args);
+ return Base.set({
type: null,
tolerance: paper.settings.hitTolerance,
fill: !options,
@@ -5115,6 +5671,7 @@
segments: !options,
handles: false,
ends: false,
+ position: false,
center: false,
bounds: false,
guides: false,
@@ -5127,39 +5684,46 @@
var Segment = Base.extend({
_class: 'Segment',
beans: true,
+ _selection: 0,
initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
var count = arguments.length,
- point, handleIn, handleOut;
- if (count === 0) {
- } else if (count === 1) {
- if (arg0.point) {
- point = arg0.point;
- handleIn = arg0.handleIn;
- handleOut = arg0.handleOut;
- } else {
- point = arg0;
- }
- } else if (count === 2 && typeof arg0 === 'number') {
- point = arguments;
- } else if (count <= 3) {
- point = arg0;
- handleIn = arg1;
- handleOut = arg2;
- } else {
- point = arg0 !== undefined ? [ arg0, arg1 ] : null;
- handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
- handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
+ point, handleIn, handleOut, selection;
+ if (count > 0) {
+ if (arg0 == null || typeof arg0 === 'object') {
+ if (count === 1 && arg0 && 'point' in arg0) {
+ point = arg0.point;
+ handleIn = arg0.handleIn;
+ handleOut = arg0.handleOut;
+ selection = arg0.selection;
+ } else {
+ point = arg0;
+ handleIn = arg1;
+ handleOut = arg2;
+ selection = arg3;
+ }
+ } else {
+ point = [ arg0, arg1 ];
+ handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
+ handleOut = arg4 !== undefined ? [ arg4, arg5 ] : null;
+ }
}
new SegmentPoint(point, this, '_point');
new SegmentPoint(handleIn, this, '_handleIn');
new SegmentPoint(handleOut, this, '_handleOut');
- },
-
- _serialize: function(options) {
- return Base.serialize(this.isStraight() ? this._point
- : [this._point, this._handleIn, this._handleOut],
- options, true);
+ if (selection)
+ this.setSelection(selection);
+ },
+
+ _serialize: function(options, dictionary) {
+ var point = this._point,
+ selection = this._selection,
+ obj = selection || this.hasHandles()
+ ? [point, this._handleIn, this._handleOut]
+ : point;
+ if (selection)
+ obj.push(selection);
+ return Base.serialize(obj, options, true, dictionary);
},
_changed: function(point) {
@@ -5178,7 +5742,7 @@
&& (curve = curves[index]))
curve._changed();
}
- path._changed(25);
+ path._changed(41);
},
getPoint: function() {
@@ -5186,8 +5750,7 @@
},
setPoint: function() {
- var point = Point.read(arguments);
- this._point.set(point.x, point.y);
+ this._point.set(Point.read(arguments));
},
getHandleIn: function() {
@@ -5195,8 +5758,7 @@
},
setHandleIn: function() {
- var point = Point.read(arguments);
- this._handleIn.set(point.x, point.y);
+ this._handleIn.set(Point.read(arguments));
},
getHandleOut: function() {
@@ -5204,70 +5766,50 @@
},
setHandleOut: function() {
- var point = Point.read(arguments);
- this._handleOut.set(point.x, point.y);
+ this._handleOut.set(Point.read(arguments));
},
hasHandles: function() {
- return !this.isStraight();
- },
-
- isStraight: function() {
- return this._handleIn.isZero() && this._handleOut.isZero();
- },
-
- isLinear: function() {
- return Segment.isLinear(this, this.getNext());
- },
-
- isCollinear: function(segment) {
- return Segment.isCollinear(this, this.getNext(),
- segment, segment.getNext());
- },
-
- isColinear: '#isCollinear',
-
- isOrthogonal: function() {
- return Segment.isOrthogonal(this.getPrevious(), this, this.getNext());
- },
-
- isOrthogonalArc: function() {
- return Segment.isOrthogonalArc(this, this.getNext());
- },
-
- isArc: '#isOrthogonalArc',
-
- _selectionState: 0,
-
- isSelected: function(_point) {
- var state = this._selectionState;
- return !_point ? !!(state & 7)
- : _point === this._point ? !!(state & 4)
- : _point === this._handleIn ? !!(state & 1)
- : _point === this._handleOut ? !!(state & 2)
- : false;
- },
-
- setSelected: function(selected, _point) {
- var path = this._path,
- selected = !!selected,
- state = this._selectionState,
- oldState = state,
- flag = !_point ? 7
- : _point === this._point ? 4
- : _point === this._handleIn ? 1
- : _point === this._handleOut ? 2
- : 0;
- if (selected) {
- state |= flag;
- } else {
- state &= ~flag;
- }
- this._selectionState = state;
- if (path && state !== oldState) {
- path._updateSelection(this, oldState, state);
- path._changed(129);
- }
+ return !this._handleIn.isZero() || !this._handleOut.isZero();
+ },
+
+ isSmooth: function() {
+ var handleIn = this._handleIn,
+ handleOut = this._handleOut;
+ return !handleIn.isZero() && !handleOut.isZero()
+ && handleIn.isCollinear(handleOut);
+ },
+
+ clearHandles: function() {
+ this._handleIn._set(0, 0);
+ this._handleOut._set(0, 0);
+ },
+
+ getSelection: function() {
+ return this._selection;
+ },
+
+ setSelection: function(selection) {
+ var oldSelection = this._selection,
+ path = this._path;
+ this._selection = selection = selection || 0;
+ if (path && selection !== oldSelection) {
+ path._updateSelection(this, oldSelection, selection);
+ path._changed(257);
+ }
+ },
+
+ _changeSelection: function(flag, selected) {
+ var selection = this._selection;
+ this.setSelection(selected ? selection | flag : selection & ~flag);
+ },
+
+ isSelected: function() {
+ return !!(this._selection & 7);
+ },
+
+ setSelected: function(selected) {
+ this._changeSelection(7, selected);
},
getIndex: function() {
@@ -5303,13 +5845,80 @@
|| this._path._closed && segments[0]) || null;
},
+ smooth: function(options, _first, _last) {
+ var opts = options || {},
+ type = opts.type,
+ factor = opts.factor,
+ prev = this.getPrevious(),
+ next = this.getNext(),
+ p0 = (prev || this)._point,
+ p1 = this._point,
+ p2 = (next || this)._point,
+ d1 = p0.getDistance(p1),
+ d2 = p1.getDistance(p2);
+ if (!type || type === 'catmull-rom') {
+ var a = factor === undefined ? 0.5 : factor,
+ d1_a = Math.pow(d1, a),
+ d1_2a = d1_a * d1_a,
+ d2_a = Math.pow(d2, a),
+ d2_2a = d2_a * d2_a;
+ if (!_first && prev) {
+ var A = 2 * d2_2a + 3 * d2_a * d1_a + d1_2a,
+ N = 3 * d2_a * (d2_a + d1_a);
+ this.setHandleIn(N !== 0
+ ? new Point(
+ (d2_2a * p0._x + A * p1._x - d1_2a * p2._x) / N - p1._x,
+ (d2_2a * p0._y + A * p1._y - d1_2a * p2._y) / N - p1._y)
+ : new Point());
+ }
+ if (!_last && next) {
+ var A = 2 * d1_2a + 3 * d1_a * d2_a + d2_2a,
+ N = 3 * d1_a * (d1_a + d2_a);
+ this.setHandleOut(N !== 0
+ ? new Point(
+ (d1_2a * p2._x + A * p1._x - d2_2a * p0._x) / N - p1._x,
+ (d1_2a * p2._y + A * p1._y - d2_2a * p0._y) / N - p1._y)
+ : new Point());
+ }
+ } else if (type === 'geometric') {
+ if (prev && next) {
+ var vector = p0.subtract(p2),
+ t = factor === undefined ? 0.4 : factor,
+ k = t * d1 / (d1 + d2);
+ if (!_first)
+ this.setHandleIn(vector.multiply(k));
+ if (!_last)
+ this.setHandleOut(vector.multiply(k - t));
+ }
+ } else {
+ throw new Error('Smoothing method \'' + type + '\' not supported.');
+ }
+ },
+
getPrevious: function() {
var segments = this._path && this._path._segments;
return segments && (segments[this._index - 1]
|| this._path._closed && segments[segments.length - 1]) || null;
},
+ isFirst: function() {
+ return !this._index;
+ },
+
+ isLast: function() {
+ var path = this._path;
+ return path && this._index === path._segments.length - 1 || false;
+ },
+
reverse: function() {
+ var handleIn = this._handleIn,
+ handleOut = this._handleOut,
+ tmp = handleIn.clone();
+ handleIn.set(handleOut);
+ handleOut.set(tmp);
+ },
+
+ reversed: function() {
return new Segment(this._point, this._handleOut, this._handleIn);
},
@@ -5343,6 +5952,27 @@
this._changed();
},
+ interpolate: function(from, to, factor) {
+ var u = 1 - factor,
+ v = factor,
+ point1 = from._point,
+ point2 = to._point,
+ handleIn1 = from._handleIn,
+ handleIn2 = to._handleIn,
+ handleOut2 = to._handleOut,
+ handleOut1 = from._handleOut;
+ this._point._set(
+ u * point1._x + v * point2._x,
+ u * point1._y + v * point2._y, true);
+ this._handleIn._set(
+ u * handleIn1._x + v * handleIn2._x,
+ u * handleIn1._y + v * handleIn2._y, true);
+ this._handleOut._set(
+ u * handleOut1._x + v * handleOut2._x,
+ u * handleOut1._y + v * handleOut2._y, true);
+ this._changed();
+ },
+
_transformCoordinates: function(matrix, coords, change) {
var point = this._point,
handleIn = !change || !this._handleIn.isZero()
@@ -5369,7 +5999,7 @@
if (change) {
point._x = x;
point._y = y;
- i = 2;
+ i = 2;
if (handleIn) {
handleIn._x = coords[i++] - x;
handleIn._y = coords[i++] - y;
@@ -5390,52 +6020,13 @@
}
}
return coords;
- },
-
- statics: {
-
- isLinear: function(seg1, seg2) {
- var l = seg2._point.subtract(seg1._point);
- return l.isCollinear(seg1._handleOut)
- && l.isCollinear(seg2._handleIn);
- },
-
- isCollinear: function(seg1, seg2, seg3, seg4) {
- return seg1._handleOut.isZero() && seg2._handleIn.isZero()
- && seg3._handleOut.isZero() && seg4._handleIn.isZero()
- && seg2._point.subtract(seg1._point).isCollinear(
- seg4._point.subtract(seg3._point));
- },
-
- isOrthogonal: function(seg1, seg2, seg3) {
- return seg1._handleOut.isZero() && seg2._handleIn.isZero()
- && seg2._handleOut.isZero() && seg3._handleIn.isZero()
- && seg2._point.subtract(seg1._point).isOrthogonal(
- seg3._point.subtract(seg2._point));
- },
-
- isOrthogonalArc: function(seg1, seg2) {
- var handle1 = seg1._handleOut,
- handle2 = seg2._handleIn,
- kappa = 0.5522847498307936;
- if (handle1.isOrthogonal(handle2)) {
- var pt1 = seg1._point,
- pt2 = seg2._point,
- corner = new Line(pt1, handle1, true).intersect(
- new Line(pt2, handle2, true), true);
- return corner && Numerical.isZero(handle1.getLength() /
- corner.subtract(pt1).getLength() - kappa)
- && Numerical.isZero(handle2.getLength() /
- corner.subtract(pt2).getLength() - kappa);
- }
- return false;
- },
}
});
var SegmentPoint = Point.extend({
initialize: function SegmentPoint(point, owner, key) {
- var x, y, selected;
+ var x, y,
+ selected;
if (!point) {
x = y = 0;
} else if ((x = point[0]) !== undefined) {
@@ -5457,22 +6048,13 @@
this.setSelected(true);
},
- set: function(x, y) {
+ _set: function(x, y) {
this._x = x;
this._y = y;
this._owner._changed(this);
return this;
},
- _serialize: function(options) {
- var f = options.formatter,
- x = f.number(this._x),
- y = f.number(this._y);
- return this.isSelected()
- ? { x: x, y: y, selected: true }
- : [x, y];
- },
-
getX: function() {
return this._x;
},
@@ -5492,65 +6074,124 @@
},
isZero: function() {
- return Numerical.isZero(this._x) && Numerical.isZero(this._y);
+ var isZero = Numerical.isZero;
+ return isZero(this._x) && isZero(this._y);
+ },
+
+ isSelected: function() {
+ return !!(this._owner._selection & this._getSelection());
},
setSelected: function(selected) {
- this._owner.setSelected(selected, this);
- },
-
- isSelected: function() {
- return this._owner.isSelected(this);
+ this._owner._changeSelection(this._getSelection(), selected);
+ },
+
+ _getSelection: function() {
+ var owner = this._owner;
+ return this === owner._point ? 1
+ : this === owner._handleIn ? 2
+ : this === owner._handleOut ? 4
+ : 0;
}
});
var Curve = Base.extend({
_class: 'Curve',
+ beans: true,
initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
- var count = arguments.length;
+ var count = arguments.length,
+ seg1, seg2,
+ point1, point2,
+ handle1, handle2;
if (count === 3) {
this._path = arg0;
- this._segment1 = arg1;
- this._segment2 = arg2;
- } else if (count === 0) {
- this._segment1 = new Segment();
- this._segment2 = new Segment();
+ seg1 = arg1;
+ seg2 = arg2;
+ } else if (!count) {
+ seg1 = new Segment();
+ seg2 = new Segment();
} else if (count === 1) {
- this._segment1 = new Segment(arg0.segment1);
- this._segment2 = new Segment(arg0.segment2);
+ if ('segment1' in arg0) {
+ seg1 = new Segment(arg0.segment1);
+ seg2 = new Segment(arg0.segment2);
+ } else if ('point1' in arg0) {
+ point1 = arg0.point1;
+ handle1 = arg0.handle1;
+ handle2 = arg0.handle2;
+ point2 = arg0.point2;
+ } else if (Array.isArray(arg0)) {
+ point1 = [arg0[0], arg0[1]];
+ point2 = [arg0[6], arg0[7]];
+ handle1 = [arg0[2] - arg0[0], arg0[3] - arg0[1]];
+ handle2 = [arg0[4] - arg0[6], arg0[5] - arg0[7]];
+ }
} else if (count === 2) {
- this._segment1 = new Segment(arg0);
- this._segment2 = new Segment(arg1);
- } else {
- var point1, handle1, handle2, point2;
- if (count === 4) {
- point1 = arg0;
- handle1 = arg1;
- handle2 = arg2;
- point2 = arg3;
- } else if (count === 8) {
- point1 = [arg0, arg1];
- point2 = [arg6, arg7];
- handle1 = [arg2 - arg0, arg3 - arg1];
- handle2 = [arg4 - arg6, arg5 - arg7];
- }
- this._segment1 = new Segment(point1, null, handle1);
- this._segment2 = new Segment(point2, handle2, null);
- }
+ seg1 = new Segment(arg0);
+ seg2 = new Segment(arg1);
+ } else if (count === 4) {
+ point1 = arg0;
+ handle1 = arg1;
+ handle2 = arg2;
+ point2 = arg3;
+ } else if (count === 8) {
+ point1 = [arg0, arg1];
+ point2 = [arg6, arg7];
+ handle1 = [arg2 - arg0, arg3 - arg1];
+ handle2 = [arg4 - arg6, arg5 - arg7];
+ }
+ this._segment1 = seg1 || new Segment(point1, null, handle1);
+ this._segment2 = seg2 || new Segment(point2, handle2, null);
+ },
+
+ _serialize: function(options, dictionary) {
+ return Base.serialize(this.hasHandles()
+ ? [this.getPoint1(), this.getHandle1(), this.getHandle2(),
+ this.getPoint2()]
+ : [this.getPoint1(), this.getPoint2()],
+ options, true, dictionary);
},
_changed: function() {
this._length = this._bounds = undefined;
},
+ clone: function() {
+ return new Curve(this._segment1, this._segment2);
+ },
+
+ toString: function() {
+ var parts = [ 'point1: ' + this._segment1._point ];
+ if (!this._segment1._handleOut.isZero())
+ parts.push('handle1: ' + this._segment1._handleOut);
+ if (!this._segment2._handleIn.isZero())
+ parts.push('handle2: ' + this._segment2._handleIn);
+ parts.push('point2: ' + this._segment2._point);
+ return '{ ' + parts.join(', ') + ' }';
+ },
+
+ classify: function() {
+ return Curve.classify(this.getValues());
+ },
+
+ remove: function() {
+ var removed = false;
+ if (this._path) {
+ var segment2 = this._segment2,
+ handleOut = segment2._handleOut;
+ removed = segment2.remove();
+ if (removed)
+ this._segment1._handleOut.set(handleOut);
+ }
+ return removed;
+ },
+
getPoint1: function() {
return this._segment1._point;
},
setPoint1: function() {
- var point = Point.read(arguments);
- this._segment1._point.set(point.x, point.y);
+ this._segment1._point.set(Point.read(arguments));
},
getPoint2: function() {
@@ -5558,8 +6199,7 @@
},
setPoint2: function() {
- var point = Point.read(arguments);
- this._segment2._point.set(point.x, point.y);
+ this._segment2._point.set(Point.read(arguments));
},
getHandle1: function() {
@@ -5567,8 +6207,7 @@
},
setHandle1: function() {
- var point = Point.read(arguments);
- this._segment1._handleOut.set(point.x, point.y);
+ this._segment1._handleOut.set(Point.read(arguments));
},
getHandle2: function() {
@@ -5576,8 +6215,7 @@
},
setHandle2: function() {
- var point = Point.read(arguments);
- this._segment2._handleIn.set(point.x, point.y);
+ this._segment2._handleIn.set(Point.read(arguments));
},
getSegment1: function() {
@@ -5608,9 +6246,19 @@
|| this._path._closed && curves[curves.length - 1]) || null;
},
+ isFirst: function() {
+ return !this._segment1._index;
+ },
+
+ isLast: function() {
+ var path = this._path;
+ return path && this._segment1._index === path._curves.length - 1
+ || false;
+ },
+
isSelected: function() {
return this.getPoint1().isSelected()
- && this.getHandle2().isSelected()
+ && this.getHandle1().isSelected()
&& this.getHandle2().isSelected()
&& this.getPoint2().isSelected();
},
@@ -5632,14 +6280,11 @@
for (var i = 0; i < 8; i += 2)
points.push(new Point(coords[i], coords[i + 1]));
return points;
- },
-
+ }
+}, {
getLength: function() {
- if (this._length == null) {
- this._length = this.isLinear()
- ? this._segment2._point.getDistance(this._segment1._point)
- : Curve.getLength(this.getValues(), 0, 1);
- }
+ if (this._length == null)
+ this._length = Curve.getLength(this.getValues(), 0, 1);
return this._length;
},
@@ -5647,6 +6292,10 @@
return Curve.getArea(this.getValues());
},
+ getLine: function() {
+ return new Line(this._segment1._point, this._segment2._point);
+ },
+
getPart: function(from, to) {
return new Curve(Curve.getPart(this.getValues(), from, to));
},
@@ -5655,245 +6304,267 @@
return Curve.getLength(this.getValues(), from, to);
},
- hasHandles: function() {
- return !this._segment1._handleOut.isZero()
- || !this._segment2._handleIn.isZero();
- },
-
- isLinear: function() {
- return Segment.isLinear(this._segment1, this._segment2);
- },
-
- isCollinear: function(curve) {
- return Ssegment.isCollinear(this._segment1, this._segment2,
- curve._segment1, curve._segment2);
- },
-
- isOrthogonalArc: function() {
- return Segment.isOrthogonalArc(this._segment1, this._segment2);
- },
-
- getIntersections: function(curve) {
- return Curve.filterIntersections(Curve.getIntersections(
- this.getValues(), curve.getValues(), this, curve, []));
- },
-
- _getParameter: function(offset, isParameter) {
- return isParameter
- ? offset
- : offset && offset.curve === this
- ? offset.parameter
- : offset === undefined && isParameter === undefined
- ? 0.5
- : this.getParameterAt(offset, 0);
- },
-
- divide: function(offset, isParameter, ignoreLinear) {
- var parameter = this._getParameter(offset, isParameter),
- tolerance = 0.000001,
+ divideAt: function(location) {
+ return this.divideAtTime(location && location.curve === this
+ ? location.time : this.getTimeAt(location));
+ },
+
+ divideAtTime: function(time, _setHandles) {
+ var tMin = 1e-8,
+ tMax = 1 - tMin,
res = null;
- if (parameter > tolerance && parameter < 1 - tolerance) {
- var parts = Curve.subdivide(this.getValues(), parameter),
- isLinear = ignoreLinear ? false : this.isLinear(),
+ if (time >= tMin && time <= tMax) {
+ var parts = Curve.subdivide(this.getValues(), time),
left = parts[0],
- right = parts[1];
-
- if (!isLinear) {
- this._segment1._handleOut.set(left[2] - left[0],
- left[3] - left[1]);
- this._segment2._handleIn.set(right[4] - right[6],
- right[5] - right[7]);
- }
-
+ right = parts[1],
+ setHandles = _setHandles || this.hasHandles(),
+ seg1 = this._segment1,
+ seg2 = this._segment2,
+ path = this._path;
+ if (setHandles) {
+ seg1._handleOut._set(left[2] - left[0], left[3] - left[1]);
+ seg2._handleIn._set(right[4] - right[6],right[5] - right[7]);
+ }
var x = left[6], y = left[7],
segment = new Segment(new Point(x, y),
- !isLinear && new Point(left[4] - x, left[5] - y),
- !isLinear && new Point(right[2] - x, right[3] - y));
-
- if (this._path) {
- if (this._segment1._index > 0 && this._segment2._index === 0) {
- this._path.add(segment);
- } else {
- this._path.insert(this._segment2._index, segment);
- }
- res = this;
- } else {
- var end = this._segment2;
+ setHandles && new Point(left[4] - x, left[5] - y),
+ setHandles && new Point(right[2] - x, right[3] - y));
+ if (path) {
+ path.insert(seg1._index + 1, segment);
+ res = this.getNext();
+ } else {
this._segment2 = segment;
- res = new Curve(segment, end);
+ this._changed();
+ res = new Curve(segment, seg2);
}
}
return res;
},
- split: function(offset, isParameter) {
- return this._path
- ? this._path.split(this._segment1._index,
- this._getParameter(offset, isParameter))
- : null;
- },
-
- reverse: function() {
- return new Curve(this._segment2.reverse(), this._segment1.reverse());
- },
-
- remove: function() {
- var removed = false;
- if (this._path) {
- var segment2 = this._segment2,
- handleOut = segment2._handleOut;
- removed = segment2.remove();
- if (removed)
- this._segment1._handleOut.set(handleOut.x, handleOut.y);
- }
- return removed;
- },
-
- clone: function() {
- return new Curve(this._segment1, this._segment2);
- },
-
- toString: function() {
- var parts = [ 'point1: ' + this._segment1._point ];
- if (!this._segment1._handleOut.isZero())
- parts.push('handle1: ' + this._segment1._handleOut);
- if (!this._segment2._handleIn.isZero())
- parts.push('handle2: ' + this._segment2._handleIn);
- parts.push('point2: ' + this._segment2._point);
- return '{ ' + parts.join(', ') + ' }';
+ splitAt: function(location) {
+ var path = this._path;
+ return path ? path.splitAt(location) : null;
+ },
+
+ splitAtTime: function(time) {
+ return this.splitAt(this.getLocationAtTime(time));
+ },
+
+ divide: function(offset, isTime) {
+ return this.divideAtTime(offset === undefined ? 0.5 : isTime ? offset
+ : this.getTimeAt(offset));
+ },
+
+ split: function(offset, isTime) {
+ return this.splitAtTime(offset === undefined ? 0.5 : isTime ? offset
+ : this.getTimeAt(offset));
+ },
+
+ reversed: function() {
+ return new Curve(this._segment2.reversed(), this._segment1.reversed());
+ },
+
+ clearHandles: function() {
+ this._segment1._handleOut._set(0, 0);
+ this._segment2._handleIn._set(0, 0);
},
statics: {
- getValues: function(segment1, segment2, matrix) {
+ getValues: function(segment1, segment2, matrix, straight) {
var p1 = segment1._point,
h1 = segment1._handleOut,
h2 = segment2._handleIn,
p2 = segment2._point,
- values = [
- p1._x, p1._y,
- p1._x + h1._x, p1._y + h1._y,
- p2._x + h2._x, p2._y + h2._y,
- p2._x, p2._y
- ];
+ x1 = p1.x, y1 = p1.y,
+ x2 = p2.x, y2 = p2.y,
+ values = straight
+ ? [ x1, y1, x1, y1, x2, y2, x2, y2 ]
+ : [
+ x1, y1,
+ x1 + h1._x, y1 + h1._y,
+ x2 + h2._x, y2 + h2._y,
+ x2, y2
+ ];
if (matrix)
matrix._transformCoordinates(values, values, 4);
return values;
},
subdivide: function(v, t) {
- var p1x = v[0], p1y = v[1],
- c1x = v[2], c1y = v[3],
- c2x = v[4], c2y = v[5],
- p2x = v[6], p2y = v[7];
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7];
if (t === undefined)
t = 0.5;
var u = 1 - t,
- p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y,
- p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y,
- p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y,
- p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y,
- p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y,
- p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y;
+ x4 = u * x0 + t * x1, y4 = u * y0 + t * y1,
+ x5 = u * x1 + t * x2, y5 = u * y1 + t * y2,
+ x6 = u * x2 + t * x3, y6 = u * y2 + t * y3,
+ x7 = u * x4 + t * x5, y7 = u * y4 + t * y5,
+ x8 = u * x5 + t * x6, y8 = u * y5 + t * y6,
+ x9 = u * x7 + t * x8, y9 = u * y7 + t * y8;
return [
- [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y],
- [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y]
+ [x0, y0, x4, y4, x7, y7, x9, y9],
+ [x9, y9, x8, y8, x6, y6, x3, y3]
];
},
+ getMonoCurves: function(v, dir) {
+ var curves = [],
+ io = dir ? 0 : 1,
+ o0 = v[io + 0],
+ o1 = v[io + 2],
+ o2 = v[io + 4],
+ o3 = v[io + 6];
+ if ((o0 >= o1) === (o1 >= o2) && (o1 >= o2) === (o2 >= o3)
+ || Curve.isStraight(v)) {
+ curves.push(v);
+ } else {
+ var a = 3 * (o1 - o2) - o0 + o3,
+ b = 2 * (o0 + o2) - 4 * o1,
+ c = o1 - o0,
+ tMin = 1e-8,
+ tMax = 1 - tMin,
+ roots = [],
+ n = Numerical.solveQuadratic(a, b, c, roots, tMin, tMax);
+ if (!n) {
+ curves.push(v);
+ } else {
+ roots.sort();
+ var t = roots[0],
+ parts = Curve.subdivide(v, t);
+ curves.push(parts[0]);
+ if (n > 1) {
+ t = (roots[1] - t) / (1 - t);
+ parts = Curve.subdivide(parts[1], t);
+ curves.push(parts[0]);
+ }
+ curves.push(parts[1]);
+ }
+ }
+ return curves;
+ },
+
solveCubic: function (v, coord, val, roots, min, max) {
- var p1 = v[coord],
- c1 = v[coord + 2],
- c2 = v[coord + 4],
- p2 = v[coord + 6],
- c = 3 * (c1 - p1),
- b = 3 * (c2 - c1) - c,
- a = p2 - p1 - c - b;
- return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
- },
-
- getParameterOf: function(v, x, y) {
- var tolerance = 0.000001;
- if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance)
- return 0;
- if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance)
- return 1;
- var txs = [],
- tys = [],
- sx = Curve.solveCubic(v, 0, x, txs, 0, 1),
- sy = Curve.solveCubic(v, 1, y, tys, 0, 1),
- tx, ty;
- for (var cx = 0; sx === -1 || cx < sx;) {
- if (sx === -1 || (tx = txs[cx++]) > 0 && tx < 1) {
- for (var cy = 0; sy === -1 || cy < sy;) {
- if (sy === -1 || (ty = tys[cy++]) > 0 && ty < 1) {
- if (sx === -1) {
- tx = ty;
- } else if (sy === -1) {
- ty = tx;
- }
- if (Math.abs(tx - ty) < tolerance)
- return (tx + ty) * 0.5;
- }
- }
- if (sx === -1)
- break;
- }
- }
- return null;
+ var v0 = v[coord],
+ v1 = v[coord + 2],
+ v2 = v[coord + 4],
+ v3 = v[coord + 6],
+ res = 0;
+ if ( !(v0 < val && v3 < val && v1 < val && v2 < val ||
+ v0 > val && v3 > val && v1 > val && v2 > val)) {
+ var c = 3 * (v1 - v0),
+ b = 3 * (v2 - v1) - c,
+ a = v3 - v0 - c - b;
+ res = Numerical.solveCubic(a, b, c, v0 - val, roots, min, max);
+ }
+ return res;
+ },
+
+ getTimeOf: function(v, point) {
+ var p0 = new Point(v[0], v[1]),
+ p3 = new Point(v[6], v[7]),
+ epsilon = 1e-12,
+ geomEpsilon = 1e-7,
+ t = point.isClose(p0, epsilon) ? 0
+ : point.isClose(p3, epsilon) ? 1
+ : null;
+ if (t === null) {
+ var coords = [point.x, point.y],
+ roots = [];
+ for (var c = 0; c < 2; c++) {
+ var count = Curve.solveCubic(v, c, coords[c], roots, 0, 1);
+ for (var i = 0; i < count; i++) {
+ var u = roots[i];
+ if (point.isClose(Curve.getPoint(v, u), geomEpsilon))
+ return u;
+ }
+ }
+ }
+ return point.isClose(p0, geomEpsilon) ? 0
+ : point.isClose(p3, geomEpsilon) ? 1
+ : null;
+ },
+
+ getNearestTime: function(v, point) {
+ if (Curve.isStraight(v)) {
+ var x0 = v[0], y0 = v[1],
+ x3 = v[6], y3 = v[7],
+ vx = x3 - x0, vy = y3 - y0,
+ det = vx * vx + vy * vy;
+ if (det === 0)
+ return 0;
+ var u = ((point.x - x0) * vx + (point.y - y0) * vy) / det;
+ return u < 1e-12 ? 0
+ : u > 0.999999999999 ? 1
+ : Curve.getTimeOf(v,
+ new Point(x0 + u * vx, y0 + u * vy));
+ }
+
+ var count = 100,
+ minDist = Infinity,
+ minT = 0;
+
+ function refine(t) {
+ if (t >= 0 && t <= 1) {
+ var dist = point.getDistance(Curve.getPoint(v, t), true);
+ if (dist < minDist) {
+ minDist = dist;
+ minT = t;
+ return true;
+ }
+ }
+ }
+
+ for (var i = 0; i <= count; i++)
+ refine(i / count);
+
+ var step = 1 / (count * 2);
+ while (step > 1e-8) {
+ if (!refine(minT - step) && !refine(minT + step))
+ step /= 2;
+ }
+ return minT;
},
getPart: function(v, from, to) {
+ var flip = from > to;
+ if (flip) {
+ var tmp = from;
+ from = to;
+ to = tmp;
+ }
if (from > 0)
v = Curve.subdivide(v, from)[1];
if (to < 1)
v = Curve.subdivide(v, (to - from) / (1 - from))[0];
- return v;
- },
-
- hasHandles: function(v) {
- var isZero = Numerical.isZero;
- return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3])
- && isZero(v[4] - v[6]) && isZero(v[5] - v[7]));
- },
-
- isLinear: function(v) {
- var p1x = v[0], p1y = v[1],
- p2x = v[6], p2y = v[7],
- l = new Point(p2x - p1x, p2y - p1y);
- return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y))
- && l.isCollinear(new Point(v[4] - p2x, v[5] - p2y));
- },
-
- isFlatEnough: function(v, tolerance) {
- var p1x = v[0], p1y = v[1],
- c1x = v[2], c1y = v[3],
- c2x = v[4], c2y = v[5],
- p2x = v[6], p2y = v[7],
- ux = 3 * c1x - 2 * p1x - p2x,
- uy = 3 * c1y - 2 * p1y - p2y,
- vx = 3 * c2x - 2 * p2x - p1x,
- vy = 3 * c2y - 2 * p2y - p1y;
+ return flip
+ ? [v[6], v[7], v[4], v[5], v[2], v[3], v[0], v[1]]
+ : v;
+ },
+
+ isFlatEnough: function(v, flatness) {
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7],
+ ux = 3 * x1 - 2 * x0 - x3,
+ uy = 3 * y1 - 2 * y0 - y3,
+ vx = 3 * x2 - 2 * x3 - x0,
+ vy = 3 * y2 - 2 * y3 - y0;
return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
- < 10 * tolerance * tolerance;
+ <= 16 * flatness * flatness;
},
getArea: function(v) {
- var p1x = v[0], p1y = v[1],
- c1x = v[2], c1y = v[3],
- c2x = v[4], c2y = v[5],
- p2x = v[6], p2y = v[7];
- return ( 3.0 * c1y * p1x - 1.5 * c1y * c2x
- - 1.5 * c1y * p2x - 3.0 * p1y * c1x
- - 1.5 * p1y * c2x - 0.5 * p1y * p2x
- + 1.5 * c2y * p1x + 1.5 * c2y * c1x
- - 3.0 * c2y * p2x + 0.5 * p2y * p1x
- + 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10;
- },
-
- getEdgeSum: function(v) {
- return (v[0] - v[2]) * (v[3] + v[1])
- + (v[2] - v[4]) * (v[5] + v[3])
- + (v[4] - v[6]) * (v[7] + v[5]);
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7];
+ return 3 * ((y3 - y0) * (x1 + x2) - (x3 - x0) * (y1 + y2)
+ + y1 * (x0 - x2) - x1 * (y0 - y2)
+ + y3 * (x2 + x0 / 3) - x3 * (y2 + y0 / 3)) / 20;
},
getBounds: function(v) {
@@ -5915,62 +6586,156 @@
if (right > max[coord])
max[coord] = right;
}
- var a = 3 * (v1 - v2) - v0 + v3,
- b = 2 * (v0 + v2) - 4 * v1,
- c = v1 - v0,
- count = Numerical.solveQuadratic(a, b, c, roots),
- tMin = 0.000001,
- tMax = 1 - tMin;
- add(v3, 0);
- for (var i = 0; i < count; i++) {
- var t = roots[i],
- u = 1 - t;
- if (tMin < t && t < tMax)
- add(u * u * u * v0
- + 3 * u * u * t * v1
- + 3 * u * t * t * v2
- + t * t * t * v3,
- padding);
+
+ padding /= 2;
+ var minPad = min[coord] - padding,
+ maxPad = max[coord] + padding;
+ if ( v0 < minPad || v1 < minPad || v2 < minPad || v3 < minPad ||
+ v0 > maxPad || v1 > maxPad || v2 > maxPad || v3 > maxPad) {
+ if (v1 < v0 != v1 < v3 && v2 < v0 != v2 < v3) {
+ add(v0, padding);
+ add(v3, padding);
+ } else {
+ var a = 3 * (v1 - v2) - v0 + v3,
+ b = 2 * (v0 + v2) - 4 * v1,
+ c = v1 - v0,
+ count = Numerical.solveQuadratic(a, b, c, roots),
+ tMin = 1e-8,
+ tMax = 1 - tMin;
+ add(v3, 0);
+ for (var i = 0; i < count; i++) {
+ var t = roots[i],
+ u = 1 - t;
+ if (tMin <= t && t <= tMax)
+ add(u * u * u * v0
+ + 3 * u * u * t * v1
+ + 3 * u * t * t * v2
+ + t * t * t * v3,
+ padding);
+ }
+ }
}
}
}}, Base.each(
- ['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'],
+ ['getBounds', 'getStrokeBounds', 'getHandleBounds'],
function(name) {
this[name] = function() {
if (!this._bounds)
this._bounds = {};
var bounds = this._bounds[name];
if (!bounds) {
- bounds = this._bounds[name] = Path[name]([this._segment1,
- this._segment2], false, this._path.getStyle());
+ bounds = this._bounds[name] = Path[name](
+ [this._segment1, this._segment2], false, this._path);
}
return bounds.clone();
};
},
{
+}), Base.each({
+ isStraight: function(p1, h1, h2, p2) {
+ if (h1.isZero() && h2.isZero()) {
+ return true;
+ } else {
+ var v = p2.subtract(p1);
+ if (v.isZero()) {
+ return false;
+ } else if (v.isCollinear(h1) && v.isCollinear(h2)) {
+ var l = new Line(p1, p2),
+ epsilon = 1e-7;
+ if (l.getDistance(p1.add(h1)) < epsilon &&
+ l.getDistance(p2.add(h2)) < epsilon) {
+ var div = v.dot(v),
+ s1 = v.dot(h1) / div,
+ s2 = v.dot(h2) / div;
+ return s1 >= 0 && s1 <= 1 && s2 <= 0 && s2 >= -1;
+ }
+ }
+ }
+ return false;
+ },
+
+ isLinear: function(p1, h1, h2, p2) {
+ var third = p2.subtract(p1).divide(3);
+ return h1.equals(third) && h2.negate().equals(third);
+ }
+}, function(test, name) {
+ this[name] = function(epsilon) {
+ var seg1 = this._segment1,
+ seg2 = this._segment2;
+ return test(seg1._point, seg1._handleOut, seg2._handleIn, seg2._point,
+ epsilon);
+ };
+
+ this.statics[name] = function(v, epsilon) {
+ var x0 = v[0], y0 = v[1],
+ x3 = v[6], y3 = v[7];
+ return test(
+ new Point(x0, y0),
+ new Point(v[2] - x0, v[3] - y0),
+ new Point(v[4] - x3, v[5] - y3),
+ new Point(x3, y3), epsilon);
+ };
+}, {
+ statics: {},
+
+ hasHandles: function() {
+ return !this._segment1._handleOut.isZero()
+ || !this._segment2._handleIn.isZero();
+ },
+
+ hasLength: function(epsilon) {
+ return (!this.getPoint1().equals(this.getPoint2()) || this.hasHandles())
+ && this.getLength() > (epsilon || 0);
+ },
+
+ isCollinear: function(curve) {
+ return curve && this.isStraight() && curve.isStraight()
+ && this.getLine().isCollinear(curve.getLine());
+ },
+
+ isHorizontal: function() {
+ return this.isStraight() && Math.abs(this.getTangentAtTime(0.5).y)
+ < 1e-8;
+ },
+
+ isVertical: function() {
+ return this.isStraight() && Math.abs(this.getTangentAtTime(0.5).x)
+ < 1e-8;
+ }
}), {
beans: false,
- getParameterAt: function(offset, start) {
- return Curve.getParameterAt(this.getValues(), offset, start);
- },
-
- getParameterOf: function() {
- var point = Point.read(arguments);
- return Curve.getParameterOf(this.getValues(), point.x, point.y);
- },
-
- getLocationAt: function(offset, isParameter) {
- var t = isParameter ? offset : this.getParameterAt(offset);
+ getLocationAt: function(offset, _isTime) {
+ return this.getLocationAtTime(
+ _isTime ? offset : this.getTimeAt(offset));
+ },
+
+ getLocationAtTime: function(t) {
return t != null && t >= 0 && t <= 1
? new CurveLocation(this, t)
: null;
},
+ getTimeAt: function(offset, start) {
+ return Curve.getTimeAt(this.getValues(), offset, start);
+ },
+
+ getParameterAt: '#getTimeAt',
+
+ getTimesWithTangent: function () {
+ var tangent = Point.read(arguments);
+ return tangent.isZero()
+ ? []
+ : Curve.getTimesWithTangent(this.getValues(), tangent);
+ },
+
+ getOffsetAtTime: function(t) {
+ return this.getPartLength(0, t);
+ },
+
getLocationOf: function() {
- return this.getLocationAt(this.getParameterOf(Point.read(arguments)),
- true);
+ return this.getLocationAtTime(this.getTimeOf(Point.read(arguments)));
},
getOffsetOf: function() {
@@ -5978,39 +6743,23 @@
return loc ? loc.getOffset() : null;
},
+ getTimeOf: function() {
+ return Curve.getTimeOf(this.getValues(), Point.read(arguments));
+ },
+
+ getParameterOf: '#getTimeOf',
+
getNearestLocation: function() {
var point = Point.read(arguments),
values = this.getValues(),
- count = 100,
- minDist = Infinity,
- minT = 0;
-
- function refine(t) {
- if (t >= 0 && t <= 1) {
- var dist = point.getDistance(Curve.getPoint(values, t), true);
- if (dist < minDist) {
- minDist = dist;
- minT = t;
- return true;
- }
- }
- }
-
- for (var i = 0; i <= count; i++)
- refine(i / count);
-
- var step = 1 / (count * 2);
- while (step > 0.000001) {
- if (!refine(minT - step) && !refine(minT + step))
- step /= 2;
- }
- var pt = Curve.getPoint(values, minT);
- return new CurveLocation(this, minT, pt, null, null, null,
- point.getDistance(pt));
+ t = Curve.getNearestTime(values, point),
+ pt = Curve.getPoint(values, t);
+ return new CurveLocation(this, t, pt, null, point.getDistance(pt));
},
getNearestPoint: function() {
- return this.getNearestLocation.apply(this, arguments).getPoint();
+ var loc = this.getNearestLocation.apply(this, arguments);
+ return loc ? loc.getPoint() : loc;
}
},
@@ -6018,33 +6767,38 @@
var methods = ['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',
'getWeightedNormal', 'getCurvature'];
return Base.each(methods,
- function(name) {
- this[name + 'At'] = function(offset, isParameter) {
- var values = this.getValues();
- return Curve[name](values, isParameter ? offset
- : Curve.getParameterAt(values, offset, 0));
- };
- }, {
- statics: {
- evaluateMethods: methods
- }
- })
+ function(name) {
+ this[name + 'At'] = function(location, _isTime) {
+ var values = this.getValues();
+ return Curve[name](values, _isTime ? location
+ : Curve.getTimeAt(values, location));
+ };
+
+ this[name + 'AtTime'] = function(time) {
+ return Curve[name](this.getValues(), time);
+ };
+ }, {
+ statics: {
+ _evaluateMethods: methods
+ }
+ }
+ );
},
new function() {
function getLengthIntegrand(v) {
- var p1x = v[0], p1y = v[1],
- c1x = v[2], c1y = v[3],
- c2x = v[4], c2y = v[5],
- p2x = v[6], p2y = v[7],
-
- ax = 9 * (c1x - c2x) + 3 * (p2x - p1x),
- bx = 6 * (p1x + c2x) - 12 * c1x,
- cx = 3 * (c1x - p1x),
-
- ay = 9 * (c1y - c2y) + 3 * (p2y - p1y),
- by = 6 * (p1y + c2y) - 12 * c1y,
- cy = 3 * (c1y - p1y);
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7],
+
+ ax = 9 * (x1 - x2) + 3 * (x3 - x0),
+ bx = 6 * (x0 + x2) - 12 * x1,
+ cx = 3 * (x1 - x0),
+
+ ay = 9 * (y1 - y2) + 3 * (y3 - y0),
+ by = 6 * (y0 + y2) - 12 * y1,
+ cy = 3 * (y1 - y0);
return function(t) {
var dx = (ax * t + bx) * t + cx,
@@ -6060,97 +6814,161 @@
function evaluate(v, t, type, normalized) {
if (t == null || t < 0 || t > 1)
return null;
- var p1x = v[0], p1y = v[1],
- c1x = v[2], c1y = v[3],
- c2x = v[4], c2y = v[5],
- p2x = v[6], p2y = v[7],
- tolerance = 0.000001,
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7],
+ isZero = Numerical.isZero;
+ if (isZero(x1 - x0) && isZero(y1 - y0)) {
+ x1 = x0;
+ y1 = y0;
+ }
+ if (isZero(x2 - x3) && isZero(y2 - y3)) {
+ x2 = x3;
+ y2 = y3;
+ }
+ var cx = 3 * (x1 - x0),
+ bx = 3 * (x2 - x1) - cx,
+ ax = x3 - x0 - cx - bx,
+ cy = 3 * (y1 - y0),
+ by = 3 * (y2 - y1) - cy,
+ ay = y3 - y0 - cy - by,
x, y;
-
- if (type === 0 && (t < tolerance || t > 1 - tolerance)) {
- var isZero = t < tolerance;
- x = isZero ? p1x : p2x;
- y = isZero ? p1y : p2y;
+ if (type === 0) {
+ x = t === 0 ? x0 : t === 1 ? x3
+ : ((ax * t + bx) * t + cx) * t + x0;
+ y = t === 0 ? y0 : t === 1 ? y3
+ : ((ay * t + by) * t + cy) * t + y0;
} else {
- var cx = 3 * (c1x - p1x),
- bx = 3 * (c2x - c1x) - cx,
- ax = p2x - p1x - cx - bx,
-
- cy = 3 * (c1y - p1y),
- by = 3 * (c2y - c1y) - cy,
- ay = p2y - p1y - cy - by;
- if (type === 0) {
- x = ((ax * t + bx) * t + cx) * t + p1x;
- y = ((ay * t + by) * t + cy) * t + p1y;
- } else {
- if (t < tolerance) {
- x = cx;
- y = cy;
- } else if (t > 1 - tolerance) {
- x = 3 * (p2x - c2x);
- y = 3 * (p2y - c2y);
- } else {
- x = (3 * ax * t + 2 * bx) * t + cx;
- y = (3 * ay * t + 2 * by) * t + cy;
- }
- if (normalized) {
- if (x === 0 && y === 0
- && (t < tolerance || t > 1 - tolerance)) {
- x = c2x - c1x;
- y = c2y - c1y;
- }
- var len = Math.sqrt(x * x + y * y);
+ var tMin = 1e-8,
+ tMax = 1 - tMin;
+ if (t < tMin) {
+ x = cx;
+ y = cy;
+ } else if (t > tMax) {
+ x = 3 * (x3 - x2);
+ y = 3 * (y3 - y2);
+ } else {
+ x = (3 * ax * t + 2 * bx) * t + cx;
+ y = (3 * ay * t + 2 * by) * t + cy;
+ }
+ if (normalized) {
+ if (x === 0 && y === 0 && (t < tMin || t > tMax)) {
+ x = x2 - x1;
+ y = y2 - y1;
+ }
+ var len = Math.sqrt(x * x + y * y);
+ if (len) {
x /= len;
y /= len;
}
- if (type === 3) {
- var x2 = 6 * ax * t + 2 * bx,
- y2 = 6 * ay * t + 2 * by,
- d = Math.pow(x * x + y * y, 3 / 2);
- x = d !== 0 ? (x * y2 - y * x2) / d : 0;
- y = 0;
- }
+ }
+ if (type === 3) {
+ var x2 = 6 * ax * t + 2 * bx,
+ y2 = 6 * ay * t + 2 * by,
+ d = Math.pow(x * x + y * y, 3 / 2);
+ x = d !== 0 ? (x * y2 - y * x2) / d : 0;
+ y = 0;
}
}
return type === 2 ? new Point(y, -x) : new Point(x, y);
}
- return {
- statics: true,
-
- getLength: function(v, a, b) {
+ return { statics: {
+
+ classify: function(v) {
+
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7],
+ a1 = x0 * (y3 - y2) + y0 * (x2 - x3) + x3 * y2 - y3 * x2,
+ a2 = x1 * (y0 - y3) + y1 * (x3 - x0) + x0 * y3 - y0 * x3,
+ a3 = x2 * (y1 - y0) + y2 * (x0 - x1) + x1 * y0 - y1 * x0,
+ d3 = 3 * a3,
+ d2 = d3 - a2,
+ d1 = d2 - a2 + a1,
+ l = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3),
+ s = l !== 0 ? 1 / l : 0,
+ isZero = Numerical.isZero,
+ serpentine = 'serpentine';
+ d1 *= s;
+ d2 *= s;
+ d3 *= s;
+
+ function type(type, t1, t2) {
+ var hasRoots = t1 !== undefined,
+ t1Ok = hasRoots && t1 > 0 && t1 < 1,
+ t2Ok = hasRoots && t2 > 0 && t2 < 1;
+ if (hasRoots && (!(t1Ok || t2Ok)
+ || type === 'loop' && !(t1Ok && t2Ok))) {
+ type = 'arch';
+ t1Ok = t2Ok = false;
+ }
+ return {
+ type: type,
+ roots: t1Ok || t2Ok
+ ? t1Ok && t2Ok
+ ? t1 < t2 ? [t1, t2] : [t2, t1]
+ : [t1Ok ? t1 : t2]
+ : null
+ };
+ }
+
+ if (isZero(d1)) {
+ return isZero(d2)
+ ? type(isZero(d3) ? 'line' : 'quadratic')
+ : type(serpentine, d3 / (3 * d2));
+ }
+ var d = 3 * d2 * d2 - 4 * d1 * d3;
+ if (isZero(d)) {
+ return type('cusp', d2 / (2 * d1));
+ }
+ var f1 = d > 0 ? Math.sqrt(d / 3) : Math.sqrt(-d),
+ f2 = 2 * d1;
+ return type(d > 0 ? serpentine : 'loop',
+ (d2 + f1) / f2,
+ (d2 - f1) / f2);
+ },
+
+ getLength: function(v, a, b, ds) {
if (a === undefined)
a = 0;
if (b === undefined)
b = 1;
- var isZero = Numerical.isZero;
- if (a === 0 && b === 1
- && isZero(v[0] - v[2]) && isZero(v[1] - v[3])
- && isZero(v[6] - v[4]) && isZero(v[7] - v[5])) {
- var dx = v[6] - v[0],
- dy = v[7] - v[1];
+ if (Curve.isStraight(v)) {
+ var c = v;
+ if (b < 1) {
+ c = Curve.subdivide(c, b)[0];
+ a /= b;
+ }
+ if (a > 0) {
+ c = Curve.subdivide(c, a)[1];
+ }
+ var dx = c[6] - c[0],
+ dy = c[7] - c[1];
return Math.sqrt(dx * dx + dy * dy);
}
- var ds = getLengthIntegrand(v);
- return Numerical.integrate(ds, a, b, getIterations(a, b));
- },
-
- getParameterAt: function(v, offset, start) {
+ return Numerical.integrate(ds || getLengthIntegrand(v), a, b,
+ getIterations(a, b));
+ },
+
+ getTimeAt: function(v, offset, start) {
if (start === undefined)
- start = offset < 0 ? 1 : 0
+ start = offset < 0 ? 1 : 0;
if (offset === 0)
return start;
- var tolerance = 0.000001,
- abs = Math.abs,
+ var abs = Math.abs,
+ epsilon = 1e-12,
forward = offset > 0,
a = forward ? start : 0,
b = forward ? 1 : start,
ds = getLengthIntegrand(v),
- rangeLength = Numerical.integrate(ds, a, b,
- getIterations(a, b));
- if (abs(offset - rangeLength) < tolerance) {
+ rangeLength = Curve.getLength(v, a, b, ds),
+ diff = abs(offset) - rangeLength;
+ if (abs(diff) < epsilon) {
return forward ? b : a;
- } else if (abs(offset) > rangeLength) {
+ } else if (diff > epsilon) {
return null;
}
var guess = offset / rangeLength,
@@ -6161,8 +6979,8 @@
start = t;
return length - offset;
}
- return Numerical.findRoot(f, ds, start + guess, a, b, 16,
- tolerance);
+ return Numerical.findRoot(f, ds, start + guess, a, b, 32,
+ 1e-12);
},
getPoint: function(v, t) {
@@ -6187,25 +7005,63 @@
getCurvature: function(v, t) {
return evaluate(v, t, 3, false).x;
- }
- };
-}, new function() {
- function addLocation(locations, include, curve1, t1, point1, curve2, t2,
- point2) {
- var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2);
- if (!include || include(loc))
- locations.push(loc);
- }
-
- function addCurveIntersections(v1, v2, curve1, curve2, locations, include,
- tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) {
- if (recursion > 32)
- return;
- var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
- tolerance = 0.000001,
+ },
+
+ getPeaks: function(v) {
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7],
+ ax = -x0 + 3 * x1 - 3 * x2 + x3,
+ bx = 3 * x0 - 6 * x1 + 3 * x2,
+ cx = -3 * x0 + 3 * x1,
+ ay = -y0 + 3 * y1 - 3 * y2 + y3,
+ by = 3 * y0 - 6 * y1 + 3 * y2,
+ cy = -3 * y0 + 3 * y1,
+ tMin = 1e-8,
+ tMax = 1 - tMin,
+ roots = [];
+ Numerical.solveCubic(
+ 9 * (ax * ax + ay * ay),
+ 9 * (ax * bx + by * ay),
+ 2 * (bx * bx + by * by) + 3 * (cx * ax + cy * ay),
+ (cx * bx + by * cy),
+ roots, tMin, tMax);
+ return roots.sort();
+ }
+ }};
+},
+new function() {
+
+ function addLocation(locations, include, c1, t1, c2, t2, overlap) {
+ var excludeStart = !overlap && c1.getPrevious() === c2,
+ excludeEnd = !overlap && c1 !== c2 && c1.getNext() === c2,
+ tMin = 1e-8,
+ tMax = 1 - tMin;
+ if (t1 !== null && t1 >= (excludeStart ? tMin : 0) &&
+ t1 <= (excludeEnd ? tMax : 1)) {
+ if (t2 !== null && t2 >= (excludeEnd ? tMin : 0) &&
+ t2 <= (excludeStart ? tMax : 1)) {
+ var loc1 = new CurveLocation(c1, t1, null, overlap),
+ loc2 = new CurveLocation(c2, t2, null, overlap);
+ loc1._intersection = loc2;
+ loc2._intersection = loc1;
+ if (!include || include(loc1)) {
+ CurveLocation.insert(locations, loc1, true);
+ }
+ }
+ }
+ }
+
+ function addCurveIntersections(v1, v2, c1, c2, locations, include, flip,
+ recursion, calls, tMin, tMax, uMin, uMax) {
+ if (++calls >= 4096 || ++recursion >= 40)
+ return calls;
+ var fatLineEpsilon = 1e-9,
+ q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7],
getSignedDistance = Line.getSignedDistance,
- d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0,
- d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0,
+ d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]),
+ d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]),
factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9,
dMin = factor * Math.min(0, d1, d2),
dMax = factor * Math.max(0, d1, d2),
@@ -6213,62 +7069,60 @@
dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]),
dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]),
dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]),
- tMinNew, tMaxNew, tDiff;
- if (q0x === q3x && uMax - uMin < tolerance && recursion > 3) {
- tMaxNew = tMinNew = (tMax + tMin) / 2;
- tDiff = 0;
+ hull = getConvexHull(dp0, dp1, dp2, dp3),
+ top = hull[0],
+ bottom = hull[1],
+ tMinClip,
+ tMaxClip;
+ if (d1 === 0 && d2 === 0
+ && dp0 === 0 && dp1 === 0 && dp2 === 0 && dp3 === 0
+ || (tMinClip = clipConvexHull(top, bottom, dMin, dMax)) == null
+ || (tMaxClip = clipConvexHull(top.reverse(), bottom.reverse(),
+ dMin, dMax)) == null)
+ return calls;
+ var tMinNew = tMin + (tMax - tMin) * tMinClip,
+ tMaxNew = tMin + (tMax - tMin) * tMaxClip;
+ if (Math.max(uMax - uMin, tMaxNew - tMinNew) < fatLineEpsilon) {
+ var t = (tMinNew + tMaxNew) / 2,
+ u = (uMin + uMax) / 2;
+ addLocation(locations, include,
+ flip ? c2 : c1, flip ? u : t,
+ flip ? c1 : c2, flip ? t : u);
} else {
- var hull = getConvexHull(dp0, dp1, dp2, dp3),
- top = hull[0],
- bottom = hull[1],
- tMinClip, tMaxClip;
- tMinClip = clipConvexHull(top, bottom, dMin, dMax);
- top.reverse();
- bottom.reverse();
- tMaxClip = clipConvexHull(top, bottom, dMin, dMax);
- if (tMinClip == null || tMaxClip == null)
- return;
v1 = Curve.getPart(v1, tMinClip, tMaxClip);
- tDiff = tMaxClip - tMinClip;
- tMinNew = tMax * tMinClip + tMin * (1 - tMinClip);
- tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip);
- }
- if (oldTDiff > 0.5 && tDiff > 0.5) {
- if (tMaxNew - tMinNew > uMax - uMin) {
- var parts = Curve.subdivide(v1, 0.5),
- t = tMinNew + (tMaxNew - tMinNew) / 2;
- addCurveIntersections(
- v2, parts[0], curve2, curve1, locations, include,
- uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion);
- addCurveIntersections(
- v2, parts[1], curve2, curve1, locations, include,
- uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion);
- } else {
- var parts = Curve.subdivide(v2, 0.5),
- t = uMin + (uMax - uMin) / 2;
- addCurveIntersections(
- parts[0], v1, curve2, curve1, locations, include,
- uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
- addCurveIntersections(
- parts[1], v1, curve2, curve1, locations, include,
- t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion);
- }
- } else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) {
- var t1 = tMinNew + (tMaxNew - tMinNew) / 2,
- t2 = uMin + (uMax - uMin) / 2;
- if (reverse) {
- addLocation(locations, include,
- curve2, t2, Curve.getPoint(v2, t2),
- curve1, t1, Curve.getPoint(v1, t1));
- } else {
- addLocation(locations, include,
- curve1, t1, Curve.getPoint(v1, t1),
- curve2, t2, Curve.getPoint(v2, t2));
- }
- } else if (tDiff > 0) {
- addCurveIntersections(v2, v1, curve2, curve1, locations, include,
- uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion);
- }
+ if (tMaxClip - tMinClip > 0.8) {
+ if (tMaxNew - tMinNew > uMax - uMin) {
+ var parts = Curve.subdivide(v1, 0.5),
+ t = (tMinNew + tMaxNew) / 2;
+ calls = addCurveIntersections(
+ v2, parts[0], c2, c1, locations, include, !flip,
+ recursion, calls, uMin, uMax, tMinNew, t);
+ calls = addCurveIntersections(
+ v2, parts[1], c2, c1, locations, include, !flip,
+ recursion, calls, uMin, uMax, t, tMaxNew);
+ } else {
+ var parts = Curve.subdivide(v2, 0.5),
+ u = (uMin + uMax) / 2;
+ calls = addCurveIntersections(
+ parts[0], v1, c2, c1, locations, include, !flip,
+ recursion, calls, uMin, u, tMinNew, tMaxNew);
+ calls = addCurveIntersections(
+ parts[1], v1, c2, c1, locations, include, !flip,
+ recursion, calls, u, uMax, tMinNew, tMaxNew);
+ }
+ } else {
+ if (uMax - uMin >= fatLineEpsilon) {
+ calls = addCurveIntersections(
+ v2, v1, c2, c1, locations, include, !flip,
+ recursion, calls, uMin, uMax, tMinNew, tMaxNew);
+ } else {
+ calls = addCurveIntersections(
+ v1, v2, c1, c2, locations, include, flip,
+ recursion, calls, tMinNew, tMaxNew, uMin, uMax);
+ }
+ }
+ }
+ return calls;
}
function getConvexHull(dq0, dq1, dq2, dq3) {
@@ -6276,32 +7130,21 @@
p1 = [ 1 / 3, dq1 ],
p2 = [ 2 / 3, dq2 ],
p3 = [ 1, dq3 ],
- getSignedDistance = Line.getSignedDistance,
- dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1),
- dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2),
- flip = false,
+ dist1 = dq1 - (2 * dq0 + dq3) / 3,
+ dist2 = dq2 - (dq0 + 2 * dq3) / 3,
hull;
if (dist1 * dist2 < 0) {
hull = [[p0, p1, p3], [p0, p2, p3]];
- flip = dist1 < 0;
} else {
- var pmax, cross = 0,
- distZero = dist1 === 0 || dist2 === 0;
- if (Math.abs(dist1) > Math.abs(dist2)) {
- pmax = p1;
- cross = (dq3 - dq2 - (dq3 - dq0) / 3)
- * (2 * (dq3 - dq2) - dq3 + dq1) / 3;
- } else {
- pmax = p2;
- cross = (dq1 - dq0 + (dq0 - dq3) / 3)
- * (-2 * (dq0 - dq1) + dq0 - dq2) / 3;
- }
- hull = cross < 0 || distZero
- ? [[p0, pmax, p3], [p0, p3]]
- : [[p0, p1, p2, p3], [p0, p3]];
- flip = dist1 ? dist1 < 0 : dist2 < 0;
- }
- return flip ? hull.reverse() : hull;
+ var distRatio = dist1 / dist2;
+ hull = [
+ distRatio >= 2 ? [p0, p1, p3]
+ : distRatio <= 0.5 ? [p0, p2, p3]
+ : [p0, p1, p2, p3],
+ [p0, p3]
+ ];
+ }
+ return (dist1 || dist2) < 0 ? hull.reverse() : hull;
}
function clipConvexHull(hullTop, hullBottom, dMin, dMax) {
@@ -6320,193 +7163,373 @@
for (var i = 1, l = part.length; i < l; i++) {
var qx = part[i][0],
qy = part[i][1];
- if (top ? qy >= threshold : qy <= threshold)
- return px + (threshold - py) * (qx - px) / (qy - py);
+ if (top ? qy >= threshold : qy <= threshold) {
+ return qy === threshold ? qx
+ : px + (threshold - py) * (qx - px) / (qy - py);
+ }
px = qx;
py = qy;
}
return null;
}
- function addCurveLineIntersections(v1, v2, curve1, curve2, locations,
- include) {
- var flip = Curve.isLinear(v1),
- vc = flip ? v2 : v1,
- vl = flip ? v1 : v2,
- lx1 = vl[0], ly1 = vl[1],
- lx2 = vl[6], ly2 = vl[7],
- ldx = lx2 - lx1,
- ldy = ly2 - ly1,
- angle = Math.atan2(-ldy, ldx),
+ function getCurveLineIntersections(v, px, py, vx, vy) {
+ var isZero = Numerical.isZero;
+ if (isZero(vx) && isZero(vy)) {
+ var t = Curve.getTimeOf(v, new Point(px, py));
+ return t === null ? [] : [t];
+ }
+ var angle = Math.atan2(-vy, vx),
sin = Math.sin(angle),
cos = Math.cos(angle),
- rlx2 = ldx * cos - ldy * sin,
- rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0],
- rvc = [];
- for(var i = 0; i < 8; i += 2) {
- var x = vc[i] - lx1,
- y = vc[i + 1] - ly1;
- rvc.push(
+ rv = [],
+ roots = [];
+ for (var i = 0; i < 8; i += 2) {
+ var x = v[i] - px,
+ y = v[i + 1] - py;
+ rv.push(
x * cos - y * sin,
- y * cos + x * sin);
- }
- var roots = [],
- count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1);
- for (var i = 0; i < count; i++) {
- var tc = roots[i],
- x = Curve.getPoint(rvc, tc).x;
- if (x >= 0 && x <= rlx2) {
- var tl = Curve.getParameterOf(rvl, x, 0),
- t1 = flip ? tl : tc,
- t2 = flip ? tc : tl;
+ x * sin + y * cos);
+ }
+ Curve.solveCubic(rv, 1, 0, roots, 0, 1);
+ return roots;
+ }
+
+ function addCurveLineIntersections(v1, v2, c1, c2, locations, include,
+ flip) {
+ var x1 = v2[0], y1 = v2[1],
+ x2 = v2[6], y2 = v2[7],
+ roots = getCurveLineIntersections(v1, x1, y1, x2 - x1, y2 - y1);
+ for (var i = 0, l = roots.length; i < l; i++) {
+ var t1 = roots[i],
+ p1 = Curve.getPoint(v1, t1),
+ t2 = Curve.getTimeOf(v2, p1);
+ if (t2 !== null) {
addLocation(locations, include,
- curve1, t1, Curve.getPoint(v1, t1),
- curve2, t2, Curve.getPoint(v2, t2));
- }
- }
- }
-
- function addLineIntersection(v1, v2, curve1, curve2, locations, include) {
- var point = Line.intersect(
+ flip ? c2 : c1, flip ? t2 : t1,
+ flip ? c1 : c2, flip ? t1 : t2);
+ }
+ }
+ }
+
+ function addLineIntersection(v1, v2, c1, c2, locations, include) {
+ var pt = Line.intersect(
v1[0], v1[1], v1[6], v1[7],
v2[0], v2[1], v2[6], v2[7]);
- if (point) {
- var x = point.x,
- y = point.y;
+ if (pt) {
+ addLocation(locations, include,
+ c1, Curve.getTimeOf(v1, pt),
+ c2, Curve.getTimeOf(v2, pt));
+ }
+ }
+
+ function getCurveIntersections(v1, v2, c1, c2, locations, include) {
+ var epsilon = 1e-12,
+ min = Math.min,
+ max = Math.max;
+
+ if (max(v1[0], v1[2], v1[4], v1[6]) + epsilon >
+ min(v2[0], v2[2], v2[4], v2[6]) &&
+ min(v1[0], v1[2], v1[4], v1[6]) - epsilon <
+ max(v2[0], v2[2], v2[4], v2[6]) &&
+ max(v1[1], v1[3], v1[5], v1[7]) + epsilon >
+ min(v2[1], v2[3], v2[5], v2[7]) &&
+ min(v1[1], v1[3], v1[5], v1[7]) - epsilon <
+ max(v2[1], v2[3], v2[5], v2[7])) {
+ var overlaps = getOverlaps(v1, v2);
+ if (overlaps) {
+ for (var i = 0; i < 2; i++) {
+ var overlap = overlaps[i];
+ addLocation(locations, include,
+ c1, overlap[0],
+ c2, overlap[1], true);
+ }
+ } else {
+ var straight1 = Curve.isStraight(v1),
+ straight2 = Curve.isStraight(v2),
+ straight = straight1 && straight2,
+ flip = straight1 && !straight2,
+ before = locations.length;
+ (straight
+ ? addLineIntersection
+ : straight1 || straight2
+ ? addCurveLineIntersections
+ : addCurveIntersections)(
+ flip ? v2 : v1, flip ? v1 : v2,
+ flip ? c2 : c1, flip ? c1 : c2,
+ locations, include, flip,
+ 0, 0, 0, 1, 0, 1);
+ if (!straight || locations.length === before) {
+ for (var i = 0; i < 4; i++) {
+ var t1 = i >> 1,
+ t2 = i & 1,
+ i1 = t1 * 6,
+ i2 = t2 * 6,
+ p1 = new Point(v1[i1], v1[i1 + 1]),
+ p2 = new Point(v2[i2], v2[i2 + 1]);
+ if (p1.isClose(p2, epsilon)) {
+ addLocation(locations, include,
+ c1, t1,
+ c2, t2);
+ }
+ }
+ }
+ }
+ }
+ return locations;
+ }
+
+ function getLoopIntersection(v1, c1, locations, include) {
+ var info = Curve.classify(v1);
+ if (info.type === 'loop') {
+ var roots = info.roots;
addLocation(locations, include,
- curve1, Curve.getParameterOf(v1, x, y), point,
- curve2, Curve.getParameterOf(v2, x, y), point);
- }
- }
-
- return { statics: {
- getIntersections: function(v1, v2, c1, c2, locations, include) {
- var linear1 = Curve.isLinear(v1),
- linear2 = Curve.isLinear(v2),
- c1p1 = c1.getPoint1(),
- c1p2 = c1.getPoint2(),
- c2p1 = c2.getPoint1(),
- c2p2 = c2.getPoint2(),
- tolerance = 0.000001;
- if (c1p1.isClose(c2p1, tolerance))
- addLocation(locations, include, c1, 0, c1p1, c2, 0, c1p1);
- if (c1p1.isClose(c2p2, tolerance))
- addLocation(locations, include, c1, 0, c1p1, c2, 1, c1p1);
- (linear1 && linear2
- ? addLineIntersection
- : linear1 || linear2
- ? addCurveLineIntersections
- : addCurveIntersections)(
- v1, v2, c1, c2, locations, include,
- 0, 1, 0, 1, 0, false, 0);
- if (c1p2.isClose(c2p1, tolerance))
- addLocation(locations, include, c1, 1, c1p2, c2, 0, c1p2);
- if (c1p2.isClose(c2p2, tolerance))
- addLocation(locations, include, c1, 1, c1p2, c2, 1, c1p2);
- return locations;
- },
-
- filterIntersections: function(locations, _expand) {
- var last = locations.length - 1,
- tMax = 1 - 0.000001;
- for (var i = last; i >= 0; i--) {
- var loc = locations[i],
- next = loc._curve.getNext(),
- next2 = loc._curve2.getNext();
- if (next && loc._parameter >= tMax) {
- loc._parameter = 0;
- loc._curve = next;
- }
- if (next2 && loc._parameter2 >= tMax) {
- loc._parameter2 = 0;
- loc._curve2 = next2;
- }
- }
-
- function compare(loc1, loc2) {
- var path1 = loc1.getPath(),
- path2 = loc2.getPath();
- return path1 === path2
- ? (loc1.getIndex() + loc1.getParameter())
- - (loc2.getIndex() + loc2.getParameter())
- : path1._id - path2._id;
- }
-
- if (last > 0) {
- locations.sort(compare);
- for (var i = last; i > 0; i--) {
- if (locations[i].equals(locations[i - 1])) {
- locations.splice(i, 1);
- last--;
- }
- }
- }
- if (_expand) {
- for (var i = last; i >= 0; i--)
- locations.push(locations[i].getIntersection());
- locations.sort(compare);
- }
- return locations;
- }
- }};
+ c1, roots[0],
+ c1, roots[1]);
+ }
+ return locations;
+ }
+
+ function getIntersections(curves1, curves2, include, matrix1, matrix2,
+ _returnFirst) {
+ var self = !curves2;
+ if (self)
+ curves2 = curves1;
+ var length1 = curves1.length,
+ length2 = curves2.length,
+ values2 = [],
+ arrays = [],
+ locations,
+ current;
+ for (var i = 0; i < length2; i++)
+ values2[i] = curves2[i].getValues(matrix2);
+ for (var i = 0; i < length1; i++) {
+ var curve1 = curves1[i],
+ values1 = self ? values2[i] : curve1.getValues(matrix1),
+ path1 = curve1.getPath();
+ if (path1 !== current) {
+ current = path1;
+ locations = [];
+ arrays.push(locations);
+ }
+ if (self) {
+ getLoopIntersection(values1, curve1, locations, include);
+ }
+ for (var j = self ? i + 1 : 0; j < length2; j++) {
+ if (_returnFirst && locations.length)
+ return locations;
+ getCurveIntersections(values1, values2[j], curve1, curves2[j],
+ locations, include);
+ }
+ }
+ locations = [];
+ for (var i = 0, l = arrays.length; i < l; i++) {
+ Base.push(locations, arrays[i]);
+ }
+ return locations;
+ }
+
+ function getOverlaps(v1, v2) {
+
+ function getSquaredLineLength(v) {
+ var x = v[6] - v[0],
+ y = v[7] - v[1];
+ return x * x + y * y;
+ }
+
+ var abs = Math.abs,
+ getDistance = Line.getDistance,
+ timeEpsilon = 1e-8,
+ geomEpsilon = 1e-7,
+ straight1 = Curve.isStraight(v1),
+ straight2 = Curve.isStraight(v2),
+ straightBoth = straight1 && straight2,
+ flip = getSquaredLineLength(v1) < getSquaredLineLength(v2),
+ l1 = flip ? v2 : v1,
+ l2 = flip ? v1 : v2,
+ px = l1[0], py = l1[1],
+ vx = l1[6] - px, vy = l1[7] - py;
+ if (getDistance(px, py, vx, vy, l2[0], l2[1], true) < geomEpsilon &&
+ getDistance(px, py, vx, vy, l2[6], l2[7], true) < geomEpsilon) {
+ if (!straightBoth &&
+ getDistance(px, py, vx, vy, l1[2], l1[3], true) < geomEpsilon &&
+ getDistance(px, py, vx, vy, l1[4], l1[5], true) < geomEpsilon &&
+ getDistance(px, py, vx, vy, l2[2], l2[3], true) < geomEpsilon &&
+ getDistance(px, py, vx, vy, l2[4], l2[5], true) < geomEpsilon) {
+ straight1 = straight2 = straightBoth = true;
+ }
+ } else if (straightBoth) {
+ return null;
+ }
+ if (straight1 ^ straight2) {
+ return null;
+ }
+
+ var v = [v1, v2],
+ pairs = [];
+ for (var i = 0; i < 4 && pairs.length < 2; i++) {
+ var i1 = i & 1,
+ i2 = i1 ^ 1,
+ t1 = i >> 1,
+ t2 = Curve.getTimeOf(v[i1], new Point(
+ v[i2][t1 ? 6 : 0],
+ v[i2][t1 ? 7 : 1]));
+ if (t2 != null) {
+ var pair = i1 ? [t1, t2] : [t2, t1];
+ if (!pairs.length ||
+ abs(pair[0] - pairs[0][0]) > timeEpsilon &&
+ abs(pair[1] - pairs[0][1]) > timeEpsilon) {
+ pairs.push(pair);
+ }
+ }
+ if (i > 2 && !pairs.length)
+ break;
+ }
+ if (pairs.length !== 2) {
+ pairs = null;
+ } else if (!straightBoth) {
+ var o1 = Curve.getPart(v1, pairs[0][0], pairs[1][0]),
+ o2 = Curve.getPart(v2, pairs[0][1], pairs[1][1]);
+ if (abs(o2[2] - o1[2]) > geomEpsilon ||
+ abs(o2[3] - o1[3]) > geomEpsilon ||
+ abs(o2[4] - o1[4]) > geomEpsilon ||
+ abs(o2[5] - o1[5]) > geomEpsilon)
+ pairs = null;
+ }
+ return pairs;
+ }
+
+ function getTimesWithTangent(v, tangent) {
+ var x0 = v[0], y0 = v[1],
+ x1 = v[2], y1 = v[3],
+ x2 = v[4], y2 = v[5],
+ x3 = v[6], y3 = v[7],
+ normalized = tangent.normalize(),
+ tx = normalized.x,
+ ty = normalized.y,
+ ax = 3 * x3 - 9 * x2 + 9 * x1 - 3 * x0,
+ ay = 3 * y3 - 9 * y2 + 9 * y1 - 3 * y0,
+ bx = 6 * x2 - 12 * x1 + 6 * x0,
+ by = 6 * y2 - 12 * y1 + 6 * y0,
+ cx = 3 * x1 - 3 * x0,
+ cy = 3 * y1 - 3 * y0,
+ den = 2 * ax * ty - 2 * ay * tx,
+ times = [];
+ if (Math.abs(den) < Numerical.CURVETIME_EPSILON) {
+ var num = ax * cy - ay * cx,
+ den = ax * by - ay * bx;
+ if (den != 0) {
+ var t = -num / den;
+ if (t >= 0 && t <= 1) times.push(t);
+ }
+ } else {
+ var delta = (bx * bx - 4 * ax * cx) * ty * ty +
+ (-2 * bx * by + 4 * ay * cx + 4 * ax * cy) * tx * ty +
+ (by * by - 4 * ay * cy) * tx * tx,
+ k = bx * ty - by * tx;
+ if (delta >= 0 && den != 0) {
+ var d = Math.sqrt(delta),
+ t0 = -(k + d) / den,
+ t1 = (-k + d) / den;
+ if (t0 >= 0 && t0 <= 1) times.push(t0);
+ if (t1 >= 0 && t1 <= 1) times.push(t1);
+ }
+ }
+ return times;
+ }
+
+ return {
+ getIntersections: function(curve) {
+ var v1 = this.getValues(),
+ v2 = curve && curve !== this && curve.getValues();
+ return v2 ? getCurveIntersections(v1, v2, this, curve, [])
+ : getLoopIntersection(v1, this, []);
+ },
+
+ statics: {
+ getOverlaps: getOverlaps,
+ getIntersections: getIntersections,
+ getCurveLineIntersections: getCurveLineIntersections,
+ getTimesWithTangent: getTimesWithTangent
+ }
+ };
});
var CurveLocation = Base.extend({
_class: 'CurveLocation',
- beans: true,
-
- initialize: function CurveLocation(curve, parameter, point, _curve2,
- _parameter2, _point2, _distance) {
- this._id = UID.get(CurveLocation);
+
+ initialize: function CurveLocation(curve, time, point, _overlap, _distance) {
+ if (time >= 0.99999999) {
+ var next = curve.getNext();
+ if (next) {
+ time = 0;
+ curve = next;
+ }
+ }
+ this._setCurve(curve);
+ this._time = time;
+ this._point = point || curve.getPointAtTime(time);
+ this._overlap = _overlap;
+ this._distance = _distance;
+ this._intersection = this._next = this._previous = null;
+ },
+
+ _setCurve: function(curve) {
var path = curve._path;
+ this._path = path;
this._version = path ? path._version : 0;
this._curve = curve;
- this._parameter = parameter;
- this._point = point || curve.getPointAt(parameter, true);
- this._curve2 = _curve2;
- this._parameter2 = _parameter2;
- this._point2 = _point2;
- this._distance = _distance;
+ this._segment = null;
this._segment1 = curve._segment1;
this._segment2 = curve._segment2;
},
- getSegment: function(_preferFirst) {
- if (!this._segment) {
+ _setSegment: function(segment) {
+ this._setCurve(segment.getCurve());
+ this._segment = segment;
+ this._time = segment === this._segment1 ? 0 : 1;
+ this._point = segment._point.clone();
+ },
+
+ getSegment: function() {
+ var segment = this._segment;
+ if (!segment) {
var curve = this.getCurve(),
- parameter = this.getParameter();
- if (parameter === 1) {
- this._segment = curve._segment2;
- } else if (parameter === 0 || _preferFirst) {
- this._segment = curve._segment1;
- } else if (parameter == null) {
- return null;
- } else {
- this._segment = curve.getPartLength(0, parameter)
- < curve.getPartLength(parameter, 1)
+ time = this.getTime();
+ if (time === 0) {
+ segment = curve._segment1;
+ } else if (time === 1) {
+ segment = curve._segment2;
+ } else if (time != null) {
+ segment = curve.getPartLength(0, time)
+ < curve.getPartLength(time, 1)
? curve._segment1
: curve._segment2;
}
- }
- return this._segment;
+ this._segment = segment;
+ }
+ return segment;
},
getCurve: function() {
- var curve = this._curve,
- path = curve && curve._path;
+ var path = this._path,
+ that = this;
if (path && path._version !== this._version) {
- curve = null;
- this._parameter = null;
- }
- if (!curve) {
- curve = this._segment1.getCurve();
- if (curve.getParameterOf(this._point) == null)
- curve = this._segment2.getPrevious().getCurve();
- this._curve = curve;
- path = curve._path;
- this._version = path ? path._version : 0;
- }
- return curve;
+ this._time = this._offset = this._curveOffset = this._curve = null;
+ }
+
+ function trySegment(segment) {
+ var curve = segment && segment.getCurve();
+ if (curve && (that._time = curve.getTimeOf(that._point)) != null) {
+ that._setCurve(curve);
+ return curve;
+ }
+ }
+
+ return this._curve
+ || trySegment(this._segment)
+ || trySegment(this._segment1)
+ || trySegment(this._segment2.getPrevious());
},
getPath: function() {
@@ -6519,37 +7542,49 @@
return curve && curve.getIndex();
},
- getParameter: function() {
+ getTime: function() {
var curve = this.getCurve(),
- parameter = this._parameter;
- return curve && parameter == null
- ? this._parameter = curve.getParameterOf(this._point)
- : parameter;
- },
+ time = this._time;
+ return curve && time == null
+ ? this._time = curve.getTimeOf(this._point)
+ : time;
+ },
+
+ getParameter: '#getTime',
getPoint: function() {
return this._point;
},
getOffset: function() {
- var path = this.getPath();
- return path ? path._getOffset(this) : this.getCurveOffset();
+ var offset = this._offset;
+ if (offset == null) {
+ offset = 0;
+ var path = this.getPath(),
+ index = this.getIndex();
+ if (path && index != null) {
+ var curves = path.getCurves();
+ for (var i = 0; i < index; i++)
+ offset += curves[i].getLength();
+ }
+ this._offset = offset += this.getCurveOffset();
+ }
+ return offset;
},
getCurveOffset: function() {
- var curve = this.getCurve(),
- parameter = this.getParameter();
- return parameter != null && curve && curve.getPartLength(0, parameter);
+ var offset = this._curveOffset;
+ if (offset == null) {
+ var curve = this.getCurve(),
+ time = this.getTime();
+ this._curveOffset = offset = time != null && curve
+ && curve.getPartLength(0, time);
+ }
+ return offset;
},
getIntersection: function() {
- var intersection = this._intersection;
- if (!intersection && this._curve2) {
- this._intersection = intersection = new CurveLocation(this._curve2,
- this._parameter2, this._point2 || this._point, this);
- intersection._intersection = this;
- }
- return intersection;
+ return this._intersection;
},
getDistance: function() {
@@ -6557,25 +7592,43 @@
},
divide: function() {
- var curve = this.getCurve();
- return curve && curve.divide(this.getParameter(), true);
+ var curve = this.getCurve(),
+ res = curve && curve.divideAtTime(this.getTime());
+ if (res) {
+ this._setSegment(res._segment1);
+ }
+ return res;
},
split: function() {
- var curve = this.getCurve();
- return curve && curve.split(this.getParameter(), true);
- },
-
- equals: function(loc) {
- var abs = Math.abs,
- tolerance = 0.000001;
- return this === loc
- || loc instanceof CurveLocation
- && this.getCurve() === loc.getCurve()
- && abs(this.getParameter() - loc.getParameter()) < tolerance
- && this._curve2 === loc._curve2
- && abs(this._parameter2 - loc._parameter2) < tolerance
- || false;
+ var curve = this.getCurve(),
+ path = curve._path,
+ res = curve && curve.splitAtTime(this.getTime());
+ if (res) {
+ this._setSegment(path.getLastSegment());
+ }
+ return res;
+ },
+
+ equals: function(loc, _ignoreOther) {
+ var res = this === loc;
+ if (!res && loc instanceof CurveLocation) {
+ var c1 = this.getCurve(),
+ c2 = loc.getCurve(),
+ p1 = c1._path,
+ p2 = c2._path;
+ if (p1 === p2) {
+ var abs = Math.abs,
+ epsilon = 1e-7,
+ diff = abs(this.getOffset() - loc.getOffset()),
+ i1 = !_ignoreOther && this._intersection,
+ i2 = !_ignoreOther && loc._intersection;
+ res = (diff < epsilon
+ || p1 && abs(p1.getLength() - diff) < epsilon)
+ && (!i1 && !i2 || i1 && i2 && i1.equals(i2, true));
+ }
+ }
+ return res;
},
toString: function() {
@@ -6587,93 +7640,217 @@
var index = this.getIndex();
if (index != null)
parts.push('index: ' + index);
- var parameter = this.getParameter();
- if (parameter != null)
- parts.push('parameter: ' + f.number(parameter));
+ var time = this.getTime();
+ if (time != null)
+ parts.push('time: ' + f.number(time));
if (this._distance != null)
parts.push('distance: ' + f.number(this._distance));
return '{ ' + parts.join(', ') + ' }';
- }
-}, Base.each(Curve.evaluateMethods, function(name) {
- if (name !== 'getPoint') {
- var get = name + 'At';
- this[name] = function() {
- var parameter = this.getParameter(),
- curve = this.getCurve();
- return parameter != null && curve && curve[get](parameter, true);
- };
- }
-}, {}));
+ },
+
+ isTouching: function() {
+ var inter = this._intersection;
+ if (inter && this.getTangent().isCollinear(inter.getTangent())) {
+ var curve1 = this.getCurve(),
+ curve2 = inter.getCurve();
+ return !(curve1.isStraight() && curve2.isStraight()
+ && curve1.getLine().intersect(curve2.getLine()));
+ }
+ return false;
+ },
+
+ isCrossing: function() {
+ var inter = this._intersection;
+ if (!inter)
+ return false;
+ var t1 = this.getTime(),
+ t2 = inter.getTime(),
+ tMin = 1e-8,
+ tMax = 1 - tMin,
+ t1Inside = t1 >= tMin && t1 <= tMax,
+ t2Inside = t2 >= tMin && t2 <= tMax;
+ if (t1Inside && t2Inside)
+ return !this.isTouching();
+ var c2 = this.getCurve(),
+ c1 = t1 < tMin ? c2.getPrevious() : c2,
+ c4 = inter.getCurve(),
+ c3 = t2 < tMin ? c4.getPrevious() : c4;
+ if (t1 > tMax)
+ c2 = c2.getNext();
+ if (t2 > tMax)
+ c4 = c4.getNext();
+ if (!c1 || !c2 || !c3 || !c4)
+ return false;
+
+ var offsets = [];
+
+ function addOffsets(curve, end) {
+ var v = curve.getValues(),
+ roots = Curve.classify(v).roots || Curve.getPeaks(v),
+ count = roots.length,
+ t = end && count > 1 ? roots[count - 1]
+ : count > 0 ? roots[0]
+ : 0.5;
+ offsets.push(Curve.getLength(v, end ? t : 0, end ? 1 : t) / 2);
+ }
+
+ function isInRange(angle, min, max) {
+ return min < max
+ ? angle > min && angle < max
+ : angle > min || angle < max;
+ }
+
+ if (!t1Inside) {
+ addOffsets(c1, true);
+ addOffsets(c2, false);
+ }
+ if (!t2Inside) {
+ addOffsets(c3, true);
+ addOffsets(c4, false);
+ }
+ var pt = this.getPoint(),
+ offset = Math.min.apply(Math, offsets),
+ v2 = t1Inside ? c2.getTangentAtTime(t1)
+ : c2.getPointAt(offset).subtract(pt),
+ v1 = t1Inside ? v2.negate()
+ : c1.getPointAt(-offset).subtract(pt),
+ v4 = t2Inside ? c4.getTangentAtTime(t2)
+ : c4.getPointAt(offset).subtract(pt),
+ v3 = t2Inside ? v4.negate()
+ : c3.getPointAt(-offset).subtract(pt),
+ a1 = v1.getAngle(),
+ a2 = v2.getAngle(),
+ a3 = v3.getAngle(),
+ a4 = v4.getAngle();
+ return !!(t1Inside
+ ? (isInRange(a1, a3, a4) ^ isInRange(a2, a3, a4)) &&
+ (isInRange(a1, a4, a3) ^ isInRange(a2, a4, a3))
+ : (isInRange(a3, a1, a2) ^ isInRange(a4, a1, a2)) &&
+ (isInRange(a3, a2, a1) ^ isInRange(a4, a2, a1)));
+ },
+
+ hasOverlap: function() {
+ return !!this._overlap;
+ }
+}, Base.each(Curve._evaluateMethods, function(name) {
+ var get = name + 'At';
+ this[name] = function() {
+ var curve = this.getCurve(),
+ time = this.getTime();
+ return time != null && curve && curve[get](time, true);
+ };
+}, {
+ preserve: true
+}),
+new function() {
+
+ function insert(locations, loc, merge) {
+ var length = locations.length,
+ l = 0,
+ r = length - 1;
+
+ function search(index, dir) {
+ for (var i = index + dir; i >= -1 && i <= length; i += dir) {
+ var loc2 = locations[((i % length) + length) % length];
+ if (!loc.getPoint().isClose(loc2.getPoint(),
+ 1e-7))
+ break;
+ if (loc.equals(loc2))
+ return loc2;
+ }
+ return null;
+ }
+
+ while (l <= r) {
+ var m = (l + r) >>> 1,
+ loc2 = locations[m],
+ found;
+ if (merge && (found = loc.equals(loc2) ? loc2
+ : (search(m, -1) || search(m, 1)))) {
+ if (loc._overlap) {
+ found._overlap = found._intersection._overlap = true;
+ }
+ return found;
+ }
+ var path1 = loc.getPath(),
+ path2 = loc2.getPath(),
+ diff = path1 !== path2
+ ? path1._id - path2._id
+ : (loc.getIndex() + loc.getTime())
+ - (loc2.getIndex() + loc2.getTime());
+ if (diff < 0) {
+ r = m - 1;
+ } else {
+ l = m + 1;
+ }
+ }
+ locations.splice(l, 0, loc);
+ return loc;
+ }
+
+ return { statics: {
+ insert: insert,
+
+ expand: function(locations) {
+ var expanded = locations.slice();
+ for (var i = locations.length - 1; i >= 0; i--) {
+ insert(expanded, locations[i]._intersection, false);
+ }
+ return expanded;
+ }
+ }};
+});
var PathItem = Item.extend({
_class: 'PathItem',
+ _selectBounds: false,
+ _canScaleStroke: true,
+ beans: true,
initialize: function PathItem() {
},
- getIntersections: function(path, _matrix, _expand) {
- if (this === path)
- path = null;
- var locations = [],
- curves1 = this.getCurves(),
- curves2 = path ? path.getCurves() : curves1,
- matrix1 = this._matrix.orNullIfIdentity(),
- matrix2 = path ? (_matrix || path._matrix).orNullIfIdentity()
- : matrix1,
- length1 = curves1.length,
- length2 = path ? curves2.length : length1,
- values2 = [],
- tMin = 0.000001,
- tMax = 1 - tMin;
- if (path && !this.getBounds(matrix1).touches(path.getBounds(matrix2)))
- return [];
- for (var i = 0; i < length2; i++)
- values2[i] = curves2[i].getValues(matrix2);
- for (var i = 0; i < length1; i++) {
- var curve1 = curves1[i],
- values1 = path ? curve1.getValues(matrix1) : values2[i];
- if (!path) {
- var seg1 = curve1.getSegment1(),
- seg2 = curve1.getSegment2(),
- h1 = seg1._handleOut,
- h2 = seg2._handleIn;
- if (new Line(seg1._point.subtract(h1), h1.multiply(2), true)
- .intersect(new Line(seg2._point.subtract(h2),
- h2.multiply(2), true), false)) {
- var parts = Curve.subdivide(values1);
- Curve.getIntersections(
- parts[0], parts[1], curve1, curve1, locations,
- function(loc) {
- if (loc._parameter <= tMax) {
- loc._parameter /= 2;
- loc._parameter2 = 0.5 + loc._parameter2 / 2;
- return true;
- }
- }
- );
- }
- }
- for (var j = path ? 0 : i + 1; j < length2; j++) {
- Curve.getIntersections(
- values1, values2[j], curve1, curves2[j], locations,
- !path && (j === i + 1 || j === length2 - 1 && i === 0)
- && function(loc) {
- var t = loc._parameter;
- return t >= tMin && t <= tMax;
- }
- );
- }
- }
- return Curve.filterIntersections(locations, _expand);
+ statics: {
+ create: function(arg) {
+ var data,
+ segments,
+ compound;
+ if (Base.isPlainObject(arg)) {
+ segments = arg.segments;
+ data = arg.pathData;
+ } else if (Array.isArray(arg)) {
+ segments = arg;
+ } else if (typeof arg === 'string') {
+ data = arg;
+ }
+ if (segments) {
+ var first = segments[0];
+ compound = first && Array.isArray(first[0]);
+ } else if (data) {
+ compound = (data.match(/m/gi) || []).length > 1
+ || /z\s*\S+/i.test(data);
+ }
+ var ctor = compound ? CompoundPath : Path;
+ return new ctor(arg);
+ }
},
_asPathItem: function() {
return this;
},
+ isClockwise: function() {
+ return this.getArea() >= 0;
+ },
+
+ setClockwise: function(clockwise) {
+ if (this.isClockwise() != (clockwise = !!clockwise))
+ this.reverse();
+ },
+
setPathData: function(data) {
- var parts = data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
+ var parts = data && data.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig),
coords,
relative = false,
previous,
@@ -6705,21 +7882,24 @@
var length = coords && coords.length;
relative = command === lower;
if (previous === 'z' && !/[mz]/.test(lower))
- this.moveTo(current = start);
+ this.moveTo(current);
switch (lower) {
case 'm':
case 'l':
var move = lower === 'm';
- for (var j = 0; j < length; j += 2)
- this[j === 0 && move ? 'moveTo' : 'lineTo'](
- current = getPoint(j));
+ for (var j = 0; j < length; j += 2) {
+ this[move ? 'moveTo' : 'lineTo'](current = getPoint(j));
+ if (move) {
+ start = current;
+ move = false;
+ }
+ }
control = current;
- if (move)
- start = current;
break;
case 'h':
case 'v':
var coord = lower === 'h' ? 'x' : 'y';
+ current = current.clone();
for (var j = 0; j < length; j++) {
current[coord] = getCoord(j, coord);
this.lineTo(current);
@@ -6770,7 +7950,8 @@
}
break;
case 'z':
- this.closePath(true);
+ this.closePath(1e-12);
+ current = start;
break;
}
previous = lower;
@@ -6782,9 +7963,110 @@
},
_contains: function(point) {
- var winding = this._getWinding(point, false, true);
- return !!(this.getWindingRule() === 'evenodd' ? winding & 1 : winding);
- }
+ var winding = point.isInside(
+ this.getBounds({ internal: true, handle: true }))
+ ? this._getWinding(point)
+ : {};
+ return winding.onPath || !!(this.getFillRule() === 'evenodd'
+ ? winding.windingL & 1 || winding.windingR & 1
+ : winding.winding);
+ },
+
+ getIntersections: function(path, include, _matrix, _returnFirst) {
+ var self = this === path || !path,
+ matrix1 = this._matrix._orNullIfIdentity(),
+ matrix2 = self ? matrix1
+ : (_matrix || path._matrix)._orNullIfIdentity();
+ return self || this.getBounds(matrix1).intersects(
+ path.getBounds(matrix2), 1e-12)
+ ? Curve.getIntersections(
+ this.getCurves(), !self && path.getCurves(), include,
+ matrix1, matrix2, _returnFirst)
+ : [];
+ },
+
+ getCrossings: function(path) {
+ return this.getIntersections(path, function(inter) {
+ return inter.hasOverlap() || inter.isCrossing();
+ });
+ },
+
+ getNearestLocation: function() {
+ var point = Point.read(arguments),
+ curves = this.getCurves(),
+ minDist = Infinity,
+ minLoc = null;
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var loc = curves[i].getNearestLocation(point);
+ if (loc._distance < minDist) {
+ minDist = loc._distance;
+ minLoc = loc;
+ }
+ }
+ return minLoc;
+ },
+
+ getNearestPoint: function() {
+ var loc = this.getNearestLocation.apply(this, arguments);
+ return loc ? loc.getPoint() : loc;
+ },
+
+ interpolate: function(from, to, factor) {
+ var isPath = !this._children,
+ name = isPath ? '_segments' : '_children',
+ itemsFrom = from[name],
+ itemsTo = to[name],
+ items = this[name];
+ if (!itemsFrom || !itemsTo || itemsFrom.length !== itemsTo.length) {
+ throw new Error('Invalid operands in interpolate() call: ' +
+ from + ', ' + to);
+ }
+ var current = items.length,
+ length = itemsTo.length;
+ if (current < length) {
+ var ctor = isPath ? Segment : Path;
+ for (var i = current; i < length; i++) {
+ this.add(new ctor());
+ }
+ } else if (current > length) {
+ this[isPath ? 'removeSegments' : 'removeChildren'](length, current);
+ }
+ for (var i = 0; i < length; i++) {
+ items[i].interpolate(itemsFrom[i], itemsTo[i], factor);
+ }
+ if (isPath) {
+ this.setClosed(from._closed);
+ this._changed(9);
+ }
+ },
+
+ compare: function(path) {
+ var ok = false;
+ if (path) {
+ var paths1 = this._children || [this],
+ paths2 = path._children ? path._children.slice() : [path],
+ length1 = paths1.length,
+ length2 = paths2.length,
+ matched = [],
+ count = 0;
+ ok = true;
+ for (var i1 = length1 - 1; i1 >= 0 && ok; i1--) {
+ var path1 = paths1[i1];
+ ok = false;
+ for (var i2 = length2 - 1; i2 >= 0 && !ok; i2--) {
+ if (path1.compare(paths2[i2])) {
+ if (!matched[i2]) {
+ matched[i2] = true;
+ count++;
+ }
+ ok = true;
+ }
+ }
+ }
+ ok = ok && count === length2;
+ }
+ return ok;
+ },
});
@@ -6811,7 +8093,7 @@
this.setSegments(segments);
} else {
this._curves = undefined;
- this._selectedSegmentState = 0;
+ this._segmentSelection = 0;
if (!segments && typeof arg === 'string') {
this.setPathData(arg);
arg = null;
@@ -6825,30 +8107,22 @@
&& Base.equals(this._segments, item._segments);
},
- clone: function(insert) {
- var copy = new Path(Item.NO_INSERT);
- copy.setSegments(this._segments);
- copy._closed = this._closed;
- if (this._clockwise !== undefined)
- copy._clockwise = this._clockwise;
- return this._clone(copy, insert);
+ copyContent: function(source) {
+ this.setSegments(source._segments);
+ this._closed = source._closed;
},
_changed: function _changed(flags) {
_changed.base.call(this, flags);
if (flags & 8) {
- var parent = this._parent;
- if (parent)
- parent._currentPath = undefined;
- this._length = this._clockwise = undefined;
- if (flags & 16) {
+ this._length = this._area = undefined;
+ if (flags & 32) {
this._version++;
} else if (this._curves) {
for (var i = 0, l = this._curves.length; i < l; i++)
this._curves[i]._changed();
}
- this._monoCurves = undefined;
- } else if (flags & 32) {
+ } else if (flags & 64) {
this._bounds = undefined;
}
},
@@ -6863,12 +8137,19 @@
},
setSegments: function(segments) {
- var fullySelected = this.isFullySelected();
+ var fullySelected = this.isFullySelected(),
+ length = segments && segments.length;
this._segments.length = 0;
- this._selectedSegmentState = 0;
+ this._segmentSelection = 0;
this._curves = undefined;
- if (segments && segments.length > 0)
- this._add(Segment.readAll(segments));
+ if (length) {
+ var last = segments[length - 1];
+ if (typeof last === 'boolean') {
+ this.setClosed(last);
+ length--;
+ }
+ this._add(Segment.readList(segments, 0, {}, length));
+ }
if (fullySelected)
this.setFullySelected(true);
},
@@ -6916,7 +8197,7 @@
this._curves[length - 1] = new Curve(this,
this._segments[length - 1], this._segments[0]);
}
- this._changed(25);
+ this._changed(41);
}
}
}, {
@@ -6935,7 +8216,7 @@
parts = [];
function addSegment(segment, skipLine) {
- segment._transformCoordinates(_matrix, coords, false);
+ segment._transformCoordinates(_matrix, coords);
curX = coords[0];
curY = coords[1];
if (first) {
@@ -6946,12 +8227,18 @@
inY = coords[3];
if (inX === curX && inY === curY
&& outX === prevX && outY === prevY) {
- if (!skipLine)
- parts.push('l' + f.pair(curX - prevX, curY - prevY));
+ if (!skipLine) {
+ var dx = curX - prevX,
+ dy = curY - prevY;
+ parts.push(
+ dx === 0 ? 'v' + f.number(dy)
+ : dy === 0 ? 'h' + f.number(dx)
+ : 'l' + f.pair(dx, dy));
+ }
} else {
parts.push('c' + f.pair(outX - prevX, outY - prevY)
- + ' ' + f.pair(inX - prevX, inY - prevY)
- + ' ' + f.pair(curX - prevX, curY - prevY));
+ + ' ' + f.pair( inX - prevX, inY - prevY)
+ + ' ' + f.pair(curX - prevX, curY - prevY));
}
}
prevX = curX;
@@ -6960,7 +8247,7 @@
outY = coords[5];
}
- if (length === 0)
+ if (!length)
return '';
for (var i = 0; i < length; i++)
@@ -6970,35 +8257,17 @@
parts.push('z');
}
return parts.join('');
- }
-}, {
+ },
isEmpty: function() {
- return this._segments.length === 0;
- },
-
- isLinear: function() {
- var segments = this._segments;
- for (var i = 0, l = segments.length; i < l; i++) {
- if (!segments[i].isLinear())
- return false;
- }
- return true;
- },
-
- hasHandles: function() {
- var segments = this._segments;
- for (var i = 0, l = segments.length; i < l; i++) {
- if (segments[i].hasHandles())
- return true;
- }
- return false;
+ return !this._segments.length;
},
_transformContent: function(matrix) {
- var coords = new Array(6);
- for (var i = 0, l = this._segments.length; i < l; i++)
- this._segments[i]._transformCoordinates(matrix, coords, true);
+ var segments = this._segments,
+ coords = new Array(6);
+ for (var i = 0, l = segments.length; i < l; i++)
+ segments[i]._transformCoordinates(matrix, coords, true);
return true;
},
@@ -7014,52 +8283,52 @@
segment = segs[i] = segment.clone();
segment._path = this;
segment._index = index + i;
- if (segment._selectionState)
- this._updateSelection(segment, 0, segment._selectionState);
+ if (segment._selection)
+ this._updateSelection(segment, 0, segment._selection);
}
if (append) {
- segments.push.apply(segments, segs);
+ Base.push(segments, segs);
} else {
segments.splice.apply(segments, [index, 0].concat(segs));
for (var i = index + amount, l = segments.length; i < l; i++)
segments[i]._index = i;
}
- if (curves || segs._curves) {
- if (!curves)
- curves = this._curves = [];
- var from = index > 0 ? index - 1 : index,
- start = from,
- to = Math.min(from + amount, this._countCurves());
+ if (curves) {
+ var total = this._countCurves(),
+ start = index > 0 && index + amount - 1 === total ? index - 1
+ : index,
+ insert = start,
+ end = Math.min(start + amount, total);
if (segs._curves) {
- curves.splice.apply(curves, [from, 0].concat(segs._curves));
- start += segs._curves.length;
- }
- for (var i = start; i < to; i++)
+ curves.splice.apply(curves, [start, 0].concat(segs._curves));
+ insert += segs._curves.length;
+ }
+ for (var i = insert; i < end; i++)
curves.splice(i, 0, new Curve(this, null, null));
- this._adjustCurves(from, to);
- }
- this._changed(25);
+ this._adjustCurves(start, end);
+ }
+ this._changed(41);
return segs;
},
- _adjustCurves: function(from, to) {
+ _adjustCurves: function(start, end) {
var segments = this._segments,
curves = this._curves,
curve;
- for (var i = from; i < to; i++) {
+ for (var i = start; i < end; i++) {
curve = curves[i];
curve._path = this;
curve._segment1 = segments[i];
curve._segment2 = segments[i + 1] || segments[0];
curve._changed();
}
- if (curve = curves[this._closed && from === 0 ? segments.length - 1
- : from - 1]) {
- curve._segment2 = segments[from] || segments[0];
+ if (curve = curves[this._closed && !start ? segments.length - 1
+ : start - 1]) {
+ curve._segment2 = segments[start] || segments[0];
curve._changed();
}
- if (curve = curves[to]) {
- curve._segment1 = segments[to];
+ if (curve = curves[end]) {
+ curve._segment1 = segments[end];
curve._changed();
}
},
@@ -7071,13 +8340,13 @@
add: function(segment1 ) {
return arguments.length > 1 && typeof segment1 !== 'number'
- ? this._add(Segment.readAll(arguments))
+ ? this._add(Segment.readList(arguments))
: this._add([ Segment.read(arguments) ])[0];
},
insert: function(index, segment1 ) {
return arguments.length > 2 && typeof segment1 !== 'number'
- ? this._add(Segment.readAll(arguments, 1), index)
+ ? this._add(Segment.readList(arguments, 1), index)
: this._add([ Segment.read(arguments, 1) ], index)[0];
},
@@ -7090,71 +8359,98 @@
},
addSegments: function(segments) {
- return this._add(Segment.readAll(segments));
+ return this._add(Segment.readList(segments));
},
insertSegments: function(index, segments) {
- return this._add(Segment.readAll(segments), index);
+ return this._add(Segment.readList(segments), index);
},
removeSegment: function(index) {
return this.removeSegments(index, index + 1)[0] || null;
},
- removeSegments: function(from, to, _includeCurves) {
- from = from || 0;
- to = Base.pick(to, this._segments.length);
+ removeSegments: function(start, end, _includeCurves) {
+ start = start || 0;
+ end = Base.pick(end, this._segments.length);
var segments = this._segments,
curves = this._curves,
count = segments.length,
- removed = segments.splice(from, to - from),
+ removed = segments.splice(start, end - start),
amount = removed.length;
if (!amount)
return removed;
for (var i = 0; i < amount; i++) {
var segment = removed[i];
- if (segment._selectionState)
- this._updateSelection(segment, segment._selectionState, 0);
+ if (segment._selection)
+ this._updateSelection(segment, segment._selection, 0);
segment._index = segment._path = null;
}
- for (var i = from, l = segments.length; i < l; i++)
+ for (var i = start, l = segments.length; i < l; i++)
segments[i]._index = i;
if (curves) {
- var index = from > 0 && to === count + (this._closed ? 1 : 0)
- ? from - 1
- : from,
+ var index = start > 0 && end === count + (this._closed ? 1 : 0)
+ ? start - 1
+ : start,
curves = curves.splice(index, amount);
+ for (var i = curves.length - 1; i >= 0; i--)
+ curves[i]._path = null;
if (_includeCurves)
removed._curves = curves.slice(1);
this._adjustCurves(index, index);
}
- this._changed(25);
+ this._changed(41);
return removed;
},
clear: '#removeSegments',
+ hasHandles: function() {
+ var segments = this._segments;
+ for (var i = 0, l = segments.length; i < l; i++) {
+ if (segments[i].hasHandles())
+ return true;
+ }
+ return false;
+ },
+
+ clearHandles: function() {
+ var segments = this._segments;
+ for (var i = 0, l = segments.length; i < l; i++)
+ segments[i].clearHandles();
+ },
+
getLength: function() {
if (this._length == null) {
- var curves = this.getCurves();
- this._length = 0;
+ var curves = this.getCurves(),
+ length = 0;
for (var i = 0, l = curves.length; i < l; i++)
- this._length += curves[i].getLength();
+ length += curves[i].getLength();
+ this._length = length;
}
return this._length;
},
getArea: function() {
- var curves = this.getCurves();
- var area = 0;
- for (var i = 0, l = curves.length; i < l; i++)
- area += curves[i].getArea();
+ var area = this._area;
+ if (area == null) {
+ var segments = this._segments,
+ closed = this._closed;
+ area = 0;
+ for (var i = 0, l = segments.length; i < l; i++) {
+ var last = i + 1 === l;
+ area += Curve.getArea(Curve.getValues(
+ segments[i], segments[last ? 0 : i + 1],
+ null, last && !closed));
+ }
+ this._area = area;
+ }
return area;
},
isFullySelected: function() {
var length = this._segments.length;
- return this._selected && length > 0 && this._selectedSegmentState
+ return this.isSelected() && length > 0 && this._segmentSelection
=== length * 7;
},
@@ -7164,79 +8460,50 @@
this.setSelected(selected);
},
- setSelected: function setSelected(selected) {
- if (!selected)
+ setSelection: function setSelection(selection) {
+ if (!(selection & 1))
this._selectSegments(false);
- setSelected.base.call(this, selected);
+ setSelection.base.call(this, selection);
},
_selectSegments: function(selected) {
- var length = this._segments.length;
- this._selectedSegmentState = selected
- ? length * 7 : 0;
+ var segments = this._segments,
+ length = segments.length,
+ selection = selected ? 7 : 0;
+ this._segmentSelection = selection * length;
for (var i = 0; i < length; i++)
- this._segments[i]._selectionState = selected
- ? 7 : 0;
- },
-
- _updateSelection: function(segment, oldState, newState) {
- segment._selectionState = newState;
- var total = this._selectedSegmentState += newState - oldState;
- if (total > 0)
+ segments[i]._selection = selection;
+ },
+
+ _updateSelection: function(segment, oldSelection, newSelection) {
+ segment._selection = newSelection;
+ var selection = this._segmentSelection += newSelection - oldSelection;
+ if (selection > 0)
this.setSelected(true);
},
- flatten: function(maxDistance) {
- var iterator = new PathIterator(this, 64, 0.1),
- pos = 0,
- step = iterator.length / Math.ceil(iterator.length / maxDistance),
- end = iterator.length + (this._closed ? -step : step) / 2;
- var segments = [];
- while (pos <= end) {
- segments.push(new Segment(iterator.getPointAt(pos)));
- pos += step;
- }
- this.setSegments(segments);
- },
-
- reduce: function() {
- var curves = this.getCurves();
- for (var i = curves.length - 1; i >= 0; i--) {
- var curve = curves[i];
- if (curve.isLinear() && curve.getLength() === 0)
- curve.remove();
- }
- return this;
- },
-
- simplify: function(tolerance) {
- if (this._segments.length > 2) {
- var fitter = new PathFitter(this, tolerance || 2.5);
- this.setSegments(fitter.fit());
- }
- },
-
- split: function(index, parameter) {
- if (parameter === null)
- return null;
- if (arguments.length === 1) {
- var arg = index;
- if (typeof arg === 'number')
- arg = this.getLocationAt(arg);
- if (!arg)
- return null
- index = arg.index;
- parameter = arg.parameter;
- }
- var tolerance = 0.000001;
- if (parameter >= 1 - tolerance) {
+ divideAt: function(location) {
+ var loc = this.getLocationAt(location),
+ curve;
+ return loc && (curve = loc.getCurve().divideAt(loc.getCurveOffset()))
+ ? curve._segment1
+ : null;
+ },
+
+ splitAt: function(location) {
+ var loc = this.getLocationAt(location),
+ index = loc && loc.index,
+ time = loc && loc.time,
+ tMin = 1e-8,
+ tMax = 1 - tMin;
+ if (time > tMax) {
index++;
- parameter--;
+ time = 0;
}
var curves = this.getCurves();
if (index >= 0 && index < curves.length) {
- if (parameter > tolerance) {
- curves[index++].divide(parameter, true);
+ if (time >= tMin) {
+ curves[index++].divideAtTime(time);
}
var segs = this.removeSegments(index, this._segments.length, true),
path;
@@ -7244,7 +8511,9 @@
this.setClosed(false);
path = this;
} else {
- path = this._clone(new Path().insertAbove(this, true));
+ path = new Path(Item.NO_INSERT);
+ path.insertAbove(this);
+ path.copyAttributes(this);
}
path._add(segs, 0);
this.addSegment(segs[0]);
@@ -7253,16 +8522,65 @@
return null;
},
- isClockwise: function() {
- if (this._clockwise !== undefined)
- return this._clockwise;
- return Path.isClockwise(this._segments);
- },
-
- setClockwise: function(clockwise) {
- if (this.isClockwise() != (clockwise = !!clockwise))
- this.reverse();
- this._clockwise = clockwise;
+ split: function(index, time) {
+ var curve,
+ location = time === undefined ? index
+ : (curve = this.getCurves()[index])
+ && curve.getLocationAtTime(time);
+ return location != null ? this.splitAt(location) : null;
+ },
+
+ join: function(path, tolerance) {
+ var epsilon = tolerance || 0;
+ if (path && path !== this) {
+ var segments = path._segments,
+ last1 = this.getLastSegment(),
+ last2 = path.getLastSegment();
+ if (!last2)
+ return this;
+ if (last1 && last1._point.isClose(last2._point, epsilon))
+ path.reverse();
+ var first2 = path.getFirstSegment();
+ if (last1 && last1._point.isClose(first2._point, epsilon)) {
+ last1.setHandleOut(first2._handleOut);
+ this._add(segments.slice(1));
+ } else {
+ var first1 = this.getFirstSegment();
+ if (first1 && first1._point.isClose(first2._point, epsilon))
+ path.reverse();
+ last2 = path.getLastSegment();
+ if (first1 && first1._point.isClose(last2._point, epsilon)) {
+ first1.setHandleIn(last2._handleIn);
+ this._add(segments.slice(0, segments.length - 1), 0);
+ } else {
+ this._add(segments.slice());
+ }
+ }
+ if (path._closed)
+ this._add([segments[0]]);
+ path.remove();
+ }
+ var first = this.getFirstSegment(),
+ last = this.getLastSegment();
+ if (first !== last && first._point.isClose(last._point, epsilon)) {
+ first.setHandleIn(last._handleIn);
+ last.remove();
+ this.setClosed(true);
+ }
+ return this;
+ },
+
+ reduce: function(options) {
+ var curves = this.getCurves(),
+ simplify = options && options.simplify,
+ tolerance = simplify ? 1e-7 : 0;
+ for (var i = curves.length - 1; i >= 0; i--) {
+ var curve = curves[i];
+ if (!curve.hasHandles() && (!curve.hasLength(tolerance)
+ || simplify && curve.isCollinear(curve.getNext())))
+ curve.remove();
+ }
+ return this;
},
reverse: function() {
@@ -7275,48 +8593,135 @@
segment._index = i;
}
this._curves = null;
- if (this._clockwise !== undefined)
- this._clockwise = !this._clockwise;
this._changed(9);
},
- join: function(path) {
- if (path) {
- var segments = path._segments,
- last1 = this.getLastSegment(),
- last2 = path.getLastSegment();
- if (!last2)
- return this;
- if (last1 && last1._point.equals(last2._point))
- path.reverse();
- var first2 = path.getFirstSegment();
- if (last1 && last1._point.equals(first2._point)) {
- last1.setHandleOut(first2._handleOut);
- this._add(segments.slice(1));
- } else {
- var first1 = this.getFirstSegment();
- if (first1 && first1._point.equals(first2._point))
- path.reverse();
- last2 = path.getLastSegment();
- if (first1 && first1._point.equals(last2._point)) {
- first1.setHandleIn(last2._handleIn);
- this._add(segments.slice(0, segments.length - 1), 0);
- } else {
- this._add(segments.slice());
- }
- }
- if (path.closed)
- this._add([segments[0]]);
- path.remove();
- }
- var first = this.getFirstSegment(),
- last = this.getLastSegment();
- if (first !== last && first._point.equals(last._point)) {
- first.setHandleIn(last._handleIn);
- last.remove();
- this.setClosed(true);
- }
- return this;
+ flatten: function(flatness) {
+ var flattener = new PathFlattener(this, flatness || 0.25, 256, true),
+ parts = flattener.parts,
+ length = parts.length,
+ segments = [];
+ for (var i = 0; i < length; i++) {
+ segments.push(new Segment(parts[i].curve.slice(0, 2)));
+ }
+ if (!this._closed && length > 0) {
+ segments.push(new Segment(parts[length - 1].curve.slice(6)));
+ }
+ this.setSegments(segments);
+ },
+
+ simplify: function(tolerance) {
+ var segments = new PathFitter(this).fit(tolerance || 2.5);
+ if (segments)
+ this.setSegments(segments);
+ return !!segments;
+ },
+
+ smooth: function(options) {
+ var that = this,
+ opts = options || {},
+ type = opts.type || 'asymmetric',
+ segments = this._segments,
+ length = segments.length,
+ closed = this._closed;
+
+ function getIndex(value, _default) {
+ var index = value && value.index;
+ if (index != null) {
+ var path = value.path;
+ if (path && path !== that)
+ throw new Error(value._class + ' ' + index + ' of ' + path
+ + ' is not part of ' + that);
+ if (_default && value instanceof Curve)
+ index++;
+ } else {
+ index = typeof value === 'number' ? value : _default;
+ }
+ return Math.min(index < 0 && closed
+ ? index % length
+ : index < 0 ? index + length : index, length - 1);
+ }
+
+ var loop = closed && opts.from === undefined && opts.to === undefined,
+ from = getIndex(opts.from, 0),
+ to = getIndex(opts.to, length - 1);
+
+ if (from > to) {
+ if (closed) {
+ from -= length;
+ } else {
+ var tmp = from;
+ from = to;
+ to = tmp;
+ }
+ }
+ if (/^(?:asymmetric|continuous)$/.test(type)) {
+ var asymmetric = type === 'asymmetric',
+ min = Math.min,
+ amount = to - from + 1,
+ n = amount - 1,
+ padding = loop ? min(amount, 4) : 1,
+ paddingLeft = padding,
+ paddingRight = padding,
+ knots = [];
+ if (!closed) {
+ paddingLeft = min(1, from);
+ paddingRight = min(1, length - to - 1);
+ }
+ n += paddingLeft + paddingRight;
+ if (n <= 1)
+ return;
+ for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
+ knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
+ }
+
+ var x = knots[0]._x + 2 * knots[1]._x,
+ y = knots[0]._y + 2 * knots[1]._y,
+ f = 2,
+ n_1 = n - 1,
+ rx = [x],
+ ry = [y],
+ rf = [f],
+ px = [],
+ py = [];
+ for (var i = 1; i < n; i++) {
+ var internal = i < n_1,
+ a = internal ? 1 : asymmetric ? 1 : 2,
+ b = internal ? 4 : asymmetric ? 2 : 7,
+ u = internal ? 4 : asymmetric ? 3 : 8,
+ v = internal ? 2 : asymmetric ? 0 : 1,
+ m = a / f;
+ f = rf[i] = b - m;
+ x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
+ y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
+ }
+
+ px[n_1] = rx[n_1] / rf[n_1];
+ py[n_1] = ry[n_1] / rf[n_1];
+ for (var i = n - 2; i >= 0; i--) {
+ px[i] = (rx[i] - px[i + 1]) / rf[i];
+ py[i] = (ry[i] - py[i + 1]) / rf[i];
+ }
+ px[n] = (3 * knots[n]._x - px[n_1]) / 2;
+ py[n] = (3 * knots[n]._y - py[n_1]) / 2;
+
+ for (var i = paddingLeft, max = n - paddingRight, j = from;
+ i <= max; i++, j++) {
+ var segment = segments[j < 0 ? j + length : j],
+ pt = segment._point,
+ hx = px[i] - pt._x,
+ hy = py[i] - pt._y;
+ if (loop || i < max)
+ segment.setHandleOut(hx, hy);
+ if (loop || i > paddingLeft)
+ segment.setHandleIn(-hx, -hy);
+ }
+ } else {
+ for (var i = from; i <= to; i++) {
+ segments[i < 0 ? i + length : i].smooth(opts,
+ !loop && i === from, !loop && i === to);
+ }
+ }
},
toShape: function(insert) {
@@ -7330,15 +8735,43 @@
topCenter;
function isCollinear(i, j) {
- return segments[i].isCollinear(segments[j]);
+ var seg1 = segments[i],
+ seg2 = seg1.getNext(),
+ seg3 = segments[j],
+ seg4 = seg3.getNext();
+ return seg1._handleOut.isZero() && seg2._handleIn.isZero()
+ && seg3._handleOut.isZero() && seg4._handleIn.isZero()
+ && seg2._point.subtract(seg1._point).isCollinear(
+ seg4._point.subtract(seg3._point));
}
function isOrthogonal(i) {
- return segments[i].isOrthogonal();
+ var seg2 = segments[i],
+ seg1 = seg2.getPrevious(),
+ seg3 = seg2.getNext();
+ return seg1._handleOut.isZero() && seg2._handleIn.isZero()
+ && seg2._handleOut.isZero() && seg3._handleIn.isZero()
+ && seg2._point.subtract(seg1._point).isOrthogonal(
+ seg3._point.subtract(seg2._point));
}
function isArc(i) {
- return segments[i].isOrthogonalArc();
+ var seg1 = segments[i],
+ seg2 = seg1.getNext(),
+ handle1 = seg1._handleOut,
+ handle2 = seg2._handleIn,
+ kappa = 0.5522847498307936;
+ if (handle1.isOrthogonal(handle2)) {
+ var pt1 = seg1._point,
+ pt2 = seg2._point,
+ corner = new Line(pt1, handle1, true).intersect(
+ new Line(pt2, handle2, true), true);
+ return corner && Numerical.isZero(handle1.getLength() /
+ corner.subtract(pt1).getLength() - kappa)
+ && Numerical.isZero(handle2.getLength() /
+ corner.subtract(pt2).getLength() - kappa);
+ }
+ return false;
}
function getDistance(i, j) {
@@ -7371,19 +8804,86 @@
if (type) {
var center = this.getPosition(true),
- shape = this._clone(new type({
+ shape = new type({
center: center,
size: size,
radius: radius,
insert: false
- }), insert, false);
+ });
+ shape.copyAttributes(this, true);
+ shape._matrix.prepend(this._matrix);
shape.rotate(topCenter.subtract(center).getAngle() + 90);
+ if (insert === undefined || insert)
+ shape.insertAbove(this);
return shape;
}
return null;
},
- _hitTestSelf: function(point, options) {
+ toPath: '#clone',
+
+ compare: function compare(path) {
+ if (!path || path instanceof CompoundPath)
+ return compare.base.call(this, path);
+ var curves1 = this.getCurves(),
+ curves2 = path.getCurves(),
+ length1 = curves1.length,
+ length2 = curves2.length;
+ if (!length1 || !length2) {
+ return length1 == length2;
+ }
+ var v1 = curves1[0].getValues(),
+ values2 = [],
+ pos1 = 0, pos2,
+ end1 = 0, end2;
+ for (var i = 0; i < length2; i++) {
+ var v2 = curves2[i].getValues();
+ values2.push(v2);
+ var overlaps = Curve.getOverlaps(v1, v2);
+ if (overlaps) {
+ pos2 = !i && overlaps[0][0] > 0 ? length2 - 1 : i;
+ end2 = overlaps[0][1];
+ break;
+ }
+ }
+ var abs = Math.abs,
+ epsilon = 1e-8,
+ v2 = values2[pos2],
+ start2;
+ while (v1 && v2) {
+ var overlaps = Curve.getOverlaps(v1, v2);
+ if (overlaps) {
+ var t1 = overlaps[0][0];
+ if (abs(t1 - end1) < epsilon) {
+ end1 = overlaps[1][0];
+ if (end1 === 1) {
+ v1 = ++pos1 < length1 ? curves1[pos1].getValues() : null;
+ end1 = 0;
+ }
+ var t2 = overlaps[0][1];
+ if (abs(t2 - end2) < epsilon) {
+ if (!start2)
+ start2 = [pos2, t2];
+ end2 = overlaps[1][1];
+ if (end2 === 1) {
+ if (++pos2 >= length2)
+ pos2 = 0;
+ v2 = values2[pos2] || curves2[pos2].getValues();
+ end2 = 0;
+ }
+ if (!v1) {
+ return start2[0] === pos2 && start2[1] === end2;
+ }
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ return false;
+ },
+
+ _hitTestSelf: function(point, options, viewMatrix, strokeMatrix) {
var that = this,
style = this.getStyle(),
segments = this._segments,
@@ -7396,16 +8896,17 @@
hitStroke = options.stroke && style.hasStroke(),
hitFill = options.fill && style.hasFill(),
hitCurves = options.curves,
- radius = hitStroke
+ strokeRadius = hitStroke
? style.getStrokeWidth() / 2
: hitFill && options.tolerance > 0 || hitCurves
? 0 : null;
- if (radius !== null) {
- if (radius > 0) {
+ if (strokeRadius !== null) {
+ if (strokeRadius > 0) {
join = style.getStrokeJoin();
cap = style.getStrokeCap();
- miterLimit = radius * style.getMiterLimit();
- strokePadding = tolerancePadding.add(new Point(radius, radius));
+ miterLimit = style.getMiterLimit();
+ strokePadding = strokePadding.add(
+ Path._getStrokePadding(strokeRadius, strokeMatrix));
} else {
join = cap = 'round';
}
@@ -7442,16 +8943,20 @@
}
function checkSegmentStroke(segment) {
- if (join !== 'round' || cap !== 'round') {
+ var isJoin = closed || segment._index > 0
+ && segment._index < numSegments - 1;
+ if ((isJoin ? join : cap) === 'round') {
+ return isCloseEnough(segment._point, strokePadding);
+ } else {
area = new Path({ internal: true, closed: true });
- if (closed || segment._index > 0
- && segment._index < numSegments - 1) {
- if (join !== 'round' && (segment._handleIn.isZero()
- || segment._handleOut.isZero()))
- Path._addBevelJoin(segment, join, radius, miterLimit,
- addToArea, true);
- } else if (cap !== 'round') {
- Path._addSquareCap(segment, cap, radius, addToArea, true);
+ if (isJoin) {
+ if (!segment.isSmooth()) {
+ Path._addBevelJoin(segment, join, strokeRadius,
+ miterLimit, null, strokeMatrix, addToArea, true);
+ }
+ } else if (cap === 'square') {
+ Path._addSquareCap(segment, cap, strokeRadius, null,
+ strokeMatrix, addToArea, true);
}
if (!area.isEmpty()) {
var loc;
@@ -7460,7 +8965,6 @@
&& isCloseEnough(loc.getPoint(), tolerancePadding);
}
}
- return isCloseEnough(segment._point, strokePadding);
}
if (options.ends && !options.segments && !closed) {
@@ -7472,11 +8976,11 @@
if (res = checkSegmentPoints(segments[i]))
return res;
}
- if (radius !== null) {
+ if (strokeRadius !== null) {
loc = this.getNearestLocation(point);
if (loc) {
- var parameter = loc.getParameter();
- if (parameter === 0 || parameter === 1 && numSegments > 1) {
+ var time = loc.getTime();
+ if (time === 0 || time === 1 && numSegments > 1) {
if (!checkSegmentStroke(loc.getSegment()))
loc = null;
} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
@@ -7486,7 +8990,8 @@
if (!loc && join === 'miter' && numSegments > 1) {
for (var i = 0; i < numSegments; i++) {
var segment = segments[i];
- if (point.getDistance(segment._point) <= miterLimit
+ if (point.getDistance(segment._point)
+ <= miterLimit * strokeRadius
&& checkSegmentStroke(segment)) {
loc = segment.getLocation();
break;
@@ -7505,32 +9010,16 @@
: null;
}
-}, Base.each(Curve.evaluateMethods,
+}, Base.each(Curve._evaluateMethods,
function(name) {
- this[name + 'At'] = function(offset, isParameter) {
- var loc = this.getLocationAt(offset, isParameter);
+ this[name + 'At'] = function(offset) {
+ var loc = this.getLocationAt(offset);
return loc && loc[name]();
};
},
{
beans: false,
- _getOffset: function(location) {
- var index = location && location.getIndex();
- if (index != null) {
- var curves = this.getCurves(),
- offset = 0;
- for (var i = 0; i < index; i++)
- offset += curves[i].getLength();
- var curve = curves[index],
- parameter = location.getParameter();
- if (parameter > 0)
- offset += curve.getPartLength(0, parameter);
- return offset;
- }
- return null;
- },
-
getLocationOf: function() {
var point = Point.read(arguments),
curves = this.getCurves();
@@ -7547,49 +9036,60 @@
return loc ? loc.getOffset() : null;
},
- getLocationAt: function(offset, isParameter) {
- var curves = this.getCurves(),
- length = 0;
- if (isParameter) {
- var index = ~~offset,
- curve = curves[index];
- return curve ? curve.getLocationAt(offset - index, true) : null;
- }
- for (var i = 0, l = curves.length; i < l; i++) {
- var start = length,
- curve = curves[i];
- length += curve.getLength();
- if (length > offset) {
- return curve.getLocationAt(offset - start);
- }
- }
- if (offset <= this.getLength())
- return new CurveLocation(curves[curves.length - 1], 1);
+ getLocationAt: function(offset) {
+ if (typeof offset === 'number') {
+ var curves = this.getCurves(),
+ length = 0;
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var start = length,
+ curve = curves[i];
+ length += curve.getLength();
+ if (length > offset) {
+ return curve.getLocationAt(offset - start);
+ }
+ }
+ if (curves.length > 0 && offset <= this.getLength()) {
+ return new CurveLocation(curves[curves.length - 1], 1);
+ }
+ } else if (offset && offset.getPath && offset.getPath() === this) {
+ return offset;
+ }
return null;
},
- getNearestLocation: function() {
- var point = Point.read(arguments),
- curves = this.getCurves(),
- minDist = Infinity,
- minLoc = null;
+ getOffsetsWithTangent: function() {
+ var tangent = Point.read(arguments);
+ if (tangent.isZero()) {
+ return [];
+ }
+
+ var offsets = [];
+ var curveStart = 0;
+ var curves = this.getCurves();
for (var i = 0, l = curves.length; i < l; i++) {
- var loc = curves[i].getNearestLocation(point);
- if (loc._distance < minDist) {
- minDist = loc._distance;
- minLoc = loc;
- }
- }
- return minLoc;
- },
-
- getNearestPoint: function() {
- return this.getNearestLocation.apply(this, arguments).getPoint();
- }
-}), new function() {
+ var curve = curves[i];
+ var curveTimes = curve.getTimesWithTangent(tangent);
+ for (var j = 0, m = curveTimes.length; j < m; j++) {
+ var offset = curveStart + curve.getOffsetAtTime(curveTimes[j]);
+ if (offsets.indexOf(offset) < 0) {
+ offsets.push(offset);
+ }
+ }
+ curveStart += curve.length;
+ }
+ return offsets;
+ }
+}),
+new function() {
function drawHandles(ctx, segments, matrix, size) {
- var half = size / 2;
+ if (size <= 0) return;
+
+ var half = size / 2,
+ miniSize = size - 2,
+ miniHalf = half - 1,
+ coords = new Array(6),
+ pX, pY;
function drawHandle(index) {
var hX = coords[index],
@@ -7605,22 +9105,21 @@
}
}
- var coords = new Array(6);
for (var i = 0, l = segments.length; i < l; i++) {
- var segment = segments[i];
- segment._transformCoordinates(matrix, coords, false);
- var state = segment._selectionState,
- pX = coords[0],
- pY = coords[1];
- if (state & 1)
+ var segment = segments[i],
+ selection = segment._selection;
+ segment._transformCoordinates(matrix, coords);
+ pX = coords[0];
+ pY = coords[1];
+ if (selection & 2)
drawHandle(2);
- if (state & 2)
+ if (selection & 4)
drawHandle(4);
ctx.fillRect(pX - half, pY - half, size, size);
- if (!(state & 4)) {
+ if (miniSize > 0 && !(selection & 1)) {
var fillStyle = ctx.fillStyle;
ctx.fillStyle = '#ffffff';
- ctx.fillRect(pX - half + 1, pY - half + 1, size - 2, size - 2);
+ ctx.fillRect(pX - miniHalf, pY - miniHalf, miniSize, miniSize);
ctx.fillStyle = fillStyle;
}
}
@@ -7638,7 +9137,7 @@
function drawSegment(segment) {
if (matrix) {
- segment._transformCoordinates(matrix, coords, false);
+ segment._transformCoordinates(matrix, coords);
curX = coords[0];
curY = coords[1];
} else {
@@ -7684,7 +9183,7 @@
}
return {
- _draw: function(ctx, param, strokeMatrix) {
+ _draw: function(ctx, param, viewMatrix, strokeMatrix) {
var dontStart = param.dontStart,
dontPaint = param.dontFinish || param.clip,
style = this.getStyle(),
@@ -7697,14 +9196,10 @@
if (!dontStart)
ctx.beginPath();
- if (!dontStart && this._currentPath) {
- ctx.currentPath = this._currentPath;
- } else if (hasFill || hasStroke && !dashLength || dontPaint) {
+ if (hasFill || hasStroke && !dashLength || dontPaint) {
drawSegments(ctx, this, strokeMatrix);
if (this._closed)
ctx.closePath();
- if (!dontStart)
- this._currentPath = ctx.currentPath;
}
function getOffset(i) {
@@ -7712,18 +9207,18 @@
}
if (!dontPaint && (hasFill || hasStroke)) {
- this._setStyles(ctx);
+ this._setStyles(ctx, param, viewMatrix);
if (hasFill) {
- ctx.fill(style.getWindingRule());
+ ctx.fill(style.getFillRule());
ctx.shadowColor = 'rgba(0,0,0,0)';
}
if (hasStroke) {
if (dashLength) {
if (!dontStart)
ctx.beginPath();
- var iterator = new PathIterator(this, 32, 0.25,
+ var flattener = new PathFlattener(this, 0.25, 32, false,
strokeMatrix),
- length = iterator.length,
+ length = flattener.length,
from = -style.getDashOffset(), to,
i = 0;
from = from % length;
@@ -7733,7 +9228,7 @@
while (from < length) {
to = from + getOffset(i++);
if (from > 0 || to > 0)
- iterator.drawPart(ctx,
+ flattener.drawPart(ctx,
Math.max(from, 0), Math.max(to, 0));
from = to + getOffset(i++);
}
@@ -7750,103 +9245,11 @@
drawHandles(ctx, this._segments, matrix, paper.settings.handleSize);
}
};
-}, new function() {
-
- function getFirstControlPoints(rhs) {
- var n = rhs.length,
- x = [],
- tmp = [],
- b = 2;
- x[0] = rhs[0] / b;
- for (var i = 1; i < n; i++) {
- tmp[i] = 1 / b;
- b = (i < n - 1 ? 4 : 2) - tmp[i];
- x[i] = (rhs[i] - x[i - 1]) / b;
- }
- for (var i = 1; i < n; i++) {
- x[n - i - 1] -= tmp[n - i] * x[n - i];
- }
- return x;
- }
-
- return {
- smooth: function() {
- var segments = this._segments,
- size = segments.length,
- closed = this._closed,
- n = size,
- overlap = 0;
- if (size <= 2)
- return;
- if (closed) {
- overlap = Math.min(size, 4);
- n += Math.min(size, overlap) * 2;
- }
- var knots = [];
- for (var i = 0; i < size; i++)
- knots[i + overlap] = segments[i]._point;
- if (closed) {
- for (var i = 0; i < overlap; i++) {
- knots[i] = segments[i + size - overlap]._point;
- knots[i + size + overlap] = segments[i]._point;
- }
- } else {
- n--;
- }
- var rhs = [];
-
- for (var i = 1; i < n - 1; i++)
- rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
- rhs[0] = knots[0]._x + 2 * knots[1]._x;
- rhs[n - 1] = 3 * knots[n - 1]._x;
- var x = getFirstControlPoints(rhs);
-
- for (var i = 1; i < n - 1; i++)
- rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
- rhs[0] = knots[0]._y + 2 * knots[1]._y;
- rhs[n - 1] = 3 * knots[n - 1]._y;
- var y = getFirstControlPoints(rhs);
-
- if (closed) {
- for (var i = 0, j = size; i < overlap; i++, j++) {
- var f1 = i / overlap,
- f2 = 1 - f1,
- ie = i + overlap,
- je = j + overlap;
- x[j] = x[i] * f1 + x[j] * f2;
- y[j] = y[i] * f1 + y[j] * f2;
- x[je] = x[ie] * f2 + x[je] * f1;
- y[je] = y[ie] * f2 + y[je] * f1;
- }
- n--;
- }
- var handleIn = null;
- for (var i = overlap; i <= n - overlap; i++) {
- var segment = segments[i - overlap];
- if (handleIn)
- segment.setHandleIn(handleIn.subtract(segment._point));
- if (i < n) {
- segment.setHandleOut(
- new Point(x[i], y[i]).subtract(segment._point));
- handleIn = i < n - 1
- ? new Point(
- 2 * knots[i + 1]._x - x[i + 1],
- 2 * knots[i + 1]._y - y[i + 1])
- : new Point(
- (knots[n]._x + x[n - 1]) / 2,
- (knots[n]._y + y[n - 1]) / 2);
- }
- }
- if (closed && handleIn) {
- var segment = this._segments[0];
- segment.setHandleIn(handleIn.subtract(segment._point));
- }
- }
- };
-}, new function() {
+},
+new function() {
function getCurrentSegment(that) {
var segments = that._segments;
- if (segments.length === 0)
+ if (!segments.length)
throw new Error('Use a moveTo() command first');
return segments[segments.length - 1];
}
@@ -7903,7 +9306,9 @@
},
arcTo: function() {
- var current = getCurrentSegment(this),
+ var abs = Math.abs,
+ sqrt = Math.sqrt,
+ current = getCurrentSegment(this),
from = current._point,
to = Point.read(arguments),
through,
@@ -7917,9 +9322,10 @@
} else if (Base.remain(arguments) <= 2) {
through = to;
to = Point.read(arguments);
- } else {
- var radius = Size.read(arguments);
- if (radius.isZero())
+ } else if (!from.equals(to)) {
+ var radius = Size.read(arguments),
+ isZero = Numerical.isZero;
+ if (isZero(radius.width) || isZero(radius.height))
return this.lineTo(to);
var rotation = Base.read(arguments),
clockwise = !!Base.read(arguments),
@@ -7928,15 +9334,13 @@
pt = from.subtract(middle).rotate(-rotation),
x = pt.x,
y = pt.y,
- abs = Math.abs,
- epsilon = 1e-12,
rx = abs(radius.width),
ry = abs(radius.height),
rxSq = rx * rx,
rySq = ry * ry,
- xSq = x * x,
- ySq = y * y;
- var factor = Math.sqrt(xSq / rxSq + ySq / rySq);
+ xSq = x * x,
+ ySq = y * y;
+ var factor = sqrt(xSq / rxSq + ySq / rySq);
if (factor > 1) {
rx *= factor;
ry *= factor;
@@ -7945,14 +9349,13 @@
}
factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) /
(rxSq * ySq + rySq * xSq);
- if (abs(factor) < epsilon)
+ if (abs(factor) < 1e-12)
factor = 0;
if (factor < 0)
throw new Error(
'Cannot create an arc with the given arguments');
center = new Point(rx * y / ry, -ry * x / rx)
- .multiply((large === clockwise ? -1 : 1)
- * Math.sqrt(factor))
+ .multiply((large === clockwise ? -1 : 1) * sqrt(factor))
.rotate(rotation).add(middle);
matrix = new Matrix().translate(center).rotate(rotation)
.scale(rx, ry);
@@ -7979,45 +9382,48 @@
}
vector = from.subtract(center);
extent = vector.getDirectedAngle(to.subtract(center));
- var centerSide = line.getSide(center);
+ var centerSide = line.getSide(center, true);
if (centerSide === 0) {
- extent = throughSide * Math.abs(extent);
+ extent = throughSide * abs(extent);
} else if (throughSide === centerSide) {
extent += extent < 0 ? 360 : -360;
}
}
- var ext = Math.abs(extent),
- count = ext >= 360 ? 4 : Math.ceil(ext / 90),
- inc = extent / count,
- half = inc * Math.PI / 360,
- z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
- segments = [];
- for (var i = 0; i <= count; i++) {
- var pt = to,
- out = null;
- if (i < count) {
- out = vector.rotate(90).multiply(z);
- if (matrix) {
- pt = matrix._transformPoint(vector);
- out = matrix._transformPoint(vector.add(out))
- .subtract(pt);
+ if (extent) {
+ var epsilon = 1e-7,
+ ext = abs(extent),
+ count = ext >= 360 ? 4 : Math.ceil((ext - epsilon) / 90),
+ inc = extent / count,
+ half = inc * Math.PI / 360,
+ z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
+ segments = [];
+ for (var i = 0; i <= count; i++) {
+ var pt = to,
+ out = null;
+ if (i < count) {
+ out = vector.rotate(90).multiply(z);
+ if (matrix) {
+ pt = matrix._transformPoint(vector);
+ out = matrix._transformPoint(vector.add(out))
+ .subtract(pt);
+ } else {
+ pt = center.add(vector);
+ }
+ }
+ if (!i) {
+ current.setHandleOut(out);
} else {
- pt = center.add(vector);
- }
- }
- if (i === 0) {
- current.setHandleOut(out);
- } else {
- var _in = vector.rotate(-90).multiply(z);
- if (matrix) {
- _in = matrix._transformPoint(vector.add(_in))
- .subtract(pt);
- }
- segments.push(new Segment(pt, _in, out));
- }
- vector = vector.rotate(inc);
- }
- this._add(segments);
+ var _in = vector.rotate(-90).multiply(z);
+ if (matrix) {
+ _in = matrix._transformPoint(vector.add(_in))
+ .subtract(pt);
+ }
+ segments.push(new Segment(pt, _in, out));
+ }
+ vector = vector.rotate(inc);
+ }
+ this._add(segments);
+ }
},
lineBy: function() {
@@ -8061,40 +9467,35 @@
}
},
- closePath: function(join) {
+ closePath: function(tolerance) {
this.setClosed(true);
- if (join)
- this.join();
+ this.join(this, tolerance);
}
};
}, {
- _getBounds: function(getter, matrix) {
- return Path[getter](this._segments, this._closed, this.getStyle(),
- matrix);
+ _getBounds: function(matrix, options) {
+ var method = options.handle
+ ? 'getHandleBounds'
+ : options.stroke
+ ? 'getStrokeBounds'
+ : 'getBounds';
+ return Path[method](this._segments, this._closed, this, matrix, options);
},
statics: {
- isClockwise: function(segments) {
- var sum = 0;
- for (var i = 0, l = segments.length; i < l; i++)
- sum += Curve.getEdgeSum(Curve.getValues(
- segments[i], segments[i + 1 < l ? i + 1 : 0]));
- return sum > 0;
- },
-
- getBounds: function(segments, closed, style, matrix, strokePadding) {
+ getBounds: function(segments, closed, path, matrix, options, strokePadding) {
var first = segments[0];
if (!first)
return new Rectangle();
var coords = new Array(6),
- prevCoords = first._transformCoordinates(matrix, new Array(6), false),
+ prevCoords = first._transformCoordinates(matrix, new Array(6)),
min = prevCoords.slice(0, 2),
max = min.slice(),
roots = new Array(2);
function processSegment(segment) {
- segment._transformCoordinates(matrix, coords, false);
+ segment._transformCoordinates(matrix, coords);
for (var i = 0; i < 2; i++) {
Curve._addBounds(
prevCoords[i],
@@ -8115,36 +9516,38 @@
return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
},
- getStrokeBounds: function(segments, closed, style, matrix) {
- if (!style.hasStroke())
- return Path.getBounds(segments, closed, style, matrix);
- var length = segments.length - (closed ? 0 : 1),
- radius = style.getStrokeWidth() / 2,
- padding = Path._getPenPadding(radius, matrix),
- bounds = Path.getBounds(segments, closed, style, matrix, padding),
+ getStrokeBounds: function(segments, closed, path, matrix, options) {
+ var style = path.getStyle(),
+ stroke = style.hasStroke(),
+ strokeWidth = style.getStrokeWidth(),
+ strokeMatrix = stroke && path._getStrokeMatrix(matrix, options),
+ strokePadding = stroke && Path._getStrokePadding(strokeWidth,
+ strokeMatrix),
+ bounds = Path.getBounds(segments, closed, path, matrix, options,
+ strokePadding);
+ if (!stroke)
+ return bounds;
+ var strokeRadius = strokeWidth / 2,
join = style.getStrokeJoin(),
cap = style.getStrokeCap(),
- miterLimit = radius * style.getMiterLimit();
- var joinBounds = new Rectangle(new Size(padding).multiply(2));
-
- function add(point) {
- bounds = bounds.include(matrix
- ? matrix._transformPoint(point, point) : point);
+ miterLimit = style.getMiterLimit(),
+ joinBounds = new Rectangle(new Size(strokePadding));
+
+ function addPoint(point) {
+ bounds = bounds.include(point);
}
function addRound(segment) {
- bounds = bounds.unite(joinBounds.setCenter(matrix
- ? matrix._transformPoint(segment._point) : segment._point));
+ bounds = bounds.unite(
+ joinBounds.setCenter(segment._point.transform(matrix)));
}
function addJoin(segment, join) {
- var handleIn = segment._handleIn,
- handleOut = segment._handleOut;
- if (join === 'round' || !handleIn.isZero() && !handleOut.isZero()
- && handleIn.isCollinear(handleOut)) {
+ if (join === 'round' || segment.isSmooth()) {
addRound(segment);
} else {
- Path._addBevelJoin(segment, join, radius, miterLimit, add);
+ Path._addBevelJoin(segment, join, strokeRadius, miterLimit,
+ matrix, strokeMatrix, addPoint);
}
}
@@ -8152,10 +9555,12 @@
if (cap === 'round') {
addRound(segment);
} else {
- Path._addSquareCap(segment, cap, radius, add);
- }
- }
-
+ Path._addSquareCap(segment, cap, strokeRadius, matrix,
+ strokeMatrix, addPoint);
+ }
+ }
+
+ var length = segments.length - (closed ? 0 : 1);
for (var i = 1; i < length; i++)
addJoin(segments[i], join);
if (closed) {
@@ -8167,72 +9572,87 @@
return bounds;
},
- _getPenPadding: function(radius, matrix) {
+ _getStrokePadding: function(radius, matrix) {
if (!matrix)
return [radius, radius];
- var mx = matrix.shiftless(),
- hor = mx.transform(new Point(radius, 0)),
- ver = mx.transform(new Point(0, radius)),
+ var hor = new Point(radius, 0).transform(matrix),
+ ver = new Point(0, radius).transform(matrix),
phi = hor.getAngleInRadians(),
a = hor.getLength(),
b = ver.getLength();
var sin = Math.sin(phi),
cos = Math.cos(phi),
tan = Math.tan(phi),
- tx = -Math.atan(b * tan / a),
- ty = Math.atan(b / (tan * a));
- return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
+ tx = Math.atan2(b * tan, a),
+ ty = Math.atan2(b, tan * a);
+ return [Math.abs(a * Math.cos(tx) * cos + b * Math.sin(tx) * sin),
Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
},
- _addBevelJoin: function(segment, join, radius, miterLimit, addPoint, area) {
+ _addBevelJoin: function(segment, join, radius, miterLimit, matrix,
+ strokeMatrix, addPoint, isArea) {
var curve2 = segment.getCurve(),
curve1 = curve2.getPrevious(),
- point = curve2.getPointAt(0, true),
- normal1 = curve1.getNormalAt(1, true),
- normal2 = curve2.getNormalAt(0, true),
- step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
- normal1.setLength(step);
- normal2.setLength(step);
- if (area) {
+ point = curve2.getPoint1().transform(matrix),
+ normal1 = curve1.getNormalAtTime(1).multiply(radius)
+ .transform(strokeMatrix),
+ normal2 = curve2.getNormalAtTime(0).multiply(radius)
+ .transform(strokeMatrix),
+ angle = normal1.getDirectedAngle(normal2);
+ if (angle < 0 || angle >= 180) {
+ normal1 = normal1.negate();
+ normal2 = normal2.negate();
+ }
+ if (isArea)
addPoint(point);
- addPoint(point.add(normal1));
- }
+ addPoint(point.add(normal1));
if (join === 'miter') {
- var corner = new Line(
- point.add(normal1),
+ var corner = new Line(point.add(normal1),
new Point(-normal1.y, normal1.x), true
- ).intersect(new Line(
- point.add(normal2),
+ ).intersect(new Line(point.add(normal2),
new Point(-normal2.y, normal2.x), true
), true);
- if (corner && point.getDistance(corner) <= miterLimit) {
+ if (corner && point.getDistance(corner) <= miterLimit * radius) {
addPoint(corner);
- if (!area)
- return;
- }
- }
- if (!area)
- addPoint(point.add(normal1));
+ }
+ }
addPoint(point.add(normal2));
},
- _addSquareCap: function(segment, cap, radius, addPoint, area) {
- var point = segment._point,
+ _addSquareCap: function(segment, cap, radius, matrix, strokeMatrix,
+ addPoint, isArea) {
+ var point = segment._point.transform(matrix),
loc = segment.getLocation(),
- normal = loc.getNormal().multiply(radius);
- if (area) {
- addPoint(point.subtract(normal));
- addPoint(point.add(normal));
- }
- if (cap === 'square')
- point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90));
+ normal = loc.getNormal()
+ .multiply(loc.getTime() === 0 ? radius : -radius)
+ .transform(strokeMatrix);
+ if (cap === 'square') {
+ if (isArea) {
+ addPoint(point.subtract(normal));
+ addPoint(point.add(normal));
+ }
+ point = point.add(normal.rotate(-90));
+ }
addPoint(point.add(normal));
addPoint(point.subtract(normal));
},
- getHandleBounds: function(segments, closed, style, matrix, strokePadding,
- joinPadding) {
+ getHandleBounds: function(segments, closed, path, matrix, options) {
+ var style = path.getStyle(),
+ stroke = options.stroke && style.hasStroke(),
+ strokePadding,
+ joinPadding;
+ if (stroke) {
+ var strokeMatrix = path._getStrokeMatrix(matrix, options),
+ strokeRadius = style.getStrokeWidth() / 2,
+ joinRadius = strokeRadius;
+ if (style.getStrokeJoin() === 'miter')
+ joinRadius = strokeRadius * style.getMiterLimit();
+ if (style.getStrokeCap() === 'square')
+ joinRadius = Math.max(joinRadius, strokeRadius * Math.SQRT2);
+ strokePadding = Path._getStrokePadding(strokeRadius, strokeMatrix);
+ joinPadding = Path._getStrokePadding(joinRadius, strokeMatrix);
+ }
var coords = new Array(6),
x1 = Infinity,
x2 = -x1,
@@ -8240,9 +9660,9 @@
y2 = x2;
for (var i = 0, l = segments.length; i < l; i++) {
var segment = segments[i];
- segment._transformCoordinates(matrix, coords, false);
+ segment._transformCoordinates(matrix, coords);
for (var j = 0; j < 6; j += 2) {
- var padding = j === 0 ? joinPadding : strokePadding,
+ var padding = !j ? joinPadding : strokePadding,
paddingX = padding ? padding[0] : 0,
paddingY = padding ? padding[1] : 0,
x = coords[j],
@@ -8258,20 +9678,6 @@
}
}
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
- },
-
- getRoughBounds: function(segments, closed, style, matrix) {
- var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
- joinRadius = strokeRadius;
- if (strokeRadius > 0) {
- if (style.getStrokeJoin() === 'miter')
- joinRadius = strokeRadius * style.getMiterLimit();
- if (style.getStrokeCap() === 'square')
- joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
- }
- return Path.getHandleBounds(segments, closed, style, matrix,
- Path._getPenPadding(strokeRadius, matrix),
- Path._getPenPadding(joinRadius, matrix));
}
}});
@@ -8287,10 +9693,10 @@
function createPath(segments, closed, args) {
var props = Base.getNamed(args),
- path = new Path(props && props.insert === false && Item.NO_INSERT);
+ path = new Path(props && props.insert == false && Item.NO_INSERT);
path._add(segments);
path._closed = closed;
- return path.set(props);
+ return path.set(props, { insert: true });
}
function createEllipse(center, radius, args) {
@@ -8370,7 +9776,7 @@
through = Point.readNamed(arguments, 'through'),
to = Point.readNamed(arguments, 'to'),
props = Base.getNamed(arguments),
- path = new Path(props && props.insert === false
+ path = new Path(props && props.insert == false
&& Item.NO_INSERT);
path.moveTo(from);
path.arcTo(through, to);
@@ -8382,7 +9788,7 @@
sides = Base.readNamed(arguments, 'sides'),
radius = Base.readNamed(arguments, 'radius'),
step = 360 / sides,
- three = !(sides % 3),
+ three = sides % 3 === 0,
vector = new Point(0, three ? -radius : radius),
offset = three ? -1 : 0.5,
segments = new Array(sides);
@@ -8413,6 +9819,7 @@
_serializeFields: {
children: []
},
+ beans: true,
initialize: function CompoundPath(arg) {
this._children = [];
@@ -8426,47 +9833,56 @@
}
},
- insertChildren: function insertChildren(index, items, _preserve) {
- items = insertChildren.base.call(this, index, items, _preserve, Path);
- for (var i = 0, l = !_preserve && items && items.length; i < l; i++) {
- var item = items[i];
- if (item._clockwise === undefined)
- item.setClockwise(item._index === 0);
- }
- return items;
- },
-
- reverse: function() {
+ insertChildren: function insertChildren(index, items) {
+ var list = items,
+ first = list[0];
+ if (first && typeof first[0] === 'number')
+ list = [list];
+ for (var i = items.length - 1; i >= 0; i--) {
+ var item = list[i];
+ if (list === items && !(item instanceof Path))
+ list = Base.slice(list);
+ if (Array.isArray(item)) {
+ list[i] = new Path({ segments: item, insert: false });
+ } else if (item instanceof CompoundPath) {
+ list.splice.apply(list, [i, 1].concat(item.removeChildren()));
+ item.remove();
+ }
+ }
+ return insertChildren.base.call(this, index, list);
+ },
+
+ reduce: function reduce(options) {
var children = this._children;
- for (var i = 0, l = children.length; i < l; i++)
- children[i].reverse();
- },
-
- smooth: function() {
- for (var i = 0, l = this._children.length; i < l; i++)
- this._children[i].smooth();
- },
-
- reduce: function reduce() {
- if (this._children.length === 0) {
+ for (var i = children.length - 1; i >= 0; i--) {
+ var path = children[i].reduce(options);
+ if (path.isEmpty())
+ path.remove();
+ }
+ if (!children.length) {
var path = new Path(Item.NO_INSERT);
+ path.copyAttributes(this);
path.insertAbove(this);
- path.setStyle(this._style);
this.remove();
return path;
- } else {
- return reduce.base.call(this);
- }
- },
-
- isClockwise: function() {
- var child = this.getFirstChild();
- return child && child.isClockwise();
- },
-
- setClockwise: function(clockwise) {
- if (this.isClockwise() !== !!clockwise)
- this.reverse();
+ }
+ return reduce.base.call(this);
+ },
+
+ isClosed: function() {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++) {
+ if (!children[i]._closed)
+ return false;
+ }
+ return true;
+ },
+
+ setClosed: function(closed) {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++) {
+ children[i].setClosed(closed);
+ }
},
getFirstSegment: function() {
@@ -8482,8 +9898,9 @@
getCurves: function() {
var children = this._children,
curves = [];
- for (var i = 0, l = children.length; i < l; i++)
- curves.push.apply(curves, children[i].getCurves());
+ for (var i = 0, l = children.length; i < l; i++) {
+ Base.push(curves, children[i].getCurves());
+ }
return curves;
},
@@ -8494,7 +9911,7 @@
getLastCurve: function() {
var last = this.getLastChild();
- return last && last.getFirstCurve();
+ return last && last.getLastCurve();
},
getArea: function() {
@@ -8503,9 +9920,15 @@
for (var i = 0, l = children.length; i < l; i++)
area += children[i].getArea();
return area;
- }
-}, {
- beans: true,
+ },
+
+ getLength: function() {
+ var children = this._children,
+ length = 0;
+ for (var i = 0, l = children.length; i < l; i++)
+ length += children[i].getLength();
+ return length;
+ },
getPathData: function(_matrix, _precision) {
var children = this._children,
@@ -8514,37 +9937,33 @@
var child = children[i],
mx = child._matrix;
paths.push(child.getPathData(_matrix && !mx.isIdentity()
- ? _matrix.chain(mx) : mx, _precision));
- }
- return paths.join(' ');
- }
-}, {
- _getChildHitTestOptions: function(options) {
- return options.class === Path || options.type === 'path'
- ? options
- : new Base(options, { fill: false });
- },
-
- _draw: function(ctx, param, strokeMatrix) {
+ ? _matrix.appended(mx) : _matrix, _precision));
+ }
+ return paths.join('');
+ },
+
+ _hitTestChildren: function _hitTestChildren(point, options, viewMatrix) {
+ return _hitTestChildren.base.call(this, point,
+ options.class === Path || options.type === 'path' ? options
+ : Base.set({}, options, { fill: false }),
+ viewMatrix);
+ },
+
+ _draw: function(ctx, param, viewMatrix, strokeMatrix) {
var children = this._children;
- if (children.length === 0)
+ if (!children.length)
return;
- if (this._currentPath) {
- ctx.currentPath = this._currentPath;
- } else {
- param = param.extend({ dontStart: true, dontFinish: true });
- ctx.beginPath();
- for (var i = 0, l = children.length; i < l; i++)
- children[i].draw(ctx, param, strokeMatrix);
- this._currentPath = ctx.currentPath;
- }
+ param = param.extend({ dontStart: true, dontFinish: true });
+ ctx.beginPath();
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].draw(ctx, param, strokeMatrix);
if (!param.clip) {
- this._setStyles(ctx);
+ this._setStyles(ctx, param, viewMatrix);
var style = this._style;
if (style.hasFill()) {
- ctx.fill(style.getWindingRule());
+ ctx.fill(style.getFillRule());
ctx.shadowColor = 'rgba(0,0,0,0)';
}
if (style.hasStroke())
@@ -8552,553 +9971,906 @@
}
},
- _drawSelected: function(ctx, matrix, selectedItems) {
+ _drawSelected: function(ctx, matrix, selectionItems) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i],
mx = child._matrix;
- if (!selectedItems[child._id])
+ if (!selectionItems[child._id]) {
child._drawSelected(ctx, mx.isIdentity() ? matrix
- : matrix.chain(mx));
- }
- }
-}, new function() {
+ : matrix.appended(mx));
+ }
+ }
+ }
+},
+new function() {
function getCurrentPath(that, check) {
var children = that._children;
- if (check && children.length === 0)
+ if (check && !children.length)
throw new Error('Use a moveTo() command first');
return children[children.length - 1];
}
- var fields = {
- moveTo: function() {
- var current = getCurrentPath(this),
- path = current && current.isEmpty() ? current : new Path();
- if (path !== current)
- this.addChild(path);
- path.moveTo.apply(path, arguments);
- },
-
- moveBy: function() {
- var current = getCurrentPath(this, true),
- last = current && current.getLastSegment(),
- point = Point.read(arguments);
- this.moveTo(last ? point.add(last._point) : point);
- },
-
- closePath: function(join) {
- getCurrentPath(this, true).closePath(join);
- }
+ return Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
+ 'arcTo', 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy',
+ 'arcBy'],
+ function(key) {
+ this[key] = function() {
+ var path = getCurrentPath(this, true);
+ path[key].apply(path, arguments);
+ };
+ }, {
+ moveTo: function() {
+ var current = getCurrentPath(this),
+ path = current && current.isEmpty() ? current
+ : new Path(Item.NO_INSERT);
+ if (path !== current)
+ this.addChild(path);
+ path.moveTo.apply(path, arguments);
+ },
+
+ moveBy: function() {
+ var current = getCurrentPath(this, true),
+ last = current && current.getLastSegment(),
+ point = Point.read(arguments);
+ this.moveTo(last ? point.add(last._point) : point);
+ },
+
+ closePath: function(tolerance) {
+ getCurrentPath(this, true).closePath(tolerance);
+ }
+ }
+ );
+}, Base.each(['reverse', 'flatten', 'simplify', 'smooth'], function(key) {
+ this[key] = function(param) {
+ var children = this._children,
+ res;
+ for (var i = 0, l = children.length; i < l; i++) {
+ res = children[i][key](param) || res;
+ }
+ return res;
};
-
- Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
- 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
- function(key) {
- fields[key] = function() {
- var path = getCurrentPath(this, true);
- path[key].apply(path, arguments);
- };
- }
- );
-
- return fields;
-});
+}, {}));
PathItem.inject(new function() {
- var operators = {
- unite: function(w) {
- return w === 1 || w === 0;
- },
-
- intersect: function(w) {
- return w === 2;
- },
-
- subtract: function(w) {
- return w === 1;
- },
-
- exclude: function(w) {
- return w === 1;
- }
- };
-
- function computeBoolean(path1, path2, operation) {
- var operator = operators[operation];
- function preparePath(path) {
- return path.clone(false).reduce().reorient().transform(null, true,
- true);
- }
-
- var _path1 = preparePath(path1),
- _path2 = path2 && path1 !== path2 && preparePath(path2);
- if (_path2 && /^(subtract|exclude)$/.test(operation)
- ^ (_path2.isClockwise() !== _path1.isClockwise()))
+ var min = Math.min,
+ max = Math.max,
+ abs = Math.abs,
+ operators = {
+ unite: { '1': true, '2': true },
+ intersect: { '2': true },
+ subtract: { '1': true },
+ exclude: { '1': true, '-1': true }
+ };
+
+ function preparePath(path, resolve) {
+ var res = path.clone(false).reduce({ simplify: true })
+ .transform(null, true, true);
+ return resolve
+ ? res.resolveCrossings().reorient(
+ res.getFillRule() === 'nonzero', true)
+ : res;
+ }
+
+ function createResult(paths, simplify, path1, path2, options) {
+ var result = new CompoundPath(Item.NO_INSERT);
+ result.addChildren(paths, true);
+ result = result.reduce({ simplify: simplify });
+ if (!(options && options.insert == false)) {
+ result.insertAbove(path2 && path1.isSibling(path2)
+ && path1.getIndex() < path2.getIndex() ? path2 : path1);
+ }
+ result.copyAttributes(path1, true);
+ return result;
+ }
+
+ function traceBoolean(path1, path2, operation, options) {
+ if (options && (options.trace == false || options.stroke) &&
+ /^(subtract|intersect)$/.test(operation))
+ return splitBoolean(path1, path2, operation);
+ var _path1 = preparePath(path1, true),
+ _path2 = path2 && path1 !== path2 && preparePath(path2, true),
+ operator = operators[operation];
+ operator[operation] = true;
+ if (_path2 && (operator.subtract || operator.exclude)
+ ^ (_path2.isClockwise() ^ _path1.isClockwise()))
_path2.reverse();
- splitPath(_path1.getIntersections(_path2, null, true));
-
- var chain = [],
+ var crossings = divideLocations(
+ CurveLocation.expand(_path1.getCrossings(_path2))),
+ paths1 = _path1._children || [_path1],
+ paths2 = _path2 && (_path2._children || [_path2]),
segments = [],
- monoCurves = [],
- tolerance = 0.000001;
+ curves = [],
+ paths;
function collect(paths) {
for (var i = 0, l = paths.length; i < l; i++) {
var path = paths[i];
- segments.push.apply(segments, path._segments);
- monoCurves.push.apply(monoCurves, path._getMonoCurves());
- }
- }
-
- collect(_path1._children || [_path1]);
- if (_path2)
- collect(_path2._children || [_path2]);
- segments.sort(function(a, b) {
- var _a = a._intersection,
- _b = b._intersection;
- return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1;
- });
- for (var i = 0, l = segments.length; i < l; i++) {
- var segment = segments[i];
- if (segment._winding != null)
- continue;
- chain.length = 0;
- var startSeg = segment,
- totalLength = 0,
- windingSum = 0;
- do {
- var length = segment.getCurve().getLength();
- chain.push({ segment: segment, length: length });
- totalLength += length;
- segment = segment.getNext();
- } while (segment && !segment._intersection && segment !== startSeg);
- for (var j = 0; j < 3; j++) {
- var length = totalLength * (j + 1) / 4;
- for (var k = 0, m = chain.length; k < m; k++) {
- var node = chain[k],
- curveLength = node.length;
- if (length <= curveLength) {
- if (length < tolerance
- || curveLength - length < tolerance)
- length = curveLength / 2;
- var curve = node.segment.getCurve(),
- pt = curve.getPointAt(length),
- hor = curve.isLinear() && Math.abs(curve
- .getTangentAt(0.5, true).y) < tolerance,
- path = curve._path;
- if (path._parent instanceof CompoundPath)
- path = path._parent;
- windingSum += operation === 'subtract' && _path2
- && (path === _path1 && _path2._getWinding(pt, hor)
- || path === _path2 && !_path1._getWinding(pt, hor))
- ? 0
- : getWinding(pt, monoCurves, hor);
+ Base.push(segments, path._segments);
+ Base.push(curves, path.getCurves());
+ path._overlapsOnly = true;
+ }
+ }
+
+ if (crossings.length) {
+ collect(paths1);
+ if (paths2)
+ collect(paths2);
+ for (var i = 0, l = crossings.length; i < l; i++) {
+ propagateWinding(crossings[i]._segment, _path1, _path2, curves,
+ operator);
+ }
+ for (var i = 0, l = segments.length; i < l; i++) {
+ var segment = segments[i],
+ inter = segment._intersection;
+ if (!segment._winding) {
+ propagateWinding(segment, _path1, _path2, curves, operator);
+ }
+ if (!(inter && inter._overlap))
+ segment._path._overlapsOnly = false;
+ }
+ paths = tracePaths(segments, operator);
+ } else {
+ paths = reorientPaths(
+ paths2 ? paths1.concat(paths2) : paths1.slice(),
+ function(w) {
+ return !!operator[w];
+ });
+ }
+
+ return createResult(paths, true, path1, path2, options);
+ }
+
+ function splitBoolean(path1, path2, operation) {
+ var _path1 = preparePath(path1),
+ _path2 = preparePath(path2),
+ crossings = _path1.getCrossings(_path2),
+ subtract = operation === 'subtract',
+ divide = operation === 'divide',
+ added = {},
+ paths = [];
+
+ function addPath(path) {
+ if (!added[path._id] && (divide ||
+ _path2.contains(path.getPointAt(path.getLength() / 2))
+ ^ subtract)) {
+ paths.unshift(path);
+ return added[path._id] = true;
+ }
+ }
+
+ for (var i = crossings.length - 1; i >= 0; i--) {
+ var path = crossings[i].split();
+ if (path) {
+ if (addPath(path))
+ path.getFirstSegment().setHandleIn(0, 0);
+ _path1.getLastSegment().setHandleOut(0, 0);
+ }
+ }
+ addPath(_path1);
+ return createResult(paths, false, path1, path2);
+ }
+
+ function linkIntersections(from, to) {
+ var prev = from;
+ while (prev) {
+ if (prev === to)
+ return;
+ prev = prev._previous;
+ }
+ while (from._next && from._next !== to)
+ from = from._next;
+ if (!from._next) {
+ while (to._previous)
+ to = to._previous;
+ from._next = to;
+ to._previous = from;
+ }
+ }
+
+ function clearCurveHandles(curves) {
+ for (var i = curves.length - 1; i >= 0; i--)
+ curves[i].clearHandles();
+ }
+
+ function reorientPaths(paths, isInside, clockwise) {
+ var length = paths && paths.length;
+ if (length) {
+ var lookup = Base.each(paths, function (path, i) {
+ this[path._id] = {
+ container: null,
+ winding: path.isClockwise() ? 1 : -1,
+ index: i
+ };
+ }, {}),
+ sorted = paths.slice().sort(function (a, b) {
+ return abs(b.getArea()) - abs(a.getArea());
+ }),
+ first = sorted[0];
+ if (clockwise == null)
+ clockwise = first.isClockwise();
+ for (var i = 0; i < length; i++) {
+ var path1 = sorted[i],
+ entry1 = lookup[path1._id],
+ point = path1.getInteriorPoint(),
+ containerWinding = 0;
+ for (var j = i - 1; j >= 0; j--) {
+ var path2 = sorted[j];
+ if (path2.contains(point)) {
+ var entry2 = lookup[path2._id];
+ containerWinding = entry2.winding;
+ entry1.winding += containerWinding;
+ entry1.container = entry2.exclude ? entry2.container
+ : path2;
break;
}
- length -= curveLength;
- }
- }
- var winding = Math.round(windingSum / 3);
- for (var j = chain.length - 1; j >= 0; j--)
- chain[j].segment._winding = winding;
- }
- var result = new CompoundPath(Item.NO_INSERT);
- result.insertAbove(path1);
- result.addChildren(tracePaths(segments, operator), true);
- result = result.reduce();
- result.setStyle(path1._style);
- return result;
- }
-
- function splitPath(intersections) {
- var tMin = 0.000001,
+ }
+ if (isInside(entry1.winding) === isInside(containerWinding)) {
+ entry1.exclude = true;
+ paths[entry1.index] = null;
+ } else {
+ var container = entry1.container;
+ path1.setClockwise(container ? !container.isClockwise()
+ : clockwise);
+ }
+ }
+ }
+ return paths;
+ }
+
+ function divideLocations(locations, include, clearLater) {
+ var results = include && [],
+ tMin = 1e-8,
tMax = 1 - tMin,
- linearHandles;
-
- function resetLinear() {
- for (var i = 0, l = linearHandles.length; i < l; i++)
- linearHandles[i].set(0, 0);
- }
-
- for (var i = intersections.length - 1, curve, prev; i >= 0; i--) {
- var loc = intersections[i],
- t = loc._parameter;
- if (prev && prev._curve === loc._curve && prev._parameter > 0) {
- t /= prev._parameter;
- } else {
- curve = loc._curve;
- if (linearHandles)
- resetLinear();
- linearHandles = curve.isLinear() ? [
- curve._segment1._handleOut,
- curve._segment2._handleIn
- ] : null;
- }
- var newCurve,
+ clearHandles = false,
+ clearCurves = clearLater || [],
+ clearLookup = clearLater && {},
+ renormalizeLocs,
+ prevCurve,
+ prevTime;
+
+ function getId(curve) {
+ return curve._path._id + '.' + curve._segment1._index;
+ }
+
+ for (var i = (clearLater && clearLater.length) - 1; i >= 0; i--) {
+ var curve = clearLater[i];
+ if (curve._path)
+ clearLookup[getId(curve)] = true;
+ }
+
+ for (var i = locations.length - 1; i >= 0; i--) {
+ var loc = locations[i],
+ time = loc._time,
+ origTime = time,
+ exclude = include && !include(loc),
+ curve = loc._curve,
segment;
- if (newCurve = curve.divide(t, true, true)) {
+ if (curve) {
+ if (curve !== prevCurve) {
+ clearHandles = !curve.hasHandles()
+ || clearLookup && clearLookup[getId(curve)];
+ renormalizeLocs = [];
+ prevTime = null;
+ prevCurve = curve;
+ } else if (prevTime >= tMin) {
+ time /= prevTime;
+ }
+ }
+ if (exclude) {
+ if (renormalizeLocs)
+ renormalizeLocs.push(loc);
+ continue;
+ } else if (include) {
+ results.unshift(loc);
+ }
+ prevTime = origTime;
+ if (time < tMin) {
+ segment = curve._segment1;
+ } else if (time > tMax) {
+ segment = curve._segment2;
+ } else {
+ var newCurve = curve.divideAtTime(time, true);
+ if (clearHandles)
+ clearCurves.push(curve, newCurve);
segment = newCurve._segment1;
- curve = newCurve.getPrevious();
- if (linearHandles)
- linearHandles.push(segment._handleOut, segment._handleIn);
- } else {
- segment = t < tMin
- ? curve._segment1
- : t > tMax
- ? curve._segment2
- : curve.getPartLength(0, t) < curve.getPartLength(t, 1)
- ? curve._segment1
- : curve._segment2;
- }
- segment._intersection = loc.getIntersection();
- loc._segment = segment;
- prev = loc;
- }
- if (linearHandles)
- resetLinear();
- }
-
- function getWinding(point, curves, horizontal, testContains) {
- var tolerance = 0.000001,
- tMin = tolerance,
- tMax = 1 - tMin,
- px = point.x,
- py = point.y,
- windLeft = 0,
- windRight = 0,
+ for (var j = renormalizeLocs.length - 1; j >= 0; j--) {
+ var l = renormalizeLocs[j];
+ l._time = (l._time - time) / (1 - time);
+ }
+ }
+ loc._setSegment(segment);
+ var inter = segment._intersection,
+ dest = loc._intersection;
+ if (inter) {
+ linkIntersections(inter, dest);
+ var other = inter;
+ while (other) {
+ linkIntersections(other._intersection, inter);
+ other = other._next;
+ }
+ } else {
+ segment._intersection = dest;
+ }
+ }
+ if (!clearLater)
+ clearCurveHandles(clearCurves);
+ return results || locations;
+ }
+
+ function getWinding(point, curves, dir, closed, dontFlip) {
+ var ia = dir ? 1 : 0,
+ io = ia ^ 1,
+ pv = [point.x, point.y],
+ pa = pv[ia],
+ po = pv[io],
+ windingEpsilon = 1e-9,
+ qualityEpsilon = 1e-6,
+ paL = pa - windingEpsilon,
+ paR = pa + windingEpsilon,
+ windingL = 0,
+ windingR = 0,
+ pathWindingL = 0,
+ pathWindingR = 0,
+ onPath = false,
+ onAnyPath = false,
+ quality = 1,
roots = [],
- abs = Math.abs;
- if (horizontal) {
- var yTop = -Infinity,
- yBottom = Infinity,
- yBefore = py - tolerance,
- yAfter = py + tolerance;
- for (var i = 0, l = curves.length; i < l; i++) {
- var values = curves[i].values;
- if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) {
- for (var j = roots.length - 1; j >= 0; j--) {
- var y = Curve.getPoint(values, roots[j]).y;
- if (y < yBefore && y > yTop) {
- yTop = y;
- } else if (y > yAfter && y < yBottom) {
- yBottom = y;
- }
- }
- }
- }
- yTop = (yTop + py) / 2;
- yBottom = (yBottom + py) / 2;
- if (yTop > -Infinity)
- windLeft = getWinding(new Point(px, yTop), curves);
- if (yBottom < Infinity)
- windRight = getWinding(new Point(px, yBottom), curves);
- } else {
- var xBefore = px - tolerance,
- xAfter = px + tolerance;
- var startCounted = false,
- prevCurve,
- prevT;
- for (var i = 0, l = curves.length; i < l; i++) {
- var curve = curves[i],
- values = curve.values,
- winding = curve.winding;
- if (winding && (winding === 1
- && py >= values[1] && py <= values[7]
- || py >= values[7] && py <= values[1])
- && Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) {
- var t = roots[0];
- if (!(
- t > tMax && startCounted && curve.next !== curves[i + 1]
- || t < tMin && prevT > tMax
- && curve.previous === prevCurve)) {
- var x = Curve.getPoint(values, t).x,
- slope = Curve.getTangent(values, t).y,
- counted = false;
- if (Numerical.isZero(slope) && !Curve.isLinear(values)
- || t < tMin && slope * Curve.getTangent(
- curve.previous.values, 1).y < 0
- || t > tMax && slope * Curve.getTangent(
- curve.next.values, 0).y < 0) {
- if (testContains && x >= xBefore && x <= xAfter) {
- ++windLeft;
- ++windRight;
- counted = true;
- }
- } else if (x <= xBefore) {
- windLeft += winding;
- counted = true;
- } else if (x >= xAfter) {
- windRight += winding;
- counted = true;
+ vPrev,
+ vClose;
+
+ function addWinding(v) {
+ var o0 = v[io + 0],
+ o3 = v[io + 6];
+ if (po < min(o0, o3) || po > max(o0, o3)) {
+ return;
+ }
+ var a0 = v[ia + 0],
+ a1 = v[ia + 2],
+ a2 = v[ia + 4],
+ a3 = v[ia + 6];
+ if (o0 === o3) {
+ if (a0 < paR && a3 > paL || a3 < paR && a0 > paL) {
+ onPath = true;
+ }
+ return;
+ }
+ var t = po === o0 ? 0
+ : po === o3 ? 1
+ : paL > max(a0, a1, a2, a3) || paR < min(a0, a1, a2, a3)
+ ? 1
+ : Curve.solveCubic(v, io, po, roots, 0, 1) > 0
+ ? roots[0]
+ : 1,
+ a = t === 0 ? a0
+ : t === 1 ? a3
+ : Curve.getPoint(v, t)[dir ? 'y' : 'x'],
+ winding = o0 > o3 ? 1 : -1,
+ windingPrev = vPrev[io] > vPrev[io + 6] ? 1 : -1,
+ a3Prev = vPrev[ia + 6];
+ if (po !== o0) {
+ if (a < paL) {
+ pathWindingL += winding;
+ } else if (a > paR) {
+ pathWindingR += winding;
+ } else {
+ onPath = true;
+ }
+ if (a > pa - qualityEpsilon && a < pa + qualityEpsilon)
+ quality /= 2;
+ } else {
+ if (winding !== windingPrev) {
+ if (a0 < paL) {
+ pathWindingL += winding;
+ } else if (a0 > paR) {
+ pathWindingR += winding;
+ }
+ } else if (a0 != a3Prev) {
+ if (a3Prev < paR && a > paR) {
+ pathWindingR += winding;
+ onPath = true;
+ } else if (a3Prev > paL && a < paL) {
+ pathWindingL += winding;
+ onPath = true;
+ }
+ }
+ quality = 0;
+ }
+ vPrev = v;
+ return !dontFlip && a > paL && a < paR
+ && Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0
+ && getWinding(point, curves, !dir, closed, true);
+ }
+
+ function handleCurve(v) {
+ var o0 = v[io + 0],
+ o1 = v[io + 2],
+ o2 = v[io + 4],
+ o3 = v[io + 6];
+ if (po <= max(o0, o1, o2, o3) && po >= min(o0, o1, o2, o3)) {
+ var a0 = v[ia + 0],
+ a1 = v[ia + 2],
+ a2 = v[ia + 4],
+ a3 = v[ia + 6],
+ monoCurves = paL > max(a0, a1, a2, a3) ||
+ paR < min(a0, a1, a2, a3)
+ ? [v] : Curve.getMonoCurves(v, dir),
+ res;
+ for (var i = 0, l = monoCurves.length; i < l; i++) {
+ if (res = addWinding(monoCurves[i]))
+ return res;
+ }
+ }
+ }
+
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var curve = curves[i],
+ path = curve._path,
+ v = curve.getValues(),
+ res;
+ if (!i || curves[i - 1]._path !== path) {
+ vPrev = null;
+ if (!path._closed) {
+ vClose = Curve.getValues(
+ path.getLastCurve().getSegment2(),
+ curve.getSegment1(),
+ null, !closed);
+ if (vClose[io] !== vClose[io + 6]) {
+ vPrev = vClose;
+ }
+ }
+
+ if (!vPrev) {
+ vPrev = v;
+ var prev = path.getLastCurve();
+ while (prev && prev !== curve) {
+ var v2 = prev.getValues();
+ if (v2[io] !== v2[io + 6]) {
+ vPrev = v2;
+ break;
}
- if (curve.previous !== curves[i - 1])
- startCounted = t < tMin && counted;
- }
- prevCurve = curve;
- prevT = t;
- }
- }
- }
- return Math.max(abs(windLeft), abs(windRight));
- }
-
- function tracePaths(segments, operator, selfOp) {
- var paths = [],
- tMin = 0.000001,
+ prev = prev.getPrevious();
+ }
+ }
+ }
+
+ if (res = handleCurve(v))
+ return res;
+
+ if (i + 1 === l || curves[i + 1]._path !== path) {
+ if (vClose && (res = handleCurve(vClose)))
+ return res;
+ if (onPath && !pathWindingL && !pathWindingR) {
+ pathWindingL = pathWindingR = path.isClockwise(closed) ^ dir
+ ? 1 : -1;
+ }
+ windingL += pathWindingL;
+ windingR += pathWindingR;
+ pathWindingL = pathWindingR = 0;
+ if (onPath) {
+ onAnyPath = true;
+ onPath = false;
+ }
+ vClose = null;
+ }
+ }
+ windingL = abs(windingL);
+ windingR = abs(windingR);
+ return {
+ winding: max(windingL, windingR),
+ windingL: windingL,
+ windingR: windingR,
+ quality: quality,
+ onPath: onAnyPath
+ };
+ }
+
+ function propagateWinding(segment, path1, path2, curves, operator) {
+ var chain = [],
+ start = segment,
+ totalLength = 0,
+ winding;
+ do {
+ var curve = segment.getCurve(),
+ length = curve.getLength();
+ chain.push({ segment: segment, curve: curve, length: length });
+ totalLength += length;
+ segment = segment.getNext();
+ } while (segment && !segment._intersection && segment !== start);
+ var offsets = [0.5, 0.25, 0.75],
+ winding = { winding: 0, quality: -1 },
+ tMin = 1e-8,
tMax = 1 - tMin;
- for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) {
- seg = startSeg = segments[i];
- if (seg._visited || !operator(seg._winding))
- continue;
- var path = new Path(Item.NO_INSERT),
- inter = seg._intersection,
- startInterSeg = inter && inter._segment,
- added = false,
- dir = 1;
- do {
- var handleIn = dir > 0 ? seg._handleIn : seg._handleOut,
- handleOut = dir > 0 ? seg._handleOut : seg._handleIn,
- interSeg;
- if (added && (!operator(seg._winding) || selfOp)
- && (inter = seg._intersection)
- && (interSeg = inter._segment)
- && interSeg !== startSeg) {
- if (selfOp) {
- seg._visited = interSeg._visited;
- seg = interSeg;
- dir = 1;
- } else {
- var c1 = seg.getCurve();
- if (dir > 0)
- c1 = c1.getPrevious();
- var t1 = c1.getTangentAt(dir < 1 ? tMin : tMax, true),
- c4 = interSeg.getCurve(),
- c3 = c4.getPrevious(),
- t3 = c3.getTangentAt(tMax, true),
- t4 = c4.getTangentAt(tMin, true),
- w3 = t1.cross(t3),
- w4 = t1.cross(t4);
- if (w3 * w4 !== 0) {
- var curve = w3 < w4 ? c3 : c4,
- nextCurve = operator(curve._segment1._winding)
- ? curve
- : w3 < w4 ? c4 : c3,
- nextSeg = nextCurve._segment1;
- dir = nextCurve === c3 ? -1 : 1;
- if (nextSeg._visited && seg._path !== nextSeg._path
- || !operator(nextSeg._winding)) {
- dir = 1;
+ for (var i = 0; i < offsets.length && winding.quality < 0.5; i++) {
+ var length = totalLength * offsets[i];
+ for (var j = 0, l = chain.length; j < l; j++) {
+ var entry = chain[j],
+ curveLength = entry.length;
+ if (length <= curveLength) {
+ var curve = entry.curve,
+ path = curve._path,
+ parent = path._parent,
+ operand = parent instanceof CompoundPath ? parent : path,
+ t = Numerical.clamp(curve.getTimeAt(length), tMin, tMax),
+ pt = curve.getPointAtTime(t),
+ dir = abs(curve.getTangentAtTime(t).y) < Math.SQRT1_2;
+ var wind = null;
+ if (operator.subtract && path2) {
+ var pathWinding = operand === path1
+ ? path2._getWinding(pt, dir, true)
+ : path1._getWinding(pt, dir, true);
+ if (operand === path1 && pathWinding.winding ||
+ operand === path2 && !pathWinding.winding) {
+ if (pathWinding.quality < 1) {
+ continue;
} else {
- seg._visited = interSeg._visited;
- seg = interSeg;
- if (nextSeg._visited)
- dir = 1;
+ wind = { winding: 0, quality: 1 };
}
- } else {
- dir = 1;
+ }
+ }
+ wind = wind || getWinding(pt, curves, dir, true);
+ if (wind.quality > winding.quality)
+ winding = wind;
+ break;
+ }
+ length -= curveLength;
+ }
+ }
+ for (var j = chain.length - 1; j >= 0; j--) {
+ chain[j].segment._winding = winding;
+ }
+ }
+
+ function tracePaths(segments, operator) {
+ var paths = [],
+ starts;
+
+ function isValid(seg) {
+ var winding;
+ return !!(seg && !seg._visited && (!operator
+ || operator[(winding = seg._winding || {}).winding]
+ && !(operator.unite && winding.winding === 2
+ && winding.windingL && winding.windingR)));
+ }
+
+ function isStart(seg) {
+ if (seg) {
+ for (var i = 0, l = starts.length; i < l; i++) {
+ if (seg === starts[i])
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function visitPath(path) {
+ var segments = path._segments;
+ for (var i = 0, l = segments.length; i < l; i++) {
+ segments[i]._visited = true;
+ }
+ }
+
+ function getCrossingSegments(segment, collectStarts) {
+ var inter = segment._intersection,
+ start = inter,
+ crossings = [];
+ if (collectStarts)
+ starts = [segment];
+
+ function collect(inter, end) {
+ while (inter && inter !== end) {
+ var other = inter._segment,
+ path = other && other._path;
+ if (path) {
+ var next = other.getNext() || path.getFirstSegment(),
+ nextInter = next._intersection;
+ if (other !== segment && (isStart(other)
+ || isStart(next)
+ || next && (isValid(other) && (isValid(next)
+ || nextInter && isValid(nextInter._segment))))
+ ) {
+ crossings.push(other);
}
- }
- handleOut = dir > 0 ? seg._handleOut : seg._handleIn;
- }
- path.add(new Segment(seg._point, added && handleIn, handleOut));
- added = true;
+ if (collectStarts)
+ starts.push(other);
+ }
+ inter = inter._next;
+ }
+ }
+
+ if (inter) {
+ collect(inter);
+ while (inter && inter._prev)
+ inter = inter._prev;
+ collect(inter, start);
+ }
+ return crossings;
+ }
+
+ segments.sort(function(seg1, seg2) {
+ var inter1 = seg1._intersection,
+ inter2 = seg2._intersection,
+ over1 = !!(inter1 && inter1._overlap),
+ over2 = !!(inter2 && inter2._overlap),
+ path1 = seg1._path,
+ path2 = seg2._path;
+ return over1 ^ over2
+ ? over1 ? 1 : -1
+ : !inter1 ^ !inter2
+ ? inter1 ? 1 : -1
+ : path1 !== path2
+ ? path1._id - path2._id
+ : seg1._index - seg2._index;
+ });
+
+ for (var i = 0, l = segments.length; i < l; i++) {
+ var seg = segments[i],
+ valid = isValid(seg),
+ path = null,
+ finished = false,
+ closed = true,
+ branches = [],
+ branch,
+ visited,
+ handleIn;
+ if (valid && seg._path._overlapsOnly) {
+ var path1 = seg._path,
+ path2 = seg._intersection._segment._path;
+ if (path1.compare(path2)) {
+ if (path1.getArea())
+ paths.push(path1.clone(false));
+ visitPath(path1);
+ visitPath(path2);
+ valid = false;
+ }
+ }
+ while (valid) {
+ var first = !path,
+ crossings = getCrossingSegments(seg, first),
+ other = crossings.shift(),
+ finished = !first && (isStart(seg) || isStart(other)),
+ cross = !finished && other;
+ if (first) {
+ path = new Path(Item.NO_INSERT);
+ branch = null;
+ }
+ if (finished) {
+ if (seg.isFirst() || seg.isLast())
+ closed = seg._path._closed;
+ seg._visited = true;
+ break;
+ }
+ if (cross && branch) {
+ branches.push(branch);
+ branch = null;
+ }
+ if (!branch) {
+ if (cross)
+ crossings.push(seg);
+ branch = {
+ start: path._segments.length,
+ crossings: crossings,
+ visited: visited = [],
+ handleIn: handleIn
+ };
+ }
+ if (cross)
+ seg = other;
+ if (!isValid(seg)) {
+ path.removeSegments(branch.start);
+ for (var j = 0, k = visited.length; j < k; j++) {
+ visited[j]._visited = false;
+ }
+ visited.length = 0;
+ do {
+ seg = branch && branch.crossings.shift();
+ if (!seg || !seg._path) {
+ seg = null;
+ branch = branches.pop();
+ if (branch) {
+ visited = branch.visited;
+ handleIn = branch.handleIn;
+ }
+ }
+ } while (branch && !isValid(seg));
+ if (!seg)
+ break;
+ }
+ var next = seg.getNext();
+ path.add(new Segment(seg._point, handleIn,
+ next && seg._handleOut));
seg._visited = true;
- seg = dir > 0 ? seg.getNext() : seg. getPrevious();
- } while (seg && !seg._visited
- && seg !== startSeg && seg !== startInterSeg
- && (seg._intersection || operator(seg._winding)));
- if (seg && (seg === startSeg || seg === startInterSeg)) {
- path.firstSegment.setHandleIn((seg === startInterSeg
- ? startInterSeg : seg)._handleIn);
- path.setClosed(true);
- } else {
- path.lastSegment._handleOut.set(0, 0);
- }
- if (path._segments.length >
- (path._closed ? path.isLinear() ? 2 : 0 : 1))
- paths.push(path);
+ visited.push(seg);
+ seg = next || seg._path.getFirstSegment();
+ handleIn = next && next._handleIn;
+ }
+ if (finished) {
+ if (closed) {
+ path.getFirstSegment().setHandleIn(handleIn);
+ path.setClosed(closed);
+ }
+ if (path.getArea() !== 0) {
+ paths.push(path);
+ }
+ }
}
return paths;
}
return {
- _getWinding: function(point, horizontal, testContains) {
- return getWinding(point, this._getMonoCurves(),
- horizontal, testContains);
- },
-
- unite: function(path) {
- return computeBoolean(this, path, 'unite');
- },
-
- intersect: function(path) {
- return computeBoolean(this, path, 'intersect');
- },
-
- subtract: function(path) {
- return computeBoolean(this, path, 'subtract');
- },
-
- exclude: function(path) {
- return computeBoolean(this, path, 'exclude');
- },
-
- divide: function(path) {
- return new Group([this.subtract(path), this.intersect(path)]);
+ _getWinding: function(point, dir, closed) {
+ return getWinding(point, this.getCurves(), dir, closed);
+ },
+
+ unite: function(path, options) {
+ return traceBoolean(this, path, 'unite', options);
+ },
+
+ intersect: function(path, options) {
+ return traceBoolean(this, path, 'intersect', options);
+ },
+
+ subtract: function(path, options) {
+ return traceBoolean(this, path, 'subtract', options);
+ },
+
+ exclude: function(path, options) {
+ return traceBoolean(this, path, 'exclude', options);
+ },
+
+ divide: function(path, options) {
+ return options && (options.trace == false || options.stroke)
+ ? splitBoolean(this, path, 'divide')
+ : createResult([
+ this.subtract(path, options),
+ this.intersect(path, options)
+ ], true, this, path, options);
+ },
+
+ resolveCrossings: function() {
+ var children = this._children,
+ paths = children || [this];
+
+ function hasOverlap(seg, path) {
+ var inter = seg && seg._intersection;
+ return inter && inter._overlap && inter._path === path;
+ }
+
+ var hasOverlaps = false,
+ hasCrossings = false,
+ intersections = this.getIntersections(null, function(inter) {
+ return inter.hasOverlap() && (hasOverlaps = true) ||
+ inter.isCrossing() && (hasCrossings = true);
+ }),
+ clearCurves = hasOverlaps && hasCrossings && [];
+ intersections = CurveLocation.expand(intersections);
+ if (hasOverlaps) {
+ var overlaps = divideLocations(intersections, function(inter) {
+ return inter.hasOverlap();
+ }, clearCurves);
+ for (var i = overlaps.length - 1; i >= 0; i--) {
+ var overlap = overlaps[i],
+ path = overlap._path,
+ seg = overlap._segment,
+ prev = seg.getPrevious(),
+ next = seg.getNext();
+ if (hasOverlap(prev, path) && hasOverlap(next, path)) {
+ seg.remove();
+ prev._handleOut._set(0, 0);
+ next._handleIn._set(0, 0);
+ if (prev !== seg && !prev.getCurve().hasLength()) {
+ next._handleIn.set(prev._handleIn);
+ prev.remove();
+ }
+ }
+ }
+ }
+ if (hasCrossings) {
+ divideLocations(intersections, hasOverlaps && function(inter) {
+ var curve1 = inter.getCurve(),
+ seg1 = inter.getSegment(),
+ other = inter._intersection,
+ curve2 = other._curve,
+ seg2 = other._segment;
+ if (curve1 && curve2 && curve1._path && curve2._path)
+ return true;
+ if (seg1)
+ seg1._intersection = null;
+ if (seg2)
+ seg2._intersection = null;
+ }, clearCurves);
+ if (clearCurves)
+ clearCurveHandles(clearCurves);
+ paths = tracePaths(Base.each(paths, function(path) {
+ Base.push(this, path._segments);
+ }, []));
+ }
+ var length = paths.length,
+ item;
+ if (length > 1 && children) {
+ if (paths !== children)
+ this.setChildren(paths);
+ item = this;
+ } else if (length === 1 && !children) {
+ if (paths[0] !== this)
+ this.setSegments(paths[0].removeSegments());
+ item = this;
+ }
+ if (!item) {
+ item = new CompoundPath(Item.NO_INSERT);
+ item.addChildren(paths);
+ item = item.reduce();
+ item.copyAttributes(this);
+ this.replaceWith(item);
+ }
+ return item;
+ },
+
+ reorient: function(nonZero, clockwise) {
+ var children = this._children;
+ if (children && children.length) {
+ this.setChildren(reorientPaths(this.removeChildren(),
+ function(w) {
+ return !!(nonZero ? w : w & 1);
+ },
+ clockwise));
+ } else if (clockwise !== undefined) {
+ this.setClockwise(clockwise);
+ }
+ return this;
+ },
+
+ getInteriorPoint: function() {
+ var bounds = this.getBounds(),
+ point = bounds.getCenter(true);
+ if (!this.contains(point)) {
+ var curves = this.getCurves(),
+ y = point.y,
+ intercepts = [],
+ roots = [];
+ for (var i = 0, l = curves.length; i < l; i++) {
+ var v = curves[i].getValues(),
+ o0 = v[1],
+ o1 = v[3],
+ o2 = v[5],
+ o3 = v[7];
+ if (y >= min(o0, o1, o2, o3) && y <= max(o0, o1, o2, o3)) {
+ var monoCurves = Curve.getMonoCurves(v);
+ for (var j = 0, m = monoCurves.length; j < m; j++) {
+ var mv = monoCurves[j],
+ mo0 = mv[1],
+ mo3 = mv[7];
+ if ((mo0 !== mo3) &&
+ (y >= mo0 && y <= mo3 || y >= mo3 && y <= mo0)){
+ var x = y === mo0 ? mv[0]
+ : y === mo3 ? mv[6]
+ : Curve.solveCubic(mv, 1, y, roots, 0, 1)
+ === 1
+ ? Curve.getPoint(mv, roots[0]).x
+ : (mv[0] + mv[6]) / 2;
+ intercepts.push(x);
+ }
+ }
+ }
+ }
+ if (intercepts.length > 1) {
+ intercepts.sort(function(a, b) { return a - b; });
+ point.x = (intercepts[0] + intercepts[1]) / 2;
+ }
+ }
+ return point;
}
};
});
-Path.inject({
- _getMonoCurves: function() {
- var monoCurves = this._monoCurves,
- prevCurve;
-
- function insertCurve(v) {
- var y0 = v[1],
- y1 = v[7],
- curve = {
- values: v,
- winding: y0 === y1
- ? 0
- : y0 > y1
- ? -1
- : 1,
- previous: prevCurve,
- next: null
- };
- if (prevCurve)
- prevCurve.next = curve;
- monoCurves.push(curve);
- prevCurve = curve;
- }
-
- function handleCurve(v) {
- if (Curve.getLength(v) === 0)
- return;
- var y0 = v[1],
- y1 = v[3],
- y2 = v[5],
- y3 = v[7];
- if (Curve.isLinear(v)) {
- insertCurve(v);
- } else {
- var a = 3 * (y1 - y2) - y0 + y3,
- b = 2 * (y0 + y2) - 4 * y1,
- c = y1 - y0,
- tolerance = 0.000001,
- roots = [];
- var count = Numerical.solveQuadratic(a, b, c, roots, tolerance,
- 1 - tolerance);
- if (count === 0) {
- insertCurve(v);
- } else {
- roots.sort();
- var t = roots[0],
- parts = Curve.subdivide(v, t);
- insertCurve(parts[0]);
- if (count > 1) {
- t = (roots[1] - t) / (1 - t);
- parts = Curve.subdivide(parts[1], t);
- insertCurve(parts[0]);
- }
- insertCurve(parts[1]);
- }
- }
- }
-
- if (!monoCurves) {
- monoCurves = this._monoCurves = [];
- var curves = this.getCurves(),
- segments = this._segments;
- for (var i = 0, l = curves.length; i < l; i++)
- handleCurve(curves[i].getValues());
- if (!this._closed && segments.length > 1) {
- var p1 = segments[segments.length - 1]._point,
- p2 = segments[0]._point,
- p1x = p1._x, p1y = p1._y,
- p2x = p2._x, p2y = p2._y;
- handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
- }
- if (monoCurves.length > 0) {
- var first = monoCurves[0],
- last = monoCurves[monoCurves.length - 1];
- first.previous = last;
- last.next = first;
- }
- }
- return monoCurves;
- },
-
- getInteriorPoint: function() {
- var bounds = this.getBounds(),
- point = bounds.getCenter(true);
- if (!this.contains(point)) {
- var curves = this._getMonoCurves(),
- roots = [],
- y = point.y,
- xIntercepts = [];
- for (var i = 0, l = curves.length; i < l; i++) {
- var values = curves[i].values;
- if ((curves[i].winding === 1
- && y >= values[1] && y <= values[7]
- || y >= values[7] && y <= values[1])
- && Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
- for (var j = roots.length - 1; j >= 0; j--)
- xIntercepts.push(Curve.getPoint(values, roots[j]).x);
- }
- if (xIntercepts.length > 1)
- break;
- }
- point.x = (xIntercepts[0] + xIntercepts[1]) / 2;
- }
- return point;
- },
-
- reorient: function() {
- this.setClockwise(true);
- return this;
- }
-});
-
-CompoundPath.inject({
- _getMonoCurves: function() {
- var children = this._children,
- monoCurves = [];
- for (var i = 0, l = children.length; i < l; i++)
- monoCurves.push.apply(monoCurves, children[i]._getMonoCurves());
- return monoCurves;
- },
-
- reorient: function() {
- var children = this.removeChildren().sort(function(a, b) {
- return b.getBounds().getArea() - a.getBounds().getArea();
- });
- if (children.length > 0) {
- this.addChildren(children);
- var clockwise = children[0].isClockwise();
- for (var i = 1, l = children.length; i < l; i++) {
- var point = children[i].getInteriorPoint(),
- counters = 0;
- for (var j = i - 1; j >= 0; j--) {
- if (children[j].contains(point))
- counters++;
- }
- children[i].setClockwise(counters % 2 === 0 && clockwise);
- }
- }
- return this;
- }
-});
-
-var PathIterator = Base.extend({
- _class: 'PathIterator',
-
- initialize: function(path, maxRecursion, tolerance, matrix) {
+var PathFlattener = Base.extend({
+ _class: 'PathFlattener',
+
+ initialize: function(path, flatness, maxRecursion, ignoreStraight, matrix) {
var curves = [],
parts = [],
length = 0,
- minDifference = 1 / (maxRecursion || 32),
+ minSpan = 1 / (maxRecursion || 32),
segments = path._segments,
segment1 = segments[0],
segment2;
@@ -9109,23 +10881,25 @@
computeParts(curve, segment1._index, 0, 1);
}
- function computeParts(curve, index, minT, maxT) {
- if ((maxT - minT) > minDifference
- && !Curve.isFlatEnough(curve, tolerance || 0.25)) {
- var split = Curve.subdivide(curve),
- halfT = (minT + maxT) / 2;
- computeParts(split[0], index, minT, halfT);
- computeParts(split[1], index, halfT, maxT);
- } else {
- var x = curve[6] - curve[0],
- y = curve[7] - curve[1],
- dist = Math.sqrt(x * x + y * y);
- if (dist > 0.000001) {
+ function computeParts(curve, index, t1, t2) {
+ if ((t2 - t1) > minSpan
+ && !(ignoreStraight && Curve.isStraight(curve))
+ && !Curve.isFlatEnough(curve, flatness || 0.25)) {
+ var halves = Curve.subdivide(curve, 0.5),
+ tMid = (t1 + t2) / 2;
+ computeParts(halves[0], index, t1, tMid);
+ computeParts(halves[1], index, tMid, t2);
+ } else {
+ var dx = curve[6] - curve[0],
+ dy = curve[7] - curve[1],
+ dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 0) {
length += dist;
parts.push({
offset: length,
- value: maxT,
- index: index
+ curve: curve,
+ index: index,
+ time: t2,
});
}
}
@@ -9137,121 +10911,120 @@
segment1 = segment2;
}
if (path._closed)
- addCurve(segment2, segments[0]);
-
+ addCurve(segment2 || segment1, segments[0]);
this.curves = curves;
this.parts = parts;
this.length = length;
this.index = 0;
},
- getParameterAt: function(offset) {
- var i, j = this.index;
+ _get: function(offset) {
+ var parts = this.parts,
+ length = parts.length,
+ start,
+ i, j = this.index;
for (;;) {
i = j;
- if (j == 0 || this.parts[--j].offset < offset)
+ if (!j || parts[--j].offset < offset)
break;
}
- for (var l = this.parts.length; i < l; i++) {
- var part = this.parts[i];
+ for (; i < length; i++) {
+ var part = parts[i];
if (part.offset >= offset) {
this.index = i;
- var prev = this.parts[i - 1];
- var prevVal = prev && prev.index == part.index ? prev.value : 0,
- prevLen = prev ? prev.offset : 0;
+ var prev = parts[i - 1],
+ prevTime = prev && prev.index === part.index ? prev.time : 0,
+ prevOffset = prev ? prev.offset : 0;
return {
- value: prevVal + (part.value - prevVal)
- * (offset - prevLen) / (part.offset - prevLen),
- index: part.index
+ index: part.index,
+ time: prevTime + (part.time - prevTime)
+ * (offset - prevOffset) / (part.offset - prevOffset)
};
}
}
- var part = this.parts[this.parts.length - 1];
return {
- value: 1,
- index: part.index
+ index: parts[length - 1].index,
+ time: 1
};
},
drawPart: function(ctx, from, to) {
- from = this.getParameterAt(from);
- to = this.getParameterAt(to);
- for (var i = from.index; i <= to.index; i++) {
+ var start = this._get(from),
+ end = this._get(to);
+ for (var i = start.index, l = end.index; i <= l; i++) {
var curve = Curve.getPart(this.curves[i],
- i == from.index ? from.value : 0,
- i == to.index ? to.value : 1);
- if (i == from.index)
+ i === start.index ? start.time : 0,
+ i === end.index ? end.time : 1);
+ if (i === start.index)
ctx.moveTo(curve[0], curve[1]);
ctx.bezierCurveTo.apply(ctx, curve.slice(2));
}
}
-}, Base.each(Curve.evaluateMethods,
+}, Base.each(Curve._evaluateMethods,
function(name) {
- this[name + 'At'] = function(offset, weighted) {
- var param = this.getParameterAt(offset);
- return Curve[name](this.curves[param.index], param.value, weighted);
+ this[name + 'At'] = function(offset) {
+ var param = this._get(offset);
+ return Curve[name](this.curves[param.index], param.time);
};
}, {})
);
var PathFitter = Base.extend({
- initialize: function(path, error) {
+ initialize: function(path) {
var points = this.points = [],
segments = path._segments,
- prev;
- for (var i = 0, l = segments.length; i < l; i++) {
- var point = segments[i].point.clone();
+ closed = path._closed;
+ for (var i = 0, prev, l = segments.length; i < l; i++) {
+ var point = segments[i].point;
if (!prev || !prev.equals(point)) {
- points.push(point);
- prev = point;
- }
- }
-
- if (path._closed) {
- this.closed = true;
+ points.push(prev = point.clone());
+ }
+ }
+ if (closed) {
points.unshift(points[points.length - 1]);
points.push(points[1]);
}
-
- this.error = error;
- },
-
- fit: function() {
+ this.closed = closed;
+ },
+
+ fit: function(error) {
var points = this.points,
length = points.length,
- segments = this.segments = length > 0
- ? [new Segment(points[0])] : [];
- if (length > 1)
- this.fitCubic(0, length - 1,
- points[1].subtract(points[0]).normalize(),
- points[length - 2].subtract(points[length - 1]).normalize());
-
- if (this.closed) {
- segments.shift();
- segments.pop();
- }
-
+ segments = null;
+ if (length > 0) {
+ segments = [new Segment(points[0])];
+ if (length > 1) {
+ this.fitCubic(segments, error, 0, length - 1,
+ points[1].subtract(points[0]),
+ points[length - 2].subtract(points[length - 1]));
+ if (this.closed) {
+ segments.shift();
+ segments.pop();
+ }
+ }
+ }
return segments;
},
- fitCubic: function(first, last, tan1, tan2) {
- if (last - first == 1) {
- var pt1 = this.points[first],
- pt2 = this.points[last],
+ fitCubic: function(segments, error, first, last, tan1, tan2) {
+ var points = this.points;
+ if (last - first === 1) {
+ var pt1 = points[first],
+ pt2 = points[last],
dist = pt1.getDistance(pt2) / 3;
- this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
+ this.addCurve(segments, [pt1, pt1.add(tan1.normalize(dist)),
pt2.add(tan2.normalize(dist)), pt2]);
return;
}
var uPrime = this.chordLengthParameterize(first, last),
- maxError = Math.max(this.error, this.error * this.error),
+ maxError = Math.max(error, error * error),
split,
parametersInOrder = true;
for (var i = 0; i <= 4; i++) {
var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
var max = this.findMaxError(first, last, curve, uPrime);
- if (max.error < this.error && parametersInOrder) {
- this.addCurve(curve);
+ if (max.error < error && parametersInOrder) {
+ this.addCurve(segments, curve);
return;
}
split = max.index;
@@ -9260,24 +11033,23 @@
parametersInOrder = this.reparameterize(first, last, uPrime, curve);
maxError = max.error;
}
- var V1 = this.points[split - 1].subtract(this.points[split]),
- V2 = this.points[split].subtract(this.points[split + 1]),
- tanCenter = V1.add(V2).divide(2).normalize();
- this.fitCubic(first, split, tan1, tanCenter);
- this.fitCubic(split, last, tanCenter.negate(), tan2);
- },
-
- addCurve: function(curve) {
- var prev = this.segments[this.segments.length - 1];
+ var tanCenter = points[split - 1].subtract(points[split + 1]);
+ this.fitCubic(segments, error, first, split, tan1, tanCenter);
+ this.fitCubic(segments, error, split, last, tanCenter.negate(), tan2);
+ },
+
+ addCurve: function(segments, curve) {
+ var prev = segments[segments.length - 1];
prev.setHandleOut(curve[1].subtract(curve[0]));
- this.segments.push(
- new Segment(curve[3], curve[2].subtract(curve[3])));
+ segments.push(new Segment(curve[3], curve[2].subtract(curve[3])));
},
generateBezier: function(first, last, uPrime, tan1, tan2) {
var epsilon = 1e-12,
- pt1 = this.points[first],
- pt2 = this.points[last],
+ abs = Math.abs,
+ points = this.points,
+ pt1 = points[first],
+ pt2 = points[last],
C = [[0, 0], [0, 0]],
X = [0, 0];
@@ -9291,7 +11063,7 @@
b3 = u * u * u,
a1 = tan1.normalize(b1),
a2 = tan2.normalize(b2),
- tmp = this.points[first + i]
+ tmp = points[first + i]
.subtract(pt1.multiply(b0 + b1))
.subtract(pt2.multiply(b2 + b3));
C[0][0] += a1.dot(a1);
@@ -9303,22 +11075,19 @@
}
var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
- alpha1, alpha2;
- if (Math.abs(detC0C1) > epsilon) {
- var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
- detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
+ alpha1,
+ alpha2;
+ if (abs(detC0C1) > epsilon) {
+ var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
+ detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
alpha1 = detXC1 / detC0C1;
alpha2 = detC0X / detC0C1;
} else {
var c0 = C[0][0] + C[0][1],
c1 = C[1][0] + C[1][1];
- if (Math.abs(c0) > epsilon) {
- alpha1 = alpha2 = X[0] / c0;
- } else if (Math.abs(c1) > epsilon) {
- alpha1 = alpha2 = X[1] / c1;
- } else {
- alpha1 = alpha2 = 0;
- }
+ alpha1 = alpha2 = abs(c0) > epsilon ? X[0] / c0
+ : abs(c1) > epsilon ? X[1] / c1
+ : 0;
}
var segLength = pt2.getDistance(pt1),
@@ -9337,8 +11106,10 @@
}
}
- return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)),
- pt2.add(handle2 || tan2.normalize(alpha2)), pt2];
+ return [pt1,
+ pt1.add(handle1 || tan1.normalize(alpha1)),
+ pt2.add(handle2 || tan2.normalize(alpha2)),
+ pt2];
},
reparameterize: function(first, last, u, curve) {
@@ -9366,9 +11137,7 @@
pt2 = this.evaluate(1, curve2, u),
diff = pt.subtract(point),
df = pt1.dot(pt1) + diff.dot(pt2);
- if (Math.abs(df) < 0.000001)
- return u;
- return u - diff.dot(pt1) / df;
+ return Numerical.isZero(df) ? u : u - diff.dot(pt1) / df;
},
evaluate: function(degree, curve, t) {
@@ -9414,13 +11183,12 @@
var TextItem = Item.extend({
_class: 'TextItem',
- _boundsSelected: true,
_applyMatrix: false,
_canApplyMatrix: false,
_serializeFields: {
content: null
},
- _boundsGetter: 'getBounds',
+ _boundsOptions: { stroke: false, handle: false },
initialize: function TextItem(arg) {
this._content = '';
@@ -9434,9 +11202,8 @@
return this._content === item._content;
},
- _clone: function _clone(copy, insert, includeMatrix) {
- copy.setContent(this._content);
- return _clone.base.call(this, copy, insert, includeMatrix);
+ copyContent: function(source) {
+ this.setContent(source._content);
},
getContent: function() {
@@ -9446,7 +11213,7 @@
setContent: function(content) {
this._content = '' + content;
this._lines = this._content.split(/\r\n|\n|\r/mg);
- this._changed(265);
+ this._changed(521);
},
isEmpty: function() {
@@ -9467,10 +11234,6 @@
TextItem.apply(this, arguments);
},
- clone: function(insert) {
- return this._clone(new PointText(Item.NO_INSERT), insert);
- },
-
getPoint: function() {
var point = this._matrix.getTranslation();
return new LinkedPoint(point.x, point.y, this, 'setPoint');
@@ -9481,12 +11244,14 @@
this.translate(point.subtract(this._matrix.getTranslation()));
},
- _draw: function(ctx) {
+ _draw: function(ctx, param, viewMatrix) {
if (!this._content)
return;
- this._setStyles(ctx);
- var style = this._style,
- lines = this._lines,
+ this._setStyles(ctx, param, viewMatrix);
+ var lines = this._lines,
+ style = this._style,
+ hasFill = style.hasFill(),
+ hasStroke = style.hasStroke(),
leading = style.getLeading(),
shadowColor = ctx.shadowColor;
ctx.font = style.getFontStyle();
@@ -9494,17 +11259,17 @@
for (var i = 0, l = lines.length; i < l; i++) {
ctx.shadowColor = shadowColor;
var line = lines[i];
- if (style.hasFill()) {
+ if (hasFill) {
ctx.fillText(line, 0, 0);
ctx.shadowColor = 'rgba(0,0,0,0)';
}
- if (style.hasStroke())
+ if (hasStroke)
ctx.strokeText(line, 0, 0);
ctx.translate(0, leading);
}
},
- _getBounds: function(getter, matrix) {
+ _getBounds: function(matrix, options) {
var style = this._style,
lines = this._lines,
numLines = lines.length,
@@ -9514,10 +11279,10 @@
x = 0;
if (justification !== 'left')
x -= width / (justification === 'center' ? 2: 1);
- var bounds = new Rectangle(x,
+ var rect = new Rectangle(x,
numLines ? - 0.75 * leading : 0,
width, numLines * leading);
- return matrix ? matrix._transformBounds(bounds, bounds) : bounds;
+ return matrix ? matrix._transformBounds(rect, rect) : rect;
}
});
@@ -9531,45 +11296,74 @@
};
var componentParsers = {},
- colorCache = {},
+ namedColors = {
+ transparent: [0, 0, 0, 0]
+ },
colorCtx;
function fromCSS(string) {
- var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
+ var match = string.match(
+ /^#([\da-f]{2})([\da-f]{2})([\da-f]{2})([\da-f]{2})?$/i
+ ) || string.match(
+ /^#([\da-f])([\da-f])([\da-f])([\da-f])?$/i
+ ),
+ type = 'rgb',
components;
if (match) {
- components = [0, 0, 0];
- for (var i = 0; i < 3; i++) {
+ var amount = match[4] ? 4 : 3;
+ components = new Array(amount);
+ for (var i = 0; i < amount; i++) {
var value = match[i + 1];
components[i] = parseInt(value.length == 1
? value + value : value, 16) / 255;
}
- } else if (match = string.match(/^rgba?\((.*)\)$/)) {
- components = match[1].split(',');
- for (var i = 0, l = components.length; i < l; i++) {
- var value = +components[i];
- components[i] = i < 3 ? value / 255 : value;
+ } else if (match = string.match(/^(rgb|hsl)a?\((.*)\)$/)) {
+ type = match[1];
+ components = match[2].trim().split(/[,\s]+/g);
+ var isHSL = type === 'hsl';
+ for (var i = 0, l = Math.min(components.length, 4); i < l; i++) {
+ var component = components[i];
+ var value = parseFloat(component);
+ if (isHSL) {
+ if (i === 0) {
+ var unit = component.match(/([a-z]*)$/)[1];
+ value *= ({
+ turn: 360,
+ rad: 180 / Math.PI,
+ grad: 0.9
+ }[unit] || 1);
+ } else if (i < 3) {
+ value /= 100;
+ }
+ } else if (i < 3) {
+ value /= 255;
+ }
+ components[i] = value;
}
} else {
- var cached = colorCache[string];
- if (!cached) {
- if (!colorCtx) {
- colorCtx = CanvasProvider.getContext(1, 1);
- colorCtx.globalCompositeOperation = 'copy';
- }
- colorCtx.fillStyle = 'rgba(0,0,0,0)';
- colorCtx.fillStyle = string;
- colorCtx.fillRect(0, 0, 1, 1);
- var data = colorCtx.getImageData(0, 0, 1, 1).data;
- cached = colorCache[string] = [
- data[0] / 255,
- data[1] / 255,
- data[2] / 255
- ];
- }
- components = cached.slice();
- }
- return components;
+ var color = namedColors[string];
+ if (!color) {
+ if (window) {
+ if (!colorCtx) {
+ colorCtx = CanvasProvider.getContext(1, 1);
+ colorCtx.globalCompositeOperation = 'copy';
+ }
+ colorCtx.fillStyle = 'rgba(0,0,0,0)';
+ colorCtx.fillStyle = string;
+ colorCtx.fillRect(0, 0, 1, 1);
+ var data = colorCtx.getImageData(0, 0, 1, 1).data;
+ color = namedColors[string] = [
+ data[0] / 255,
+ data[1] / 255,
+ data[2] / 255
+ ];
+ } else {
+ color = [0, 0, 0];
+ }
+ }
+ components = color.slice();
+ }
+ return [type, components];
}
var hsbIndices = [
@@ -9587,9 +11381,9 @@
min = Math.min(r, g, b),
delta = max - min,
h = delta === 0 ? 0
- : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
+ : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
: max == g ? (b - r) / delta + 2
- : (r - g) / delta + 4) * 60;
+ : (r - g) / delta + 4) * 60;
return [h, max === 0 ? 0 : delta / max, max];
},
@@ -9613,9 +11407,9 @@
delta = max - min,
achromatic = delta === 0,
h = achromatic ? 0
- : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
+ : ( max == r ? (g - b) / delta + (g < b ? 6 : 0)
: max == g ? (b - r) / delta + 2
- : (r - g) / delta + 4) * 60,
+ : (r - g) / delta + 4) * 60,
l = (max + min) / 2,
s = achromatic ? 0 : l < 0.5
? delta / (max + min)
@@ -9677,30 +11471,32 @@
Base.each(properties, function(name, index) {
var part = Base.capitalize(name),
hasOverlap = /^(hue|saturation)$/.test(name),
- parser = componentParsers[type][index] = name === 'gradient'
- ? function(value) {
- var current = this._components[0];
- value = Gradient.read(Array.isArray(value) ? value
- : arguments, 0, { readNull: true });
- if (current !== value) {
- if (current)
- current._removeOwner(this);
- if (value)
- value._addOwner(this);
+ parser = componentParsers[type][index] = type === 'gradient'
+ ? name === 'gradient'
+ ? function(value) {
+ var current = this._components[0];
+ value = Gradient.read(
+ Array.isArray(value)
+ ? value
+ : arguments, 0, { readNull: true }
+ );
+ if (current !== value) {
+ if (current)
+ current._removeOwner(this);
+ if (value)
+ value._addOwner(this);
+ }
+ return value;
}
- return value;
- }
- : type === 'gradient'
- ? function() {
+ : function() {
return Point.read(arguments, 0, {
readNull: name === 'highlight',
clone: true
});
}
- : function(value) {
- return value == null || isNaN(value) ? 0 : value;
- };
-
+ : function(value) {
+ return value == null || isNaN(value) ? 0 : +value;
+ };
this['get' + part] = function() {
return this._type === type
|| hasOverlap && /^hs[bl]$/.test(this._type)
@@ -9724,8 +11520,8 @@
_readIndex: true,
initialize: function Color(arg) {
- var slice = Array.prototype.slice,
- args = arguments,
+ var args = arguments,
+ reading = this.__read,
read = 0,
type,
components,
@@ -9743,9 +11539,9 @@
components = arg;
alpha = args[2];
} else {
- if (this.__read)
+ if (reading)
read = 1;
- args = slice.call(args, 1);
+ args = Base.slice(args, 1);
argType = typeof arg;
}
}
@@ -9762,15 +11558,17 @@
: 'gray';
var length = types[type].length;
alpha = values[length];
- if (this.__read)
+ if (reading) {
read += values === arguments
? length + (alpha != null ? 1 : 0)
: 1;
+ }
if (values.length > length)
- values = slice.call(values, 0, length);
+ values = Base.slice(values, 0, length);
} else if (argType === 'string') {
- type = 'rgb';
- components = fromCSS(arg);
+ var converted = fromCSS(arg);
+ type = converted[0];
+ components = converted[1];
if (components.length === 4) {
alpha = components[3];
components.length--;
@@ -9801,12 +11599,12 @@
: 'gray' in arg
? 'gray'
: 'rgb';
- var properties = types[type];
+ var properties = types[type],
parsers = componentParsers[type];
this._components = components = [];
for (var i = 0, l = properties.length; i < l; i++) {
var value = arg[properties[i]];
- if (value == null && i === 0 && type === 'gradient'
+ if (value == null && !i && type === 'gradient'
&& 'stops' in arg) {
value = {
stops: arg.stops,
@@ -9820,11 +11618,10 @@
alpha = arg.alpha;
}
}
- if (this.__read && type)
+ if (reading && type)
read = 1;
}
this._type = type || 'rgb';
- this._id = UID.get(Color);
if (!components) {
this._components = components = [];
var parsers = componentParsers[this._type];
@@ -9837,9 +11634,12 @@
this._components = components;
this._properties = types[this._type];
this._alpha = alpha;
- if (this.__read)
+ if (reading)
this.__read = read;
- },
+ return this;
+ },
+
+ set: '#initialize',
_serialize: function(options, dictionary) {
var components = this.getComponents();
@@ -9852,8 +11652,13 @@
_changed: function() {
this._canvasStyle = null;
- if (this._owner)
- this._owner._changed(65);
+ if (this._owner) {
+ if (this._setter) {
+ this._owner[this._setter](this);
+ } else {
+ this._owner._changed(129);
+ }
+ }
},
_convert: function(type) {
@@ -9907,7 +11712,7 @@
: color;
return col === this || col && this._class === col._class
&& this._type === col._type
- && this._alpha === col._alpha
+ && this.getAlpha() === col.getAlpha()
&& Base.equals(this._components, col._components)
|| false;
},
@@ -9949,7 +11754,7 @@
+ components.join(',') + ')';
},
- toCanvasStyle: function(ctx) {
+ toCanvasStyle: function(ctx, matrix) {
if (this._canvasStyle)
return this._canvasStyle;
if (this._type !== 'gradient')
@@ -9959,10 +11764,17 @@
stops = gradient._stops,
origin = components[1],
destination = components[2],
+ highlight = components[3],
+ inverse = matrix && matrix.inverted(),
canvasGradient;
+ if (inverse) {
+ origin = inverse._transformPoint(origin);
+ destination = inverse._transformPoint(destination);
+ if (highlight)
+ highlight = inverse._transformPoint(highlight);
+ }
if (gradient._radial) {
- var radius = destination.getDistance(origin),
- highlight = components[3];
+ var radius = destination.getDistance(origin);
if (highlight) {
var vector = highlight.subtract(origin);
if (vector.getLength() > radius)
@@ -9976,8 +11788,10 @@
destination.x, destination.y);
}
for (var i = 0, l = stops.length; i < l; i++) {
- var stop = stops[i];
- canvasGradient.addColorStop(stop._rampPoint,
+ var stop = stops[i],
+ offset = stop._offset;
+ canvasGradient.addColorStop(
+ offset == null ? i / (l - 1) : offset,
stop._color.toCanvasStyle());
}
return this._canvasStyle = canvasGradient;
@@ -10000,10 +11814,24 @@
random: function() {
var random = Math.random;
return new Color(random(), random(), random());
+ },
+
+ _setOwner: function(color, owner, setter) {
+ if (color) {
+ if (color._owner && owner && color._owner !== owner) {
+ color = color.clone();
+ }
+ if (!color._owner ^ !owner) {
+ color._owner = owner || null;
+ color._setter = setter || null;
+ }
+ }
+ return color;
}
}
});
-}, new function() {
+},
+new function() {
var operators = {
add: function(a, b) {
return a + b;
@@ -10039,36 +11867,22 @@
});
});
-Base.each(Color._types, function(properties, type) {
- var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) {
- var argType = arg != null && typeof arg,
- components = argType === 'object' && arg.length != null
- ? arg
- : argType === 'string'
- ? null
- : arguments;
- return components
- ? new Color(type, components)
- : new Color(arg);
- };
- if (type.length == 3) {
- var acronym = type.toUpperCase();
- Color[acronym] = this[acronym + 'Color'] = ctor;
- }
-}, Base.exports);
-
var Gradient = Base.extend({
_class: 'Gradient',
initialize: function Gradient(stops, radial) {
this._id = UID.get();
- if (stops && this._set(stops))
+ if (stops && Base.isPlainObject(stops)) {
+ this.set(stops);
stops = radial = null;
- if (!this._stops)
+ }
+ if (this._stops == null) {
this.setStops(stops || ['white', 'black']);
- if (this._radial == null)
+ }
+ if (this._radial == null) {
this.setRadial(typeof radial === 'string' && radial === 'radial'
|| radial || false);
+ }
},
_serialize: function(options, dictionary) {
@@ -10079,8 +11893,9 @@
},
_changed: function() {
- for (var i = 0, l = this._owners && this._owners.length; i < l; i++)
+ for (var i = 0, l = this._owners && this._owners.length; i < l; i++) {
this._owners[i]._changed();
+ }
},
_addOwner: function(color) {
@@ -10093,15 +11908,16 @@
var index = this._owners ? this._owners.indexOf(color) : -1;
if (index != -1) {
this._owners.splice(index, 1);
- if (this._owners.length === 0)
+ if (!this._owners.length)
this._owners = undefined;
}
},
clone: function() {
var stops = [];
- for (var i = 0, l = this._stops.length; i < l; i++)
+ for (var i = 0, l = this._stops.length; i < l; i++) {
stops[i] = this._stops[i].clone();
+ }
return new Gradient(stops, this._radial);
},
@@ -10110,20 +11926,18 @@
},
setStops: function(stops) {
- if (this.stops) {
- for (var i = 0, l = this._stops.length; i < l; i++)
- this._stops[i]._owner = undefined;
- }
- if (stops.length < 2)
+ if (stops.length < 2) {
throw new Error(
'Gradient stop list needs to contain at least two stops.');
- this._stops = GradientStop.readAll(stops, 0, { clone: true });
- for (var i = 0, l = this._stops.length; i < l; i++) {
- var stop = this._stops[i];
- stop._owner = this;
- if (stop._defaultRamp)
- stop.setRampPoint(i / (l - 1));
- }
+ }
+ var _stops = this._stops;
+ if (_stops) {
+ for (var i = 0, l = _stops.length; i < l; i++)
+ _stops[i]._owner = undefined;
+ }
+ _stops = this._stops = GradientStop.readList(stops, 0, { clone: true });
+ for (var i = 0, l = _stops.length; i < l; i++)
+ _stops[i]._owner = this;
this._changed();
},
@@ -10139,13 +11953,17 @@
equals: function(gradient) {
if (gradient === this)
return true;
- if (gradient && this._class === gradient._class
- && this._stops.length === gradient._stops.length) {
- for (var i = 0, l = this._stops.length; i < l; i++) {
- if (!this._stops[i].equals(gradient._stops[i]))
- return false;
- }
- return true;
+ if (gradient && this._class === gradient._class) {
+ var stops1 = this._stops,
+ stops2 = gradient._stops,
+ length = stops1.length;
+ if (length === stops2.length) {
+ for (var i = 0; i < length; i++) {
+ if (!stops1[i].equals(stops2[i]))
+ return false;
+ }
+ return true;
+ }
}
return false;
}
@@ -10155,71 +11973,74 @@
_class: 'GradientStop',
initialize: function GradientStop(arg0, arg1) {
- if (arg0) {
- var color, rampPoint;
- if (arg1 === undefined && Array.isArray(arg0)) {
+ var color = arg0,
+ offset = arg1;
+ if (typeof arg0 === 'object' && arg1 === undefined) {
+ if (Array.isArray(arg0) && typeof arg0[0] !== 'number') {
color = arg0[0];
- rampPoint = arg0[1];
- } else if (arg0.color) {
+ offset = arg0[1];
+ } else if ('color' in arg0 || 'offset' in arg0
+ || 'rampPoint' in arg0) {
color = arg0.color;
- rampPoint = arg0.rampPoint;
- } else {
- color = arg0;
- rampPoint = arg1;
- }
- this.setColor(color);
- this.setRampPoint(rampPoint);
- }
+ offset = arg0.offset || arg0.rampPoint || 0;
+ }
+ }
+ this.setColor(color);
+ this.setOffset(offset);
},
clone: function() {
- return new GradientStop(this._color.clone(), this._rampPoint);
+ return new GradientStop(this._color.clone(), this._offset);
},
_serialize: function(options, dictionary) {
- return Base.serialize([this._color, this._rampPoint], options, true,
- dictionary);
+ var color = this._color,
+ offset = this._offset;
+ return Base.serialize(offset == null ? [color] : [color, offset],
+ options, true, dictionary);
},
_changed: function() {
if (this._owner)
- this._owner._changed(65);
- },
-
- getRampPoint: function() {
- return this._rampPoint;
- },
-
- setRampPoint: function(rampPoint) {
- this._defaultRamp = rampPoint == null;
- this._rampPoint = rampPoint || 0;
+ this._owner._changed(129);
+ },
+
+ getOffset: function() {
+ return this._offset;
+ },
+
+ setOffset: function(offset) {
+ this._offset = offset;
this._changed();
},
+ getRampPoint: '#getOffset',
+ setRampPoint: '#setOffset',
+
getColor: function() {
return this._color;
},
- setColor: function(color) {
- this._color = Color.read(arguments);
- if (this._color === color)
- this._color = color.clone();
- this._color._owner = this;
+ setColor: function() {
+ Color._setOwner(this._color, null);
+ this._color = Color._setOwner(Color.read(arguments, 0), this,
+ 'setColor');
this._changed();
},
equals: function(stop) {
return stop === this || stop && this._class === stop._class
&& this._color.equals(stop._color)
- && this._rampPoint == stop._rampPoint
+ && this._offset == stop._offset
|| false;
}
});
var Style = Base.extend(new function() {
- var defaults = {
- fillColor: undefined,
- strokeColor: undefined,
+ var itemDefaults = {
+ fillColor: null,
+ fillRule: 'nonzero',
+ strokeColor: null,
strokeWidth: 1,
strokeCap: 'butt',
strokeJoin: 'miter',
@@ -10227,43 +12048,55 @@
miterLimit: 10,
dashOffset: 0,
dashArray: [],
- windingRule: 'nonzero',
- shadowColor: undefined,
+ shadowColor: null,
shadowBlur: 0,
shadowOffset: new Point(),
- selectedColor: undefined,
+ selectedColor: null
+ },
+ groupDefaults = Base.set({}, itemDefaults, {
fontFamily: 'sans-serif',
fontWeight: 'normal',
fontSize: 12,
- font: 'sans-serif',
leading: null,
justification: 'left'
- };
-
- var flags = {
- strokeWidth: 97,
- strokeCap: 97,
- strokeJoin: 97,
- strokeScaling: 105,
- miterLimit: 97,
+ }),
+ textDefaults = Base.set({}, groupDefaults, {
+ fillColor: new Color()
+ }),
+ flags = {
+ strokeWidth: 193,
+ strokeCap: 193,
+ strokeJoin: 193,
+ strokeScaling: 201,
+ miterLimit: 193,
fontFamily: 9,
fontWeight: 9,
fontSize: 9,
font: 9,
leading: 9,
justification: 9
+ },
+ item = {
+ beans: true
+ },
+ fields = {
+ _class: 'Style',
+ beans: true,
+
+ initialize: function Style(style, _owner, _project) {
+ this._values = {};
+ this._owner = _owner;
+ this._project = _owner && _owner._project || _project
+ || paper.project;
+ this._defaults = !_owner || _owner instanceof Group ? groupDefaults
+ : _owner instanceof TextItem ? textDefaults
+ : itemDefaults;
+ if (style)
+ this.set(style);
+ }
};
- var item = { beans: true },
- fields = {
- _defaults: defaults,
- _textDefaults: new Base(defaults, {
- fillColor: new Color()
- }),
- beans: true
- };
-
- Base.each(defaults, function(value, key) {
+ Base.each(groupDefaults, function(value, key) {
var isColor = /Color$/.test(key),
isPoint = key === 'shadowOffset',
part = Base.capitalize(key),
@@ -10273,26 +12106,30 @@
fields[set] = function(value) {
var owner = this._owner,
- children = owner && owner._children;
- if (children && children.length > 0
- && !(owner instanceof CompoundPath)) {
+ children = owner && owner._children,
+ applyToChildren = children && children.length > 0
+ && !(owner instanceof CompoundPath);
+ if (applyToChildren) {
for (var i = 0, l = children.length; i < l; i++)
children[i]._style[set](value);
- } else {
+ }
+ if ((key === 'selectedColor' || !applyToChildren)
+ && key in this._defaults) {
var old = this._values[key];
if (old !== value) {
if (isColor) {
- if (old)
- old._owner = undefined;
+ if (old) {
+ Color._setOwner(old, null);
+ old._canvasStyle = null;
+ }
if (value && value.constructor === Color) {
- if (value._owner)
- value = value.clone();
- value._owner = owner;
+ value = Color._setOwner(value, owner,
+ applyToChildren && set);
}
}
this._values[key] = value;
if (owner)
- owner._changed(flag || 65);
+ owner._changed(flag || 129);
}
}
};
@@ -10300,32 +12137,35 @@
fields[get] = function(_dontMerge) {
var owner = this._owner,
children = owner && owner._children,
+ applyToChildren = children && children.length > 0
+ && !(owner instanceof CompoundPath),
value;
- if (!children || children.length === 0 || _dontMerge
- || owner instanceof CompoundPath) {
+ if (applyToChildren && !_dontMerge) {
+ for (var i = 0, l = children.length; i < l; i++) {
+ var childValue = children[i]._style[get]();
+ if (!i) {
+ value = childValue;
+ } else if (!Base.equals(value, childValue)) {
+ return undefined;
+ }
+ }
+ } else if (key in this._defaults) {
var value = this._values[key];
if (value === undefined) {
value = this._defaults[key];
- if (value && value.clone)
+ if (value && value.clone) {
value = value.clone();
+ }
} else {
var ctor = isColor ? Color : isPoint ? Point : null;
if (ctor && !(value && value.constructor === ctor)) {
this._values[key] = value = ctor.read([value], 0,
{ readNull: true, clone: true });
- if (value && isColor)
- value._owner = owner;
- }
- }
- return value;
- }
- for (var i = 0, l = children.length; i < l; i++) {
- var childValue = children[i]._style[get]();
- if (i === 0) {
- value = childValue;
- } else if (!Base.equals(value, childValue)) {
- return undefined;
- }
+ }
+ }
+ }
+ if (value && isColor) {
+ value = Color._setOwner(value, owner, applyToChildren && set);
}
return value;
};
@@ -10339,21 +12179,19 @@
};
});
+ Base.each({
+ Font: 'FontFamily',
+ WindingRule: 'FillRule'
+ }, function(value, key) {
+ var get = 'get' + key,
+ set = 'set' + key;
+ fields[get] = item[get] = '#get' + value;
+ fields[set] = item[set] = '#set' + value;
+ });
+
Item.inject(item);
return fields;
}, {
- _class: 'Style',
-
- initialize: function Style(style, _owner, _project) {
- this._values = {};
- this._owner = _owner;
- this._project = _owner && _owner._project || _project || paper.project;
- if (_owner instanceof TextItem)
- this._defaults = this._textDefaults;
- if (style)
- this.set(style);
- },
-
set: function(style) {
var isStyle = style instanceof Style,
values = isStyle ? style._values : style;
@@ -10369,25 +12207,54 @@
},
equals: function(style) {
+ function compare(style1, style2, secondary) {
+ var values1 = style1._values,
+ values2 = style2._values,
+ defaults2 = style2._defaults;
+ for (var key in values1) {
+ var value1 = values1[key],
+ value2 = values2[key];
+ if (!(secondary && key in values2) && !Base.equals(value1,
+ value2 === undefined ? defaults2[key] : value2))
+ return false;
+ }
+ return true;
+ }
+
return style === this || style && this._class === style._class
- && Base.equals(this._values, style._values)
+ && compare(this, style)
+ && compare(style, this, true)
|| false;
},
+ _dispose: function() {
+ var color;
+ color = this.getFillColor();
+ if (color) color._canvasStyle = null;
+ color = this.getStrokeColor();
+ if (color) color._canvasStyle = null;
+ color = this.getShadowColor();
+ if (color) color._canvasStyle = null;
+ },
+
hasFill: function() {
- return !!this.getFillColor();
+ var color = this.getFillColor();
+ return !!color && color.alpha > 0;
},
hasStroke: function() {
- return !!this.getStrokeColor() && this.getStrokeWidth() > 0;
+ var color = this.getStrokeColor();
+ return !!color && color.alpha > 0 && this.getStrokeWidth() > 0;
},
hasShadow: function() {
- return !!this.getShadowColor() && this.getShadowBlur() > 0;
+ var color = this.getShadowColor();
+ return !!color && color.alpha > 0 && (this.getShadowBlur() > 0
+ || !this.getShadowOffset().isZero());
},
getView: function() {
- return this._project.getView();
+ return this._project._view;
},
getFontStyle: function() {
@@ -10483,8 +12350,12 @@
DomElement.getBounds(el, true));
},
+ isInserted: function(el) {
+ return document.body.contains(el);
+ },
+
getPrefixed: function(el, name) {
- return handlePrefix(el, name);
+ return el && handlePrefix(el, name);
},
setPrefixed: function(el, name, value) {
@@ -10500,20 +12371,30 @@
var DomEvent = {
add: function(el, events) {
- for (var type in events) {
- var func = events[type],
- parts = type.split(/[\s,]+/g);
- for (var i = 0, l = parts.length; i < l; i++)
- el.addEventListener(parts[i], func, false);
+ if (el) {
+ for (var type in events) {
+ var func = events[type],
+ parts = type.split(/[\s,]+/g);
+ for (var i = 0, l = parts.length; i < l; i++) {
+ var name = parts[i];
+ var options = (
+ el === document
+ && (name === 'touchstart' || name === 'touchmove')
+ ) ? { passive: false } : false;
+ el.addEventListener(name, func, options);
+ }
+ }
}
},
remove: function(el, events) {
- for (var type in events) {
- var func = events[type],
- parts = type.split(/[\s,]+/g);
- for (var i = 0, l = parts.length; i < l; i++)
- el.removeEventListener(parts[i], func, false);
+ if (el) {
+ for (var type in events) {
+ var func = events[type],
+ parts = type.split(/[\s,]+/g);
+ for (var i = 0, l = parts.length; i < l; i++)
+ el.removeEventListener(parts[i], func, false);
+ }
}
},
@@ -10540,11 +12421,6 @@
getOffset: function(event, target) {
return DomEvent.getPoint(event).subtract(DomElement.getOffset(
target || DomEvent.getTarget(event)));
- },
-
- stop: function(event) {
- event.stopPropagation();
- event.preventDefault();
}
};
@@ -10552,40 +12428,20 @@
var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
requested = false,
callbacks = [],
- focused = true,
timer;
- DomEvent.add(window, {
- focus: function() {
- focused = true;
- },
- blur: function() {
- focused = false;
- }
- });
-
function handleCallbacks() {
- for (var i = callbacks.length - 1; i >= 0; i--) {
- var entry = callbacks[i],
- func = entry[0],
- el = entry[1];
- if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
- || focused) && DomElement.isInView(el)) {
- callbacks.splice(i, 1);
- func();
- }
- }
- if (nativeRequest) {
- if (callbacks.length) {
- nativeRequest(handleCallbacks);
- } else {
- requested = false;
- }
- }
- }
-
- return function(callback, element) {
- callbacks.push([callback, element]);
+ var functions = callbacks;
+ callbacks = [];
+ for (var i = 0, l = functions.length; i < l; i++)
+ functions[i]();
+ requested = nativeRequest && callbacks.length;
+ if (requested)
+ nativeRequest(handleCallbacks);
+ }
+
+ return function(callback) {
+ callbacks.push(callback);
if (nativeRequest) {
if (!requested) {
nativeRequest(handleCallbacks);
@@ -10601,66 +12457,76 @@
_class: 'View',
initialize: function View(project, element) {
- this._project = project;
- this._scope = project._scope;
- this._element = element;
- var size;
- if (!this._pixelRatio)
- this._pixelRatio = window.devicePixelRatio || 1;
- this._id = element.getAttribute('id');
- if (this._id == null)
- element.setAttribute('id', this._id = 'view-' + View._id++);
- DomEvent.add(element, this._viewEvents);
- var none = 'none';
- DomElement.setPrefixed(element.style, {
- userSelect: none,
- touchAction: none,
- touchCallout: none,
- contentZooming: none,
- userDrag: none,
- tapHighlightColor: 'rgba(0,0,0,0)'
- });
function getSize(name) {
return element[name] || parseInt(element.getAttribute(name), 10);
- };
+ }
function getCanvasSize() {
var size = DomElement.getSize(element);
return size.isNaN() || size.isZero()
? new Size(getSize('width'), getSize('height'))
: size;
- };
-
- if (PaperScope.hasAttribute(element, 'resize')) {
- var that = this;
- DomEvent.add(window, this._windowEvents = {
- resize: function() {
- that.setViewSize(getCanvasSize());
- }
+ }
+
+ var size;
+ if (window && element) {
+ this._id = element.getAttribute('id');
+ if (this._id == null)
+ element.setAttribute('id', this._id = 'view-' + View._id++);
+ DomEvent.add(element, this._viewEvents);
+ var none = 'none';
+ DomElement.setPrefixed(element.style, {
+ userDrag: none,
+ userSelect: none,
+ touchCallout: none,
+ contentZooming: none,
+ tapHighlightColor: 'rgba(0,0,0,0)'
});
- }
- this._setViewSize(size = getCanvasSize());
- if (PaperScope.hasAttribute(element, 'stats')
- && typeof Stats !== 'undefined') {
- this._stats = new Stats();
- var stats = this._stats.domElement,
- style = stats.style,
- offset = DomElement.getOffset(element);
- style.position = 'absolute';
- style.left = offset.x + 'px';
- style.top = offset.y + 'px';
- document.body.appendChild(stats);
- }
+
+ if (PaperScope.hasAttribute(element, 'resize')) {
+ var that = this;
+ DomEvent.add(window, this._windowEvents = {
+ resize: function() {
+ that.setViewSize(getCanvasSize());
+ }
+ });
+ }
+
+ size = getCanvasSize();
+
+ if (PaperScope.hasAttribute(element, 'stats')
+ && typeof Stats !== 'undefined') {
+ this._stats = new Stats();
+ var stats = this._stats.domElement,
+ style = stats.style,
+ offset = DomElement.getOffset(element);
+ style.position = 'absolute';
+ style.left = offset.x + 'px';
+ style.top = offset.y + 'px';
+ document.body.appendChild(stats);
+ }
+ } else {
+ size = new Size(element);
+ element = null;
+ }
+ this._project = project;
+ this._scope = project._scope;
+ this._element = element;
+ if (!this._pixelRatio)
+ this._pixelRatio = window && window.devicePixelRatio || 1;
+ this._setElementSize(size.width, size.height);
+ this._viewSize = size;
View._views.push(this);
View._viewsById[this._id] = this;
- this._viewSize = size;
(this._matrix = new Matrix())._owner = this;
- this._zoom = 1;
if (!View._focused)
View._focused = this;
this._frameItems = {};
this._frameItemCount = 0;
+ this._itemEvents = { native: {}, virtual: {} };
+ this._autoUpdate = !paper.agent.node;
+ this._needsUpdate = false;
},
remove: function() {
@@ -10670,8 +12536,9 @@
View._focused = null;
View._views.splice(View._views.indexOf(this), 1);
delete View._viewsById[this._id];
- if (this._project._view === this)
- this._project._view = null;
+ var project = this._project;
+ if (project._view === this)
+ project._view = null;
DomEvent.remove(this._element, this._viewEvents);
DomEvent.remove(window, this._windowEvents);
this._element = this._project = null;
@@ -10681,42 +12548,79 @@
return true;
},
- _events: {
- onFrame: {
- install: function() {
- this.play();
- },
-
- uninstall: function() {
- this.pause();
- }
- },
-
- onResize: {}
- },
+ _events: Base.each(
+ Item._itemHandlers.concat(['onResize', 'onKeyDown', 'onKeyUp']),
+ function(name) {
+ this[name] = {};
+ }, {
+ onFrame: {
+ install: function() {
+ this.play();
+ },
+
+ uninstall: function() {
+ this.pause();
+ }
+ }
+ }
+ ),
_animate: false,
_time: 0,
_count: 0,
- _requestFrame: function() {
- var that = this;
- DomEvent.requestAnimationFrame(function() {
- that._requested = false;
- if (!that._animate)
- return;
- that._requestFrame();
- that._handleFrame();
- }, this._element);
- this._requested = true;
+ getAutoUpdate: function() {
+ return this._autoUpdate;
+ },
+
+ setAutoUpdate: function(autoUpdate) {
+ this._autoUpdate = autoUpdate;
+ if (autoUpdate)
+ this.requestUpdate();
+ },
+
+ update: function() {
+ },
+
+ draw: function() {
+ this.update();
+ },
+
+ requestUpdate: function() {
+ if (!this._requested) {
+ var that = this;
+ DomEvent.requestAnimationFrame(function() {
+ that._requested = false;
+ if (that._animate) {
+ that.requestUpdate();
+ var element = that._element;
+ if ((!DomElement.getPrefixed(document, 'hidden')
+ || PaperScope.getAttribute(element, 'keepalive')
+ === 'true') && DomElement.isInView(element)) {
+ that._handleFrame();
+ }
+ }
+ if (that._autoUpdate)
+ that.update();
+ });
+ this._requested = true;
+ }
+ },
+
+ play: function() {
+ this._animate = true;
+ this.requestUpdate();
+ },
+
+ pause: function() {
+ this._animate = false;
},
_handleFrame: function() {
paper = this._scope;
var now = Date.now() / 1000,
- delta = this._before ? now - this._before : 0;
- this._before = now;
- this._handlingFrame = true;
+ delta = this._last ? now - this._last : 0;
+ this._last = now;
this.emit('frame', new Base({
delta: delta,
time: this._time += delta,
@@ -10724,8 +12628,6 @@
}));
if (this._stats)
this._stats.update();
- this._handlingFrame = false;
- this.update();
},
_animateItem: function(item, animate) {
@@ -10756,26 +12658,9 @@
}
},
- _update: function() {
- this._project._needsUpdate = true;
- if (this._handlingFrame)
- return;
- if (this._animate) {
- this._handleFrame();
- } else {
- this.update();
- }
- },
-
- _changed: function(flags) {
- if (flags & 1)
- this._project._needsUpdate = true;
- },
-
- _transform: function(matrix) {
- this._matrix.concatenate(matrix);
- this._bounds = null;
- this._update();
+ _changed: function() {
+ this._project._changed(4097);
+ this._bounds = this._decomposed = undefined;
},
getElement: function() {
@@ -10800,20 +12685,23 @@
delta = size.subtract(this._viewSize);
if (delta.isZero())
return;
- this._viewSize.set(size.width, size.height);
- this._setViewSize(size);
- this._bounds = null;
- this.emit('resize', {
- size: size,
- delta: delta
- });
- this._update();
- },
-
- _setViewSize: function(size) {
+ this._setElementSize(size.width, size.height);
+ this._viewSize.set(size);
+ this._changed();
+ this.emit('resize', { size: size, delta: delta });
+ if (this._autoUpdate) {
+ this.update();
+ }
+ },
+
+ _setElementSize: function(width, height) {
var element = this._element;
- element.width = size.width;
- element.height = size.height;
+ if (element) {
+ if (element.width !== width)
+ element.width = width;
+ if (element.height !== height)
+ element.height = height;
+ }
},
getBounds: function() {
@@ -10827,46 +12715,111 @@
return this.getBounds().getSize();
},
+ isVisible: function() {
+ return DomElement.isInView(this._element);
+ },
+
+ isInserted: function() {
+ return DomElement.isInserted(this._element);
+ },
+
+ getPixelSize: function(size) {
+ var element = this._element,
+ pixels;
+ if (element) {
+ var parent = element.parentNode,
+ temp = document.createElement('div');
+ temp.style.fontSize = size;
+ parent.appendChild(temp);
+ pixels = parseFloat(DomElement.getStyles(temp).fontSize);
+ parent.removeChild(temp);
+ } else {
+ pixels = parseFloat(pixels);
+ }
+ return pixels;
+ },
+
+ getTextWidth: function(font, lines) {
+ return 0;
+ }
+}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {
+ var rotate = key === 'rotate';
+ this[key] = function() {
+ var value = (rotate ? Base : Point).read(arguments),
+ center = Point.read(arguments, 0, { readNull: true });
+ return this.transform(new Matrix()[key](value,
+ center || this.getCenter(true)));
+ };
+}, {
+ _decompose: function() {
+ return this._decomposed || (this._decomposed = this._matrix.decompose());
+ },
+
+ translate: function() {
+ var mx = new Matrix();
+ return this.transform(mx.translate.apply(mx, arguments));
+ },
+
getCenter: function() {
return this.getBounds().getCenter();
},
setCenter: function() {
var center = Point.read(arguments);
- this.scrollBy(center.subtract(this.getCenter()));
+ this.translate(this.getCenter().subtract(center));
},
getZoom: function() {
- return this._zoom;
+ var scaling = this._decompose().scaling;
+ return (scaling.x + scaling.y) / 2;
},
setZoom: function(zoom) {
- this._transform(new Matrix().scale(zoom / this._zoom,
+ this.transform(new Matrix().scale(zoom / this.getZoom(),
this.getCenter()));
- this._zoom = zoom;
- },
-
- isVisible: function() {
- return DomElement.isInView(this._element);
+ },
+
+ getRotation: function() {
+ return this._decompose().rotation;
+ },
+
+ setRotation: function(rotation) {
+ var current = this.getRotation();
+ if (current != null && rotation != null) {
+ this.rotate(rotation - current);
+ }
+ },
+
+ getScaling: function() {
+ var scaling = this._decompose().scaling;
+ return new LinkedPoint(scaling.x, scaling.y, this, 'setScaling');
+ },
+
+ setScaling: function() {
+ var current = this.getScaling(),
+ scaling = Point.read(arguments, 0, { clone: true, readNull: true });
+ if (current && scaling) {
+ this.scale(scaling.x / current.x, scaling.y / current.y);
+ }
+ },
+
+ getMatrix: function() {
+ return this._matrix;
+ },
+
+ setMatrix: function() {
+ var matrix = this._matrix;
+ matrix.initialize.apply(matrix, arguments);
+ },
+
+ transform: function(matrix) {
+ this._matrix.append(matrix);
},
scrollBy: function() {
- this._transform(new Matrix().translate(Point.read(arguments).negate()));
- },
-
- play: function() {
- this._animate = true;
- if (!this._requested)
- this._requestFrame();
- },
-
- pause: function() {
- this._animate = false;
- },
-
- draw: function() {
- this.update();
- },
+ this.translate(Point.read(arguments).negate());
+ }
+}), {
projectToView: function() {
return this._matrix._transformPoint(Point.read(arguments));
@@ -10874,7 +12827,11 @@
viewToProject: function() {
return this._matrix._inverseTransform(Point.read(arguments));
- }
+ },
+
+ getEventPoint: function(event) {
+ return this.viewToProject(DomEvent.getOffset(event, this._element));
+ },
}, {
statics: {
@@ -10883,31 +12840,32 @@
_id: 0,
create: function(project, element) {
- if (typeof element === 'string')
+ if (document && typeof element === 'string')
element = document.getElementById(element);
- return new CanvasView(project, element);
- }
- }
-}, new function() {
- var tool,
- prevFocus,
+ var ctor = window ? CanvasView : View;
+ return new ctor(project, element);
+ }
+ }
+},
+new function() {
+ if (!window)
+ return;
+ var prevFocus,
tempFocus,
- dragging = false;
+ dragging = false,
+ mouseDown = false;
function getView(event) {
var target = DomEvent.getTarget(event);
- return target.getAttribute && View._viewsById[target.getAttribute('id')];
- }
-
- function viewToProject(view, event) {
- return view.viewToProject(DomEvent.getOffset(event, view._element));
+ return target.getAttribute && View._viewsById[
+ target.getAttribute('id')];
}
function updateFocus() {
- if (!View._focused || !View._focused.isVisible()) {
+ var view = View._focused;
+ if (!view || !view.isVisible()) {
for (var i = 0, l = View._views.length; i < l; i++) {
- var view = View._views[i];
- if (view && view.isVisible()) {
+ if ((view = View._views[i]).isVisible()) {
View._focused = tempFocus = view;
break;
}
@@ -10915,15 +12873,8 @@
}
}
- function handleMouseMove(view, point, event) {
- view._handleEvent('mousemove', point, event);
- var tool = view._scope.tool;
- if (tool) {
- tool._handleEvent(dragging && tool.responds('mousedrag')
- ? 'mousedrag' : 'mousemove', point, event);
- }
- view.update();
- return tool;
+ function handleMouseMove(view, event, point) {
+ view._handleMouseEvent('mousemove', event, point);
}
var navigator = window.navigator,
@@ -10944,65 +12895,67 @@
}
}
- var viewEvents = {
- 'selectstart dragstart': function(event) {
- if (dragging)
- event.preventDefault();
- }
- };
-
- var docEvents = {
- mouseout: function(event) {
- var view = View._focused,
- target = DomEvent.getRelatedTarget(event);
- if (view && (!target || target.nodeName === 'HTML'))
- handleMouseMove(view, viewToProject(view, event), event);
- },
-
- scroll: updateFocus
- };
+ var viewEvents = {},
+ docEvents = {
+ mouseout: function(event) {
+ var view = View._focused,
+ target = DomEvent.getRelatedTarget(event);
+ if (view && (!target || target.nodeName === 'HTML')) {
+ var offset = DomEvent.getOffset(event, view._element),
+ x = offset.x,
+ abs = Math.abs,
+ ax = abs(x),
+ max = 1 << 25,
+ diff = ax - max;
+ offset.x = abs(diff) < ax ? diff * (x < 0 ? -1 : 1) : x;
+ handleMouseMove(view, event, view.viewToProject(offset));
+ }
+ },
+
+ scroll: updateFocus
+ };
viewEvents[mousedown] = function(event) {
- var view = View._focused = getView(event),
- point = viewToProject(view, event);
- dragging = true;
- view._handleEvent('mousedown', point, event);
- if (tool = view._scope.tool)
- tool._handleEvent('mousedown', point, event);
- view.update();
+ var view = View._focused = getView(event);
+ if (!dragging) {
+ dragging = true;
+ view._handleMouseEvent('mousedown', event);
+ }
};
docEvents[mousemove] = function(event) {
var view = View._focused;
- if (!dragging) {
+ if (!mouseDown) {
var target = getView(event);
if (target) {
- if (view !== target)
- handleMouseMove(view, viewToProject(view, event), event);
- prevFocus = view;
- view = View._focused = tempFocus = target;
+ if (view !== target) {
+ if (view)
+ handleMouseMove(view, event);
+ if (!prevFocus)
+ prevFocus = view;
+ view = View._focused = tempFocus = target;
+ }
} else if (tempFocus && tempFocus === view) {
+ if (prevFocus && !prevFocus.isInserted())
+ prevFocus = null;
view = View._focused = prevFocus;
+ prevFocus = null;
updateFocus();
}
}
- if (view) {
- var point = viewToProject(view, event);
- if (dragging || view.getBounds().contains(point))
- tool = handleMouseMove(view, point, event);
- }
+ if (view)
+ handleMouseMove(view, event);
+ };
+
+ docEvents[mousedown] = function() {
+ mouseDown = true;
};
docEvents[mouseup] = function(event) {
var view = View._focused;
- if (!view || !dragging)
- return;
- var point = viewToProject(view, event);
- dragging = false;
- view._handleEvent('mouseup', point, event);
- if (tool)
- tool._handleEvent('mouseup', point, event);
- view.update();
+ if (view && dragging)
+ view._handleMouseEvent('mouseup', event);
+ mouseDown = dragging = false;
};
DomEvent.add(document, docEvents);
@@ -11011,13 +12964,223 @@
load: updateFocus
});
+ var called = false,
+ prevented = false,
+ fallbacks = {
+ doubleclick: 'click',
+ mousedrag: 'mousemove'
+ },
+ wasInView = false,
+ overView,
+ downPoint,
+ lastPoint,
+ downItem,
+ overItem,
+ dragItem,
+ clickItem,
+ clickTime,
+ dblClick;
+
+ function emitMouseEvent(obj, target, type, event, point, prevPoint,
+ stopItem) {
+ var stopped = false,
+ mouseEvent;
+
+ function emit(obj, type) {
+ if (obj.responds(type)) {
+ if (!mouseEvent) {
+ mouseEvent = new MouseEvent(type, event, point,
+ target || obj,
+ prevPoint ? point.subtract(prevPoint) : null);
+ }
+ if (obj.emit(type, mouseEvent)) {
+ called = true;
+ if (mouseEvent.prevented)
+ prevented = true;
+ if (mouseEvent.stopped)
+ return stopped = true;
+ }
+ } else {
+ var fallback = fallbacks[type];
+ if (fallback)
+ return emit(obj, fallback);
+ }
+ }
+
+ while (obj && obj !== stopItem) {
+ if (emit(obj, type))
+ break;
+ obj = obj._parent;
+ }
+ return stopped;
+ }
+
+ function emitMouseEvents(view, hitItem, type, event, point, prevPoint) {
+ view._project.removeOn(type);
+ prevented = called = false;
+ return (dragItem && emitMouseEvent(dragItem, null, type, event,
+ point, prevPoint)
+ || hitItem && hitItem !== dragItem
+ && !hitItem.isDescendant(dragItem)
+ && emitMouseEvent(hitItem, null, type === 'mousedrag' ?
+ 'mousemove' : type, event, point, prevPoint, dragItem)
+ || emitMouseEvent(view, dragItem || hitItem || view, type, event,
+ point, prevPoint));
+ }
+
+ var itemEventsMap = {
+ mousedown: {
+ mousedown: 1,
+ mousedrag: 1,
+ click: 1,
+ doubleclick: 1
+ },
+ mouseup: {
+ mouseup: 1,
+ mousedrag: 1,
+ click: 1,
+ doubleclick: 1
+ },
+ mousemove: {
+ mousedrag: 1,
+ mousemove: 1,
+ mouseenter: 1,
+ mouseleave: 1
+ }
+ };
+
return {
_viewEvents: viewEvents,
- _handleEvent: function() {},
+ _handleMouseEvent: function(type, event, point) {
+ var itemEvents = this._itemEvents,
+ hitItems = itemEvents.native[type],
+ nativeMove = type === 'mousemove',
+ tool = this._scope.tool,
+ view = this;
+
+ function responds(type) {
+ return itemEvents.virtual[type] || view.responds(type)
+ || tool && tool.responds(type);
+ }
+
+ if (nativeMove && dragging && responds('mousedrag'))
+ type = 'mousedrag';
+ if (!point)
+ point = this.getEventPoint(event);
+
+ var inView = this.getBounds().contains(point),
+ hit = hitItems && inView && view._project.hitTest(point, {
+ tolerance: 0,
+ fill: true,
+ stroke: true
+ }),
+ hitItem = hit && hit.item || null,
+ handle = false,
+ mouse = {};
+ mouse[type.substr(5)] = true;
+
+ if (hitItems && hitItem !== overItem) {
+ if (overItem) {
+ emitMouseEvent(overItem, null, 'mouseleave', event, point);
+ }
+ if (hitItem) {
+ emitMouseEvent(hitItem, null, 'mouseenter', event, point);
+ }
+ overItem = hitItem;
+ }
+ if (wasInView ^ inView) {
+ emitMouseEvent(this, null, inView ? 'mouseenter' : 'mouseleave',
+ event, point);
+ overView = inView ? this : null;
+ handle = true;
+ }
+ if ((inView || mouse.drag) && !point.equals(lastPoint)) {
+ emitMouseEvents(this, hitItem, nativeMove ? type : 'mousemove',
+ event, point, lastPoint);
+ handle = true;
+ }
+ wasInView = inView;
+ if (mouse.down && inView || mouse.up && downPoint) {
+ emitMouseEvents(this, hitItem, type, event, point, downPoint);
+ if (mouse.down) {
+ dblClick = hitItem === clickItem
+ && (Date.now() - clickTime < 300);
+ downItem = clickItem = hitItem;
+ if (!prevented && hitItem) {
+ var item = hitItem;
+ while (item && !item.responds('mousedrag'))
+ item = item._parent;
+ if (item)
+ dragItem = hitItem;
+ }
+ downPoint = point;
+ } else if (mouse.up) {
+ if (!prevented && hitItem === downItem) {
+ clickTime = Date.now();
+ emitMouseEvents(this, hitItem, dblClick ? 'doubleclick'
+ : 'click', event, point, downPoint);
+ dblClick = false;
+ }
+ downItem = dragItem = null;
+ }
+ wasInView = false;
+ handle = true;
+ }
+ lastPoint = point;
+ if (handle && tool) {
+ called = tool._handleMouseEvent(type, event, point, mouse)
+ || called;
+ }
+
+ if (
+ event.cancelable !== false
+ && (called && !mouse.move || mouse.down && responds('mouseup'))
+ ) {
+ event.preventDefault();
+ }
+ },
+
+ _handleKeyEvent: function(type, event, key, character) {
+ var scope = this._scope,
+ tool = scope.tool,
+ keyEvent;
+
+ function emit(obj) {
+ if (obj.responds(type)) {
+ paper = scope;
+ obj.emit(type, keyEvent = keyEvent
+ || new KeyEvent(type, event, key, character));
+ }
+ }
+
+ if (this.isVisible()) {
+ emit(this);
+ if (tool && tool.responds(type))
+ emit(tool);
+ }
+ },
+
+ _countItemEvent: function(type, sign) {
+ var itemEvents = this._itemEvents,
+ native = itemEvents.native,
+ virtual = itemEvents.virtual;
+ for (var key in itemEventsMap) {
+ native[key] = (native[key] || 0)
+ + (itemEventsMap[key][type] || 0) * sign;
+ }
+ virtual[type] = (virtual[type] || 0) + sign;
+ },
statics: {
- updateFocus: updateFocus
+ updateFocus: updateFocus,
+
+ _resetState: function() {
+ dragging = mouseDown = called = wasInView = false;
+ prevFocus = tempFocus = overView = downPoint = lastPoint =
+ downItem = overItem = dragItem = clickItem = clickTime =
+ dblClick = null;
+ }
}
};
});
@@ -11026,53 +13189,58 @@
_class: 'CanvasView',
initialize: function CanvasView(project, canvas) {
- if (!(canvas instanceof HTMLCanvasElement)) {
+ if (!(canvas instanceof window.HTMLCanvasElement)) {
var size = Size.read(arguments, 1);
if (size.isZero())
throw new Error(
'Cannot create CanvasView with the provided argument: '
- + [].slice.call(arguments, 1));
+ + Base.slice(arguments, 1));
canvas = CanvasProvider.getCanvas(size);
}
- this._context = canvas.getContext('2d');
- this._eventCounters = {};
+ var ctx = this._context = canvas.getContext('2d');
+ ctx.save();
this._pixelRatio = 1;
if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) {
var deviceRatio = window.devicePixelRatio || 1,
- backingStoreRatio = DomElement.getPrefixed(this._context,
+ backingStoreRatio = DomElement.getPrefixed(ctx,
'backingStorePixelRatio') || 1;
this._pixelRatio = deviceRatio / backingStoreRatio;
}
View.call(this, project, canvas);
- },
-
- _setViewSize: function(size) {
- var element = this._element,
- pixelRatio = this._pixelRatio,
- width = size.width,
- height = size.height;
- element.width = width * pixelRatio;
- element.height = height * pixelRatio;
+ this._needsUpdate = true;
+ },
+
+ remove: function remove() {
+ this._context.restore();
+ return remove.base.call(this);
+ },
+
+ _setElementSize: function _setElementSize(width, height) {
+ var pixelRatio = this._pixelRatio;
+ _setElementSize.base.call(this, width * pixelRatio, height * pixelRatio);
if (pixelRatio !== 1) {
+ var element = this._element,
+ ctx = this._context;
if (!PaperScope.hasAttribute(element, 'resize')) {
var style = element.style;
style.width = width + 'px';
style.height = height + 'px';
}
- this._context.scale(pixelRatio, pixelRatio);
- }
- },
-
- getPixelSize: function(size) {
- var browser = paper.browser,
+ ctx.restore();
+ ctx.save();
+ ctx.scale(pixelRatio, pixelRatio);
+ }
+ },
+
+ getContext: function() {
+ return this._context;
+ },
+
+ getPixelSize: function getPixelSize(size) {
+ var agent = paper.agent,
pixels;
- if (browser && browser.firefox) {
- var parent = this._element.parentNode,
- temp = document.createElement('div');
- temp.style.fontSize = size;
- parent.appendChild(temp);
- pixels = parseFloat(DomElement.getStyles(temp).fontSize);
- parent.removeChild(temp);
+ if (agent && agent.firefox) {
+ pixels = getPixelSize.base.call(this, size);
} else {
var ctx = this._context,
prevFont = ctx.font;
@@ -11094,119 +13262,18 @@
return width;
},
- update: function(force) {
- var project = this._project;
- if (!project || !force && !project._needsUpdate)
+ update: function() {
+ if (!this._needsUpdate)
return false;
- var ctx = this._context,
+ var project = this._project,
+ ctx = this._context,
size = this._viewSize;
ctx.clearRect(0, 0, size.width + 1, size.height + 1);
- project.draw(ctx, this._matrix, this._pixelRatio);
- project._needsUpdate = false;
+ if (project)
+ project.draw(ctx, this._matrix, this._pixelRatio);
+ this._needsUpdate = false;
return true;
}
-}, new function() {
-
- var downPoint,
- lastPoint,
- overPoint,
- downItem,
- lastItem,
- overItem,
- dragItem,
- dblClick,
- clickTime;
-
- function callEvent(view, type, event, point, target, lastPoint) {
- var item = target,
- mouseEvent;
-
- function call(obj) {
- if (obj.responds(type)) {
- if (!mouseEvent) {
- mouseEvent = new MouseEvent(type, event, point, target,
- lastPoint ? point.subtract(lastPoint) : null);
- }
- if (obj.emit(type, mouseEvent) && mouseEvent.isStopped) {
- event.preventDefault();
- return true;
- }
- }
- }
-
- while (item) {
- if (call(item))
- return true;
- item = item.getParent();
- }
- if (call(view))
- return true;
- return false;
- }
-
- return {
- _handleEvent: function(type, point, event) {
- if (!this._eventCounters[type])
- return;
- var project = this._project,
- hit = project.hitTest(point, {
- tolerance: 0,
- fill: true,
- stroke: true
- }),
- item = hit && hit.item,
- stopped = false;
- switch (type) {
- case 'mousedown':
- stopped = callEvent(this, type, event, point, item);
- dblClick = lastItem == item && (Date.now() - clickTime < 300);
- downItem = lastItem = item;
- downPoint = lastPoint = overPoint = point;
- dragItem = !stopped && item;
- while (dragItem && !dragItem.responds('mousedrag'))
- dragItem = dragItem._parent;
- break;
- case 'mouseup':
- stopped = callEvent(this, type, event, point, item, downPoint);
- if (dragItem) {
- if (lastPoint && !lastPoint.equals(point))
- callEvent(this, 'mousedrag', event, point, dragItem,
- lastPoint);
- if (item !== dragItem) {
- overPoint = point;
- callEvent(this, 'mousemove', event, point, item,
- overPoint);
- }
- }
- if (!stopped && item && item === downItem) {
- clickTime = Date.now();
- callEvent(this, dblClick && downItem.responds('doubleclick')
- ? 'doubleclick' : 'click', event, downPoint, item);
- dblClick = false;
- }
- downItem = dragItem = null;
- break;
- case 'mousemove':
- if (dragItem)
- stopped = callEvent(this, 'mousedrag', event, point,
- dragItem, lastPoint);
- if (!stopped) {
- if (item !== overItem)
- overPoint = point;
- stopped = callEvent(this, type, event, point, item,
- overPoint);
- }
- lastPoint = overPoint = point;
- if (item !== overItem) {
- callEvent(this, 'mouseleave', event, point, overItem);
- overItem = item;
- callEvent(this, 'mouseenter', event, point, item);
- }
- break;
- }
- return stopped;
- }
- };
});
var Event = Base.extend({
@@ -11214,18 +13281,19 @@
initialize: function Event(event) {
this.event = event;
- },
-
- isPrevented: false,
- isStopped: false,
+ this.type = event && event.type;
+ },
+
+ prevented: false,
+ stopped: false,
preventDefault: function() {
- this.isPrevented = true;
+ this.prevented = true;
this.event.preventDefault();
},
stopPropagation: function() {
- this.isStopped = true;
+ this.stopped = true;
this.event.stopPropagation();
},
@@ -11234,6 +13302,10 @@
this.preventDefault();
},
+ getTimeStamp: function() {
+ return this.event.timeStamp;
+ },
+
getModifiers: function() {
return Key.modifiers;
}
@@ -11242,9 +13314,9 @@
var KeyEvent = Event.extend({
_class: 'KeyEvent',
- initialize: function KeyEvent(down, key, character, event) {
- Event.call(this, event);
- this.type = down ? 'keydown' : 'keyup';
+ initialize: function KeyEvent(type, event, key, character) {
+ this.type = type;
+ this.event = event;
this.key = key;
this.character = character;
},
@@ -11259,120 +13331,134 @@
});
var Key = new function() {
-
- var specialKeys = {
- 8: 'backspace',
- 9: 'tab',
- 13: 'enter',
- 16: 'shift',
- 17: 'control',
- 18: 'option',
- 19: 'pause',
- 20: 'caps-lock',
- 27: 'escape',
- 32: 'space',
- 35: 'end',
- 36: 'home',
- 37: 'left',
- 38: 'up',
- 39: 'right',
- 40: 'down',
- 46: 'delete',
- 91: 'command',
- 93: 'command',
- 224: 'command'
- },
-
- specialChars = {
- 9: true,
- 13: true,
- 32: true
- },
-
- modifiers = new Base({
- shift: false,
- control: false,
- option: false,
- command: false,
- capsLock: false,
- space: false
- }),
-
- charCodeMap = {},
- keyMap = {},
- commandFixMap,
- downCode;
-
- function handleKey(down, keyCode, charCode, event) {
- var character = charCode ? String.fromCharCode(charCode) : '',
- specialKey = specialKeys[keyCode],
- key = specialKey || character.toLowerCase(),
- type = down ? 'keydown' : 'keyup',
+ var keyLookup = {
+ '\t': 'tab',
+ ' ': 'space',
+ '\b': 'backspace',
+ '\x7f': 'delete',
+ 'Spacebar': 'space',
+ 'Del': 'delete',
+ 'Win': 'meta',
+ 'Esc': 'escape'
+ },
+
+ charLookup = {
+ 'tab': '\t',
+ 'space': ' ',
+ 'enter': '\r'
+ },
+
+ keyMap = {},
+ charMap = {},
+ metaFixMap,
+ downKey,
+
+ modifiers = new Base({
+ shift: false,
+ control: false,
+ alt: false,
+ meta: false,
+ capsLock: false,
+ space: false
+ }).inject({
+ option: {
+ get: function() {
+ return this.alt;
+ }
+ },
+
+ command: {
+ get: function() {
+ var agent = paper && paper.agent;
+ return agent && agent.mac ? this.meta : this.control;
+ }
+ }
+ });
+
+ function getKey(event) {
+ var key = event.key || event.keyIdentifier;
+ key = /^U\+/.test(key)
+ ? String.fromCharCode(parseInt(key.substr(2), 16))
+ : /^Arrow[A-Z]/.test(key) ? key.substr(5)
+ : key === 'Unidentified' || key === undefined
+ ? String.fromCharCode(event.keyCode)
+ : key;
+ return keyLookup[key] ||
+ (key.length > 1 ? Base.hyphenate(key) : key.toLowerCase());
+ }
+
+ function handleKey(down, key, character, event) {
+ var type = down ? 'keydown' : 'keyup',
view = View._focused,
- scope = view && view.isVisible() && view._scope,
- tool = scope && scope.tool,
name;
keyMap[key] = down;
if (down) {
- charCodeMap[keyCode] = charCode;
+ charMap[key] = character;
} else {
- delete charCodeMap[keyCode];
- }
- if (specialKey && (name = Base.camelize(specialKey)) in modifiers) {
+ delete charMap[key];
+ }
+ if (key.length > 1 && (name = Base.camelize(key)) in modifiers) {
modifiers[name] = down;
- var browser = paper.browser;
- if (name === 'command' && browser && browser.mac) {
+ var agent = paper && paper.agent;
+ if (name === 'meta' && agent && agent.mac) {
if (down) {
- commandFixMap = {};
+ metaFixMap = {};
} else {
- for (var code in commandFixMap) {
- if (code in charCodeMap)
- handleKey(false, code, commandFixMap[code], event);
- }
- commandFixMap = null;
- }
- }
- } else if (down && commandFixMap) {
- commandFixMap[keyCode] = charCode;
- }
- if (tool && tool.responds(type)) {
- paper = scope;
- tool.emit(type, new KeyEvent(down, key, character, event));
- if (view)
- view.update();
+ for (var k in metaFixMap) {
+ if (k in charMap)
+ handleKey(false, k, metaFixMap[k], event);
+ }
+ metaFixMap = null;
+ }
+ }
+ } else if (down && metaFixMap) {
+ metaFixMap[key] = character;
+ }
+ if (view) {
+ view._handleKeyEvent(down ? 'keydown' : 'keyup', event, key,
+ character);
}
}
DomEvent.add(document, {
keydown: function(event) {
- var code = event.which || event.keyCode;
- if (code in specialKeys || modifiers.command) {
- handleKey(true, code,
- code in specialChars || modifiers.command ? code : 0,
- event);
- } else {
- downCode = code;
+ var key = getKey(event),
+ agent = paper && paper.agent;
+ if (key.length > 1 || agent && (agent.chrome && (event.altKey
+ || agent.mac && event.metaKey
+ || !agent.mac && event.ctrlKey))) {
+ handleKey(true, key,
+ charLookup[key] || (key.length > 1 ? '' : key), event);
+ } else {
+ downKey = key;
}
},
keypress: function(event) {
- if (downCode != null) {
- handleKey(true, downCode, event.which || event.keyCode, event);
- downCode = null;
+ if (downKey) {
+ var key = getKey(event),
+ code = event.charCode,
+ character = code >= 32 ? String.fromCharCode(code)
+ : key.length > 1 ? '' : key;
+ if (key !== downKey) {
+ key = character.toLowerCase();
+ }
+ handleKey(true, key, character, event);
+ downKey = null;
}
},
keyup: function(event) {
- var code = event.which || event.keyCode;
- if (code in charCodeMap)
- handleKey(false, code, charCodeMap[code], event);
+ var key = getKey(event);
+ if (key in charMap)
+ handleKey(false, key, charMap[key], event);
}
});
DomEvent.add(window, {
blur: function(event) {
- for (var code in charCodeMap)
- handleKey(false, code, charCodeMap[code], event);
+ for (var key in charMap)
+ handleKey(false, key, charMap[key], event);
}
});
@@ -11389,8 +13475,8 @@
_class: 'MouseEvent',
initialize: function MouseEvent(type, event, point, target, delta) {
- Event.call(this, event);
this.type = type;
+ this.event = event;
this.point = point;
this.target = target;
this.delta = delta;
@@ -11466,9 +13552,8 @@
},
getCount: function() {
- return /^mouse(down|up)$/.test(this.type)
- ? this.tool._downCount
- : this.tool._count;
+ return this.tool[/^mouse(down|up)$/.test(this.type)
+ ? '_downCount' : '_moveCount'];
},
setCount: function(count) {
@@ -11509,16 +13594,15 @@
_class: 'Tool',
_list: 'tools',
_reference: 'tool',
- _events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
- 'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
- 'onKeyDown', 'onKeyUp' ],
+ _events: ['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
+ 'onActivate', 'onDeactivate', 'onEditOptions', 'onKeyDown',
+ 'onKeyUp'],
initialize: function Tool(props) {
PaperScopeItem.call(this);
- this._firstMove = true;
- this._count = 0;
- this._downCount = 0;
- this._set(props);
+ this._moveCount = -1;
+ this._downCount = -1;
+ this.set(props);
},
getMinDistance: function() {
@@ -11551,131 +13635,334 @@
},
setFixedDistance: function(distance) {
- this._minDistance = distance;
- this._maxDistance = distance;
- },
-
- _updateEvent: function(type, point, minDistance, maxDistance, start,
- needsChange, matchMaxDistance) {
- if (!start) {
- if (minDistance != null || maxDistance != null) {
- var minDist = minDistance != null ? minDistance : 0,
- vector = point.subtract(this._point),
- distance = vector.getLength();
- if (distance < minDist)
+ this._minDistance = this._maxDistance = distance;
+ },
+
+ _handleMouseEvent: function(type, event, point, mouse) {
+ paper = this._scope;
+ if (mouse.drag && !this.responds(type))
+ type = 'mousemove';
+ var move = mouse.move || mouse.drag,
+ responds = this.responds(type),
+ minDistance = this.minDistance,
+ maxDistance = this.maxDistance,
+ called = false,
+ tool = this;
+ function update(minDistance, maxDistance) {
+ var pt = point,
+ toolPoint = move ? tool._point : (tool._downPoint || pt);
+ if (move) {
+ if (tool._moveCount >= 0 && pt.equals(toolPoint)) {
return false;
- if (maxDistance != null && maxDistance != 0) {
- if (distance > maxDistance) {
- point = this._point.add(vector.normalize(maxDistance));
- } else if (matchMaxDistance) {
+ }
+ if (toolPoint && (minDistance != null || maxDistance != null)) {
+ var vector = pt.subtract(toolPoint),
+ distance = vector.getLength();
+ if (distance < (minDistance || 0))
return false;
- }
- }
- }
- if (needsChange && point.equals(this._point))
- return false;
- }
- this._lastPoint = start && type == 'mousemove' ? point : this._point;
- this._point = point;
- switch (type) {
- case 'mousedown':
- this._lastPoint = this._downPoint;
- this._downPoint = this._point;
- this._downCount++;
- break;
- case 'mouseup':
- this._lastPoint = this._downPoint;
- break;
- }
- this._count = start ? 0 : this._count + 1;
- return true;
- },
-
- _fireEvent: function(type, event) {
- var sets = paper.project._removeSets;
- if (sets) {
- if (type === 'mouseup')
- sets.mousedrag = null;
- var set = sets[type];
- if (set) {
- for (var id in set) {
- var item = set[id];
- for (var key in sets) {
- var other = sets[key];
- if (other && other != set)
- delete other[item._id];
- }
- item.remove();
- }
- sets[type] = null;
- }
- }
- return this.responds(type)
- && this.emit(type, new ToolEvent(this, type, event));
- },
-
- _handleEvent: function(type, point, event) {
- paper = this._scope;
- var called = false;
- switch (type) {
- case 'mousedown':
- this._updateEvent(type, point, null, null, true, false, false);
- called = this._fireEvent(type, event);
- break;
- case 'mousedrag':
- var needsChange = false,
- matchMaxDistance = false;
- while (this._updateEvent(type, point, this.minDistance,
- this.maxDistance, false, needsChange, matchMaxDistance)) {
- called = this._fireEvent(type, event) || called;
- needsChange = true;
- matchMaxDistance = true;
- }
- break;
- case 'mouseup':
- if (!point.equals(this._point)
- && this._updateEvent('mousedrag', point, this.minDistance,
- this.maxDistance, false, false, false)) {
- called = this._fireEvent('mousedrag', event);
- }
- this._updateEvent(type, point, null, this.maxDistance, false,
- false, false);
- called = this._fireEvent(type, event) || called;
- this._updateEvent(type, point, null, null, true, false, false);
- this._firstMove = true;
- break;
- case 'mousemove':
- while (this._updateEvent(type, point, this.minDistance,
- this.maxDistance, this._firstMove, true, false)) {
- called = this._fireEvent(type, event) || called;
- this._firstMove = false;
- }
- break;
- }
- if (called)
- event.preventDefault();
+ if (maxDistance) {
+ pt = toolPoint.add(vector.normalize(
+ Math.min(distance, maxDistance)));
+ }
+ }
+ tool._moveCount++;
+ }
+ tool._point = pt;
+ tool._lastPoint = toolPoint || pt;
+ if (mouse.down) {
+ tool._moveCount = -1;
+ tool._downPoint = pt;
+ tool._downCount++;
+ }
+ return true;
+ }
+
+ function emit() {
+ if (responds) {
+ called = tool.emit(type, new ToolEvent(tool, type, event))
+ || called;
+ }
+ }
+
+ if (mouse.down) {
+ update();
+ emit();
+ } else if (mouse.up) {
+ update(null, maxDistance);
+ emit();
+ } else if (responds) {
+ while (update(minDistance, maxDistance))
+ emit();
+ }
return called;
}
});
+var Tween = Base.extend(Emitter, {
+ _class: 'Tween',
+
+ statics: {
+ easings: {
+ linear: function(t) {
+ return t;
+ },
+
+ easeInQuad: function(t) {
+ return t * t;
+ },
+
+ easeOutQuad: function(t) {
+ return t * (2 - t);
+ },
+
+ easeInOutQuad: function(t) {
+ return t < 0.5
+ ? 2 * t * t
+ : -1 + 2 * (2 - t) * t;
+ },
+
+ easeInCubic: function(t) {
+ return t * t * t;
+ },
+
+ easeOutCubic: function(t) {
+ return --t * t * t + 1;
+ },
+
+ easeInOutCubic: function(t) {
+ return t < 0.5
+ ? 4 * t * t * t
+ : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
+ },
+
+ easeInQuart: function(t) {
+ return t * t * t * t;
+ },
+
+ easeOutQuart: function(t) {
+ return 1 - (--t) * t * t * t;
+ },
+
+ easeInOutQuart: function(t) {
+ return t < 0.5
+ ? 8 * t * t * t * t
+ : 1 - 8 * (--t) * t * t * t;
+ },
+
+ easeInQuint: function(t) {
+ return t * t * t * t * t;
+ },
+
+ easeOutQuint: function(t) {
+ return 1 + --t * t * t * t * t;
+ },
+
+ easeInOutQuint: function(t) {
+ return t < 0.5
+ ? 16 * t * t * t * t * t
+ : 1 + 16 * (--t) * t * t * t * t;
+ }
+ }
+ },
+
+ initialize: function Tween(object, from, to, duration, easing, start) {
+ this.object = object;
+ var type = typeof easing;
+ var isFunction = type === 'function';
+ this.type = isFunction
+ ? type
+ : type === 'string'
+ ? easing
+ : 'linear';
+ this.easing = isFunction ? easing : Tween.easings[this.type];
+ this.duration = duration;
+ this.running = false;
+
+ this._then = null;
+ this._startTime = null;
+ var state = from || to;
+ this._keys = state ? Object.keys(state) : [];
+ this._parsedKeys = this._parseKeys(this._keys);
+ this._from = state && this._getState(from);
+ this._to = state && this._getState(to);
+ if (start !== false) {
+ this.start();
+ }
+ },
+
+ then: function(then) {
+ this._then = then;
+ return this;
+ },
+
+ start: function() {
+ this._startTime = null;
+ this.running = true;
+ return this;
+ },
+
+ stop: function() {
+ this.running = false;
+ return this;
+ },
+
+ update: function(progress) {
+ if (this.running) {
+ if (progress > 1) {
+ progress = 1;
+ this.running = false;
+ }
+
+ var factor = this.easing(progress),
+ keys = this._keys,
+ getValue = function(value) {
+ return typeof value === 'function'
+ ? value(factor, progress)
+ : value;
+ };
+ for (var i = 0, l = keys && keys.length; i < l; i++) {
+ var key = keys[i],
+ from = getValue(this._from[key]),
+ to = getValue(this._to[key]),
+ value = (from && to && from.__add && to.__add)
+ ? to.__subtract(from).__multiply(factor).__add(from)
+ : ((to - from) * factor) + from;
+ this._setProperty(this._parsedKeys[key], value);
+ }
+
+ if (!this.running && this._then) {
+ this._then(this.object);
+ }
+ if (this.responds('update')) {
+ this.emit('update', new Base({
+ progress: progress,
+ factor: factor
+ }));
+ }
+ }
+ return this;
+ },
+
+ _events: {
+ onUpdate: {}
+ },
+
+ _handleFrame: function(time) {
+ var startTime = this._startTime,
+ progress = startTime
+ ? (time - startTime) / this.duration
+ : 0;
+ if (!startTime) {
+ this._startTime = time;
+ }
+ this.update(progress);
+ },
+
+ _getState: function(state) {
+ var keys = this._keys,
+ result = {};
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i],
+ path = this._parsedKeys[key],
+ current = this._getProperty(path),
+ value;
+ if (state) {
+ var resolved = this._resolveValue(current, state[key]);
+ this._setProperty(path, resolved);
+ value = this._getProperty(path);
+ value = value && value.clone ? value.clone() : value;
+ this._setProperty(path, current);
+ } else {
+ value = current && current.clone ? current.clone() : current;
+ }
+ result[key] = value;
+ }
+ return result;
+ },
+
+ _resolveValue: function(current, value) {
+ if (value) {
+ if (Array.isArray(value) && value.length === 2) {
+ var operator = value[0];
+ return (
+ operator &&
+ operator.match &&
+ operator.match(/^[+\-\*\/]=/)
+ )
+ ? this._calculate(current, operator[0], value[1])
+ : value;
+ } else if (typeof value === 'string') {
+ var match = value.match(/^[+\-*/]=(.*)/);
+ if (match) {
+ var parsed = JSON.parse(match[1].replace(
+ /(['"])?([a-zA-Z0-9_]+)(['"])?:/g,
+ '"$2": '
+ ));
+ return this._calculate(current, value[0], parsed);
+ }
+ }
+ }
+ return value;
+ },
+
+ _calculate: function(left, operator, right) {
+ return paper.PaperScript.calculateBinary(left, operator, right);
+ },
+
+ _parseKeys: function(keys) {
+ var parsed = {};
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i],
+ path = key
+ .replace(/\.([^.]*)/g, '/$1')
+ .replace(/\[['"]?([^'"\]]*)['"]?\]/g, '/$1');
+ parsed[key] = path.split('/');
+ }
+ return parsed;
+ },
+
+ _getProperty: function(path, offset) {
+ var obj = this.object;
+ for (var i = 0, l = path.length - (offset || 0); i < l && obj; i++) {
+ obj = obj[path[i]];
+ }
+ return obj;
+ },
+
+ _setProperty: function(path, value) {
+ var dest = this._getProperty(path, 1);
+ if (dest) {
+ dest[path[path.length - 1]] = value;
+ }
+ }
+});
+
var Http = {
- request: function(method, url, callback, async) {
- async = (async === undefined) ? true : async;
- var xhr = new (window.ActiveXObject || XMLHttpRequest)(
- 'Microsoft.XMLHTTP');
- xhr.open(method.toUpperCase(), url, async);
- if ('overrideMimeType' in xhr)
- xhr.overrideMimeType('text/plain');
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- var status = xhr.status;
- if (status === 0 || status === 200) {
- callback.call(xhr, xhr.responseText);
- } else {
- throw new Error('Could not load ' + url + ' (Error '
- + status + ')');
- }
+ request: function(options) {
+ var xhr = new self.XMLHttpRequest();
+ xhr.open((options.method || 'get').toUpperCase(), options.url,
+ Base.pick(options.async, true));
+ if (options.mimeType)
+ xhr.overrideMimeType(options.mimeType);
+ xhr.onload = function() {
+ var status = xhr.status;
+ if (status === 0 || status === 200) {
+ if (options.onLoad) {
+ options.onLoad.call(xhr, xhr.responseText);
+ }
+ } else {
+ xhr.onerror();
+ }
+ };
+ xhr.onerror = function() {
+ var status = xhr.status,
+ message = 'Could not load "' + options.url + '" (Status: '
+ + status + ')';
+ if (options.onError) {
+ options.onError(message, status);
+ } else {
+ throw new Error(message);
}
};
return xhr.send(null);
@@ -11686,6 +13973,8 @@
canvases: [],
getCanvas: function(width, height) {
+ if (!window)
+ return null;
var canvas,
clear = true;
if (typeof width === 'object') {
@@ -11696,8 +13985,13 @@
canvas = this.canvases.pop();
} else {
canvas = document.createElement('canvas');
+ clear = false;
}
var ctx = canvas.getContext('2d');
+ if (!ctx) {
+ throw new Error('Canvas ' + canvas +
+ ' is unable to provide a 2D context.');
+ }
if (canvas.width === width && canvas.height === height) {
if (clear)
ctx.clearRect(0, 0, width + 1, height + 1);
@@ -11710,13 +14004,16 @@
},
getContext: function(width, height) {
- return this.getCanvas(width, height).getContext('2d');
+ var canvas = this.getCanvas(width, height);
+ return canvas ? canvas.getContext('2d') : null;
},
release: function(obj) {
- var canvas = obj.canvas ? obj.canvas : obj;
- canvas.getContext('2d').restore();
- this.canvases.push(canvas);
+ var canvas = obj && obj.canvas ? obj.canvas : obj;
+ if (canvas && canvas.getContext) {
+ canvas.getContext('2d').restore();
+ this.canvases.push(canvas);
+ }
}
};
@@ -11907,24 +14204,27 @@
}, {});
var ctx = CanvasProvider.getContext(1, 1);
- Base.each(modes, function(func, mode) {
- var darken = mode === 'darken',
- ok = false;
- ctx.save();
- try {
- ctx.fillStyle = darken ? '#300' : '#a00';
- ctx.fillRect(0, 0, 1, 1);
- ctx.globalCompositeOperation = mode;
- if (ctx.globalCompositeOperation === mode) {
- ctx.fillStyle = darken ? '#a00' : '#300';
+ if (ctx) {
+ Base.each(modes, function(func, mode) {
+ var darken = mode === 'darken',
+ ok = false;
+ ctx.save();
+ try {
+ ctx.fillStyle = darken ? '#300' : '#a00';
ctx.fillRect(0, 0, 1, 1);
- ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken ? 170 : 51;
- }
- } catch (e) {}
- ctx.restore();
- nativeModes[mode] = ok;
- });
- CanvasProvider.release(ctx);
+ ctx.globalCompositeOperation = mode;
+ if (ctx.globalCompositeOperation === mode) {
+ ctx.fillStyle = darken ? '#a00' : '#300';
+ ctx.fillRect(0, 0, 1, 1);
+ ok = ctx.getImageData(0, 0, 1, 1).data[0] !== darken
+ ? 170 : 51;
+ }
+ } catch (e) {}
+ ctx.restore();
+ nativeModes[mode] = ok;
+ });
+ CanvasProvider.release(ctx);
+ }
this.process = function(mode, srcContext, dstContext, alpha, offset) {
var srcCanvas = srcContext.canvas,
@@ -11968,8 +14268,58 @@
};
};
-var SVGStyles = Base.each({
+var SvgElement = new function() {
+ var svg = 'http://www.w3.org/2000/svg',
+ xmlns = 'http://www.w3.org/2000/xmlns',
+ xlink = 'http://www.w3.org/1999/xlink',
+ attributeNamespace = {
+ href: xlink,
+ xlink: xmlns,
+ xmlns: xmlns + '/',
+ 'xmlns:xlink': xmlns + '/'
+ };
+
+ function create(tag, attributes, formatter) {
+ return set(document.createElementNS(svg, tag), attributes, formatter);
+ }
+
+ function get(node, name) {
+ var namespace = attributeNamespace[name],
+ value = namespace
+ ? node.getAttributeNS(namespace, name)
+ : node.getAttribute(name);
+ return value === 'null' ? null : value;
+ }
+
+ function set(node, attributes, formatter) {
+ for (var name in attributes) {
+ var value = attributes[name],
+ namespace = attributeNamespace[name];
+ if (typeof value === 'number' && formatter)
+ value = formatter.number(value);
+ if (namespace) {
+ node.setAttributeNS(namespace, name, value);
+ } else {
+ node.setAttribute(name, value);
+ }
+ }
+ return node;
+ }
+
+ return {
+ svg: svg,
+ xmlns: xmlns,
+ xlink: xlink,
+
+ create: create,
+ get: get,
+ set: set
+ };
+};
+
+var SvgStyles = Base.each({
fillColor: ['fill', 'color'],
+ fillRule: ['fill-rule', 'string'],
strokeColor: ['stroke', 'color'],
strokeWidth: ['stroke-width', 'number'],
strokeCap: ['stroke-linecap', 'string'],
@@ -11995,7 +14345,7 @@
right: 'end'
}],
opacity: ['opacity', 'number'],
- blendMode: ['mix-blend-mode', 'string']
+ blendMode: ['mix-blend-mode', 'style']
}, function(entry, key) {
var part = Base.capitalize(key),
lookup = entry[2];
@@ -12013,57 +14363,42 @@
};
}, {});
-var SVGNamespaces = {
- href: 'http://www.w3.org/1999/xlink',
- xlink: 'http://www.w3.org/2000/xmlns'
-};
-
new function() {
var formatter;
- function setAttributes(node, attrs) {
- for (var key in attrs) {
- var val = attrs[key],
- namespace = SVGNamespaces[key];
- if (typeof val === 'number')
- val = formatter.number(val);
- if (namespace) {
- node.setAttributeNS(namespace, key, val);
- } else {
- node.setAttribute(key, val);
- }
- }
- return node;
- }
-
- function createElement(tag, attrs) {
- return setAttributes(
- document.createElementNS('http://www.w3.org/2000/svg', tag), attrs);
- }
-
function getTransform(matrix, coordinates, center) {
var attrs = new Base(),
trans = matrix.getTranslation();
if (coordinates) {
- matrix = matrix.shiftless();
- var point = matrix._inverseTransform(trans);
+ var point;
+ if (matrix.isInvertible()) {
+ matrix = matrix._shiftless();
+ point = matrix._inverseTransform(trans);
+ trans = null;
+ } else {
+ point = new Point();
+ }
attrs[center ? 'cx' : 'x'] = point.x;
attrs[center ? 'cy' : 'y'] = point.y;
- trans = null;
}
if (!matrix.isIdentity()) {
var decomposed = matrix.decompose();
- if (decomposed && !decomposed.shearing) {
+ if (decomposed) {
var parts = [],
angle = decomposed.rotation,
- scale = decomposed.scaling;
+ scale = decomposed.scaling,
+ skew = decomposed.skewing;
if (trans && !trans.isZero())
parts.push('translate(' + formatter.point(trans) + ')');
+ if (angle)
+ parts.push('rotate(' + formatter.number(angle) + ')');
if (!Numerical.isZero(scale.x - 1)
|| !Numerical.isZero(scale.y - 1))
parts.push('scale(' + formatter.point(scale) +')');
- if (angle)
- parts.push('rotate(' + formatter.number(angle) + ')');
+ if (skew.x)
+ parts.push('skewX(' + formatter.number(skew.x) + ')');
+ if (skew.y)
+ parts.push('skewY(' + formatter.number(skew.y) + ')');
attrs.transform = parts.join(' ');
} else {
attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
@@ -12075,16 +14410,16 @@
function exportGroup(item, options) {
var attrs = getTransform(item._matrix),
children = item._children;
- var node = createElement('g', attrs);
+ var node = SvgElement.create('g', attrs, formatter);
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i];
var childNode = exportSVG(child, options);
if (childNode) {
if (child.isClipMask()) {
- var clip = createElement('clipPath');
+ var clip = SvgElement.create('clipPath');
clip.appendChild(childNode);
setDefinition(child, clip, 'clip');
- setAttributes(node, {
+ SvgElement.set(node, {
'clip-path': 'url(#' + clip.id + ')'
});
} else {
@@ -12103,9 +14438,9 @@
attrs.y -= size.height / 2;
attrs.width = size.width;
attrs.height = size.height;
- attrs.href = options.embedImages === false && image && image.src
+ attrs.href = options.embedImages == false && image && image.src
|| item.toDataURL();
- return createElement('image', attrs);
+ return SvgElement.create('image', attrs, formatter);
}
function exportPath(item, options) {
@@ -12116,33 +14451,33 @@
return exportShape(shape, options);
}
var segments = item._segments,
+ length = segments.length,
type,
attrs = getTransform(item._matrix);
- if (segments.length === 0)
- return null;
- if (matchShapes && !item.hasHandles()) {
- if (segments.length >= 3) {
+ if (matchShapes && length >= 2 && !item.hasHandles()) {
+ if (length > 2) {
type = item._closed ? 'polygon' : 'polyline';
var parts = [];
- for(var i = 0, l = segments.length; i < l; i++)
+ for (var i = 0; i < length; i++) {
parts.push(formatter.point(segments[i]._point));
+ }
attrs.points = parts.join(' ');
} else {
type = 'line';
- var first = segments[0]._point,
- last = segments[segments.length - 1]._point;
+ var start = segments[0]._point,
+ end = segments[1]._point;
attrs.set({
- x1: first.x,
- y1: first.y,
- x2: last.x,
- y2: last.y
+ x1: start.x,
+ y1: start.y,
+ x2: end.x,
+ y2: end.y
});
}
} else {
type = 'path';
attrs.d = item.getPathData(null, options.precision);
}
- return createElement(type, attrs);
+ return SvgElement.create(type, attrs, formatter);
}
function exportShape(item) {
@@ -12169,7 +14504,7 @@
attrs.ry = radius.height;
}
}
- return createElement(type, attrs);
+ return SvgElement.create(type, attrs, formatter);
}
function exportCompoundPath(item, options) {
@@ -12177,29 +14512,29 @@
var data = item.getPathData(null, options.precision);
if (data)
attrs.d = data;
- return createElement('path', attrs);
- }
-
- function exportPlacedSymbol(item, options) {
+ return SvgElement.create('path', attrs, formatter);
+ }
+
+ function exportSymbolItem(item, options) {
var attrs = getTransform(item._matrix, true),
- symbol = item.getSymbol(),
- symbolNode = getDefinition(symbol, 'symbol'),
- definition = symbol.getDefinition(),
- bounds = definition.getBounds();
- if (!symbolNode) {
- symbolNode = createElement('symbol', {
+ definition = item._definition,
+ node = getDefinition(definition, 'symbol'),
+ definitionItem = definition._item,
+ bounds = definitionItem.getStrokeBounds();
+ if (!node) {
+ node = SvgElement.create('symbol', {
viewBox: formatter.rectangle(bounds)
});
- symbolNode.appendChild(exportSVG(definition, options));
- setDefinition(symbol, symbolNode, 'symbol');
- }
- attrs.href = '#' + symbolNode.id;
+ node.appendChild(exportSVG(definitionItem, options));
+ setDefinition(definition, node, 'symbol');
+ }
+ attrs.href = '#' + node.id;
attrs.x += bounds.x;
attrs.y += bounds.y;
- attrs.width = formatter.number(bounds.width);
- attrs.height = formatter.number(bounds.height);
+ attrs.width = bounds.width;
+ attrs.height = bounds.height;
attrs.overflow = 'visible';
- return createElement('use', attrs);
+ return SvgElement.create('use', attrs, formatter);
}
function exportGradient(color) {
@@ -12207,8 +14542,8 @@
if (!gradientNode) {
var gradient = color.getGradient(),
radial = gradient._radial,
- origin = color.getOrigin().transform(),
- destination = color.getDestination().transform(),
+ origin = color.getOrigin(),
+ destination = color.getDestination(),
attrs;
if (radial) {
attrs = {
@@ -12218,7 +14553,6 @@
};
var highlight = color.getHighlight();
if (highlight) {
- highlight = highlight.transform();
attrs.fx = highlight.x;
attrs.fy = highlight.y;
}
@@ -12231,20 +14565,23 @@
};
}
attrs.gradientUnits = 'userSpaceOnUse';
- gradientNode = createElement(
- (radial ? 'radial' : 'linear') + 'Gradient', attrs);
+ gradientNode = SvgElement.create((radial ? 'radial' : 'linear')
+ + 'Gradient', attrs, formatter);
var stops = gradient._stops;
for (var i = 0, l = stops.length; i < l; i++) {
var stop = stops[i],
stopColor = stop._color,
- alpha = stopColor.getAlpha();
+ alpha = stopColor.getAlpha(),
+ offset = stop._offset;
attrs = {
- offset: stop._rampPoint,
- 'stop-color': stopColor.toCSS(true)
+ offset: offset == null ? i / (l - 1) : offset
};
+ if (stopColor)
+ attrs['stop-color'] = stopColor.toCSS(true);
if (alpha < 1)
attrs['stop-opacity'] = alpha;
- gradientNode.appendChild(createElement('stop', attrs));
+ gradientNode.appendChild(
+ SvgElement.create('stop', attrs, formatter));
}
setDefinition(color, gradientNode, 'color');
}
@@ -12252,7 +14589,8 @@
}
function exportText(item) {
- var node = createElement('text', getTransform(item._matrix, true));
+ var node = SvgElement.create('text', getTransform(item._matrix, true),
+ formatter);
node.textContent = item._content;
return node;
}
@@ -12264,18 +14602,19 @@
Path: exportPath,
Shape: exportShape,
CompoundPath: exportCompoundPath,
- PlacedSymbol: exportPlacedSymbol,
+ SymbolItem: exportSymbolItem,
PointText: exportText
};
function applyStyle(item, node, isRoot) {
var attrs = {},
- parent = !isRoot && item.getParent();
+ parent = !isRoot && item.getParent(),
+ style = [];
if (item._name != null)
attrs.id = item._name;
- Base.each(SVGStyles, function(entry) {
+ Base.each(SvgStyles, function(entry) {
var get = entry.get,
type = entry.type,
value = item[get]();
@@ -12287,44 +14626,46 @@
if (alpha < 1)
attrs[entry.attribute + '-opacity'] = alpha;
}
- attrs[entry.attribute] = value == null
- ? 'none'
- : type === 'number'
- ? formatter.number(value)
- : type === 'color'
- ? value.gradient
+ if (type === 'style') {
+ style.push(entry.attribute + ': ' + value);
+ } else {
+ attrs[entry.attribute] = value == null ? 'none'
+ : type === 'color' ? value.gradient
? exportGradient(value, item)
: value.toCSS(true)
- : type === 'array'
- ? value.join(',')
- : type === 'lookup'
- ? entry.toSVG[value]
- : value;
+ : type === 'array' ? value.join(',')
+ : type === 'lookup' ? entry.toSVG[value]
+ : value;
+ }
}
});
+ if (style.length)
+ attrs.style = style.join(';');
+
if (attrs.opacity === 1)
delete attrs.opacity;
if (!item._visible)
attrs.visibility = 'hidden';
- return setAttributes(node, attrs);
+ return SvgElement.set(node, attrs, formatter);
}
var definitions;
function getDefinition(item, type) {
if (!definitions)
definitions = { ids: {}, svgs: {} };
- return item && definitions.svgs[type + '-' + item._id];
+ return item && definitions.svgs[type + '-'
+ + (item._id || item.__id || (item.__id = UID.get('svg')))];
}
function setDefinition(item, node, type) {
if (!definitions)
getDefinition();
- var id = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
- node.id = type + '-' + id;
- definitions.svgs[type + '-' + item._id] = node;
+ var typeId = definitions.ids[type] = (definitions.ids[type] || 0) + 1;
+ node.id = type + '-' + typeId;
+ definitions.svgs[type + '-' + (item._id || item.__id)] = node;
}
function exportDefinitions(node, options) {
@@ -12335,10 +14676,10 @@
for (var i in definitions.svgs) {
if (!defs) {
if (!svg) {
- svg = createElement('svg');
+ svg = SvgElement.create('svg');
svg.appendChild(node);
}
- defs = svg.insertBefore(createElement('defs'),
+ defs = svg.insertBefore(SvgElement.create('defs'),
svg.firstChild);
}
defs.appendChild(definitions.svgs[i]);
@@ -12346,7 +14687,7 @@
definitions = null;
}
return options.asString
- ? new XMLSerializer().serializeToString(svg)
+ ? new self.XMLSerializer().serializeToString(svg)
: svg;
}
@@ -12381,25 +14722,37 @@
Project.inject({
exportSVG: function(options) {
options = setOptions(options);
- var layers = this.layers,
+ var children = this._children,
view = this.getView(),
- size = view.getViewSize(),
- node = createElement('svg', {
- x: 0,
- y: 0,
- width: size.width,
- height: size.height,
+ bounds = Base.pick(options.bounds, 'view'),
+ mx = options.matrix || bounds === 'view' && view._matrix,
+ matrix = mx && Matrix.read([mx]),
+ rect = bounds === 'view'
+ ? new Rectangle([0, 0], view.getViewSize())
+ : bounds === 'content'
+ ? Item._getBounds(children, matrix, { stroke: true })
+ .rect
+ : Rectangle.read([bounds], 0, { readNull: true }),
+ attrs = {
version: '1.1',
- xmlns: 'http://www.w3.org/2000/svg',
- 'xmlns:xlink': 'http://www.w3.org/1999/xlink'
- }),
- parent = node,
- matrix = view._matrix;
- if (!matrix.isIdentity())
- parent = node.appendChild(
- createElement('g', getTransform(matrix)));
- for (var i = 0, l = layers.length; i < l; i++)
- parent.appendChild(exportSVG(layers[i], options, true));
+ xmlns: SvgElement.svg,
+ 'xmlns:xlink': SvgElement.xlink,
+ };
+ if (rect) {
+ attrs.width = rect.width;
+ attrs.height = rect.height;
+ if (rect.x || rect.x === 0 || rect.y || rect.y === 0)
+ attrs.viewBox = formatter.rectangle(rect);
+ }
+ var node = SvgElement.create('svg', attrs, formatter),
+ parent = node;
+ if (matrix && !matrix.isIdentity()) {
+ parent = node.appendChild(SvgElement.create('g',
+ getTransform(matrix), formatter));
+ }
+ for (var i = 0, l = children.length; i < l; i++) {
+ parent.appendChild(exportSVG(children[i], options, true));
+ }
return exportDefinitions(node, options);
}
});
@@ -12407,83 +14760,81 @@
new function() {
- function getValue(node, name, isString, allowNull) {
- var namespace = SVGNamespaces[name],
- value = namespace
- ? node.getAttributeNS(namespace, name)
- : node.getAttribute(name);
- if (value === 'null')
- value = null;
- return value == null
+ var definitions = {},
+ rootSize;
+
+ function getValue(node, name, isString, allowNull, allowPercent,
+ defaultValue) {
+ var value = SvgElement.get(node, name) || defaultValue,
+ res = value == null
? allowNull
? null
- : isString
- ? ''
- : 0
+ : isString ? '' : 0
: isString
? value
: parseFloat(value);
- }
-
- function getPoint(node, x, y, allowNull) {
- x = getValue(node, x, false, allowNull);
- y = getValue(node, y, false, allowNull);
+ return /%\s*$/.test(value)
+ ? (res / 100) * (allowPercent ? 1
+ : rootSize[/x|^width/.test(name) ? 'width' : 'height'])
+ : res;
+ }
+
+ function getPoint(node, x, y, allowNull, allowPercent, defaultX, defaultY) {
+ x = getValue(node, x || 'x', false, allowNull, allowPercent, defaultX);
+ y = getValue(node, y || 'y', false, allowNull, allowPercent, defaultY);
return allowNull && (x == null || y == null) ? null
: new Point(x, y);
}
- function getSize(node, w, h, allowNull) {
- w = getValue(node, w, false, allowNull);
- h = getValue(node, h, false, allowNull);
+ function getSize(node, w, h, allowNull, allowPercent) {
+ w = getValue(node, w || 'width', false, allowNull, allowPercent);
+ h = getValue(node, h || 'height', false, allowNull, allowPercent);
return allowNull && (w == null || h == null) ? null
: new Size(w, h);
}
function convertValue(value, type, lookup) {
- return value === 'none'
- ? null
- : type === 'number'
- ? parseFloat(value)
- : type === 'array'
- ? value ? value.split(/[\s,]+/g).map(parseFloat) : []
- : type === 'color'
- ? getDefinition(value) || value
- : type === 'lookup'
- ? lookup[value]
- : value;
+ return value === 'none' ? null
+ : type === 'number' ? parseFloat(value)
+ : type === 'array' ?
+ value ? value.split(/[\s,]+/g).map(parseFloat) : []
+ : type === 'color' ? getDefinition(value) || value
+ : type === 'lookup' ? lookup[value]
+ : value;
}
function importGroup(node, type, options, isRoot) {
var nodes = node.childNodes,
isClip = type === 'clippath',
+ isDefs = type === 'defs',
item = new Group(),
project = item._project,
currentStyle = project._currentStyle,
children = [];
- if (!isClip) {
+ if (!isClip && !isDefs) {
item = applyAttributes(item, node, isRoot);
project._currentStyle = item._style.clone();
}
if (isRoot) {
var defs = node.querySelectorAll('defs');
for (var i = 0, l = defs.length; i < l; i++) {
- importSVG(defs[i], options, false);
+ importNode(defs[i], options, false);
}
}
for (var i = 0, l = nodes.length; i < l; i++) {
var childNode = nodes[i],
child;
if (childNode.nodeType === 1
- && childNode.nodeName.toLowerCase() !== 'defs'
- && (child = importSVG(childNode, options, false))
- && !(child instanceof Symbol))
+ && !/^defs$/i.test(childNode.nodeName)
+ && (child = importNode(childNode, options, false))
+ && !(child instanceof SymbolDefinition))
children.push(child);
}
item.addChildren(children);
if (isClip)
item = applyAttributes(item.reduce(), node, isRoot);
project._currentStyle = currentStyle;
- if (isClip || type === 'defs') {
+ if (isClip || isDefs) {
item.remove();
item = null;
}
@@ -12505,19 +14856,19 @@
}
function importPath(node) {
- var data = node.getAttribute('d'),
- param = { pathData: data };
- return (data.match(/m/gi) || []).length > 1 || /z\S+/i.test(data)
- ? new CompoundPath(param)
- : new Path(param);
+ return PathItem.create(node.getAttribute('d'));
}
function importGradient(node, type) {
var id = (getValue(node, 'href', true) || '').substring(1),
- isRadial = type === 'radialgradient',
+ radial = type === 'radialgradient',
gradient;
if (id) {
gradient = definitions[id].getGradient();
+ if (gradient._radial ^ radial) {
+ gradient = gradient.clone();
+ gradient._radial = radial;
+ }
} else {
var nodes = node.childNodes,
stops = [];
@@ -12526,19 +14877,26 @@
if (child.nodeType === 1)
stops.push(applyAttributes(new GradientStop(), child));
}
- gradient = new Gradient(stops, isRadial);
- }
- var origin, destination, highlight;
- if (isRadial) {
- origin = getPoint(node, 'cx', 'cy');
- destination = origin.add(getValue(node, 'r'), 0);
- highlight = getPoint(node, 'fx', 'fy', true);
+ gradient = new Gradient(stops, radial);
+ }
+ var origin, destination, highlight,
+ scaleToBounds = getValue(node, 'gradientUnits', true) !==
+ 'userSpaceOnUse';
+ if (radial) {
+ origin = getPoint(node, 'cx', 'cy', false, scaleToBounds,
+ '50%', '50%');
+ destination = origin.add(
+ getValue(node, 'r', false, false, scaleToBounds, '50%'), 0);
+ highlight = getPoint(node, 'fx', 'fy', true, scaleToBounds);
} else {
- origin = getPoint(node, 'x1', 'y1');
- destination = getPoint(node, 'x2', 'y2');
- }
- applyAttributes(
- new Color(gradient, origin, destination, highlight), node);
+ origin = getPoint(node, 'x1', 'y1', false, scaleToBounds,
+ '0%', '0%');
+ destination = getPoint(node, 'x2', 'y2', false, scaleToBounds,
+ '100%', '0%');
+ }
+ var color = applyAttributes(
+ new Color(gradient, origin, destination, highlight), node);
+ color._scaleToBounds = scaleToBounds;
return null;
}
@@ -12547,17 +14905,8 @@
var nodes = node.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
var child = nodes[i];
- if (child.nodeType === 1) {
- var next = child.nextSibling;
- document.body.appendChild(child);
- var item = importSVG(child, options, isRoot);
- if (next) {
- node.insertBefore(child, next);
- } else {
- node.appendChild(child);
- }
- return item;
- }
+ if (child.nodeType === 1)
+ return importNode(child, options, isRoot);
}
},
g: importGroup,
@@ -12572,17 +14921,17 @@
image: function (node) {
var raster = new Raster(getValue(node, 'href', true));
raster.on('load', function() {
- var size = getSize(node, 'width', 'height');
+ var size = getSize(node);
this.setSize(size);
- var center = this._matrix._transformPoint(
- getPoint(node, 'x', 'y').add(size.divide(2)));
- this.translate(center);
+ var center = getPoint(node).add(size.divide(2));
+ this._matrix.append(new Matrix().translate(center));
});
return raster;
},
symbol: function(node, type, options, isRoot) {
- return new Symbol(importGroup(node, type, options, isRoot), true);
+ return new SymbolDefinition(
+ importGroup(node, type, options, isRoot), true);
},
defs: importGroup,
@@ -12590,16 +14939,17 @@
use: function(node) {
var id = (getValue(node, 'href', true) || '').substring(1),
definition = definitions[id],
- point = getPoint(node, 'x', 'y');
+ point = getPoint(node);
return definition
- ? definition instanceof Symbol
+ ? definition instanceof SymbolDefinition
? definition.place(point)
: definition.clone().translate(point)
: null;
},
circle: function(node) {
- return new Shape.Circle(getPoint(node, 'cx', 'cy'),
+ return new Shape.Circle(
+ getPoint(node, 'cx', 'cy'),
getValue(node, 'r'));
},
@@ -12611,77 +14961,89 @@
},
rect: function(node) {
- var point = getPoint(node, 'x', 'y'),
- size = getSize(node, 'width', 'height'),
- radius = getSize(node, 'rx', 'ry');
- return new Shape.Rectangle(new Rectangle(point, size), radius);
- },
+ return new Shape.Rectangle(new Rectangle(
+ getPoint(node),
+ getSize(node)
+ ), getSize(node, 'rx', 'ry'));
+ },
line: function(node) {
- return new Path.Line(getPoint(node, 'x1', 'y1'),
+ return new Path.Line(
+ getPoint(node, 'x1', 'y1'),
getPoint(node, 'x2', 'y2'));
},
text: function(node) {
- var text = new PointText(getPoint(node, 'x', 'y')
- .add(getPoint(node, 'dx', 'dy')));
+ var text = new PointText(getPoint(node).add(
+ getPoint(node, 'dx', 'dy')));
text.setContent(node.textContent.trim() || '');
return text;
- }
+ },
+
+ switch: importGroup
};
function applyTransform(item, value, name, node) {
- var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
- matrix = new Matrix();
- for (var i = 0, l = transforms.length; i < l; i++) {
- var transform = transforms[i];
- if (!transform)
- break;
- var parts = transform.split(/\(\s*/),
- command = parts[0],
- v = parts[1].split(/[\s,]+/g);
- for (var j = 0, m = v.length; j < m; j++)
- v[j] = parseFloat(v[j]);
- switch (command) {
- case 'matrix':
- matrix.concatenate(
- new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
- break;
- case 'rotate':
- matrix.rotate(v[0], v[1], v[2]);
- break;
- case 'translate':
- matrix.translate(v[0], v[1]);
- break;
- case 'scale':
- matrix.scale(v);
- break;
- case 'skewX':
- matrix.skew(v[0], 0);
- break;
- case 'skewY':
- matrix.skew(0, v[0]);
- break;
- }
- }
- item.transform(matrix);
+ if (item.transform) {
+ var transforms = (node.getAttribute(name) || '').split(/\)\s*/g),
+ matrix = new Matrix();
+ for (var i = 0, l = transforms.length; i < l; i++) {
+ var transform = transforms[i];
+ if (!transform)
+ break;
+ var parts = transform.split(/\(\s*/),
+ command = parts[0],
+ v = parts[1].split(/[\s,]+/g);
+ for (var j = 0, m = v.length; j < m; j++)
+ v[j] = parseFloat(v[j]);
+ switch (command) {
+ case 'matrix':
+ matrix.append(
+ new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
+ break;
+ case 'rotate':
+ matrix.rotate(v[0], v[1] || 0, v[2] || 0);
+ break;
+ case 'translate':
+ matrix.translate(v[0], v[1] || 0);
+ break;
+ case 'scale':
+ matrix.scale(v);
+ break;
+ case 'skewX':
+ matrix.skew(v[0], 0);
+ break;
+ case 'skewY':
+ matrix.skew(0, v[0]);
+ break;
+ }
+ }
+ item.transform(matrix);
+ }
}
function applyOpacity(item, value, name) {
- var color = item[name === 'fill-opacity' ? 'getFillColor'
- : 'getStrokeColor']();
+ var key = name === 'fill-opacity' ? 'getFillColor' : 'getStrokeColor',
+ color = item[key] && item[key]();
if (color)
color.setAlpha(parseFloat(value));
}
- var attributes = Base.set(Base.each(SVGStyles, function(entry) {
+ var attributes = Base.set(Base.each(SvgStyles, function(entry) {
this[entry.attribute] = function(item, value) {
- item[entry.set](convertValue(value, entry.type, entry.fromSVG));
- if (entry.type === 'color' && item instanceof Shape) {
- var color = item[entry.get]();
- if (color)
- color.transform(new Matrix().translate(
- item.getPosition(true).negate()));
+ if (item[entry.set]) {
+ item[entry.set](convertValue(value, entry.type, entry.fromSVG));
+ if (entry.type === 'color') {
+ var color = item[entry.get]();
+ if (color) {
+ if (color._scaleToBounds) {
+ var bounds = item.getBounds();
+ color.transform(new Matrix()
+ .translate(bounds.getPoint())
+ .scale(bounds.getSize()));
+ }
+ }
+ }
}
};
}, {}), {
@@ -12711,11 +15073,13 @@
'stroke-opacity': applyOpacity,
visibility: function(item, value) {
- item.setVisible(value === 'visible');
+ if (item.setVisible)
+ item.setVisible(value === 'visible');
},
display: function(item, value) {
- item.setVisible(value !== null);
+ if (item.setVisible)
+ item.setVisible(value !== null);
},
'stop-color': function(item, value) {
@@ -12729,29 +15093,35 @@
},
offset: function(item, value) {
- var percentage = value.match(/(.*)%$/);
- item.setRampPoint(percentage
- ? percentage[1] / 100
- : parseFloat(value));
+ if (item.setOffset) {
+ var percent = value.match(/(.*)%$/);
+ item.setOffset(percent ? percent[1] / 100 : parseFloat(value));
+ }
},
viewBox: function(item, value, name, node, styles) {
var rect = new Rectangle(convertValue(value, 'array')),
- size = getSize(node, 'width', 'height', true);
+ size = getSize(node, null, null, true),
+ group,
+ matrix;
if (item instanceof Group) {
- var scale = size ? rect.getSize().divide(size) : 1,
- matrix = new Matrix().translate(rect.getPoint()).scale(scale);
- item.transform(matrix.inverted());
- } else if (item instanceof Symbol) {
+ var scale = size ? size.divide(rect.getSize()) : 1,
+ matrix = new Matrix().scale(scale)
+ .translate(rect.getPoint().negate());
+ group = item;
+ } else if (item instanceof SymbolDefinition) {
if (size)
rect.setSize(size);
- var clip = getAttribute(node, 'overflow', styles) != 'visible',
- group = item._definition;
- if (clip && !rect.contains(group.getBounds())) {
- clip = new Shape.Rectangle(rect).transform(group._matrix);
+ group = item._item;
+ }
+ if (group) {
+ if (getAttribute(node, 'overflow', styles) !== 'visible') {
+ var clip = new Shape.Rectangle(rect);
clip.setClipMask(true);
group.addChild(clip);
}
+ if (matrix)
+ group.transform(matrix);
}
}
});
@@ -12759,94 +15129,77 @@
function getAttribute(node, name, styles) {
var attr = node.attributes[name],
value = attr && attr.value;
- if (!value) {
+ if (!value && node.style) {
var style = Base.camelize(name);
value = node.style[style];
if (!value && styles.node[style] !== styles.parent[style])
value = styles.node[style];
}
- return !value
- ? undefined
- : value === 'none'
- ? null
- : value;
+ return !value ? undefined
+ : value === 'none' ? null
+ : value;
}
function applyAttributes(item, node, isRoot) {
- var styles = {
- node: DomElement.getStyles(node) || {},
- parent: !isRoot && DomElement.getStyles(node.parentNode) || {}
- };
+ var parent = node.parentNode,
+ styles = {
+ node: DomElement.getStyles(node) || {},
+ parent: !isRoot && !/^defs$/i.test(parent.tagName)
+ && DomElement.getStyles(parent) || {}
+ };
Base.each(attributes, function(apply, name) {
var value = getAttribute(node, name, styles);
- if (value !== undefined)
- item = Base.pick(apply(item, value, name, node, styles), item);
+ item = value !== undefined
+ && apply(item, value, name, node, styles) || item;
});
return item;
}
- var definitions = {};
function getDefinition(value) {
- var match = value && value.match(/\((?:#|)([^)']+)/);
- return match && definitions[match[1]];
- }
-
- function importSVG(source, options, isRoot) {
- if (!source)
- return null;
- if (!options) {
- options = {};
- } else if (typeof options === 'function') {
- options = { onLoad: options };
- }
-
- var node = source,
- scope = paper;
-
- function onLoadCallback(svg) {
- paper = scope;
- var item = importSVG(svg, options, isRoot),
- onLoad = options.onLoad,
- view = scope.project && scope.getView();
- if (onLoad)
- onLoad.call(this, item);
- view.update();
- }
-
- if (isRoot) {
- if (typeof source === 'string' && !/^.*</.test(source)) {
- node = document.getElementById(source);
- if (node) {
- source = null;
- } else {
- return Http.request('get', source, onLoadCallback);
- }
- } else if (typeof File !== 'undefined' && source instanceof File) {
- var reader = new FileReader();
- reader.onload = function() {
- onLoadCallback(reader.result);
- };
- return reader.readAsText(source);
- }
- }
-
- if (typeof source === 'string')
- node = new DOMParser().parseFromString(source, 'image/svg+xml');
- if (!node.nodeName)
- throw new Error('Unsupported SVG source: ' + source);
+ var match = value && value.match(/\((?:["'#]*)([^"')]+)/),
+ name = match && match[1],
+ res = name && definitions[window
+ ? name.replace(window.location.href.split('#')[0] + '#', '')
+ : name];
+ if (res && res._scaleToBounds) {
+ res = res.clone();
+ res._scaleToBounds = true;
+ }
+ return res;
+ }
+
+ function importNode(node, options, isRoot) {
var type = node.nodeName.toLowerCase(),
- importer = importers[type],
- item,
- data = node.getAttribute && node.getAttribute('data-paper-data'),
- settings = scope.settings,
- applyMatrix = settings.applyMatrix;
+ isElement = type !== '#document',
+ body = document.body,
+ container,
+ parent,
+ next;
+ if (isRoot && isElement) {
+ rootSize = paper.getView().getSize();
+ rootSize = getSize(node, null, null, true) || rootSize;
+ container = SvgElement.create('svg', {
+ style: 'stroke-width: 1px; stroke-miterlimit: 10'
+ });
+ parent = node.parentNode;
+ next = node.nextSibling;
+ container.appendChild(node);
+ body.appendChild(container);
+ }
+ var settings = paper.settings,
+ applyMatrix = settings.applyMatrix,
+ insertItems = settings.insertItems;
settings.applyMatrix = false;
- item = importer && importer(node, type, options, isRoot) || null;
+ settings.insertItems = false;
+ var importer = importers[type],
+ item = importer && importer(node, type, options, isRoot) || null;
+ settings.insertItems = insertItems;
settings.applyMatrix = applyMatrix;
if (item) {
- if (type !== '#document' && !(item instanceof Group))
+ if (isElement && !(item instanceof Group))
item = applyAttributes(item, node, isRoot);
- var onImport = options.onImport;
+ var onImport = options.onImport,
+ data = isElement && node.getAttribute('data-paper-data');
if (onImport)
item = onImport(node, item, options) || item;
if (options.expandShapes && item instanceof Shape) {
@@ -12856,6 +15209,16 @@
if (data)
item._data = JSON.parse(data);
}
+ if (container) {
+ body.removeChild(container);
+ if (parent) {
+ if (next) {
+ parent.insertBefore(node, next);
+ } else {
+ parent.appendChild(node);
+ }
+ }
+ }
if (isRoot) {
definitions = {};
if (item && Base.pick(options.applyMatrix, applyMatrix))
@@ -12864,24 +15227,1412 @@
return item;
}
+ function importSVG(source, options, owner) {
+ if (!source)
+ return null;
+ options = typeof options === 'function' ? { onLoad: options }
+ : options || {};
+ var scope = paper,
+ item = null;
+
+ function onLoad(svg) {
+ try {
+ var node = typeof svg === 'object' ? svg : new self.DOMParser()
+ .parseFromString(svg, 'image/svg+xml');
+ if (!node.nodeName) {
+ node = null;
+ throw new Error('Unsupported SVG source: ' + source);
+ }
+ paper = scope;
+ item = importNode(node, options, true);
+ if (!options || options.insert !== false) {
+ owner._insertItem(undefined, item);
+ }
+ var onLoad = options.onLoad;
+ if (onLoad)
+ onLoad(item, svg);
+ } catch (e) {
+ onError(e);
+ }
+ }
+
+ function onError(message, status) {
+ var onError = options.onError;
+ if (onError) {
+ onError(message, status);
+ } else {
+ throw new Error(message);
+ }
+ }
+
+ if (typeof source === 'string' && !/^.*</.test(source)) {
+ var node = document.getElementById(source);
+ if (node) {
+ onLoad(node);
+ } else {
+ Http.request({
+ url: source,
+ async: true,
+ onLoad: onLoad,
+ onError: onError
+ });
+ }
+ } else if (typeof File !== 'undefined' && source instanceof File) {
+ var reader = new FileReader();
+ reader.onload = function() {
+ onLoad(reader.result);
+ };
+ reader.onerror = function() {
+ onError(reader.error);
+ };
+ return reader.readAsText(source);
+ } else {
+ onLoad(source);
+ }
+
+ return item;
+ }
+
Item.inject({
importSVG: function(node, options) {
- return this.addChild(importSVG(node, options, true));
+ return importSVG(node, options, this);
}
});
Project.inject({
importSVG: function(node, options) {
this.activate();
- return importSVG(node, options, true);
+ return importSVG(node, options, this);
}
});
};
-Base.exports.PaperScript = (function() {
- var exports, define,
- scope = this;
-!function(e,r){return"object"==typeof exports&&"object"==typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):(r(e.acorn||(e.acorn={})),void 0)}(this,function(e){"use strict";function r(e){fr=e||{};for(var r in mr)Object.prototype.hasOwnProperty.call(fr,r)||(fr[r]=mr[r]);hr=fr.sourceFile||null}function t(e,r){var t=vr(dr,e);r+=" ("+t.line+":"+t.column+")";var n=new SyntaxError(r);throw n.pos=e,n.loc=t,n.raisedAt=br,n}function n(e){function r(e){if(1==e.length)return t+="return str === "+JSON.stringify(e[0])+";";t+="switch(str){";for(var r=0;r<e.length;++r)t+="case "+JSON.stringify(e[r])+":";t+="return true}return false;"}e=e.split(" ");var t="",n=[];e:for(var a=0;a<e.length;++a){for(var o=0;o<n.length;++o)if(n[o][0].length==e[a].length){n[o].push(e[a]);continue e}n.push([e[a]])}if(n.length>3){n.sort(function(e,r){return r.length-e.length}),t+="switch(str.length){";for(var a=0;a<n.length;++a){var i=n[a];t+="case "+i[0].length+":",r(i)}t+="}"}else r(e);return new Function("str",t)}function a(){this.line=Ar,this.column=br-Sr}function o(){Ar=1,br=Sr=0,Er=!0,u()}function i(e,r){gr=br,fr.locations&&(kr=new a),wr=e,u(),Cr=r,Er=e.beforeExpr}function s(){var e=fr.onComment&&fr.locations&&new a,r=br,n=dr.indexOf("*/",br+=2);if(-1===n&&t(br-2,"Unterminated comment"),br=n+2,fr.locations){Kt.lastIndex=r;for(var o;(o=Kt.exec(dr))&&o.index<br;)++Ar,Sr=o.index+o[0].length}fr.onComment&&fr.onComment(!0,dr.slice(r+2,n),r,br,e,fr.locations&&new a)}function c(){for(var e=br,r=fr.onComment&&fr.locations&&new a,t=dr.charCodeAt(br+=2);pr>br&&10!==t&&13!==t&&8232!==t&&8233!==t;)++br,t=dr.charCodeAt(br);fr.onComment&&fr.onComment(!1,dr.slice(e+2,br),e,br,r,fr.locations&&new a)}function u(){for(;pr>br;){var e=dr.charCodeAt(br);if(32===e)++br;else if(13===e){++br;var r=dr.charCodeAt(br);10===r&&++br,fr.locations&&(++Ar,Sr=br)}else if(10===e||8232===e||8233===e)++br,fr.locations&&(++Ar,Sr=br);else if(e>8&&14>e)++br;else if(47===e){var r=dr.charCodeAt(br+1);if(42===r)s();else{if(47!==r)break;c()}}else if(160===e)++br;else{if(!(e>=5760&&Jt.test(String.fromCharCode(e))))break;++br}}}function l(){var e=dr.charCodeAt(br+1);return e>=48&&57>=e?E(!0):(++br,i(xt))}function f(){var e=dr.charCodeAt(br+1);return Er?(++br,k()):61===e?x(Et,2):x(wt,1)}function d(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Dt,1)}function p(e){var r=dr.charCodeAt(br+1);return r===e?x(124===e?Lt:Ut,2):61===r?x(Et,2):x(124===e?Rt:Tt,1)}function h(){var e=dr.charCodeAt(br+1);return 61===e?x(Et,2):x(Vt,1)}function m(e){var r=dr.charCodeAt(br+1);return r===e?45==r&&62==dr.charCodeAt(br+2)&&Gt.test(dr.slice(Lr,br))?(br+=3,c(),u(),g()):x(St,2):61===r?x(Et,2):x(At,1)}function v(e){var r=dr.charCodeAt(br+1),t=1;return r===e?(t=62===e&&62===dr.charCodeAt(br+2)?3:2,61===dr.charCodeAt(br+t)?x(Et,t+1):x(jt,t)):33==r&&60==e&&45==dr.charCodeAt(br+2)&&45==dr.charCodeAt(br+3)?(br+=4,c(),u(),g()):(61===r&&(t=61===dr.charCodeAt(br+2)?3:2),x(Ot,t))}function b(e){var r=dr.charCodeAt(br+1);return 61===r?x(qt,61===dr.charCodeAt(br+2)?3:2):x(61===e?Ct:It,1)}function y(e){switch(e){case 46:return l();case 40:return++br,i(mt);case 41:return++br,i(vt);case 59:return++br,i(yt);case 44:return++br,i(bt);case 91:return++br,i(ft);case 93:return++br,i(dt);case 123:return++br,i(pt);case 125:return++br,i(ht);case 58:return++br,i(gt);case 63:return++br,i(kt);case 48:var r=dr.charCodeAt(br+1);if(120===r||88===r)return C();case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return E(!1);case 34:case 39:return A(e);case 47:return f(e);case 37:case 42:return d();case 124:case 38:return p(e);case 94:return h();case 43:case 45:return m(e);case 60:case 62:return v(e);case 61:case 33:return b(e);case 126:return x(It,1)}return!1}function g(e){if(e?br=yr+1:yr=br,fr.locations&&(xr=new a),e)return k();if(br>=pr)return i(Br);var r=dr.charCodeAt(br);if(Qt(r)||92===r)return L();var n=y(r);if(n===!1){var o=String.fromCharCode(r);if("\\"===o||$t.test(o))return L();t(br,"Unexpected character '"+o+"'")}return n}function x(e,r){var t=dr.slice(br,br+r);br+=r,i(e,t)}function k(){for(var e,r,n="",a=br;;){br>=pr&&t(a,"Unterminated regular expression");var o=dr.charAt(br);if(Gt.test(o)&&t(a,"Unterminated regular expression"),e)e=!1;else{if("["===o)r=!0;else if("]"===o&&r)r=!1;else if("/"===o&&!r)break;e="\\"===o}++br}var n=dr.slice(a,br);++br;var s=I();return s&&!/^[gmsiy]*$/.test(s)&&t(a,"Invalid regexp flag"),i(jr,new RegExp(n,s))}function w(e,r){for(var t=br,n=0,a=0,o=null==r?1/0:r;o>a;++a){var i,s=dr.charCodeAt(br);if(i=s>=97?s-97+10:s>=65?s-65+10:s>=48&&57>=s?s-48:1/0,i>=e)break;++br,n=n*e+i}return br===t||null!=r&&br-t!==r?null:n}function C(){br+=2;var e=w(16);return null==e&&t(yr+2,"Expected hexadecimal number"),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number"),i(Or,e)}function E(e){var r=br,n=!1,a=48===dr.charCodeAt(br);e||null!==w(10)||t(r,"Invalid number"),46===dr.charCodeAt(br)&&(++br,w(10),n=!0);var o=dr.charCodeAt(br);(69===o||101===o)&&(o=dr.charCodeAt(++br),(43===o||45===o)&&++br,null===w(10)&&t(r,"Invalid number"),n=!0),Qt(dr.charCodeAt(br))&&t(br,"Identifier directly after number");var s,c=dr.slice(r,br);return n?s=parseFloat(c):a&&1!==c.length?/[89]/.test(c)||Tr?t(r,"Invalid number"):s=parseInt(c,8):s=parseInt(c,10),i(Or,s)}function A(e){br++;for(var r="";;){br>=pr&&t(yr,"Unterminated string constant");var n=dr.charCodeAt(br);if(n===e)return++br,i(Dr,r);if(92===n){n=dr.charCodeAt(++br);var a=/^[0-7]+/.exec(dr.slice(br,br+3));for(a&&(a=a[0]);a&&parseInt(a,8)>255;)a=a.slice(0,a.length-1);if("0"===a&&(a=null),++br,a)Tr&&t(br-2,"Octal literal in strict mode"),r+=String.fromCharCode(parseInt(a,8)),br+=a.length-1;else switch(n){case 110:r+="\n";break;case 114:r+="\r";break;case 120:r+=String.fromCharCode(S(2));break;case 117:r+=String.fromCharCode(S(4));break;case 85:r+=String.fromCharCode(S(8));break;case 116:r+=" ";break;case 98:r+="\b";break;case 118:r+="";break;case 102:r+="\f";break;case 48:r+="\0";break;case 13:10===dr.charCodeAt(br)&&++br;case 10:fr.locations&&(Sr=br,++Ar);break;default:r+=String.fromCharCode(n)}}else(13===n||10===n||8232===n||8233===n)&&t(yr,"Unterminated string constant"),r+=String.fromCharCode(n),++br}}function S(e){var r=w(16,e);return null===r&&t(yr,"Bad character escape sequence"),r}function I(){Bt=!1;for(var e,r=!0,n=br;;){var a=dr.charCodeAt(br);if(Yt(a))Bt&&(e+=dr.charAt(br)),++br;else{if(92!==a)break;Bt||(e=dr.slice(n,br)),Bt=!0,117!=dr.charCodeAt(++br)&&t(br,"Expecting Unicode escape sequence \\uXXXX"),++br;var o=S(4),i=String.fromCharCode(o);i||t(br-1,"Invalid Unicode escape"),(r?Qt(o):Yt(o))||t(br-4,"Invalid Unicode escape"),e+=i}r=!1}return Bt?e:dr.slice(n,br)}function L(){var e=I(),r=Fr;return Bt||(Wt(e)?r=lt[e]:(fr.forbidReserved&&(3===fr.ecmaVersion?Mt:zt)(e)||Tr&&Xt(e))&&t(yr,"The keyword '"+e+"' is reserved")),i(r,e)}function U(){Ir=yr,Lr=gr,Ur=kr,g()}function R(e){if(Tr=e,br=Lr,fr.locations)for(;Sr>br;)Sr=dr.lastIndexOf("\n",Sr-2)+1,--Ar;u(),g()}function V(){this.type=null,this.start=yr,this.end=null}function T(){this.start=xr,this.end=null,null!==hr&&(this.source=hr)}function q(){var e=new V;return fr.locations&&(e.loc=new T),fr.ranges&&(e.range=[yr,0]),e}function O(e){var r=new V;return r.start=e.start,fr.locations&&(r.loc=new T,r.loc.start=e.loc.start),fr.ranges&&(r.range=[e.range[0],0]),r}function j(e,r){return e.type=r,e.end=Lr,fr.locations&&(e.loc.end=Ur),fr.ranges&&(e.range[1]=Lr),e}function D(e){return fr.ecmaVersion>=5&&"ExpressionStatement"===e.type&&"Literal"===e.expression.type&&"use strict"===e.expression.value}function F(e){return wr===e?(U(),!0):void 0}function B(){return!fr.strictSemicolons&&(wr===Br||wr===ht||Gt.test(dr.slice(Lr,yr)))}function M(){F(yt)||B()||X()}function z(e){wr===e?U():X()}function X(){t(yr,"Unexpected token")}function N(e){"Identifier"!==e.type&&"MemberExpression"!==e.type&&t(e.start,"Assigning to rvalue"),Tr&&"Identifier"===e.type&&Nt(e.name)&&t(e.start,"Assigning to "+e.name+" in strict mode")}function W(e){Ir=Lr=br,fr.locations&&(Ur=new a),Rr=Tr=null,Vr=[],g();var r=e||q(),t=!0;for(e||(r.body=[]);wr!==Br;){var n=J();r.body.push(n),t&&D(n)&&R(!0),t=!1}return j(r,"Program")}function J(){(wr===wt||wr===Et&&"/="==Cr)&&g(!0);var e=wr,r=q();switch(e){case Mr:case Nr:U();var n=e===Mr;F(yt)||B()?r.label=null:wr!==Fr?X():(r.label=lr(),M());for(var a=0;a<Vr.length;++a){var o=Vr[a];if(null==r.label||o.name===r.label.name){if(null!=o.kind&&(n||"loop"===o.kind))break;if(r.label&&n)break}}return a===Vr.length&&t(r.start,"Unsyntactic "+e.keyword),j(r,n?"BreakStatement":"ContinueStatement");case Wr:return U(),M(),j(r,"DebuggerStatement");case Pr:return U(),Vr.push(Zt),r.body=J(),Vr.pop(),z(tt),r.test=P(),M(),j(r,"DoWhileStatement");case _r:if(U(),Vr.push(Zt),z(mt),wr===yt)return $(r,null);if(wr===rt){var i=q();return U(),G(i,!0),j(i,"VariableDeclaration"),1===i.declarations.length&&F(ut)?_(r,i):$(r,i)}var i=K(!1,!0);return F(ut)?(N(i),_(r,i)):$(r,i);case Gr:return U(),cr(r,!0);case Kr:return U(),r.test=P(),r.consequent=J(),r.alternate=F(Hr)?J():null,j(r,"IfStatement");case Qr:return Rr||t(yr,"'return' outside of function"),U(),F(yt)||B()?r.argument=null:(r.argument=K(),M()),j(r,"ReturnStatement");case Yr:U(),r.discriminant=P(),r.cases=[],z(pt),Vr.push(en);for(var s,c;wr!=ht;)if(wr===zr||wr===Jr){var u=wr===zr;s&&j(s,"SwitchCase"),r.cases.push(s=q()),s.consequent=[],U(),u?s.test=K():(c&&t(Ir,"Multiple default clauses"),c=!0,s.test=null),z(gt)}else s||X(),s.consequent.push(J());return s&&j(s,"SwitchCase"),U(),Vr.pop(),j(r,"SwitchStatement");case Zr:return U(),Gt.test(dr.slice(Lr,yr))&&t(Lr,"Illegal newline after throw"),r.argument=K(),M(),j(r,"ThrowStatement");case et:if(U(),r.block=H(),r.handler=null,wr===Xr){var l=q();U(),z(mt),l.param=lr(),Tr&&Nt(l.param.name)&&t(l.param.start,"Binding "+l.param.name+" in strict mode"),z(vt),l.guard=null,l.body=H(),r.handler=j(l,"CatchClause")}return r.guardedHandlers=qr,r.finalizer=F($r)?H():null,r.handler||r.finalizer||t(r.start,"Missing catch or finally clause"),j(r,"TryStatement");case rt:return U(),G(r),M(),j(r,"VariableDeclaration");case tt:return U(),r.test=P(),Vr.push(Zt),r.body=J(),Vr.pop(),j(r,"WhileStatement");case nt:return Tr&&t(yr,"'with' in strict mode"),U(),r.object=P(),r.body=J(),j(r,"WithStatement");case pt:return H();case yt:return U(),j(r,"EmptyStatement");default:var f=Cr,d=K();if(e===Fr&&"Identifier"===d.type&&F(gt)){for(var a=0;a<Vr.length;++a)Vr[a].name===f&&t(d.start,"Label '"+f+"' is already declared");var p=wr.isLoop?"loop":wr===Yr?"switch":null;return Vr.push({name:f,kind:p}),r.body=J(),Vr.pop(),r.label=d,j(r,"LabeledStatement")}return r.expression=d,M(),j(r,"ExpressionStatement")}}function P(){z(mt);var e=K();return z(vt),e}function H(e){var r,t=q(),n=!0,a=!1;for(t.body=[],z(pt);!F(ht);){var o=J();t.body.push(o),n&&e&&D(o)&&(r=a,R(a=!0)),n=!1}return a&&!r&&R(!1),j(t,"BlockStatement")}function $(e,r){return e.init=r,z(yt),e.test=wr===yt?null:K(),z(yt),e.update=wr===vt?null:K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForStatement")}function _(e,r){return e.left=r,e.right=K(),z(vt),e.body=J(),Vr.pop(),j(e,"ForInStatement")}function G(e,r){for(e.declarations=[],e.kind="var";;){var n=q();if(n.id=lr(),Tr&&Nt(n.id.name)&&t(n.id.start,"Binding "+n.id.name+" in strict mode"),n.init=F(Ct)?K(!0,r):null,e.declarations.push(j(n,"VariableDeclarator")),!F(bt))break}return e}function K(e,r){var t=Q(r);if(!e&&wr===bt){var n=O(t);for(n.expressions=[t];F(bt);)n.expressions.push(Q(r));return j(n,"SequenceExpression")}return t}function Q(e){var r=Y(e);if(wr.isAssign){var t=O(r);return t.operator=Cr,t.left=r,U(),t.right=Q(e),N(r),j(t,"AssignmentExpression")}return r}function Y(e){var r=Z(e);if(F(kt)){var t=O(r);return t.test=r,t.consequent=K(!0),z(gt),t.alternate=K(!0,e),j(t,"ConditionalExpression")}return r}function Z(e){return er(rr(),-1,e)}function er(e,r,t){var n=wr.binop;if(null!=n&&(!t||wr!==ut)&&n>r){var a=O(e);a.left=e,a.operator=Cr,U(),a.right=er(rr(),n,t);var o=j(a,/&&|\|\|/.test(a.operator)?"LogicalExpression":"BinaryExpression");return er(o,r,t)}return e}function rr(){if(wr.prefix){var e=q(),r=wr.isUpdate;return e.operator=Cr,e.prefix=!0,Er=!0,U(),e.argument=rr(),r?N(e.argument):Tr&&"delete"===e.operator&&"Identifier"===e.argument.type&&t(e.start,"Deleting local variable in strict mode"),j(e,r?"UpdateExpression":"UnaryExpression")}for(var n=tr();wr.postfix&&!B();){var e=O(n);e.operator=Cr,e.prefix=!1,e.argument=n,N(n),U(),n=j(e,"UpdateExpression")}return n}function tr(){return nr(ar())}function nr(e,r){if(F(xt)){var t=O(e);return t.object=e,t.property=lr(!0),t.computed=!1,nr(j(t,"MemberExpression"),r)}if(F(ft)){var t=O(e);return t.object=e,t.property=K(),t.computed=!0,z(dt),nr(j(t,"MemberExpression"),r)}if(!r&&F(mt)){var t=O(e);return t.callee=e,t.arguments=ur(vt,!1),nr(j(t,"CallExpression"),r)}return e}function ar(){switch(wr){case ot:var e=q();return U(),j(e,"ThisExpression");case Fr:return lr();case Or:case Dr:case jr:var e=q();return e.value=Cr,e.raw=dr.slice(yr,gr),U(),j(e,"Literal");case it:case st:case ct:var e=q();return e.value=wr.atomValue,e.raw=wr.keyword,U(),j(e,"Literal");case mt:var r=xr,t=yr;U();var n=K();return n.start=t,n.end=gr,fr.locations&&(n.loc.start=r,n.loc.end=kr),fr.ranges&&(n.range=[t,gr]),z(vt),n;case ft:var e=q();return U(),e.elements=ur(dt,!0,!0),j(e,"ArrayExpression");case pt:return ir();case Gr:var e=q();return U(),cr(e,!1);case at:return or();default:X()}}function or(){var e=q();return U(),e.callee=nr(ar(),!0),e.arguments=F(mt)?ur(vt,!1):qr,j(e,"NewExpression")}function ir(){var e=q(),r=!0,n=!1;for(e.properties=[],U();!F(ht);){if(r)r=!1;else if(z(bt),fr.allowTrailingCommas&&F(ht))break;var a,o={key:sr()},i=!1;if(F(gt)?(o.value=K(!0),a=o.kind="init"):fr.ecmaVersion>=5&&"Identifier"===o.key.type&&("get"===o.key.name||"set"===o.key.name)?(i=n=!0,a=o.kind=o.key.name,o.key=sr(),wr!==mt&&X(),o.value=cr(q(),!1)):X(),"Identifier"===o.key.type&&(Tr||n))for(var s=0;s<e.properties.length;++s){var c=e.properties[s];if(c.key.name===o.key.name){var u=a==c.kind||i&&"init"===c.kind||"init"===a&&("get"===c.kind||"set"===c.kind);u&&!Tr&&"init"===a&&"init"===c.kind&&(u=!1),u&&t(o.key.start,"Redefinition of property")}}e.properties.push(o)}return j(e,"ObjectExpression")}function sr(){return wr===Or||wr===Dr?ar():lr(!0)}function cr(e,r){wr===Fr?e.id=lr():r?X():e.id=null,e.params=[];var n=!0;for(z(mt);!F(vt);)n?n=!1:z(bt),e.params.push(lr());var a=Rr,o=Vr;if(Rr=!0,Vr=[],e.body=H(!0),Rr=a,Vr=o,Tr||e.body.body.length&&D(e.body.body[0]))for(var i=e.id?-1:0;i<e.params.length;++i){var s=0>i?e.id:e.params[i];if((Xt(s.name)||Nt(s.name))&&t(s.start,"Defining '"+s.name+"' in strict mode"),i>=0)for(var c=0;i>c;++c)s.name===e.params[c].name&&t(s.start,"Argument name clash in strict mode")}return j(e,r?"FunctionDeclaration":"FunctionExpression")}function ur(e,r,t){for(var n=[],a=!0;!F(e);){if(a)a=!1;else if(z(bt),r&&fr.allowTrailingCommas&&F(e))break;t&&wr===bt?n.push(null):n.push(K(!0))}return n}function lr(e){var r=q();return r.name=wr===Fr?Cr:e&&!fr.forbidReserved&&wr.keyword||X(),Er=!1,U(),j(r,"Identifier")}e.version="0.4.0";var fr,dr,pr,hr;e.parse=function(e,t){return dr=String(e),pr=dr.length,r(t),o(),W(fr.program)};var mr=e.defaultOptions={ecmaVersion:5,strictSemicolons:!1,allowTrailingCommas:!0,forbidReserved:!1,locations:!1,onComment:null,ranges:!1,program:null,sourceFile:null},vr=e.getLineInfo=function(e,r){for(var t=1,n=0;;){Kt.lastIndex=n;var a=Kt.exec(e);if(!(a&&a.index<r))break;++t,n=a.index+a[0].length}return{line:t,column:r-n}};e.tokenize=function(e,t){function n(e){return g(e),a.start=yr,a.end=gr,a.startLoc=xr,a.endLoc=kr,a.type=wr,a.value=Cr,a}dr=String(e),pr=dr.length,r(t),o();var a={};return n.jumpTo=function(e,r){if(br=e,fr.locations){Ar=1,Sr=Kt.lastIndex=0;for(var t;(t=Kt.exec(dr))&&t.index<e;)++Ar,Sr=t.index+t[0].length}Er=r,u()},n};var br,yr,gr,xr,kr,wr,Cr,Er,Ar,Sr,Ir,Lr,Ur,Rr,Vr,Tr,qr=[],Or={type:"num"},jr={type:"regexp"},Dr={type:"string"},Fr={type:"name"},Br={type:"eof"},Mr={keyword:"break"},zr={keyword:"case",beforeExpr:!0},Xr={keyword:"catch"},Nr={keyword:"continue"},Wr={keyword:"debugger"},Jr={keyword:"default"},Pr={keyword:"do",isLoop:!0},Hr={keyword:"else",beforeExpr:!0},$r={keyword:"finally"},_r={keyword:"for",isLoop:!0},Gr={keyword:"function"},Kr={keyword:"if"},Qr={keyword:"return",beforeExpr:!0},Yr={keyword:"switch"},Zr={keyword:"throw",beforeExpr:!0},et={keyword:"try"},rt={keyword:"var"},tt={keyword:"while",isLoop:!0},nt={keyword:"with"},at={keyword:"new",beforeExpr:!0},ot={keyword:"this"},it={keyword:"null",atomValue:null},st={keyword:"true",atomValue:!0},ct={keyword:"false",atomValue:!1},ut={keyword:"in",binop:7,beforeExpr:!0},lt={"break":Mr,"case":zr,"catch":Xr,"continue":Nr,"debugger":Wr,"default":Jr,"do":Pr,"else":Hr,"finally":$r,"for":_r,"function":Gr,"if":Kr,"return":Qr,"switch":Yr,"throw":Zr,"try":et,"var":rt,"while":tt,"with":nt,"null":it,"true":st,"false":ct,"new":at,"in":ut,"instanceof":{keyword:"instanceof",binop:7,beforeExpr:!0},"this":ot,"typeof":{keyword:"typeof",prefix:!0,beforeExpr:!0},"void":{keyword:"void",prefix:!0,beforeExpr:!0},"delete":{keyword:"delete",prefix:!0,beforeExpr:!0}},ft={type:"[",beforeExpr:!0},dt={type:"]"},pt={type:"{",beforeExpr:!0},ht={type:"}"},mt={type:"(",beforeExpr:!0},vt={type:")"},bt={type:",",beforeExpr:!0},yt={type:";",beforeExpr:!0},gt={type:":",beforeExpr:!0},xt={type:"."},kt={type:"?",beforeExpr:!0},wt={binop:10,beforeExpr:!0},Ct={isAssign:!0,beforeExpr:!0},Et={isAssign:!0,beforeExpr:!0},At={binop:9,prefix:!0,beforeExpr:!0},St={postfix:!0,prefix:!0,isUpdate:!0},It={prefix:!0,beforeExpr:!0},Lt={binop:1,beforeExpr:!0},Ut={binop:2,beforeExpr:!0},Rt={binop:3,beforeExpr:!0},Vt={binop:4,beforeExpr:!0},Tt={binop:5,beforeExpr:!0},qt={binop:6,beforeExpr:!0},Ot={binop:7,beforeExpr:!0},jt={binop:8,beforeExpr:!0},Dt={binop:10,beforeExpr:!0};e.tokTypes={bracketL:ft,bracketR:dt,braceL:pt,braceR:ht,parenL:mt,parenR:vt,comma:bt,semi:yt,colon:gt,dot:xt,question:kt,slash:wt,eq:Ct,name:Fr,eof:Br,num:Or,regexp:jr,string:Dr};for(var Ft in lt)e.tokTypes["_"+Ft]=lt[Ft];var Bt,Mt=n("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),zt=n("class enum extends super const export import"),Xt=n("implements interface let package private protected public static yield"),Nt=n("eval arguments"),Wt=n("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"),Jt=/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/,Pt="\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc",Ht="\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f",$t=new RegExp("["+Pt+"]"),_t=new RegExp("["+Pt+Ht+"]"),Gt=/[\n\r\u2028\u2029]/,Kt=/\r\n|[\n\r\u2028\u2029]/g,Qt=e.isIdentifierStart=function(e){return 65>e?36===e:91>e?!0:97>e?95===e:123>e?!0:e>=170&&$t.test(String.fromCharCode(e))},Yt=e.isIdentifierChar=function(e){return 48>e?36===e:58>e?!0:65>e?!1:91>e?!0:97>e?95===e:123>e?!0:e>=170&&_t.test(String.fromCharCode(e))},Zt={kind:"loop"},en={kind:"switch"}});
+Base.exports.PaperScript = function() {
+ var global = this,
+ acorn = global.acorn;
+ if (!acorn && typeof require !== 'undefined') {
+ try { acorn = require('acorn'); } catch(e) {}
+ }
+ if (!acorn) {
+ var exports, module;
+ acorn = exports = module = {};
+
+(function(root, mod) {
+ if (typeof exports == "object" && typeof module == "object") return mod(exports);
+ if (typeof define == "function" && define.amd) return define(["exports"], mod);
+ mod(root.acorn || (root.acorn = {}));
+})(this, function(exports) {
+ "use strict";
+
+ exports.version = "0.5.0";
+
+ var options, input, inputLen, sourceFile;
+
+ exports.parse = function(inpt, opts) {
+ input = String(inpt); inputLen = input.length;
+ setOptions(opts);
+ initTokenState();
+ return parseTopLevel(options.program);
+ };
+
+ var defaultOptions = exports.defaultOptions = {
+ ecmaVersion: 5,
+ strictSemicolons: false,
+ allowTrailingCommas: true,
+ forbidReserved: false,
+ allowReturnOutsideFunction: false,
+ locations: false,
+ onComment: null,
+ ranges: false,
+ program: null,
+ sourceFile: null,
+ directSourceFile: null
+ };
+
+ function setOptions(opts) {
+ options = opts || {};
+ for (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt))
+ options[opt] = defaultOptions[opt];
+ sourceFile = options.sourceFile || null;
+ }
+
+ var getLineInfo = exports.getLineInfo = function(input, offset) {
+ for (var line = 1, cur = 0;;) {
+ lineBreak.lastIndex = cur;
+ var match = lineBreak.exec(input);
+ if (match && match.index < offset) {
+ ++line;
+ cur = match.index + match[0].length;
+ } else break;
+ }
+ return {line: line, column: offset - cur};
+ };
+
+ exports.tokenize = function(inpt, opts) {
+ input = String(inpt); inputLen = input.length;
+ setOptions(opts);
+ initTokenState();
+
+ var t = {};
+ function getToken(forceRegexp) {
+ lastEnd = tokEnd;
+ readToken(forceRegexp);
+ t.start = tokStart; t.end = tokEnd;
+ t.startLoc = tokStartLoc; t.endLoc = tokEndLoc;
+ t.type = tokType; t.value = tokVal;
+ return t;
+ }
+ getToken.jumpTo = function(pos, reAllowed) {
+ tokPos = pos;
+ if (options.locations) {
+ tokCurLine = 1;
+ tokLineStart = lineBreak.lastIndex = 0;
+ var match;
+ while ((match = lineBreak.exec(input)) && match.index < pos) {
+ ++tokCurLine;
+ tokLineStart = match.index + match[0].length;
+ }
+ }
+ tokRegexpAllowed = reAllowed;
+ skipSpace();
+ };
+ return getToken;
+ };
+
+ var tokPos;
+
+ var tokStart, tokEnd;
+
+ var tokStartLoc, tokEndLoc;
+
+ var tokType, tokVal;
+
+ var tokRegexpAllowed;
+
+ var tokCurLine, tokLineStart;
+
+ var lastStart, lastEnd, lastEndLoc;
+
+ var inFunction, labels, strict;
+
+ function raise(pos, message) {
+ var loc = getLineInfo(input, pos);
+ message += " (" + loc.line + ":" + loc.column + ")";
+ var err = new SyntaxError(message);
+ err.pos = pos; err.loc = loc; err.raisedAt = tokPos;
+ throw err;
+ }
+
+ var empty = [];
+
+ var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"};
+ var _name = {type: "name"}, _eof = {type: "eof"};
+
+ var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"};
+ var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"};
+ var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true};
+ var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"};
+ var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"};
+ var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"};
+ var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true};
+ var _this = {keyword: "this"};
+
+ var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true};
+ var _false = {keyword: "false", atomValue: false};
+
+ var _in = {keyword: "in", binop: 7, beforeExpr: true};
+
+ var keywordTypes = {"break": _break, "case": _case, "catch": _catch,
+ "continue": _continue, "debugger": _debugger, "default": _default,
+ "do": _do, "else": _else, "finally": _finally, "for": _for,
+ "function": _function, "if": _if, "return": _return, "switch": _switch,
+ "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with,
+ "null": _null, "true": _true, "false": _false, "new": _new, "in": _in,
+ "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this,
+ "typeof": {keyword: "typeof", prefix: true, beforeExpr: true},
+ "void": {keyword: "void", prefix: true, beforeExpr: true},
+ "delete": {keyword: "delete", prefix: true, beforeExpr: true}};
+
+ var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true};
+ var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"};
+ var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true};
+ var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true};
+
+ var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true};
+ var _assign = {isAssign: true, beforeExpr: true};
+ var _incDec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true};
+ var _logicalOR = {binop: 1, beforeExpr: true};
+ var _logicalAND = {binop: 2, beforeExpr: true};
+ var _bitwiseOR = {binop: 3, beforeExpr: true};
+ var _bitwiseXOR = {binop: 4, beforeExpr: true};
+ var _bitwiseAND = {binop: 5, beforeExpr: true};
+ var _equality = {binop: 6, beforeExpr: true};
+ var _relational = {binop: 7, beforeExpr: true};
+ var _bitShift = {binop: 8, beforeExpr: true};
+ var _plusMin = {binop: 9, prefix: true, beforeExpr: true};
+ var _multiplyModulo = {binop: 10, beforeExpr: true};
+
+ exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR,
+ parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon,
+ dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof,
+ num: _num, regexp: _regexp, string: _string};
+ for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw];
+
+ function makePredicate(words) {
+ words = words.split(" ");
+ var f = "", cats = [];
+ out: for (var i = 0; i < words.length; ++i) {
+ for (var j = 0; j < cats.length; ++j)
+ if (cats[j][0].length == words[i].length) {
+ cats[j].push(words[i]);
+ continue out;
+ }
+ cats.push([words[i]]);
+ }
+ function compareTo(arr) {
+ if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
+ f += "switch(str){";
+ for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
+ f += "return true}return false;";
+ }
+
+ if (cats.length > 3) {
+ cats.sort(function(a, b) {return b.length - a.length;});
+ f += "switch(str.length){";
+ for (var i = 0; i < cats.length; ++i) {
+ var cat = cats[i];
+ f += "case " + cat[0].length + ":";
+ compareTo(cat);
+ }
+ f += "}";
+
+ } else {
+ compareTo(words);
+ }
+ return new Function("str", f);
+ }
+
+ var isReservedWord3 = makePredicate("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");
+
+ var isReservedWord5 = makePredicate("class enum extends super const export import");
+
+ var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield");
+
+ var isStrictBadIdWord = makePredicate("eval arguments");
+
+ var isKeyword = makePredicate("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");
+
+ var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
+ var nonASCIIidentifierStartChars = "\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";
+ var nonASCIIidentifierChars = "\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";
+ var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
+ var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
+
+ var newline = /[\n\r\u2028\u2029]/;
+
+ var lineBreak = /\r\n|[\n\r\u2028\u2029]/g;
+
+ var isIdentifierStart = exports.isIdentifierStart = function(code) {
+ if (code < 65) return code === 36;
+ if (code < 91) return true;
+ if (code < 97) return code === 95;
+ if (code < 123)return true;
+ return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
+ };
+
+ var isIdentifierChar = exports.isIdentifierChar = function(code) {
+ if (code < 48) return code === 36;
+ if (code < 58) return true;
+ if (code < 65) return false;
+ if (code < 91) return true;
+ if (code < 97) return code === 95;
+ if (code < 123)return true;
+ return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
+ };
+
+ function line_loc_t() {
+ this.line = tokCurLine;
+ this.column = tokPos - tokLineStart;
+ }
+
+ function initTokenState() {
+ tokCurLine = 1;
+ tokPos = tokLineStart = 0;
+ tokRegexpAllowed = true;
+ skipSpace();
+ }
+
+ function finishToken(type, val) {
+ tokEnd = tokPos;
+ if (options.locations) tokEndLoc = new line_loc_t;
+ tokType = type;
+ skipSpace();
+ tokVal = val;
+ tokRegexpAllowed = type.beforeExpr;
+ }
+
+ function skipBlockComment() {
+ var startLoc = options.onComment && options.locations && new line_loc_t;
+ var start = tokPos, end = input.indexOf("*/", tokPos += 2);
+ if (end === -1) raise(tokPos - 2, "Unterminated comment");
+ tokPos = end + 2;
+ if (options.locations) {
+ lineBreak.lastIndex = start;
+ var match;
+ while ((match = lineBreak.exec(input)) && match.index < tokPos) {
+ ++tokCurLine;
+ tokLineStart = match.index + match[0].length;
+ }
+ }
+ if (options.onComment)
+ options.onComment(true, input.slice(start + 2, end), start, tokPos,
+ startLoc, options.locations && new line_loc_t);
+ }
+
+ function skipLineComment() {
+ var start = tokPos;
+ var startLoc = options.onComment && options.locations && new line_loc_t;
+ var ch = input.charCodeAt(tokPos+=2);
+ while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
+ ++tokPos;
+ ch = input.charCodeAt(tokPos);
+ }
+ if (options.onComment)
+ options.onComment(false, input.slice(start + 2, tokPos), start, tokPos,
+ startLoc, options.locations && new line_loc_t);
+ }
+
+ function skipSpace() {
+ while (tokPos < inputLen) {
+ var ch = input.charCodeAt(tokPos);
+ if (ch === 32) {
+ ++tokPos;
+ } else if (ch === 13) {
+ ++tokPos;
+ var next = input.charCodeAt(tokPos);
+ if (next === 10) {
+ ++tokPos;
+ }
+ if (options.locations) {
+ ++tokCurLine;
+ tokLineStart = tokPos;
+ }
+ } else if (ch === 10 || ch === 8232 || ch === 8233) {
+ ++tokPos;
+ if (options.locations) {
+ ++tokCurLine;
+ tokLineStart = tokPos;
+ }
+ } else if (ch > 8 && ch < 14) {
+ ++tokPos;
+ } else if (ch === 47) {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === 42) {
+ skipBlockComment();
+ } else if (next === 47) {
+ skipLineComment();
+ } else break;
+ } else if (ch === 160) {
+ ++tokPos;
+ } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
+ ++tokPos;
+ } else {
+ break;
+ }
+ }
+ }
+
+ function readToken_dot() {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next >= 48 && next <= 57) return readNumber(true);
+ ++tokPos;
+ return finishToken(_dot);
+ }
+
+ function readToken_slash() {
+ var next = input.charCodeAt(tokPos + 1);
+ if (tokRegexpAllowed) {++tokPos; return readRegexp();}
+ if (next === 61) return finishOp(_assign, 2);
+ return finishOp(_slash, 1);
+ }
+
+ function readToken_mult_modulo() {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === 61) return finishOp(_assign, 2);
+ return finishOp(_multiplyModulo, 1);
+ }
+
+ function readToken_pipe_amp(code) {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === code) return finishOp(code === 124 ? _logicalOR : _logicalAND, 2);
+ if (next === 61) return finishOp(_assign, 2);
+ return finishOp(code === 124 ? _bitwiseOR : _bitwiseAND, 1);
+ }
+
+ function readToken_caret() {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === 61) return finishOp(_assign, 2);
+ return finishOp(_bitwiseXOR, 1);
+ }
+
+ function readToken_plus_min(code) {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === code) {
+ if (next == 45 && input.charCodeAt(tokPos + 2) == 62 &&
+ newline.test(input.slice(lastEnd, tokPos))) {
+ tokPos += 3;
+ skipLineComment();
+ skipSpace();
+ return readToken();
+ }
+ return finishOp(_incDec, 2);
+ }
+ if (next === 61) return finishOp(_assign, 2);
+ return finishOp(_plusMin, 1);
+ }
+
+ function readToken_lt_gt(code) {
+ var next = input.charCodeAt(tokPos + 1);
+ var size = 1;
+ if (next === code) {
+ size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2;
+ if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1);
+ return finishOp(_bitShift, size);
+ }
+ if (next == 33 && code == 60 && input.charCodeAt(tokPos + 2) == 45 &&
+ input.charCodeAt(tokPos + 3) == 45) {
+ tokPos += 4;
+ skipLineComment();
+ skipSpace();
+ return readToken();
+ }
+ if (next === 61)
+ size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2;
+ return finishOp(_relational, size);
+ }
+
+ function readToken_eq_excl(code) {
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === 61) return finishOp(_equality, input.charCodeAt(tokPos + 2) === 61 ? 3 : 2);
+ return finishOp(code === 61 ? _eq : _prefix, 1);
+ }
+
+ function getTokenFromCode(code) {
+ switch(code) {
+ case 46:
+ return readToken_dot();
+
+ case 40: ++tokPos; return finishToken(_parenL);
+ case 41: ++tokPos; return finishToken(_parenR);
+ case 59: ++tokPos; return finishToken(_semi);
+ case 44: ++tokPos; return finishToken(_comma);
+ case 91: ++tokPos; return finishToken(_bracketL);
+ case 93: ++tokPos; return finishToken(_bracketR);
+ case 123: ++tokPos; return finishToken(_braceL);
+ case 125: ++tokPos; return finishToken(_braceR);
+ case 58: ++tokPos; return finishToken(_colon);
+ case 63: ++tokPos; return finishToken(_question);
+
+ case 48:
+ var next = input.charCodeAt(tokPos + 1);
+ if (next === 120 || next === 88) return readHexNumber();
+ case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57:
+ return readNumber(false);
+
+ case 34: case 39:
+ return readString(code);
+
+ case 47:
+ return readToken_slash(code);
+
+ case 37: case 42:
+ return readToken_mult_modulo();
+
+ case 124: case 38:
+ return readToken_pipe_amp(code);
+
+ case 94:
+ return readToken_caret();
+
+ case 43: case 45:
+ return readToken_plus_min(code);
+
+ case 60: case 62:
+ return readToken_lt_gt(code);
+
+ case 61: case 33:
+ return readToken_eq_excl(code);
+
+ case 126:
+ return finishOp(_prefix, 1);
+ }
+
+ return false;
+ }
+
+ function readToken(forceRegexp) {
+ if (!forceRegexp) tokStart = tokPos;
+ else tokPos = tokStart + 1;
+ if (options.locations) tokStartLoc = new line_loc_t;
+ if (forceRegexp) return readRegexp();
+ if (tokPos >= inputLen) return finishToken(_eof);
+
+ var code = input.charCodeAt(tokPos);
+ if (isIdentifierStart(code) || code === 92 ) return readWord();
+
+ var tok = getTokenFromCode(code);
+
+ if (tok === false) {
+ var ch = String.fromCharCode(code);
+ if (ch === "\\" || nonASCIIidentifierStart.test(ch)) return readWord();
+ raise(tokPos, "Unexpected character '" + ch + "'");
+ }
+ return tok;
+ }
+
+ function finishOp(type, size) {
+ var str = input.slice(tokPos, tokPos + size);
+ tokPos += size;
+ finishToken(type, str);
+ }
+
+ function readRegexp() {
+ var content = "", escaped, inClass, start = tokPos;
+ for (;;) {
+ if (tokPos >= inputLen) raise(start, "Unterminated regular expression");
+ var ch = input.charAt(tokPos);
+ if (newline.test(ch)) raise(start, "Unterminated regular expression");
+ if (!escaped) {
+ if (ch === "[") inClass = true;
+ else if (ch === "]" && inClass) inClass = false;
+ else if (ch === "/" && !inClass) break;
+ escaped = ch === "\\";
+ } else escaped = false;
+ ++tokPos;
+ }
+ var content = input.slice(start, tokPos);
+ ++tokPos;
+ var mods = readWord1();
+ if (mods && !/^[gmsiy]*$/.test(mods)) raise(start, "Invalid regexp flag");
+ try {
+ var value = new RegExp(content, mods);
+ } catch (e) {
+ if (e instanceof SyntaxError) raise(start, e.message);
+ raise(e);
+ }
+ return finishToken(_regexp, value);
+ }
+
+ function readInt(radix, len) {
+ var start = tokPos, total = 0;
+ for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
+ var code = input.charCodeAt(tokPos), val;
+ if (code >= 97) val = code - 97 + 10;
+ else if (code >= 65) val = code - 65 + 10;
+ else if (code >= 48 && code <= 57) val = code - 48;
+ else val = Infinity;
+ if (val >= radix) break;
+ ++tokPos;
+ total = total * radix + val;
+ }
+ if (tokPos === start || len != null && tokPos - start !== len) return null;
+
+ return total;
+ }
+
+ function readHexNumber() {
+ tokPos += 2;
+ var val = readInt(16);
+ if (val == null) raise(tokStart + 2, "Expected hexadecimal number");
+ if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number");
+ return finishToken(_num, val);
+ }
+
+ function readNumber(startsWithDot) {
+ var start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48;
+ if (!startsWithDot && readInt(10) === null) raise(start, "Invalid number");
+ if (input.charCodeAt(tokPos) === 46) {
+ ++tokPos;
+ readInt(10);
+ isFloat = true;
+ }
+ var next = input.charCodeAt(tokPos);
+ if (next === 69 || next === 101) {
+ next = input.charCodeAt(++tokPos);
+ if (next === 43 || next === 45) ++tokPos;
+ if (readInt(10) === null) raise(start, "Invalid number");
+ isFloat = true;
+ }
+ if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number");
+
+ var str = input.slice(start, tokPos), val;
+ if (isFloat) val = parseFloat(str);
+ else if (!octal || str.length === 1) val = parseInt(str, 10);
+ else if (/[89]/.test(str) || strict) raise(start, "Invalid number");
+ else val = parseInt(str, 8);
+ return finishToken(_num, val);
+ }
+
+ function readString(quote) {
+ tokPos++;
+ var out = "";
+ for (;;) {
+ if (tokPos >= inputLen) raise(tokStart, "Unterminated string constant");
+ var ch = input.charCodeAt(tokPos);
+ if (ch === quote) {
+ ++tokPos;
+ return finishToken(_string, out);
+ }
+ if (ch === 92) {
+ ch = input.charCodeAt(++tokPos);
+ var octal = /^[0-7]+/.exec(input.slice(tokPos, tokPos + 3));
+ if (octal) octal = octal[0];
+ while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1);
+ if (octal === "0") octal = null;
+ ++tokPos;
+ if (octal) {
+ if (strict) raise(tokPos - 2, "Octal literal in strict mode");
+ out += String.fromCharCode(parseInt(octal, 8));
+ tokPos += octal.length - 1;
+ } else {
+ switch (ch) {
+ case 110: out += "\n"; break;
+ case 114: out += "\r"; break;
+ case 120: out += String.fromCharCode(readHexChar(2)); break;
+ case 117: out += String.fromCharCode(readHexChar(4)); break;
+ case 85: out += String.fromCharCode(readHexChar(8)); break;
+ case 116: out += "\t"; break;
+ case 98: out += "\b"; break;
+ case 118: out += "\u000b"; break;
+ case 102: out += "\f"; break;
+ case 48: out += "\0"; break;
+ case 13: if (input.charCodeAt(tokPos) === 10) ++tokPos;
+ case 10:
+ if (options.locations) { tokLineStart = tokPos; ++tokCurLine; }
+ break;
+ default: out += String.fromCharCode(ch); break;
+ }
+ }
+ } else {
+ if (ch === 13 || ch === 10 || ch === 8232 || ch === 8233) raise(tokStart, "Unterminated string constant");
+ out += String.fromCharCode(ch);
+ ++tokPos;
+ }
+ }
+ }
+
+ function readHexChar(len) {
+ var n = readInt(16, len);
+ if (n === null) raise(tokStart, "Bad character escape sequence");
+ return n;
+ }
+
+ var containsEsc;
+
+ function readWord1() {
+ containsEsc = false;
+ var word, first = true, start = tokPos;
+ for (;;) {
+ var ch = input.charCodeAt(tokPos);
+ if (isIdentifierChar(ch)) {
+ if (containsEsc) word += input.charAt(tokPos);
+ ++tokPos;
+ } else if (ch === 92) {
+ if (!containsEsc) word = input.slice(start, tokPos);
+ containsEsc = true;
+ if (input.charCodeAt(++tokPos) != 117)
+ raise(tokPos, "Expecting Unicode escape sequence \\uXXXX");
+ ++tokPos;
+ var esc = readHexChar(4);
+ var escStr = String.fromCharCode(esc);
+ if (!escStr) raise(tokPos - 1, "Invalid Unicode escape");
+ if (!(first ? isIdentifierStart(esc) : isIdentifierChar(esc)))
+ raise(tokPos - 4, "Invalid Unicode escape");
+ word += escStr;
+ } else {
+ break;
+ }
+ first = false;
+ }
+ return containsEsc ? word : input.slice(start, tokPos);
+ }
+
+ function readWord() {
+ var word = readWord1();
+ var type = _name;
+ if (!containsEsc && isKeyword(word))
+ type = keywordTypes[word];
+ return finishToken(type, word);
+ }
+
+ function next() {
+ lastStart = tokStart;
+ lastEnd = tokEnd;
+ lastEndLoc = tokEndLoc;
+ readToken();
+ }
+
+ function setStrict(strct) {
+ strict = strct;
+ tokPos = tokStart;
+ if (options.locations) {
+ while (tokPos < tokLineStart) {
+ tokLineStart = input.lastIndexOf("\n", tokLineStart - 2) + 1;
+ --tokCurLine;
+ }
+ }
+ skipSpace();
+ readToken();
+ }
+
+ function node_t() {
+ this.type = null;
+ this.start = tokStart;
+ this.end = null;
+ }
+
+ function node_loc_t() {
+ this.start = tokStartLoc;
+ this.end = null;
+ if (sourceFile !== null) this.source = sourceFile;
+ }
+
+ function startNode() {
+ var node = new node_t();
+ if (options.locations)
+ node.loc = new node_loc_t();
+ if (options.directSourceFile)
+ node.sourceFile = options.directSourceFile;
+ if (options.ranges)
+ node.range = [tokStart, 0];
+ return node;
+ }
+
+ function startNodeFrom(other) {
+ var node = new node_t();
+ node.start = other.start;
+ if (options.locations) {
+ node.loc = new node_loc_t();
+ node.loc.start = other.loc.start;
+ }
+ if (options.ranges)
+ node.range = [other.range[0], 0];
+
+ return node;
+ }
+
+ function finishNode(node, type) {
+ node.type = type;
+ node.end = lastEnd;
+ if (options.locations)
+ node.loc.end = lastEndLoc;
+ if (options.ranges)
+ node.range[1] = lastEnd;
+ return node;
+ }
+
+ function isUseStrict(stmt) {
+ return options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
+ stmt.expression.type === "Literal" && stmt.expression.value === "use strict";
+ }
+
+ function eat(type) {
+ if (tokType === type) {
+ next();
+ return true;
+ }
+ }
+
+ function canInsertSemicolon() {
+ return !options.strictSemicolons &&
+ (tokType === _eof || tokType === _braceR || newline.test(input.slice(lastEnd, tokStart)));
+ }
+
+ function semicolon() {
+ if (!eat(_semi) && !canInsertSemicolon()) unexpected();
+ }
+
+ function expect(type) {
+ if (tokType === type) next();
+ else unexpected();
+ }
+
+ function unexpected() {
+ raise(tokStart, "Unexpected token");
+ }
+
+ function checkLVal(expr) {
+ if (expr.type !== "Identifier" && expr.type !== "MemberExpression")
+ raise(expr.start, "Assigning to rvalue");
+ if (strict && expr.type === "Identifier" && isStrictBadIdWord(expr.name))
+ raise(expr.start, "Assigning to " + expr.name + " in strict mode");
+ }
+
+ function parseTopLevel(program) {
+ lastStart = lastEnd = tokPos;
+ if (options.locations) lastEndLoc = new line_loc_t;
+ inFunction = strict = null;
+ labels = [];
+ readToken();
+
+ var node = program || startNode(), first = true;
+ if (!program) node.body = [];
+ while (tokType !== _eof) {
+ var stmt = parseStatement();
+ node.body.push(stmt);
+ if (first && isUseStrict(stmt)) setStrict(true);
+ first = false;
+ }
+ return finishNode(node, "Program");
+ }
+
+ var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"};
+
+ function parseStatement() {
+ if (tokType === _slash || tokType === _assign && tokVal == "/=")
+ readToken(true);
+
+ var starttype = tokType, node = startNode();
+
+ switch (starttype) {
+ case _break: case _continue:
+ next();
+ var isBreak = starttype === _break;
+ if (eat(_semi) || canInsertSemicolon()) node.label = null;
+ else if (tokType !== _name) unexpected();
+ else {
+ node.label = parseIdent();
+ semicolon();
+ }
+
+ for (var i = 0; i < labels.length; ++i) {
+ var lab = labels[i];
+ if (node.label == null || lab.name === node.label.name) {
+ if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
+ if (node.label && isBreak) break;
+ }
+ }
+ if (i === labels.length) raise(node.start, "Unsyntactic " + starttype.keyword);
+ return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+
+ case _debugger:
+ next();
+ semicolon();
+ return finishNode(node, "DebuggerStatement");
+
+ case _do:
+ next();
+ labels.push(loopLabel);
+ node.body = parseStatement();
+ labels.pop();
+ expect(_while);
+ node.test = parseParenExpression();
+ semicolon();
+ return finishNode(node, "DoWhileStatement");
+
+ case _for:
+ next();
+ labels.push(loopLabel);
+ expect(_parenL);
+ if (tokType === _semi) return parseFor(node, null);
+ if (tokType === _var) {
+ var init = startNode();
+ next();
+ parseVar(init, true);
+ finishNode(init, "VariableDeclaration");
+ if (init.declarations.length === 1 && eat(_in))
+ return parseForIn(node, init);
+ return parseFor(node, init);
+ }
+ var init = parseExpression(false, true);
+ if (eat(_in)) {checkLVal(init); return parseForIn(node, init);}
+ return parseFor(node, init);
+
+ case _function:
+ next();
+ return parseFunction(node, true);
+
+ case _if:
+ next();
+ node.test = parseParenExpression();
+ node.consequent = parseStatement();
+ node.alternate = eat(_else) ? parseStatement() : null;
+ return finishNode(node, "IfStatement");
+
+ case _return:
+ if (!inFunction && !options.allowReturnOutsideFunction)
+ raise(tokStart, "'return' outside of function");
+ next();
+
+ if (eat(_semi) || canInsertSemicolon()) node.argument = null;
+ else { node.argument = parseExpression(); semicolon(); }
+ return finishNode(node, "ReturnStatement");
+
+ case _switch:
+ next();
+ node.discriminant = parseParenExpression();
+ node.cases = [];
+ expect(_braceL);
+ labels.push(switchLabel);
+
+ for (var cur, sawDefault; tokType != _braceR;) {
+ if (tokType === _case || tokType === _default) {
+ var isCase = tokType === _case;
+ if (cur) finishNode(cur, "SwitchCase");
+ node.cases.push(cur = startNode());
+ cur.consequent = [];
+ next();
+ if (isCase) cur.test = parseExpression();
+ else {
+ if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true;
+ cur.test = null;
+ }
+ expect(_colon);
+ } else {
+ if (!cur) unexpected();
+ cur.consequent.push(parseStatement());
+ }
+ }
+ if (cur) finishNode(cur, "SwitchCase");
+ next();
+ labels.pop();
+ return finishNode(node, "SwitchStatement");
+
+ case _throw:
+ next();
+ if (newline.test(input.slice(lastEnd, tokStart)))
+ raise(lastEnd, "Illegal newline after throw");
+ node.argument = parseExpression();
+ semicolon();
+ return finishNode(node, "ThrowStatement");
+
+ case _try:
+ next();
+ node.block = parseBlock();
+ node.handler = null;
+ if (tokType === _catch) {
+ var clause = startNode();
+ next();
+ expect(_parenL);
+ clause.param = parseIdent();
+ if (strict && isStrictBadIdWord(clause.param.name))
+ raise(clause.param.start, "Binding " + clause.param.name + " in strict mode");
+ expect(_parenR);
+ clause.guard = null;
+ clause.body = parseBlock();
+ node.handler = finishNode(clause, "CatchClause");
+ }
+ node.guardedHandlers = empty;
+ node.finalizer = eat(_finally) ? parseBlock() : null;
+ if (!node.handler && !node.finalizer)
+ raise(node.start, "Missing catch or finally clause");
+ return finishNode(node, "TryStatement");
+
+ case _var:
+ next();
+ parseVar(node);
+ semicolon();
+ return finishNode(node, "VariableDeclaration");
+
+ case _while:
+ next();
+ node.test = parseParenExpression();
+ labels.push(loopLabel);
+ node.body = parseStatement();
+ labels.pop();
+ return finishNode(node, "WhileStatement");
+
+ case _with:
+ if (strict) raise(tokStart, "'with' in strict mode");
+ next();
+ node.object = parseParenExpression();
+ node.body = parseStatement();
+ return finishNode(node, "WithStatement");
+
+ case _braceL:
+ return parseBlock();
+
+ case _semi:
+ next();
+ return finishNode(node, "EmptyStatement");
+
+ default:
+ var maybeName = tokVal, expr = parseExpression();
+ if (starttype === _name && expr.type === "Identifier" && eat(_colon)) {
+ for (var i = 0; i < labels.length; ++i)
+ if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared");
+ var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null;
+ labels.push({name: maybeName, kind: kind});
+ node.body = parseStatement();
+ labels.pop();
+ node.label = expr;
+ return finishNode(node, "LabeledStatement");
+ } else {
+ node.expression = expr;
+ semicolon();
+ return finishNode(node, "ExpressionStatement");
+ }
+ }
+ }
+
+ function parseParenExpression() {
+ expect(_parenL);
+ var val = parseExpression();
+ expect(_parenR);
+ return val;
+ }
+
+ function parseBlock(allowStrict) {
+ var node = startNode(), first = true, strict = false, oldStrict;
+ node.body = [];
+ expect(_braceL);
+ while (!eat(_braceR)) {
+ var stmt = parseStatement();
+ node.body.push(stmt);
+ if (first && allowStrict && isUseStrict(stmt)) {
+ oldStrict = strict;
+ setStrict(strict = true);
+ }
+ first = false;
+ }
+ if (strict && !oldStrict) setStrict(false);
+ return finishNode(node, "BlockStatement");
+ }
+
+ function parseFor(node, init) {
+ node.init = init;
+ expect(_semi);
+ node.test = tokType === _semi ? null : parseExpression();
+ expect(_semi);
+ node.update = tokType === _parenR ? null : parseExpression();
+ expect(_parenR);
+ node.body = parseStatement();
+ labels.pop();
+ return finishNode(node, "ForStatement");
+ }
+
+ function parseForIn(node, init) {
+ node.left = init;
+ node.right = parseExpression();
+ expect(_parenR);
+ node.body = parseStatement();
+ labels.pop();
+ return finishNode(node, "ForInStatement");
+ }
+
+ function parseVar(node, noIn) {
+ node.declarations = [];
+ node.kind = "var";
+ for (;;) {
+ var decl = startNode();
+ decl.id = parseIdent();
+ if (strict && isStrictBadIdWord(decl.id.name))
+ raise(decl.id.start, "Binding " + decl.id.name + " in strict mode");
+ decl.init = eat(_eq) ? parseExpression(true, noIn) : null;
+ node.declarations.push(finishNode(decl, "VariableDeclarator"));
+ if (!eat(_comma)) break;
+ }
+ return node;
+ }
+
+ function parseExpression(noComma, noIn) {
+ var expr = parseMaybeAssign(noIn);
+ if (!noComma && tokType === _comma) {
+ var node = startNodeFrom(expr);
+ node.expressions = [expr];
+ while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn));
+ return finishNode(node, "SequenceExpression");
+ }
+ return expr;
+ }
+
+ function parseMaybeAssign(noIn) {
+ var left = parseMaybeConditional(noIn);
+ if (tokType.isAssign) {
+ var node = startNodeFrom(left);
+ node.operator = tokVal;
+ node.left = left;
+ next();
+ node.right = parseMaybeAssign(noIn);
+ checkLVal(left);
+ return finishNode(node, "AssignmentExpression");
+ }
+ return left;
+ }
+
+ function parseMaybeConditional(noIn) {
+ var expr = parseExprOps(noIn);
+ if (eat(_question)) {
+ var node = startNodeFrom(expr);
+ node.test = expr;
+ node.consequent = parseExpression(true);
+ expect(_colon);
+ node.alternate = parseExpression(true, noIn);
+ return finishNode(node, "ConditionalExpression");
+ }
+ return expr;
+ }
+
+ function parseExprOps(noIn) {
+ return parseExprOp(parseMaybeUnary(), -1, noIn);
+ }
+
+ function parseExprOp(left, minPrec, noIn) {
+ var prec = tokType.binop;
+ if (prec != null && (!noIn || tokType !== _in)) {
+ if (prec > minPrec) {
+ var node = startNodeFrom(left);
+ node.left = left;
+ node.operator = tokVal;
+ var op = tokType;
+ next();
+ node.right = parseExprOp(parseMaybeUnary(), prec, noIn);
+ var exprNode = finishNode(node, (op === _logicalOR || op === _logicalAND) ? "LogicalExpression" : "BinaryExpression");
+ return parseExprOp(exprNode, minPrec, noIn);
+ }
+ }
+ return left;
+ }
+
+ function parseMaybeUnary() {
+ if (tokType.prefix) {
+ var node = startNode(), update = tokType.isUpdate;
+ node.operator = tokVal;
+ node.prefix = true;
+ tokRegexpAllowed = true;
+ next();
+ node.argument = parseMaybeUnary();
+ if (update) checkLVal(node.argument);
+ else if (strict && node.operator === "delete" &&
+ node.argument.type === "Identifier")
+ raise(node.start, "Deleting local variable in strict mode");
+ return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+ }
+ var expr = parseExprSubscripts();
+ while (tokType.postfix && !canInsertSemicolon()) {
+ var node = startNodeFrom(expr);
+ node.operator = tokVal;
+ node.prefix = false;
+ node.argument = expr;
+ checkLVal(expr);
+ next();
+ expr = finishNode(node, "UpdateExpression");
+ }
+ return expr;
+ }
+
+ function parseExprSubscripts() {
+ return parseSubscripts(parseExprAtom());
+ }
+
+ function parseSubscripts(base, noCalls) {
+ if (eat(_dot)) {
+ var node = startNodeFrom(base);
+ node.object = base;
+ node.property = parseIdent(true);
+ node.computed = false;
+ return parseSubscripts(finishNode(node, "MemberExpression"), noCalls);
+ } else if (eat(_bracketL)) {
+ var node = startNodeFrom(base);
+ node.object = base;
+ node.property = parseExpression();
+ node.computed = true;
+ expect(_bracketR);
+ return parseSubscripts(finishNode(node, "MemberExpression"), noCalls);
+ } else if (!noCalls && eat(_parenL)) {
+ var node = startNodeFrom(base);
+ node.callee = base;
+ node.arguments = parseExprList(_parenR, false);
+ return parseSubscripts(finishNode(node, "CallExpression"), noCalls);
+ } else return base;
+ }
+
+ function parseExprAtom() {
+ switch (tokType) {
+ case _this:
+ var node = startNode();
+ next();
+ return finishNode(node, "ThisExpression");
+ case _name:
+ return parseIdent();
+ case _num: case _string: case _regexp:
+ var node = startNode();
+ node.value = tokVal;
+ node.raw = input.slice(tokStart, tokEnd);
+ next();
+ return finishNode(node, "Literal");
+
+ case _null: case _true: case _false:
+ var node = startNode();
+ node.value = tokType.atomValue;
+ node.raw = tokType.keyword;
+ next();
+ return finishNode(node, "Literal");
+
+ case _parenL:
+ var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart;
+ next();
+ var val = parseExpression();
+ val.start = tokStart1;
+ val.end = tokEnd;
+ if (options.locations) {
+ val.loc.start = tokStartLoc1;
+ val.loc.end = tokEndLoc;
+ }
+ if (options.ranges)
+ val.range = [tokStart1, tokEnd];
+ expect(_parenR);
+ return val;
+
+ case _bracketL:
+ var node = startNode();
+ next();
+ node.elements = parseExprList(_bracketR, true, true);
+ return finishNode(node, "ArrayExpression");
+
+ case _braceL:
+ return parseObj();
+
+ case _function:
+ var node = startNode();
+ next();
+ return parseFunction(node, false);
+
+ case _new:
+ return parseNew();
+
+ default:
+ unexpected();
+ }
+ }
+
+ function parseNew() {
+ var node = startNode();
+ next();
+ node.callee = parseSubscripts(parseExprAtom(), true);
+ if (eat(_parenL)) node.arguments = parseExprList(_parenR, false);
+ else node.arguments = empty;
+ return finishNode(node, "NewExpression");
+ }
+
+ function parseObj() {
+ var node = startNode(), first = true, sawGetSet = false;
+ node.properties = [];
+ next();
+ while (!eat(_braceR)) {
+ if (!first) {
+ expect(_comma);
+ if (options.allowTrailingCommas && eat(_braceR)) break;
+ } else first = false;
+
+ var prop = {key: parsePropertyName()}, isGetSet = false, kind;
+ if (eat(_colon)) {
+ prop.value = parseExpression(true);
+ kind = prop.kind = "init";
+ } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
+ (prop.key.name === "get" || prop.key.name === "set")) {
+ isGetSet = sawGetSet = true;
+ kind = prop.kind = prop.key.name;
+ prop.key = parsePropertyName();
+ if (tokType !== _parenL) unexpected();
+ prop.value = parseFunction(startNode(), false);
+ } else unexpected();
+
+ if (prop.key.type === "Identifier" && (strict || sawGetSet)) {
+ for (var i = 0; i < node.properties.length; ++i) {
+ var other = node.properties[i];
+ if (other.key.name === prop.key.name) {
+ var conflict = kind == other.kind || isGetSet && other.kind === "init" ||
+ kind === "init" && (other.kind === "get" || other.kind === "set");
+ if (conflict && !strict && kind === "init" && other.kind === "init") conflict = false;
+ if (conflict) raise(prop.key.start, "Redefinition of property");
+ }
+ }
+ }
+ node.properties.push(prop);
+ }
+ return finishNode(node, "ObjectExpression");
+ }
+
+ function parsePropertyName() {
+ if (tokType === _num || tokType === _string) return parseExprAtom();
+ return parseIdent(true);
+ }
+
+ function parseFunction(node, isStatement) {
+ if (tokType === _name) node.id = parseIdent();
+ else if (isStatement) unexpected();
+ else node.id = null;
+ node.params = [];
+ var first = true;
+ expect(_parenL);
+ while (!eat(_parenR)) {
+ if (!first) expect(_comma); else first = false;
+ node.params.push(parseIdent());
+ }
+
+ var oldInFunc = inFunction, oldLabels = labels;
+ inFunction = true; labels = [];
+ node.body = parseBlock(true);
+ inFunction = oldInFunc; labels = oldLabels;
+
+ if (strict || node.body.body.length && isUseStrict(node.body.body[0])) {
+ for (var i = node.id ? -1 : 0; i < node.params.length; ++i) {
+ var id = i < 0 ? node.id : node.params[i];
+ if (isStrictReservedWord(id.name) || isStrictBadIdWord(id.name))
+ raise(id.start, "Defining '" + id.name + "' in strict mode");
+ if (i >= 0) for (var j = 0; j < i; ++j) if (id.name === node.params[j].name)
+ raise(id.start, "Argument name clash in strict mode");
+ }
+ }
+
+ return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+ }
+
+ function parseExprList(close, allowTrailingComma, allowEmpty) {
+ var elts = [], first = true;
+ while (!eat(close)) {
+ if (!first) {
+ expect(_comma);
+ if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break;
+ } else first = false;
+
+ if (allowEmpty && tokType === _comma) elts.push(null);
+ else elts.push(parseExpression(true));
+ }
+ return elts;
+ }
+
+ function parseIdent(liberal) {
+ var node = startNode();
+ if (liberal && options.forbidReserved == "everywhere") liberal = false;
+ if (tokType === _name) {
+ if (!liberal &&
+ (options.forbidReserved &&
+ (options.ecmaVersion === 3 ? isReservedWord3 : isReservedWord5)(tokVal) ||
+ strict && isStrictReservedWord(tokVal)) &&
+ input.slice(tokStart, tokEnd).indexOf("\\") == -1)
+ raise(tokStart, "The keyword '" + tokVal + "' is reserved");
+ node.name = tokVal;
+ } else if (liberal && tokType.keyword) {
+ node.name = tokType.keyword;
+ } else {
+ unexpected();
+ }
+ tokRegexpAllowed = false;
+ next();
+ return finishNode(node, "Identifier");
+ }
+
+});
+
+ if (!acorn.version)
+ acorn = null;
+ }
+
+ function parse(code, options) {
+ return (global.acorn || acorn).parse(code, options);
+ }
var binaryOperators = {
'+': '__add',
@@ -12889,21 +16640,25 @@
'*': '__multiply',
'/': '__divide',
'%': '__modulo',
- '==': 'equals',
- '!=': 'equals'
+ '==': '__equals',
+ '!=': '__equals'
};
var unaryOperators = {
'-': '__negate',
- '+': null
+ '+': '__self'
};
var fields = Base.each(
- ['add', 'subtract', 'multiply', 'divide', 'modulo', 'negate'],
+ ['add', 'subtract', 'multiply', 'divide', 'modulo', 'equals', 'negate'],
function(name) {
this['__' + name] = '#' + name;
},
- {}
+ {
+ __self: function() {
+ return this;
+ }
+ }
);
Point.inject(fields);
Size.inject(fields);
@@ -12928,7 +16683,7 @@
function $__(operator, value) {
var handler = unaryOperators[operator];
- if (handler && value && value[handler])
+ if (value && value[handler])
return value[handler]();
switch (operator) {
case '+': return +value;
@@ -12936,15 +16691,10 @@
}
}
- function parse(code, options) {
- return scope.acorn.parse(code, options);
- }
-
- function compile(code, url, options) {
+ function compile(code, options) {
if (!code)
return '';
options = options || {};
- url = url || '';
var insertions = [];
@@ -13032,9 +16782,13 @@
exp = '__$__(' + arg + ', "' + node.operator[0]
+ '", 1)',
str = arg + ' = ' + exp;
- if (!node.prefix
- && (parentType === 'AssignmentExpression'
- || parentType === 'VariableDeclarator')) {
+ if (node.prefix) {
+ str = '(' + str + ')';
+ } else if (
+ parentType === 'AssignmentExpression' ||
+ parentType === 'VariableDeclarator' ||
+ parentType === 'BinaryExpression'
+ ) {
if (getCode(parent.left || parent.id) === arg)
str = exp;
str = arg + '; ' + str;
@@ -13044,65 +16798,138 @@
if (/^.=$/.test(node.operator)
&& node.left.type !== 'Literal') {
var left = getCode(node.left),
- right = getCode(node.right);
- replaceCode(node, left + ' = __$__(' + left + ', "'
- + node.operator[0] + '", ' + right + ')');
+ right = getCode(node.right),
+ exp = left + ' = __$__(' + left + ', "'
+ + node.operator[0] + '", ' + right + ')';
+ replaceCode(node, /^\(.*\)$/.test(getCode(node))
+ ? '(' + exp + ')' : exp);
}
}
}
break;
- }
- }
- var sourceMap = null,
- browser = paper.browser,
- version = browser.versionNumber,
- lineBreaks = /\r\n|\n|\r/mg;
- if (browser.chrome && version >= 30
- || browser.webkit && version >= 537.76
- || browser.firefox && version >= 23) {
- var offset = 0;
- if (window.location.href.indexOf(url) === 0) {
+ case 'ExportDefaultDeclaration':
+ replaceCode({
+ range: [node.start, node.declaration.start]
+ }, 'module.exports = ');
+ break;
+ case 'ExportNamedDeclaration':
+ var declaration = node.declaration;
+ var specifiers = node.specifiers;
+ if (declaration) {
+ var declarations = declaration.declarations;
+ if (declarations) {
+ declarations.forEach(function(dec) {
+ replaceCode(dec, 'module.exports.' + getCode(dec));
+ });
+ replaceCode({
+ range: [
+ node.start,
+ declaration.start + declaration.kind.length
+ ]
+ }, '');
+ }
+ } else if (specifiers) {
+ var exports = specifiers.map(function(specifier) {
+ var name = getCode(specifier);
+ return 'module.exports.' + name + ' = ' + name + '; ';
+ }).join('');
+ if (exports) {
+ replaceCode(node, exports);
+ }
+ }
+ break;
+ }
+ }
+
+ function encodeVLQ(value) {
+ var res = '',
+ base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ value = (Math.abs(value) << 1) + (value < 0 ? 1 : 0);
+ while (value || !res) {
+ var next = value & (32 - 1);
+ value >>= 5;
+ if (value)
+ next |= 32;
+ res += base64[next];
+ }
+ return res;
+ }
+
+ var url = options.url || '',
+ agent = paper.agent,
+ version = agent.versionNumber,
+ offsetCode = false,
+ sourceMaps = options.sourceMaps,
+ source = options.source || code,
+ lineBreaks = /\r\n|\n|\r/mg,
+ offset = options.offset || 0,
+ map;
+ if (sourceMaps && (agent.chrome && version >= 30
+ || agent.webkit && version >= 537.76
+ || agent.firefox && version >= 23
+ || agent.node)) {
+ if (agent.node) {
+ offset -= 2;
+ } else if (window && url && !window.location.href.indexOf(url)) {
var html = document.getElementsByTagName('html')[0].innerHTML;
offset = html.substr(0, html.indexOf(code) + 1).match(
lineBreaks).length + 1;
}
- var mappings = ['AAAA'];
- mappings.length = (code.match(lineBreaks) || []).length + 1 + offset;
- sourceMap = {
+ offsetCode = offset > 0 && !(
+ agent.chrome && version >= 36 ||
+ agent.safari && version >= 600 ||
+ agent.firefox && version >= 40 ||
+ agent.node);
+ var mappings = ['AA' + encodeVLQ(offsetCode ? 0 : offset) + 'A'];
+ mappings.length = (code.match(lineBreaks) || []).length + 1
+ + (offsetCode ? offset : 0);
+ map = {
version: 3,
file: url,
names:[],
mappings: mappings.join(';AACA'),
sourceRoot: '',
- sources: [url]
+ sources: [url],
+ sourcesContent: [source]
};
- var source = options.source || !url && code;
- if (source)
- sourceMap.sourcesContent = [source];
- }
- walkAST(parse(code, { ranges: true }));
- if (sourceMap) {
- code = new Array(offset + 1).join('\n') + code
- + "\n//# sourceMappingURL=data:application/json;base64,"
- + (btoa(unescape(encodeURIComponent(
- JSON.stringify(sourceMap)))))
- + "\n//# sourceURL=" + (url || 'paperscript');
- }
- return code;
- }
-
- function execute(code, scope, url, options) {
+ }
+ walkAST(parse(code, {
+ ranges: true,
+ preserveParens: true,
+ sourceType: 'module'
+ }));
+ if (map) {
+ if (offsetCode) {
+ code = new Array(offset + 1).join('\n') + code;
+ }
+ if (/^(inline|both)$/.test(sourceMaps)) {
+ code += "\n//# sourceMappingURL=data:application/json;base64,"
+ + self.btoa(unescape(encodeURIComponent(
+ JSON.stringify(map))));
+ }
+ code += "\n//# sourceURL=" + (url || 'paperscript');
+ }
+ return {
+ url: url,
+ source: source,
+ code: code,
+ map: map
+ };
+ }
+
+ function execute(code, scope, options) {
paper = scope;
var view = scope.getView(),
- tool = /\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/.test(code)
- ? new Tool()
- : null,
+ tool = /\btool\.\w+|\s+on(?:Key|Mouse)(?:Up|Down|Move|Drag)\b/
+ .test(code) && !/\bnew\s+Tool\b/.test(code)
+ ? new Tool() : null,
toolHandlers = tool ? tool._events : [],
handlers = ['onFrame', 'onResize'].concat(toolHandlers),
params = [],
args = [],
- func;
- code = compile(code, url, options);
+ func,
+ compiled = typeof code === 'object' ? code : compile(code, options);
+ code = compiled.code;
function expose(scope, hidden) {
for (var key in scope) {
if ((hidden || !/^_/.test(key)) && new RegExp('([\\b\\s\\W]|^)'
@@ -13112,50 +16939,58 @@
}
}
}
- expose({ __$__: __$__, $__: $__, paper: scope, view: view, tool: tool },
+ expose({ __$__: __$__, $__: $__, paper: scope, tool: tool },
true);
expose(scope);
- handlers = Base.each(handlers, function(key) {
+ code = 'var module = { exports: {} }; ' + code;
+ var exports = Base.each(handlers, function(key) {
if (new RegExp('\\s+' + key + '\\b').test(code)) {
params.push(key);
- this.push(key + ': ' + key);
- }
- }, []).join(', ');
- if (handlers)
- code += '\nreturn { ' + handlers + ' };';
- var browser = paper.browser;
- if (browser.chrome || browser.firefox) {
+ this.push('module.exports.' + key + ' = ' + key + ';');
+ }
+ }, []).join('\n');
+ if (exports) {
+ code += '\n' + exports;
+ }
+ code += '\nreturn module.exports;';
+ var agent = paper.agent;
+ if (document && (agent.chrome
+ || agent.firefox && agent.versionNumber < 40)) {
var script = document.createElement('script'),
head = document.head || document.getElementsByTagName('head')[0];
- if (browser.firefox)
+ if (agent.firefox)
code = '\n' + code;
script.appendChild(document.createTextNode(
- 'paper._execute = function(' + params + ') {' + code + '\n}'
+ 'document.__paperscript__ = function(' + params + ') {' +
+ code +
+ '\n}'
));
head.appendChild(script);
- func = paper._execute;
- delete paper._execute;
+ func = document.__paperscript__;
+ delete document.__paperscript__;
head.removeChild(script);
} else {
func = Function(params, code);
}
- var res = func.apply(scope, args) || {};
+ var exports = func && func.apply(scope, args);
+ var obj = exports || {};
Base.each(toolHandlers, function(key) {
- var value = res[key];
+ var value = obj[key];
if (value)
tool[key] = value;
});
if (view) {
- if (res.onResize)
- view.setOnResize(res.onResize);
+ if (obj.onResize)
+ view.setOnResize(obj.onResize);
view.emit('resize', {
size: view.size,
delta: new Point()
});
- if (res.onFrame)
- view.setOnFrame(res.onFrame);
- view.update();
- }
+ if (obj.onFrame)
+ view.setOnFrame(obj.onFrame);
+ view.requestUpdate();
+ }
+ return exports;
}
function loadScript(script) {
@@ -13173,9 +17008,14 @@
|| new PaperScope().setup(canvas);
canvas.setAttribute(scopeAttribute, scope._id);
if (src) {
- Http.request('get', src, function(code) {
- execute(code, scope, src);
- }, async);
+ Http.request({
+ url: src,
+ async: async,
+ mimeType: 'text/plain',
+ onLoad: function(code) {
+ execute(code, scope, src);
+ }
+ });
} else {
execute(script.innerHTML, scope, script.baseURI);
}
@@ -13185,35 +17025,49 @@
}
function loadAll() {
- Base.each(document.getElementsByTagName('script'), loadScript);
+ Base.each(document && document.getElementsByTagName('script'),
+ loadScript);
}
function load(script) {
return script ? loadScript(script) : loadAll();
}
- if (document.readyState === 'complete') {
- setTimeout(loadAll);
- } else {
- DomEvent.add(window, { load: loadAll });
+ if (window) {
+ if (document.readyState === 'complete') {
+ setTimeout(loadAll);
+ } else {
+ DomEvent.add(window, { load: loadAll });
+ }
}
return {
compile: compile,
execute: execute,
load: load,
- parse: parse
+ parse: parse,
+ calculateBinary: __$__,
+ calculateUnary: $__
};
-}).call(this);
-
-paper = new (PaperScope.inject(Base.exports, {
- enumerable: true,
+}.call(this);
+
+var paper = new (PaperScope.inject(Base.exports, {
Base: Base,
Numerical: Numerical,
- Key: Key
+ Key: Key,
+ DomEvent: DomEvent,
+ DomElement: DomElement,
+ document: document,
+ window: window,
+ Symbol: SymbolDefinition,
+ PlacedSymbol: SymbolItem
}))();
+if (paper.agent.node) {
+ require('./node/extend.js')(paper);
+}
+
if (typeof define === 'function' && define.amd) {
define('paper', paper);
} else if (typeof module === 'object' && module) {
@@ -13221,4 +17075,4 @@
}
return paper;
-};
+}.call(this, typeof self === 'object' ? self : null);