diff --git a/src/mixins/canvas_serialization.mixin.js b/src/mixins/canvas_serialization.mixin.js index 68dd84b2..646fbe62 100644 --- a/src/mixins/canvas_serialization.mixin.js +++ b/src/mixins/canvas_serialization.mixin.js @@ -8,11 +8,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @param {Function} callback Callback, invoked when json is parsed * and corresponding objects (e.g: fabric.Image) * are initialized + * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created. * @return {fabric.Canvas} instance * @chainable */ - loadFromDatalessJSON: function (json, callback) { - return this.loadFromJSON(json, callback); + loadFromDatalessJSON: function (json, callback, reviver) { + return this.loadFromJSON(json, callback, reviver); }, /** @@ -22,10 +23,11 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @param {Function} callback Callback, invoked when json is parsed * and corresponding objects (e.g: fabric.Image) * are initialized + * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created. * @return {fabric.Canvas} instance * @chainable */ - loadFromJSON: function (json, callback) { + loadFromJSON: function (json, callback, reviver) { if (!json) return; // serialize if it wasn't already @@ -38,7 +40,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati var _this = this; this._enlivenObjects(serialized.objects, function () { _this._setBgOverlayImages(serialized, callback); - }); + }, reviver); return this; }, @@ -109,8 +111,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @private * @param {Array} objects * @param {Function} callback + * @param {Function} [reviver] */ - _enlivenObjects: function (objects, callback) { + _enlivenObjects: function (objects, callback, reviver) { var _this = this; if (objects.length === 0) { @@ -127,7 +130,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati _this.renderOnAddRemove = renderOnAddRemove; callback && callback(); - }); + }, null, reviver); }, /** diff --git a/src/util/misc.js b/src/util/misc.js index 3f856271..15766eed 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -163,8 +163,9 @@ * @memberOf fabric.util * @param {Array} objects Objects to enliven * @param {Function} callback Callback to invoke when all objects are created + * @param {Function} [reviver] Method for further parsing of object elements, called after each fabric object created. */ - function enlivenObjects(objects, callback, namespace) { + function enlivenObjects(objects, callback, namespace, reviver) { function onLoaded() { if (++numLoadedObjects === numTotalObjects) { @@ -184,15 +185,17 @@ } var klass = fabric.util.getKlass(o.type, namespace); if (klass.async) { - klass.fromObject(o, function (o, error) { + klass.fromObject(o, function (obj, error) { if (!error) { - enlivenedObjects[index] = o; + enlivenedObjects[index] = obj; + reviver && reviver(o, enlivenedObjects[index]); } onLoaded(); }); } else { enlivenedObjects[index] = klass.fromObject(o); + reviver && reviver(o, enlivenedObjects[index]); onLoaded(); } }); diff --git a/test/unit/canvas.js b/test/unit/canvas.js index dc334184..40fa1baa 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -21,6 +21,23 @@ '["c", 0.877, -9.979, 2.893, -12.905, 4.942, -15.621], ["C", 17.878, 21.775, 18.713, 17.397, 18.511, '+ '13.99], ["z", null]]}], "background": "#ff5555"}'; + var PATH_OBJ_JSON = '{"type": "path", "originX": "center", "originY": "center", "left": 268, "top": 266, "width": 51, "height": 49,'+ + ' "fill": "rgb(0,0,0)", "overlayFill": null, "stroke": null, "strokeWidth": 1, "scaleX": 1, "scaleY": 1, '+ + '"angle": 0, "flipX": false, "flipY": false, "opacity": 1, "path": [["M", 18.511, 13.99],'+ + ' ["c", 0, 0, -2.269, -4.487, -12.643, 4.411], ["c", 0, 0, 4.824, -14.161, 19.222, -9.059],'+ + ' ["l", 0.379, -2.1], ["c", -0.759, -0.405, -1.375, -1.139, -1.645, -2.117], ["c", -0.531, '+ + '-1.864, 0.371, -3.854, 1.999, -4.453], ["c", 0.312, -0.118, 0.633, -0.169, 0.953, -0.169], '+ + '["c", 1.299, 0, 2.514, 0.953, 2.936, 2.455], ["c", 0.522, 1.864, -0.372, 3.854, -1.999, '+ + '4.453], ["c", -0.229, 0.084, -0.464, 0.127, -0.692, 0.152], ["l", -0.379, 2.37], ["c", '+ + '1.146, 0.625, 2.024, 1.569, 2.674, 2.758], ["c", 3.213, 2.514, 8.561, 4.184, 11.774, -8.232],'+ + ' ["c", 0, 0, 0.86, 16.059, -12.424, 14.533], ["c", 0.008, 2.859, 0.615, 5.364, -0.076, 8.224],'+ + ' ["c", 8.679, 3.146, 15.376, 14.389, 17.897, 18.168], ["l", 2.497, -2.151], ["l", 1.206, 1.839],'+ + ' ["l", -3.889, 3.458], ["C", 46.286, 48.503, 31.036, 32.225, 22.72, 35.81], ["c", -1.307, 2.851,'+ + ' -3.56, 6.891, -7.481, 8.848], ["c", -4.689, 2.336, -9.084, -0.802, -11.277, -2.868], ["l",'+ + ' -1.948, 3.104], ["l", -1.628, -1.333], ["l", 3.138, -4.689], ["c", 0.025, 0, 9, 1.932, 9, 1.932], '+ + '["c", 0.877, -9.979, 2.893, -12.905, 4.942, -15.621], ["C", 17.878, 21.775, 18.713, 17.397, 18.511, '+ + '13.99], ["z", null]]}'; + var PATH_DATALESS_JSON = '{"objects":[{"type":"path","originX":"center","originY":"center","left":200,"top":200,"width":200,"height":200,"fill":"rgb(0,0,0)",'+ '"overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+ '"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+ @@ -346,6 +363,42 @@ }); }); + test('loadFromJSON with reviver function', function() { + ok(typeof canvas.loadFromJSON == 'function'); + + function reviver(obj, instance) { + deepEqual(obj, JSON.parse(PATH_OBJ_JSON)); + + if (instance.type === 'path') { + instance.customID = 'fabric_1'; + } + } + + canvas.loadFromJSON(JSON.parse(PATH_JSON), function(){ + var obj = canvas.item(0); + + ok(!canvas.isEmpty(), 'canvas is not empty'); + equal(obj.type, 'path', 'first object is a path object'); + equal(canvas.backgroundColor, '#ff5555', 'backgroundColor is populated properly'); + + equal(obj.get('left'), 268); + equal(obj.get('top'), 266); + equal(obj.get('width'), 51); + equal(obj.get('height'), 49); + equal(obj.get('fill'), 'rgb(0,0,0)'); + equal(obj.get('stroke'), null); + equal(obj.get('strokeWidth'), 1); + equal(obj.get('scaleX'), 1); + equal(obj.get('scaleY'), 1); + equal(obj.get('angle'), 0); + equal(obj.get('flipX'), false); + equal(obj.get('flipY'), false); + equal(obj.get('opacity'), 1); + equal(obj.get('customID'), 'fabric_1'); + ok(obj.get('path').length > 0); + }, reviver); + }); + asyncTest('loadFromJSON with no objects', function() { var c1 = new fabric.Canvas('c1', { backgroundColor: 'green' }), c2 = new fabric.Canvas('c2', { backgroundColor: 'red' }); @@ -363,6 +416,52 @@ }, 500); }); + asyncTest('loadFromJSON with async content', function() { + var group = new fabric.Group([ + new fabric.Rect({ width: 10, height: 20 }), + new fabric.Circle({ radius: 10 }) + ]); + var rect = new fabric.Rect({ width: 20, height: 10 }); + var circle = new fabric.Circle({ radius: 25 }); + + canvas.add(group, rect, circle); + var json = JSON.stringify(canvas); + canvas.clear(); + + equal(0, canvas.getObjects().length); + + canvas.loadFromJSON(json, function() { + equal(3, canvas.getObjects().length); + + start(); + }); + }); + + asyncTest('loadFromDatalessJSON with async content', function() { + + var circ1 = new fabric.Circle({ radius: 30, fill: '#55f', top: 0, left: 0 }); + var circ2 = new fabric.Circle({ radius: 30, fill: '#f55', top: 50, left: 50 }); + var circ3 = new fabric.Circle({ radius: 30, fill: '#5f5', top: 50, left: 50 }); + + var arr = [circ1, circ2]; + var group = new fabric.Group(arr, { top: 150, left: 150 }); + + canvas.add(circ3); + canvas.add(group); + canvas.renderAll(); + + canvas.deactivateAll(); + var json = JSON.stringify( canvas.toDatalessJSON() ); + canvas.clear(); + canvas.loadFromDatalessJSON(json, function() { + + equal(2, canvas.getObjects().length); + equal('group', canvas.getObjects()[1].type); + + start(); + }); + }); + // asyncTest('loadFromJSON with backgroundImage', function() { // canvas.setBackgroundImage('../../assets/pug.jpg'); // var anotherCanvas = new fabric.Canvas(); @@ -901,52 +1000,6 @@ // equal(isFired, true, 'removing active object should fire "selection:cleared"'); }); - asyncTest('loadFromJSON with async content', function() { - var group = new fabric.Group([ - new fabric.Rect({ width: 10, height: 20 }), - new fabric.Circle({ radius: 10 }) - ]); - var rect = new fabric.Rect({ width: 20, height: 10 }); - var circle = new fabric.Circle({ radius: 25 }); - - canvas.add(group, rect, circle); - var json = JSON.stringify(canvas); - canvas.clear(); - - equal(0, canvas.getObjects().length); - - canvas.loadFromJSON(json, function() { - equal(3, canvas.getObjects().length); - - start(); - }); - }); - - asyncTest('loadFromDatalessJSON with async content', function() { - - var circ1 = new fabric.Circle({ radius: 30, fill: '#55f', top: 0, left: 0 }); - var circ2 = new fabric.Circle({ radius: 30, fill: '#f55', top: 50, left: 50 }); - var circ3 = new fabric.Circle({ radius: 30, fill: '#5f5', top: 50, left: 50 }); - - var arr = [circ1, circ2]; - var group = new fabric.Group(arr, { top: 150, left: 150 }); - - canvas.add(circ3); - canvas.add(group); - canvas.renderAll(); - - canvas.deactivateAll(); - var json = JSON.stringify( canvas.toDatalessJSON() ); - canvas.clear(); - canvas.loadFromDatalessJSON(json, function() { - - equal(2, canvas.getObjects().length); - equal('group', canvas.getObjects()[1].type); - - start(); - }); - }); - test('clipTo', function() { canvas.clipTo = function(ctx) { ctx.arc(0, 0, 10, 0, Math.PI * 2, false);