From c9d866dcf922d65c12791a56eaa8620c6ed76b04 Mon Sep 17 00:00:00 2001 From: Kienz Date: Thu, 18 Jul 2013 22:21:19 +0200 Subject: [PATCH] Fixed fabric.PathGroup.fromObject - use fabric.util.enlivenObjects to instatiate fabric.Path objects Corrected jsdoc tags for fabric.PathGroup.fromObject, fabric.Path.fromObject and fabric.Path.fromElement Fixed quint tests for fabric.Path and fabric.PathGroup --- src/shapes/path.class.js | 5 +- src/shapes/path_group.class.js | 21 +-- test/unit/path.js | 157 ++++++++++++---------- test/unit/path_group.js | 237 ++++++++++++++++++++------------- 4 files changed, 242 insertions(+), 178 deletions(-) diff --git a/src/shapes/path.class.js b/src/shapes/path.class.js index 7e81d24f..80df0b09 100644 --- a/src/shapes/path.class.js +++ b/src/shapes/path.class.js @@ -679,7 +679,8 @@ /** * Creates an instance of fabric.Path from an object * @static - * @return {fabric.Path} Instance of fabric.Path + * @param {Object} object + * @param {Function} callback Callback to invoke when an fabric.Path instance is created */ fabric.Path.fromObject = function(object, callback) { if (typeof object.path === 'string') { @@ -712,8 +713,8 @@ * Creates an instance of fabric.Path from an SVG element * @static * @param {SVGElement} element to parse + * @param {Function} callback Callback to invoke when an fabric.Path instance is created * @param {Object} [options] Options object - * @return {fabric.Path} Instance of fabric.Path */ fabric.Path.fromElement = function(element, callback, options) { var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES); diff --git a/src/shapes/path_group.class.js b/src/shapes/path_group.class.js index 5ffcccd7..fe10ef1a 100644 --- a/src/shapes/path_group.class.js +++ b/src/shapes/path_group.class.js @@ -207,24 +207,11 @@ } }); - /** - * @private - */ - function instantiatePaths(paths) { - for (var i = 0, len = paths.length; i < len; i++) { - if (!(paths[i] instanceof fabric.Object)) { - var klassName = camelize(capitalize(paths[i].type)); - paths[i] = fabric[klassName].fromObject(paths[i]); - } - } - return paths; - } - /** * Creates fabric.PathGroup instance from an object representation * @static * @param {Object} object - * @return {fabric.PathGroup} + * @param {Function} callback Callback to invoke when an fabric.PathGroup instance is created */ fabric.PathGroup.fromObject = function(object, callback) { if (typeof object.paths === 'string') { @@ -239,8 +226,10 @@ }); } else { - var paths = instantiatePaths(object.paths); - callback(new fabric.PathGroup(paths, object)); + fabric.util.enlivenObjects(object.paths, function(enlivenedObjects) { + delete object.paths; + callback(new fabric.PathGroup(enlivenedObjects, object)); + }); } }; diff --git a/test/unit/path.js b/test/unit/path.js index ed5ab9b0..a9f2bd8c 100644 --- a/test/unit/path.js +++ b/test/unit/path.js @@ -47,63 +47,73 @@ return el; } - function getPathObject(path) { - return fabric.Path.fromElement(getPathElement(path)); + function getPathObject(path, callback) { + fabric.Path.fromElement(getPathElement(path), callback); } - function makePathObject() { - return getPathObject("M 100 100 L 300 100 L 200 300 z"); + function makePathObject(callback) { + getPathObject("M 100 100 L 300 100 L 200 300 z", callback); } QUnit.module('fabric.Path'); - test('constructor', function() { + asyncTest('constructor', function() { ok(fabric.Path); - var path = makePathObject(); - ok(path instanceof fabric.Path); - ok(path instanceof fabric.Object); + makePathObject(function(path) { + ok(path instanceof fabric.Path); + ok(path instanceof fabric.Object); - equal(path.get('type'), 'path'); + equal(path.get('type'), 'path'); - var error; - try { - new fabric.Path(); - } - catch(err) { - error = err; - } + var error; + try { + new fabric.Path(); + } + catch(err) { + error = err; + } - ok(error, 'should throw error'); + ok(error, 'should throw error'); + start(); + }); }); - test('toString', function() { - var path = makePathObject(); - ok(typeof path.toString == 'function'); - equal(path.toString(), '#'); + asyncTest('toString', function() { + makePathObject(function(path) { + ok(typeof path.toString == 'function'); + equal(path.toString(), '#'); + start(); + }); }); - test('toObject', function() { - var path = makePathObject(); - ok(typeof path.toObject == 'function'); - deepEqual(path.toObject(), REFERENCE_PATH_OBJECT); + asyncTest('toObject', function() { + makePathObject(function(path) { + ok(typeof path.toObject == 'function'); + deepEqual(path.toObject(), REFERENCE_PATH_OBJECT); + start(); + }); }); - test('toDatalessObject', function() { - var path = makePathObject(); - ok(typeof path.toDatalessObject == 'function'); - deepEqual(path.toDatalessObject(), REFERENCE_PATH_OBJECT); + asyncTest('toDatalessObject', function() { + makePathObject(function(path) { + ok(typeof path.toDatalessObject == 'function'); + deepEqual(path.toDatalessObject(), REFERENCE_PATH_OBJECT); - var src = 'http://example.com/'; - path.setSourcePath(src); - deepEqual(path.toDatalessObject(), fabric.util.object.extend(fabric.util.object.clone(REFERENCE_PATH_OBJECT), { + var src = 'http://example.com/'; + path.setSourcePath(src); + deepEqual(path.toDatalessObject(), fabric.util.object.extend(fabric.util.object.clone(REFERENCE_PATH_OBJECT), { path: src - })); + })); + start(); + }); }); - test('complexity', function() { - var path = makePathObject(); - ok(typeof path.complexity == 'function'); + asyncTest('complexity', function() { + makePathObject(function(path) { + ok(typeof path.complexity == 'function'); + start(); + }); }); asyncTest('fromObject', function() { @@ -115,7 +125,7 @@ }); }); - test('fromElement', function() { + asyncTest('fromElement', function() { ok(typeof fabric.Path.fromElement == 'function'); var elPath = fabric.document.createElement('path'); @@ -133,52 +143,59 @@ //elPath.setAttribute('transform', 'scale(2) translate(10, -20)'); elPath.setAttribute('transform', 'scale(2)'); - var path = fabric.Path.fromElement(elPath); - ok(path instanceof fabric.Path); + fabric.Path.fromElement(elPath, function(path) { + ok(path instanceof fabric.Path); - deepEqual(path.toObject(), fabric.util.object.extend(REFERENCE_PATH_OBJECT, { - strokeDashArray: [5, 2], - strokeLineCap: 'round', - strokeLineJoin: 'bevil', - strokeMiterLimit: 5, - transformMatrix: [2, 0, 0, 2, 0, 0] - })); + deepEqual(path.toObject(), fabric.util.object.extend(REFERENCE_PATH_OBJECT, { + strokeDashArray: [5, 2], + strokeLineCap: 'round', + strokeLineJoin: 'bevil', + strokeMiterLimit: 5, + transformMatrix: [2, 0, 0, 2, 0, 0] + })); - var ANGLE = 90; + var ANGLE = 90; - elPath.setAttribute('transform', 'rotate(' + ANGLE + ')'); - path = fabric.Path.fromElement(elPath); + elPath.setAttribute('transform', 'rotate(' + ANGLE + ')'); + fabric.Path.fromElement(elPath, function(path) { - deepEqual( - path.get('transformMatrix'), - [ Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0 ] - ); + deepEqual( + path.get('transformMatrix'), + [ Math.cos(ANGLE), Math.sin(ANGLE), -Math.sin(ANGLE), Math.cos(ANGLE), 0, 0 ] + ); + start(); + }); + }); }); - test('multiple sequences in path commands', function() { + asyncTest('multiple sequences in path commands', function() { var el = getPathElement('M100 100 l 200 200 300 300 400 -50 z'); - var obj = fabric.Path.fromElement(el); + fabric.Path.fromElement(el, function(obj) { - deepEqual(obj.path[0], ['M', 100, 100]); - deepEqual(obj.path[1], ['l', 200, 200]); - deepEqual(obj.path[2], ['l', 300, 300]); - deepEqual(obj.path[3], ['l', 400, -50]); + deepEqual(obj.path[0], ['M', 100, 100]); + deepEqual(obj.path[1], ['l', 200, 200]); + deepEqual(obj.path[2], ['l', 300, 300]); + deepEqual(obj.path[3], ['l', 400, -50]); - el = getPathElement('c 0,-53.25604 43.17254,-96.42858 96.42857,-96.42857 53.25603,0 96.42857,43.17254 96.42857,96.42857'); - obj = fabric.Path.fromElement(el); + el = getPathElement('c 0,-53.25604 43.17254,-96.42858 96.42857,-96.42857 53.25603,0 96.42857,43.17254 96.42857,96.42857'); + fabric.Path.fromElement(el, function(obj) { - deepEqual(obj.path[0], ['c', 0, -53.25604, 43.17254, -96.42858, 96.42857, -96.42857]); - deepEqual(obj.path[1], ['c', 53.25603, 0, 96.42857, 43.17254, 96.42857, 96.42857]); + deepEqual(obj.path[0], ['c', 0, -53.25604, 43.17254, -96.42858, 96.42857, -96.42857]); + deepEqual(obj.path[1], ['c', 53.25603, 0, 96.42857, 43.17254, 96.42857, 96.42857]); + start(); + }); + }); }); - test('compressed path commands', function() { - + asyncTest('compressed path commands', function() { var el = getPathElement('M56.224 84.12c-.047.132-.138.221-.322.215.046-.131.137-.221.322-.215z'); - var obj = fabric.Path.fromElement(el); + fabric.Path.fromElement(el, function(obj) { - deepEqual(obj.path[0], ['M', 56.224, 84.12]); - deepEqual(obj.path[1], ['c', -0.047, 0.132, -0.138, 0.221, -0.322, 0.215]); - deepEqual(obj.path[2], ['c', 0.046, -0.131, 0.137, -0.221, 0.322, -0.215]); - deepEqual(obj.path[3], ['z']); + deepEqual(obj.path[0], ['M', 56.224, 84.12]); + deepEqual(obj.path[1], ['c', -0.047, 0.132, -0.138, 0.221, -0.322, 0.215]); + deepEqual(obj.path[2], ['c', 0.046, -0.131, 0.137, -0.221, 0.322, -0.215]); + deepEqual(obj.path[3], ['z']); + start(); + }); }); })(); diff --git a/test/unit/path_group.js b/test/unit/path_group.js index 5b1841f1..e9cdb21d 100644 --- a/test/unit/path_group.js +++ b/test/unit/path_group.js @@ -43,51 +43,76 @@ return el; } - function getPathObject(path) { - return fabric.Path.fromElement(getPathElement(path)); + function getPathObject(path, callback) { + fabric.Path.fromElement(getPathElement(path), callback); } - function getPathObjects() { - return [getPathObject("M 100 100 L 300 100 L 200 300 z"), - getPathObject("M 200 200 L 100 200 L 400 50 z")]; + function getPathObjects(callback) { + function onLoaded() { + if (++numLoadedObjects === numTotalObjects) { + if (callback) { + callback(objects); + } + } + } + + var objects = [ ], + paths = ["M 100 100 L 300 100 L 200 300 z", "M 200 200 L 100 200 L 400 50 z"], + numLoadedObjects = 0, + numTotalObjects = paths.length; + + paths.forEach(function (o, index) { + getPathObject(o, function(o) { + objects[index] = o; + onLoaded(); + }); + }); } - function getPathGroupObject() { - return new fabric.PathGroup(getPathObjects()); + function getPathGroupObject(callback) { + getPathObjects(function(objects) { + callback(new fabric.PathGroup(objects)); + }) } QUnit.module('fabric.PathGroup'); - test('constructor', function() { + asyncTest('constructor', function() { ok(fabric.PathGroup); - var pathGroup = getPathGroupObject(); + getPathGroupObject(function(pathGroup) { - ok(pathGroup instanceof fabric.PathGroup); - ok(pathGroup instanceof fabric.Object); - //this.assertHasMixin(Enumerable, pathGroup); + ok(pathGroup instanceof fabric.PathGroup); + ok(pathGroup instanceof fabric.Object); + //this.assertHasMixin(Enumerable, pathGroup); - equal(pathGroup.get('type'), 'path-group'); + equal(pathGroup.get('type'), 'path-group'); + start(); + }); }); - test('getObjects', function() { - var paths = getPathObjects(); - var pathGroup = new fabric.PathGroup(paths); - ok(typeof pathGroup.getObjects == 'function'); + asyncTest('getObjects', function() { + getPathObjects(function(paths) { + var pathGroup = new fabric.PathGroup(paths); + ok(typeof pathGroup.getObjects == 'function'); - // nulling group to avoid circular reference (qUnit goes into inifinite loop) - paths[0].group = null; - paths[1].group = null; + // nulling group to avoid circular reference (qUnit goes into inifinite loop) + paths[0].group = null; + paths[1].group = null; - deepEqual(pathGroup.getObjects(), paths); + deepEqual(pathGroup.getObjects(), paths); + start(); + }); }); - test('toObject', function() { - var pathGroup = getPathGroupObject(); - ok(typeof pathGroup.toObject == 'function'); - var object = pathGroup.toObject(); + asyncTest('toObject', function() { + getPathGroupObject(function(pathGroup) { + ok(typeof pathGroup.toObject == 'function'); + var object = pathGroup.toObject(); + start(); + }); }); - test('complexity', function() { + asyncTest('complexity', function() { function sum(objects) { var i = objects.length, total = 0; while (i--) { @@ -95,100 +120,132 @@ } return total; } - var pathGroup = getPathGroupObject(); + getPathGroupObject(function(pathGroup) { - ok(typeof pathGroup.complexity == 'function'); + ok(typeof pathGroup.complexity == 'function'); - var objectsTotalComplexity = pathGroup.getObjects().reduce(function(total, current) { - total += current.complexity(); - return total; - }, 0); + var objectsTotalComplexity = pathGroup.getObjects().reduce(function(total, current) { + total += current.complexity(); + return total; + }, 0); - equal(pathGroup.complexity(), objectsTotalComplexity); - }); - - test('toDatalessObject', function() { - var pathGroup = getPathGroupObject(); - ok(typeof pathGroup.toDatalessObject == 'function'); - - pathGroup.setSourcePath('http://example.com/'); - var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_PATH_GROUP_OBJECT), { - 'paths': 'http://example.com/', - 'sourcePath': 'http://example.com/' + equal(pathGroup.complexity(), objectsTotalComplexity); + start(); }); - deepEqual(pathGroup.toDatalessObject(), expectedObject); }); - test('toString', function() { - var pathGroup = getPathGroupObject(); - ok(typeof pathGroup.toString == 'function'); - equal(pathGroup.toString(), '#'); + asyncTest('toDatalessObject', function() { + getPathGroupObject(function(pathGroup) { + ok(typeof pathGroup.toDatalessObject == 'function'); + + pathGroup.setSourcePath('http://example.com/'); + var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_PATH_GROUP_OBJECT), { + 'paths': 'http://example.com/', + 'sourcePath': 'http://example.com/' + }); + deepEqual(pathGroup.toDatalessObject(), expectedObject); + start(); + }); }); - test('isSameColor', function() { - var pathGroup = getPathGroupObject(); + asyncTest('fromObject', function() { + getPathGroupObject(function(pathGroup) { - ok(typeof pathGroup.isSameColor == 'function'); - equal(pathGroup.isSameColor(), true); + ok(typeof fabric.PathGroup.fromObject == 'function'); + var pathGroupObject = pathGroup.toObject(); - pathGroup.getObjects()[0].set('fill', 'black'); - equal(pathGroup.isSameColor(), false); + fabric.PathGroup.fromObject(pathGroupObject, function(newPathGroupFromObject) { + + var objectFromOldPathGroup = pathGroup.toObject(); + var objectFromNewPathGroup = newPathGroupFromObject.toObject(); + + ok(newPathGroupFromObject instanceof fabric.PathGroup); + + deepEqual(objectFromNewPathGroup, objectFromOldPathGroup); + + start(); + }); + }); }); - test('set', function() { + asyncTest('toString', function() { + getPathGroupObject(function(pathGroup) { + ok(typeof pathGroup.toString == 'function'); + equal(pathGroup.toString(), '#'); + start(); + }); + }); + + asyncTest('isSameColor', function() { + getPathGroupObject(function(pathGroup) { + + ok(typeof pathGroup.isSameColor == 'function'); + equal(pathGroup.isSameColor(), true); + + pathGroup.getObjects()[0].set('fill', 'black'); + equal(pathGroup.isSameColor(), false); + start(); + }); + }); + + asyncTest('set', function() { var fillValue = 'rgb(100,200,100)'; - var pathGroup = getPathGroupObject(); + getPathGroupObject(function(pathGroup) { - pathGroup.getObjects()[0].group = null; - pathGroup.getObjects()[1].group = null; + pathGroup.getObjects()[0].group = null; + pathGroup.getObjects()[1].group = null; - ok(typeof pathGroup.set == 'function'); - equal(pathGroup.set('fill', fillValue), pathGroup, 'should be chainable'); + ok(typeof pathGroup.set == 'function'); + equal(pathGroup.set('fill', fillValue), pathGroup, 'should be chainable'); - pathGroup.getObjects().forEach(function(path) { - equal(path.get('fill'), fillValue); - }, this); + pathGroup.getObjects().forEach(function(path) { + equal(path.get('fill'), fillValue); + }, this); - equal(pathGroup.get('fill'), fillValue); + equal(pathGroup.get('fill'), fillValue); - // set different color to one of the paths - pathGroup.getObjects()[1].set('fill', 'black'); - pathGroup.set('fill', 'rgb(255,255,255)'); + // set different color to one of the paths + pathGroup.getObjects()[1].set('fill', 'black'); + pathGroup.set('fill', 'rgb(255,255,255)'); - equal(pathGroup.getObjects()[0].get('fill'), 'rgb(100,200,100)', - 'when paths are of different fill, setting fill of a group should not change them'); + equal(pathGroup.getObjects()[0].get('fill'), 'rgb(100,200,100)', + 'when paths are of different fill, setting fill of a group should not change them'); - pathGroup.getObjects()[1].set('fill', 'red'); + pathGroup.getObjects()[1].set('fill', 'red'); - pathGroup.set('left', 1234); - ok(pathGroup.getObjects()[0].get('left') !== 1234); - equal(pathGroup.get('left'), 1234); + pathGroup.set('left', 1234); + ok(pathGroup.getObjects()[0].get('left') !== 1234); + equal(pathGroup.get('left'), 1234); + start(); + }); }); - test('grayscale', function() { + asyncTest('grayscale', function() { - var pathGroup = getPathGroupObject(); + getPathGroupObject(function(pathGroup) { - pathGroup.getObjects()[0].group = null; - pathGroup.getObjects()[1].group = null; + pathGroup.getObjects()[0].group = null; + pathGroup.getObjects()[1].group = null; - ok(typeof pathGroup.toGrayscale == 'function'); - equal(pathGroup.toGrayscale(), pathGroup, 'should be chainable'); - var firstObject = pathGroup.getObjects()[0], - secondObject = pathGroup.getObjects()[1]; + ok(typeof pathGroup.toGrayscale == 'function'); + equal(pathGroup.toGrayscale(), pathGroup, 'should be chainable'); + var firstObject = pathGroup.getObjects()[0], + secondObject = pathGroup.getObjects()[1]; - firstObject.set('overlayFill', null); - secondObject.set('overlayFill', null); + firstObject.set('overlayFill', null); + secondObject.set('overlayFill', null); - firstObject.set('fill', 'rgb(200,0,0)'); - secondObject.set('fill', '0000FF'); + firstObject.set('fill', 'rgb(200,0,0)'); + secondObject.set('fill', '0000FF'); - pathGroup.toGrayscale(); + pathGroup.toGrayscale(); - equal(firstObject.get('overlayFill'), 'rgb(60,60,60)'); - equal(secondObject.get('overlayFill'), 'rgb(28,28,28)'); + equal(firstObject.get('overlayFill'), 'rgb(60,60,60)'); + equal(secondObject.get('overlayFill'), 'rgb(28,28,28)'); - equal(firstObject.get('fill'), 'rgb(200,0,0)', 'toGrayscale should not change original fill value'); - equal(new fabric.Color(secondObject.get('fill')).toRgb(), 'rgb(0,0,255)', 'toGrayscale should not change original fill value'); + equal(firstObject.get('fill'), 'rgb(200,0,0)', 'toGrayscale should not change original fill value'); + equal(new fabric.Color(secondObject.get('fill')).toRgb(), 'rgb(0,0,255)', 'toGrayscale should not change original fill value'); + start(); + }); }); })();