Commit first version of serialization / deserialization mechanism.

It appears to work fine already for Paths and Groups.
This commit is contained in:
Jürg Lehni 2012-12-27 12:45:55 +01:00
parent ae4e5d4be5
commit 4f83e0eee6
7 changed files with 147 additions and 4 deletions

View file

@ -42,6 +42,8 @@
* matrix multiplication).
*/
var Matrix = this.Matrix = Base.extend(/** @lends Matrix# */{
_type: 'matrix',
/**
* Creates a 2D affine transform.
*
@ -74,6 +76,10 @@ var Matrix = this.Matrix = Base.extend(/** @lends Matrix# */{
throw new Error('Unsupported matrix parameters');
},
_serialize: function() {
return this.getValues();
},
/**
* @return {Matrix} A copy of this transform.
*/

View file

@ -168,6 +168,10 @@ var Point = this.Point = Base.extend(/** @lends Point# */{
}
},
_serialize: function() {
return [this.x, this.y];
},
/**
* The x coordinate of the point
*

View file

@ -282,6 +282,17 @@ var Color = this.Color = Base.extend(new function() {
return res;
},
_serialize: function() {
var res = [];
for (var i = 0, l = this._components.length; i < l; i++) {
var component = this._components[i],
value = this['_' + component];
if (component !== 'alpha' || value != null && value < 1)
res.push(value);
}
return res;
},
/**
* @return {RgbColor|GrayColor|HsbColor} a copy of the color object
*/

View file

@ -50,8 +50,23 @@ this.Base = Base.inject(/** @lends Base# */{
}, []).join(', ') + ' }';
},
toJson: function() {
return Base.toJson(this);
},
statics: /** @lends Base */{
_types: {},
extend: function(src) {
// Override Base.extend() with a version that registers classes that
// define #_type inside the Base._types lookup, for deserialization.
var res = this.base.apply(this, arguments);
if (src._type)
Base._types[src._type] = res;
return res;
},
/**
* Checks if two values or objects are equals to each other, by using their
* equals() methods if available, and also comparing elements of arrays
@ -164,6 +179,75 @@ this.Base = Base.inject(/** @lends Base# */{
return res;
},
/**
* Serializes the passed object into a format that can be passed to
* JSON.stringify() for JSON serialization.
*/
serialize: function(obj, compact) {
if (obj && obj._serialize) {
var res = obj._serialize();
if (!compact && res[0] !== obj._type)
res.unshift(obj._type);
return res;
}
if (typeof obj !== 'object')
return obj;
var res = obj;
if (Array.isArray(obj)) {
res = [];
for (var i = 0, l = obj.length; i < l; i++)
res[i] = Base.serialize(obj[i], true);
} else if (Base.isObject(obj)) {
res = {};
for (var i in obj)
if (obj.hasOwnProperty(i))
res[i] = Base.serialize(obj[i], true);
}
return res;
},
/**
* 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._types, and the values following in
* the array are the arguments to their initialize function.
* Any other value is passed on unmodified.
* The passed data is recoursively traversed and converted, leaves first
*/
deserialize: function(obj) {
var res = obj;
if (Array.isArray(obj)) {
// 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 = Base._types[obj[0]];
res = [];
// Skip first type entry for arguments
for (var i = type ? 1 : 0, l = obj.length; i < l; i++)
res.push(Base.deserialize(obj[i]));
if (type) {
// Create serialized type and pass collected arguments to
// #initialize().
var args = res;
res = Base.create(type);
res.initialize.apply(res, args);
}
} else if (Base.isObject(obj)) {
res = {};
for (var key in obj)
res[key] = Base.deserialize(obj[key]);
}
return res;
},
toJson: function(obj) {
return JSON.stringify(Base.serialize(obj));
},
fromJson: function(json) {
return Base.deserialize(JSON.parse(json));
},
/**
* 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

View file

@ -25,6 +25,13 @@
* that they inherit from Item.
*/
var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
// Provide information about fields to be serialized, with their defaults
// that can be ommited.
_serializeFields: {
name: null,
children: [],
matrix: new Matrix()
},
initialize: function(point) {
// Define this Item's unique id.
@ -116,10 +123,8 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
// one object literal describing all the properties to be set on the created
// instance.
_setProperties: function(props) {
if (Base.isObject(props)) {
this.set(props);
return true;
}
if (Base.isObject(props))
return this.set(props);
},
/**
@ -133,6 +138,27 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
return this;
},
_serialize: function() {
var props = {},
that = this;
function serialize(fields) {
for (var key in fields) {
var value = that[key];
if (!Base.equals(value, fields[key]))
props[key] = Base.serialize(value);
}
}
// Serialize fields that this Item subclass defines first
serialize(this._serializeFields);
// Serialize style fields, but only if they differ from defaults
serialize(this._style._defaults);
// There is no compact form for Item serialization, we always keep the
// type.
return [ this._type, props ];
},
/**
* Private notifier that is called whenever a change occurs in this item or
* its sub-elements, such as Segments, Curves, PathStyles, etc.

View file

@ -24,6 +24,10 @@
// DOCS: Explain that path matrix is always applied with each transformation.
var Path = this.Path = PathItem.extend(/** @lends Path# */{
_type: 'path',
_serializeFields: {
segments: [],
closed: false
},
/**
* Creates a new Path item and places it at the top of the active layer.

View file

@ -27,6 +27,8 @@
* objects that are connected by this segment.
*/
var Segment = this.Segment = Base.extend(/** @lends Segment# */{
_type: 'segment',
/**
* Creates a new Segment object.
*
@ -86,6 +88,12 @@ var Segment = this.Segment = Base.extend(/** @lends Segment# */{
createPoint(this, '_handleOut', handleOut);
},
_serialize: function() {
return Base.serialize(this._handleIn.isZero() && this._handleOut.isZero()
? this._point
: [this._point, this._handleIn, this._handleOut], true);
},
_changed: function(point) {
if (!this._path)
return;