|
|
|
@ -17,6 +17,8 @@
|
|
|
|
|
*/
|
|
|
|
|
// Extend Base with utility functions used across the library.
|
|
|
|
|
Base.inject(/** @lends Base# */{
|
|
|
|
|
enumerable: false,
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders base objects to strings in object literal notation.
|
|
|
|
|
*/
|
|
|
|
@ -92,14 +94,15 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
if (props)
|
|
|
|
|
Base.filter(this, props, exclude, this._prioritize);
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
statics: /** @lends Base */{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}, /** @lends Base# */{
|
|
|
|
|
// Mess with indentation in order to get more line-space for the statics below.
|
|
|
|
|
// Explicitly deactivate the creation of beans, as we have functions here
|
|
|
|
|
// that look like bean getters but actually read arguments, see getNamed().
|
|
|
|
|
beans: false,
|
|
|
|
|
statics: /** @lends Base */{
|
|
|
|
|
// Keep track of all named classes for serialization and exporting.
|
|
|
|
|
exports: {
|
|
|
|
|
enumerable: true // For PaperScope.inject() in export.js
|
|
|
|
|
},
|
|
|
|
|
exports: {},
|
|
|
|
|
|
|
|
|
|
extend: function extend() {
|
|
|
|
|
// Override Base.extend() to register named classes in Base.exports,
|
|
|
|
@ -112,9 +115,9 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if two values or objects are equals to each other, by using
|
|
|
|
|
* their equals() methods if available, and also comparing elements of
|
|
|
|
|
* arrays and properties of objects.
|
|
|
|
|
* Checks if two values or objects are equals to each other, by using their
|
|
|
|
|
* equals() methods if available, and also comparing elements of arrays and
|
|
|
|
|
* properties of objects.
|
|
|
|
|
*/
|
|
|
|
|
equals: function(obj1, obj2) {
|
|
|
|
|
if (obj1 === obj2)
|
|
|
|
@ -158,26 +161,25 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When called on a subclass of Base, it reads arguments of the type of
|
|
|
|
|
* the subclass from the passed arguments list or array, at the given
|
|
|
|
|
* index, up to the specified length.
|
|
|
|
|
* When called directly on Base, it reads any value without conversion
|
|
|
|
|
* from the passed arguments list or array.
|
|
|
|
|
* This is used in argument conversion, e.g. by all basic types (Point,
|
|
|
|
|
* Size, Rectangle) and also higher classes such as Color and Segment.
|
|
|
|
|
* When called on a subclass of Base, it reads arguments of the type of the
|
|
|
|
|
* subclass from the passed arguments list or array, at the given index, up
|
|
|
|
|
* to the specified length. When called directly on Base, it reads any value
|
|
|
|
|
* without conversion from the passed arguments list or array. This is used
|
|
|
|
|
* in argument conversion, e.g. by all basic types (Point, Size, Rectangle)
|
|
|
|
|
* and also higher classes such as Color and Segment.
|
|
|
|
|
*
|
|
|
|
|
* @param {Array} list the list to read from, either an arguments object
|
|
|
|
|
* or a normal array
|
|
|
|
|
* @param {Array} list the list to read from, either an arguments object or
|
|
|
|
|
* a normal array
|
|
|
|
|
* @param {Number} start the index at which to start reading in the list
|
|
|
|
|
* @param {Object} options `options.readNull` controls whether null is
|
|
|
|
|
* returned or converted. `options.clone` controls whether passed
|
|
|
|
|
* objects should be cloned if they are already provided in the
|
|
|
|
|
* required type
|
|
|
|
|
* objects should be cloned if they are already provided in the required
|
|
|
|
|
* type
|
|
|
|
|
* @param {Number} length the amount of elements that can be read
|
|
|
|
|
*/
|
|
|
|
|
read: function(list, start, options, amount) {
|
|
|
|
|
// See if it's called directly on Base, and if so, read value and
|
|
|
|
|
// return without object conversion.
|
|
|
|
|
// See if it's called directly on Base, and if so, read value and return
|
|
|
|
|
// without object conversion.
|
|
|
|
|
if (this === Base) {
|
|
|
|
|
var value = this.peek(list, start);
|
|
|
|
|
list.__index++;
|
|
|
|
@ -189,9 +191,9 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
length = list.length,
|
|
|
|
|
obj = list[begin];
|
|
|
|
|
amount = amount || length - begin;
|
|
|
|
|
// When read() is called on a sub-class of which the object is
|
|
|
|
|
// already an instance, or when there is only one value in the list
|
|
|
|
|
// and it's null or undefined, return the obj.
|
|
|
|
|
// When read() is called on a sub-class of which the object is already
|
|
|
|
|
// an instance, or when there is only one value in the list and it's
|
|
|
|
|
// null or undefined, return the obj.
|
|
|
|
|
if (obj instanceof this
|
|
|
|
|
|| options && options.readNull && obj == null && amount <= 1) {
|
|
|
|
|
if (readIndex)
|
|
|
|
@ -209,7 +211,7 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
if (readIndex) {
|
|
|
|
|
list.__index = begin + obj.__read;
|
|
|
|
|
// This is only in use in Rectangle so far: Nested calls to
|
|
|
|
|
// Base.readNamed() would loose __filtered if it wasn't returned
|
|
|
|
|
// `Base.readNamed()` would loose __filtered if it wasn't returned
|
|
|
|
|
// on the object.
|
|
|
|
|
var filtered = obj.__filtered;
|
|
|
|
|
if (filtered) {
|
|
|
|
@ -267,20 +269,20 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Allows using of Base.read() mechanism in combination with reading
|
|
|
|
|
* named arguments form a passed property object literal. Calling
|
|
|
|
|
* Base.readNamed() can read both from such named properties and normal
|
|
|
|
|
* unnamed arguments through Base.read(). In use for example for the
|
|
|
|
|
* various Path.Constructors.
|
|
|
|
|
* Allows using of Base.read() mechanism in combination with reading named
|
|
|
|
|
* arguments form a passed property object literal. Calling Base.readNamed()
|
|
|
|
|
* can read both from such named properties and normal unnamed arguments
|
|
|
|
|
* through Base.read(). In use for example for the various
|
|
|
|
|
* Path.Constructors.
|
|
|
|
|
*
|
|
|
|
|
* @param {Array} list the list to read from, either an arguments object
|
|
|
|
|
* or a normal array
|
|
|
|
|
* @param {Array} list the list to read from, either an arguments object or
|
|
|
|
|
* a normal array
|
|
|
|
|
* @param {String} name the property name to read from
|
|
|
|
|
* @param {Number} start the index at which to start reading in the list
|
|
|
|
|
* @param {Object} options `options.readNull` controls whether null is
|
|
|
|
|
* returned or converted. `options.clone` controls whether passed
|
|
|
|
|
* objects should be cloned if they are already provided in the
|
|
|
|
|
* required type
|
|
|
|
|
* objects should be cloned if they are already provided in the required
|
|
|
|
|
* type
|
|
|
|
|
* @param {Number} amount the amount of elements that can be read
|
|
|
|
|
*/
|
|
|
|
|
readNamed: function(list, name, start, options, amount) {
|
|
|
|
@ -321,9 +323,8 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if the argument list has a named argument with the given name.
|
|
|
|
|
* If name is `null`, it returns `true` if there are any named
|
|
|
|
|
* arguments.
|
|
|
|
|
* Checks if the argument list has a named argument with the given name. If
|
|
|
|
|
* name is `null`, it returns `true` if there are any named arguments.
|
|
|
|
|
*/
|
|
|
|
|
hasNamed: function(list, name) {
|
|
|
|
|
return !!this.getNamed(list, name);
|
|
|
|
@ -331,18 +332,17 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Copies all properties from `source` over to `dest`, supporting
|
|
|
|
|
* `_filtered` handling as required by {@link Base.readNamed()}
|
|
|
|
|
* mechanism, as well as a way to exclude and prioritize properties.
|
|
|
|
|
* `_filtered` handling as required by {@link Base.readNamed()} mechanism,
|
|
|
|
|
* as well as a way to exclude and prioritize properties.
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} dest the destination that is to receive the
|
|
|
|
|
* properties
|
|
|
|
|
* @param {Object} source the source from where to retrieve the
|
|
|
|
|
* properties to be copied
|
|
|
|
|
* @param {Object} [exclude] an object that can define any properties
|
|
|
|
|
* as `true` that should be excluded when copying
|
|
|
|
|
* @param {String[]} [prioritize] a list of keys that should be
|
|
|
|
|
* prioritized when copying, if they are defined in `source`,
|
|
|
|
|
* processed in the order of appearance
|
|
|
|
|
* @param {Object} dest the destination that is to receive the properties
|
|
|
|
|
* @param {Object} source the source from where to retrieve the properties
|
|
|
|
|
* to be copied
|
|
|
|
|
* @param {Object} [exclude] an object that can define any properties as
|
|
|
|
|
* `true` that should be excluded when copying
|
|
|
|
|
* @param {String[]} [prioritize] a list of keys that should be prioritized
|
|
|
|
|
* when copying, if they are defined in `source`, processed in the order
|
|
|
|
|
* of appearance
|
|
|
|
|
*/
|
|
|
|
|
filter: function(dest, source, exclude, prioritize) {
|
|
|
|
|
var processed;
|
|
|
|
@ -372,16 +372,16 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
processed = keys;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If source is a filtered object, we get the keys from the
|
|
|
|
|
// the original object (it's parent / prototype). See _filtered
|
|
|
|
|
// inheritance trick in the argument reading code.
|
|
|
|
|
// If source is a filtered object, we get the keys from the the original
|
|
|
|
|
// object (it's parent / prototype). See _filtered inheritance trick in
|
|
|
|
|
// the argument reading code.
|
|
|
|
|
Object.keys(source.__unfiltered || source).forEach(handleKey);
|
|
|
|
|
return dest;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if obj is either a plain object or an array, as used by
|
|
|
|
|
* many argument reading methods.
|
|
|
|
|
* Returns true if obj is either a plain object or an array, as used by many
|
|
|
|
|
* argument reading methods.
|
|
|
|
|
*/
|
|
|
|
|
isPlainValue: function(obj, asString) {
|
|
|
|
|
return Base.isPlainObject(obj) || Array.isArray(obj)
|
|
|
|
@ -390,7 +390,7 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Serializes the passed object into a format that can be passed to
|
|
|
|
|
* JSON.stringify() for JSON serialization.
|
|
|
|
|
* `JSON.stringify()` for JSON serialization.
|
|
|
|
|
*/
|
|
|
|
|
serialize: function(obj, options, compact, dictionary) {
|
|
|
|
|
options = options || {};
|
|
|
|
@ -399,10 +399,10 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
res;
|
|
|
|
|
if (isRoot) {
|
|
|
|
|
options.formatter = new Formatter(options.precision);
|
|
|
|
|
// Create a simple dictionary object that handles all the
|
|
|
|
|
// storing and retrieving of dictionary definitions and
|
|
|
|
|
// references, e.g. for symbols and gradients. Items that want
|
|
|
|
|
// to support this need to define globally unique _id attribute.
|
|
|
|
|
// Create a simple dictionary object that handles all the storing
|
|
|
|
|
// and retrieving of dictionary definitions and references, e.g. for
|
|
|
|
|
// symbols and gradients. Items that want to support this need to
|
|
|
|
|
// define globally unique _id attribute.
|
|
|
|
|
/**
|
|
|
|
|
* @namespace
|
|
|
|
|
* @private
|
|
|
|
@ -412,10 +412,9 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
definitions: {},
|
|
|
|
|
references: {},
|
|
|
|
|
add: function(item, create) {
|
|
|
|
|
// See if we have reference entry with the given id
|
|
|
|
|
// already. If not, call create on the item to allow it
|
|
|
|
|
// to create the definition, then store the reference
|
|
|
|
|
// to it and return it.
|
|
|
|
|
// See if we have reference entry with the given id already.
|
|
|
|
|
// If not, call create on the item to allow it to create the
|
|
|
|
|
// definition, then store the reference to it and return it.
|
|
|
|
|
var id = '#' + item._id,
|
|
|
|
|
ref = this.references[id];
|
|
|
|
|
if (!ref) {
|
|
|
|
@ -436,11 +435,11 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
if (obj && obj._serialize) {
|
|
|
|
|
res = obj._serialize(options, dictionary);
|
|
|
|
|
// If we don't serialize to compact form (meaning no type
|
|
|
|
|
// identifier), see if _serialize didn't already add the class,
|
|
|
|
|
// e.g. for classes that do not support compact form.
|
|
|
|
|
// identifier), see if _serialize didn't already add the class, e.g.
|
|
|
|
|
// for classes that do not support compact form.
|
|
|
|
|
var name = obj._class;
|
|
|
|
|
// Enforce class names on root level, except if the class
|
|
|
|
|
// explicitly asks to be serialized in compact form (Project).
|
|
|
|
|
// Enforce class names on root level, except if the class explicitly
|
|
|
|
|
// asks to be serialized in compact form (Project).
|
|
|
|
|
if (name && !obj._compactSerialize && (isRoot || !compact)
|
|
|
|
|
&& res[0] !== name) {
|
|
|
|
|
res.unshift(name);
|
|
|
|
@ -448,8 +447,7 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
} 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);
|
|
|
|
|
res[i] = Base.serialize(obj[i], options, compact, dictionary);
|
|
|
|
|
} else if (Base.isPlainObject(obj)) {
|
|
|
|
|
res = {};
|
|
|
|
|
var keys = Object.keys(obj);
|
|
|
|
@ -471,37 +469,36 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
/**
|
|
|
|
|
* Deserializes from parsed JSON data. A simple convention is followed:
|
|
|
|
|
* Array values with a string at the first position are links to
|
|
|
|
|
* deserializable types through Base.exports, and the values following
|
|
|
|
|
* in the array are the arguments to their initialize function.
|
|
|
|
|
* Any other value is passed on unmodified.
|
|
|
|
|
* The passed json data is recoursively traversed and converted, leaves
|
|
|
|
|
* first
|
|
|
|
|
* deserializable types through Base.exports, and the values following in
|
|
|
|
|
* the array are the arguments to their initialize function. Any other value
|
|
|
|
|
* is passed on unmodified. The passed json data is recoursively traversed
|
|
|
|
|
* and converted, leaves first.
|
|
|
|
|
*/
|
|
|
|
|
deserialize: function(json, create, _data, _setDictionary, _isRoot) {
|
|
|
|
|
var res = json,
|
|
|
|
|
isFirst = !_data,
|
|
|
|
|
hasDictionary = isFirst && json && json.length
|
|
|
|
|
&& json[0][0] === 'dictionary';
|
|
|
|
|
// A _data side-car to deserialize that can hold any kind of
|
|
|
|
|
// 'global' data across a deserialization. It's currently only used
|
|
|
|
|
// to hold dictionary definitions.
|
|
|
|
|
// A _data side-car to deserialize that can hold any kind of 'global'
|
|
|
|
|
// data across a deserialization. It's currently only used to hold
|
|
|
|
|
// dictionary definitions.
|
|
|
|
|
_data = _data || {};
|
|
|
|
|
if (Array.isArray(json)) {
|
|
|
|
|
// See if it's a serialized type. If so, the rest of the array
|
|
|
|
|
// are the arguments to #initialize(). Either way, we simply
|
|
|
|
|
// deserialize all elements of the array.
|
|
|
|
|
// See if it's a serialized type. If so, the rest of the array are
|
|
|
|
|
// the arguments to #initialize(). Either way, we simply deserialize
|
|
|
|
|
// all elements of the array.
|
|
|
|
|
var type = json[0],
|
|
|
|
|
// Handle stored dictionary specially, since we need to
|
|
|
|
|
// keep a lookup table to retrieve referenced items from.
|
|
|
|
|
// Handle stored dictionary specially, since we need to keep a
|
|
|
|
|
// lookup table to retrieve referenced items from.
|
|
|
|
|
isDictionary = type === 'dictionary';
|
|
|
|
|
// First see if this is perhaps a dictionary reference, and
|
|
|
|
|
// if so return its definition instead.
|
|
|
|
|
// First see if this is perhaps a dictionary reference, and if so
|
|
|
|
|
// return its definition instead.
|
|
|
|
|
if (json.length == 1 && /^#/.test(type)) {
|
|
|
|
|
return _data.dictionary[type];
|
|
|
|
|
}
|
|
|
|
|
type = Base.exports[type];
|
|
|
|
|
res = [];
|
|
|
|
|
// Skip first type entry for arguments
|
|
|
|
|
// Skip first type entry for arguments.
|
|
|
|
|
// Pass true for _isRoot in children if we have a dictionary,
|
|
|
|
|
// in which case we need to shift the root level one down.
|
|
|
|
|
for (var i = type ? 1 : 0, l = json.length; i < l; i++) {
|
|
|
|
@ -512,9 +509,9 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
// Create serialized type and pass collected arguments to
|
|
|
|
|
// constructor().
|
|
|
|
|
var args = res;
|
|
|
|
|
// If a create method is provided, handle our own
|
|
|
|
|
// creation. This is used in #importJSON() to pass
|
|
|
|
|
// on insert = false to all items except layers.
|
|
|
|
|
// If a create method is provided, handle our own creation. This
|
|
|
|
|
// is used in #importJSON() to pass on insert = false to all
|
|
|
|
|
// items except layers.
|
|
|
|
|
if (create) {
|
|
|
|
|
res = create(type, args, isFirst || _isRoot);
|
|
|
|
|
} else {
|
|
|
|
@ -555,10 +552,10 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
&& target.constructor === ctor,
|
|
|
|
|
obj = useTarget ? target
|
|
|
|
|
: Base.create(ctor.prototype);
|
|
|
|
|
// NOTE: We don't set insert false for layers since we
|
|
|
|
|
// want these to be created on the fly in the active
|
|
|
|
|
// project into which we're importing (except for if
|
|
|
|
|
// it's a preexisting target layer).
|
|
|
|
|
// NOTE: We don't set insert false for layers since we want
|
|
|
|
|
// these to be created on the fly in the active project into
|
|
|
|
|
// which we're importing (except for if it's a preexisting
|
|
|
|
|
// target layer).
|
|
|
|
|
if (args.length === 1 && obj instanceof Item
|
|
|
|
|
&& (useTarget || !(obj instanceof Layer))) {
|
|
|
|
|
var arg = args[0];
|
|
|
|
@ -576,9 +573,9 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Utility function for adding and removing items from a list of which
|
|
|
|
|
* each entry keeps a reference to its index in the list in the private
|
|
|
|
|
* _index property. Used for PaperScope#projects and Item#children.
|
|
|
|
|
* Utility function for adding and removing items from a list of which each
|
|
|
|
|
* entry keeps a reference to its index in the list in the private _index
|
|
|
|
|
* property. Used for PaperScope#projects and Item#children.
|
|
|
|
|
*/
|
|
|
|
|
splice: function(list, items, index, remove) {
|
|
|
|
|
var amount = items && items.length,
|
|
|
|
@ -634,5 +631,4 @@ Base.inject(/** @lends Base# */{
|
|
|
|
|
hyphenate: function(str) {
|
|
|
|
|
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}});
|
|
|
|
|