(function(){ var canvas = this.canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.StaticCanvas(); function getAbsolutePath(path) { var isAbsolute = /^https?:/.test(path); if (isAbsolute) return path; var imgEl = _createImageElement(); imgEl.src = path; var src = imgEl.src; imgEl = null; return src; } var IMG_SRC = fabric.isLikelyNode ? (__dirname + '/../fixtures/test_image.gif') : getAbsolutePath('../fixtures/test_image.gif'), IMG_WIDTH = 276, IMG_HEIGHT = 110; function _createImageElement() { return fabric.isLikelyNode ? new (require('canvas').Image)() : fabric.document.createElement('img'); } function createImageObject(callback) { var elImage = _createImageElement(); elImage.width = IMG_WIDTH; elImage.height = IMG_HEIGHT; setSrc(elImage, IMG_SRC, function() { callback(elImage); }); } function setSrc(img, src, callback) { if (fabric.isLikelyNode) { require('fs').readFile(src, function(err, imgData) { if (err) throw err; img.src = imgData; callback && callback(); }); } else { img.src = src; callback && callback(); } } QUnit.module('fabric.Object', { teardown: function() { canvas.clear(); canvas.backgroundColor = fabric.Canvas.prototype.backgroundColor; canvas.calcOffset(); } }); test('constructor & properties', function() { ok(typeof fabric.Object == 'function'); var cObj = new fabric.Object(); ok(cObj); ok(cObj instanceof fabric.Object); ok(cObj.constructor === fabric.Object); equal(cObj.type, 'object'); equal(cObj.includeDefaultValues, true); equal(cObj.selectable, true); }); test('get', function() { var cObj = new fabric.Object({ left: 11, top: 22, width: 50, height: 60, opacity: 0.7 }); equal(cObj.get('left'), 11); equal(cObj.get('top'), 22); equal(cObj.get('width'), 50); equal(cObj.get('height'), 60); equal(cObj.get('opacity'), 0.7); }); test('set', function() { var cObj = new fabric.Object({ left: 11, top: 22, width: 50, height: 60, opacity: 0.7 }); cObj.set('left', 12); cObj.set('top', 23); cObj.set('width', 51); cObj.set('height', 61); cObj.set('opacity', 0.5); equal(cObj.get('left'), 12); equal(cObj.get('top'), 23); equal(cObj.get('width'), 51); equal(cObj.get('height'), 61); equal(cObj.get('opacity'), 0.5); equal(cObj.set('opacity', 0.5), cObj, 'chainable'); }); test('set and minScaleLimit', function() { var cObj = new fabric.Object({ left: 11, top: 22, width: 50, height: 60, opacity: 0.7 }); //the min scale limit is given by height. equal(cObj.minScaleLimit.toFixed(3), 0.017); cObj.set('width', 1000); equal(cObj.width, 1000); //the min scale limit is given by width. equal(cObj.minScaleLimit, 0.001); cObj.set('width', 1); equal(cObj.width, 1); //the min scale limit is given by height. equal(cObj.minScaleLimit.toFixed(3), 0.017); }); test('set with object of prop/values', function() { var cObj = new fabric.Object({ }); equal(cObj, cObj.set({ width: 99, height: 88, fill: 'red' }), 'chainable'); equal('red', cObj.get('fill')); equal(99, cObj.get('width')); equal(88, cObj.get('height')); }); test('Dinamically generated accessors', function() { var cObj = new fabric.Object({ }); equal('function', typeof cObj.getWidth); equal('function', typeof cObj.setWidth); equal('function', typeof cObj.getFill); equal('function', typeof cObj.setFill); equal(cObj, cObj.setFill('red'), 'chainable'); equal('red', cObj.getFill()); cObj.setScaleX(2.3); equal(2.3, cObj.getScaleX()); cObj.setOpacity(0.123); equal(0.123, cObj.getOpacity()); }); test('setSourcePath', function() { var cObj = new fabric.Object(); var SRC_PATH = 'http://example.com/'; ok(typeof cObj.setSourcePath == 'function'); cObj.setSourcePath(SRC_PATH); equal(cObj.get('sourcePath'), SRC_PATH); }); test('stateProperties', function() { var cObj = new fabric.Object(); ok(cObj.stateProperties); ok(cObj.stateProperties.length > 0); }); test('transform', function() { var cObj = new fabric.Object(); ok(typeof cObj.transform == 'function'); }); test('toJSON', function() { var emptyObjectJSON = '{"type":"object","originX":"left","originY":"top","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)",'+ '"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+ '"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+ '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over",'+ '"transformMatrix":null,"skewX":0,"skewY":0}'; var augmentedJSON = '{"type":"object","originX":"left","originY":"top","left":0,"top":0,"width":122,"height":0,"fill":"rgb(0,0,0)",'+ '"stroke":null,"strokeWidth":1,"strokeDashArray":[5,2],"strokeLineCap":"round","strokeLineJoin":"bevil","strokeMiterLimit":5,'+ '"scaleX":1.3,"scaleY":1,"angle":0,"flipX":false,"flipY":true,"opacity":0.88,'+ '"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over",'+ '"transformMatrix":null,"skewX":0,"skewY":0}'; var cObj = new fabric.Object(); ok(typeof cObj.toJSON == 'function'); equal(JSON.stringify(cObj.toJSON()), emptyObjectJSON); cObj.set('opacity', 0.88) .set('scaleX', 1.3) .set('width', 122) .set('flipY', true) .set('strokeDashArray', [5, 2]) .set('strokeLineCap', 'round') .set('strokeLineJoin', 'bevil') .set('strokeMiterLimit', 5); equal(JSON.stringify(cObj.toJSON()), augmentedJSON); }); test('toObject', function() { var emptyObjectRepr = { 'type': 'object', 'originX': 'left', 'originY': 'top', 'left': 0, 'top': 0, 'width': 0, 'height': 0, 'fill': 'rgb(0,0,0)', 'stroke': null, 'strokeWidth': 1, 'strokeDashArray': null, 'strokeLineCap': 'butt', 'strokeLineJoin': 'miter', 'strokeMiterLimit': 10, 'scaleX': 1, 'scaleY': 1, 'angle': 0, 'flipX': false, 'flipY': false, 'opacity': 1, 'shadow': null, 'visible': true, 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, 'transformMatrix': null }; var augmentedObjectRepr = { 'type': 'object', 'originX': 'left', 'originY': 'top', 'left': 10, 'top': 20, 'width': 30, 'height': 40, 'fill': 'rgb(0,0,0)', 'stroke': null, 'strokeWidth': 1, 'strokeDashArray': [5, 2], 'strokeLineCap': 'round', 'strokeLineJoin': 'bevil', 'strokeMiterLimit': 5, 'scaleX': 1, 'scaleY': 1, 'angle': 0, 'flipX': true, 'flipY': false, 'opacity': 0.13, 'shadow': null, 'visible': true, 'backgroundColor': '', 'clipTo': null, 'fillRule': 'nonzero', 'globalCompositeOperation': 'source-over', 'transformMatrix': null, 'skewX': 0, 'skewY': 0 }; var cObj = new fabric.Object(); deepEqual(emptyObjectRepr, cObj.toObject()); cObj.set('left', 10) .set('top', 20) .set('width', 30) .set('height', 40) .set('flipX', true) .set('opacity', 0.13) .set('strokeDashArray', [5, 2]) .set('strokeLineCap', 'round') .set('strokeLineJoin', 'bevil') .set('strokeMiterLimit', 5); deepEqual(augmentedObjectRepr, cObj.toObject()); var fractionalValue = 166.66666666666666, testedProperties = 'left top width height'.split(' '), fractionDigitsDefault = 2; function testFractionDigits(fractionDigits, expectedValue) { fabric.Object.NUM_FRACTION_DIGITS = fractionDigits; testedProperties.forEach(function(property) { cObj.set(property, fractionalValue); equal(cObj.toObject()[property], expectedValue, 'value of ' + property + ' should have ' + fractionDigits + ' fractional digits'); }, this); fabric.Object.NUM_FRACTION_DIGITS = fractionDigitsDefault; } testFractionDigits.call(this, 2, 166.67); testFractionDigits.call(this, 3, 166.667); testFractionDigits.call(this, 0, 167); }); test('toObject without default values', function() { var emptyObjectRepr = { type: 'object' }; var augmentedObjectRepr = { type: 'object', left: 10, top: 20, width: 30, height: 40, strokeDashArray: [ 5, 2 ], strokeLineCap: 'round', strokeLineJoin: 'bevil', strokeMiterLimit: 5, flipX: true, opacity: 0.13, transformMatrix: [3, 0, 3, 1, 0, 0] }; var cObj = new fabric.Object(), toObjectObj; cObj.includeDefaultValues = false; deepEqual(emptyObjectRepr, cObj.toObject()); cObj.set('left', 10) .set('top', 20) .set('width', 30) .set('height', 40) .set('flipX', true) .set('opacity', 0.13) .set('strokeDashArray', [5, 2]) .set('strokeLineCap', 'round') .set('strokeLineJoin', 'bevil') .set('strokeMiterLimit', 5) .set('transformMatrix', [3, 0, 3, 1, 0, 0]); toObjectObj = cObj.toObject(); deepEqual(augmentedObjectRepr, toObjectObj); notEqual(augmentedObjectRepr.transformMatrix, toObjectObj.transformMatrix); deepEqual(augmentedObjectRepr.transformMatrix, toObjectObj.transformMatrix); notEqual(augmentedObjectRepr.strokeDashArray, toObjectObj.strokeDashArray); deepEqual(augmentedObjectRepr.strokeDashArray, toObjectObj.strokeDashArray); }); test('toDatalessObject', function() { var cObj = new fabric.Object(); ok(typeof cObj.toDatalessObject == 'function'); deepEqual(cObj.toObject(), cObj.toDatalessObject()); }); test('toString', function() { var cObj = new fabric.Object(); equal(cObj.toString(), '#'); cObj.type = 'moo'; equal(cObj.toString(), '#'); }); test('render', function() { var cObj = new fabric.Object(); ok(typeof cObj.render == 'function'); }); test('getBoundingRect', function() { var cObj = new fabric.Object({ strokeWidth: 0 }), boundingRect; ok(typeof cObj.getBoundingRect == 'function'); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left, 0); equal(boundingRect.top, 0); equal(boundingRect.width, 0); equal(boundingRect.height, 0); cObj.set('width', 123).setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left, 0); equal(boundingRect.top, 0); equal(boundingRect.width, 123); equal(boundingRect.height, 0); cObj.set('height', 167).setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left, 0); equal(Math.abs(boundingRect.top).toFixed(13), 0); equal(boundingRect.width, 123); equal(boundingRect.height, 167); cObj.scale(2).setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left, 0); equal(Math.abs(boundingRect.top).toFixed(13), 0); equal(boundingRect.width, 246); equal(boundingRect.height, 334); }); test('getBoundingRectWithStroke', function() { var cObj = new fabric.Object(), boundingRect; ok(typeof cObj.getBoundingRect == 'function'); cObj.setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left.toFixed(2), 0); equal(boundingRect.top.toFixed(2), 0); equal(boundingRect.width.toFixed(2), 1); equal(boundingRect.height.toFixed(2), 1); cObj.set('width', 123).setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left.toFixed(2), 0); equal(boundingRect.top.toFixed(2), 0); equal(boundingRect.width.toFixed(2), 124); equal(boundingRect.height.toFixed(2), 1); cObj.set('height', 167).setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left.toFixed(2), 0); equal(boundingRect.top.toFixed(2), 0); equal(boundingRect.width.toFixed(2), 124); equal(boundingRect.height.toFixed(2), 168); cObj.scale(2).setCoords(); boundingRect = cObj.getBoundingRect(); equal(boundingRect.left.toFixed(2), 0); equal(boundingRect.top.toFixed(2), 0); equal(boundingRect.width.toFixed(2), 248); equal(boundingRect.height.toFixed(2), 336); }); test('getWidth', function() { var cObj = new fabric.Object(); ok(typeof cObj.getWidth == 'function'); equal(cObj.getWidth(), 0 + cObj.strokeWidth); cObj.set('width', 123); equal(cObj.getWidth(), 123 + cObj.strokeWidth); cObj.set('scaleX', 2); equal(cObj.getWidth(), 246 + cObj.strokeWidth * 2); }); test('getHeight', function() { var cObj = new fabric.Object({strokeWidth: 0}); ok(typeof cObj.getHeight == 'function'); equal(cObj.getHeight(), 0); cObj.set('height', 123); equal(cObj.getHeight(), 123); cObj.set('scaleY', 2); equal(cObj.getHeight(), 246); }); test('rotate', function() { var cObj = new fabric.Object(); ok(typeof cObj.rotate == 'function'); equal(cObj.get('angle'), 0); equal(cObj.rotate(45), cObj, 'chainable'); equal(cObj.get('angle'), 45); }); test('scale', function() { var cObj = new fabric.Object(); ok(typeof cObj.scale == 'function'); equal(cObj.get('scaleX'), 1); equal(cObj.get('scaleY'), 1); cObj.scale(1.5); equal(cObj.get('scaleX'), 1.5); equal(cObj.get('scaleY'), 1.5); equal(cObj.scale(2), cObj, 'chainable'); }); test('scaleToWidth', function() { var cObj = new fabric.Object({ width: 560, strokeWidth: 0 }); ok(typeof cObj.scaleToWidth == 'function'); equal(cObj.scaleToWidth(100), cObj, 'chainable'); equal(cObj.getWidth(), 100); equal(cObj.get('scaleX'), 100/560); }); test('scaleToHeight', function() { var cObj = new fabric.Object({ height: 560, strokeWidth: 0 }); ok(typeof cObj.scaleToHeight == 'function'); equal(cObj.scaleToHeight(100), cObj, 'chainable'); equal(cObj.getHeight(), 100); equal(cObj.get('scaleY'), 100/560); }); test('scaleToWidth on rotated object', function() { var obj = new fabric.Object({ height: 100, width: 100, strokeWidth: 0 }); obj.rotate(45); obj.scaleToWidth(200); equal(Math.round(obj.getBoundingRect().width), 200); }); test('scaleToHeight on rotated object', function() { var obj = new fabric.Object({ height: 100, width: 100, strokeWidth: 0 }); obj.rotate(45); obj.scaleToHeight(300); equal(Math.round(obj.getBoundingRect().height), 300); }); test('setOpacity', function() { var cObj = new fabric.Object(); ok(typeof cObj.setOpacity == 'function'); equal(cObj.get('opacity'), 1); cObj.setOpacity(0.68); equal(cObj.get('opacity'), 0.68); equal(cObj.setOpacity(1), cObj, 'chainable'); }); test('getAngle', function() { var cObj = new fabric.Object(); ok(typeof cObj.getAngle == 'function'); equal(cObj.getAngle(), 0); cObj.rotate(45); equal(cObj.getAngle(), 45); }); test('setAngle', function() { var cObj = new fabric.Object(); ok(typeof cObj.setAngle == 'function'); equal(cObj.get('angle'), 0); equal(cObj.setAngle(45), cObj, 'chainable'); equal(cObj.get('angle'), 45); }); test('drawBorders', function() { var cObj = new fabric.Object(), canvas = fabric.document.createElement('canvas'); //let excanvas kick in for IE8 and lower if (!canvas.getContext && typeof G_vmlCanvasManager != 'undefined') { G_vmlCanvasManager.initElement(canvas); } var dummyContext = canvas.getContext('2d'); ok(typeof cObj.drawBorders == 'function'); equal(cObj.drawBorders(dummyContext), cObj, 'chainable'); }); test('drawControls', function() { var cObj = new fabric.Object(), canvas = fabric.document.createElement('canvas'); //let excanvas kick in for IE8 and lower if (!canvas.getContext && typeof G_vmlCanvasManager != 'undefined') { G_vmlCanvasManager.initElement(canvas); } var dummyContext = canvas.getContext('2d'); ok(typeof cObj.drawControls == 'function'); equal(cObj.drawControls(dummyContext), cObj, 'chainable'); }); test('clone', function() { var cObj = new fabric.Object({ left: 123, top: 456, opacity: 0.66 }); ok(typeof cObj.clone == 'function'); var clone = cObj.clone(); equal(clone.get('left'), 123); equal(clone.get('top'), 456); equal(clone.get('opacity'), 0.66); // augmenting clone properties should not affect original instance clone.set('left', 12).set('scaleX', 2.5).setAngle(33); equal(cObj.get('left'), 123); equal(cObj.get('scaleX'), 1); equal(cObj.getAngle(), 0); }); asyncTest('cloneAsImage', function() { var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red' }); ok(typeof cObj.cloneAsImage == 'function'); if (!fabric.Canvas.supports('toDataURL')) { fabric.log('`toDataURL` is not supported by this environment; skipping `cloneAsImage` test (as it relies on `toDataURL`)'); start(); } else { var image; setTimeout(function() { ok(image); ok(image instanceof fabric.Image); start(); }, 500); cObj.cloneAsImage(function(i) { image = i; }); } }); test('toDataURL', function() { // var data = // 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQA'+ // 'AABkCAYAAABw4pVUAAAA+UlEQVR4nO3RoRHAQBDEsOu/6YR+B2s'+ // 'gIO4Z3919pMwDMCRtHoAhafMADEmbB2BI2jwAQ9LmARiSNg/AkLR5AI'+ // 'akzQMwJG0egCFp8wAMSZsHYEjaPABD0uYBGJI2D8CQtHkAhqTNAzAkbR'+ // '6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPwJC0eQCGpM0DMCRtHoAhafMADEm'+ // 'bB2BI2jwAQ9LmARiSNg/AkLR5AIakzQMwJG0egCFp8wAMSZsHYEjaPABD0'+ // 'uYBGJI2D8CQtHkAhqTNAzAkbR6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPw'+ // 'JC0eQCGpM0DMCRtHsDjB5K06yueJFXJAAAAAElFTkSuQmCC'; var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red' }); ok(typeof cObj.toDataURL == 'function'); if (!fabric.Canvas.supports('toDataURL')) { window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.'); } else { var dataURL = cObj.toDataURL(); equal(typeof dataURL, 'string'); equal(dataURL.substring(0, 21), 'data:image/png;base64'); try { dataURL = cObj.toDataURL({ format: 'jpeg' }); equal(dataURL.substring(0, 22), 'data:image/jpeg;base64'); } catch(err) { fabric.log('jpeg toDataURL not supported'); } } }); test('toDataURL & reference to canvas', function() { // var data = // 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQA'+ // 'AABkCAYAAABw4pVUAAAA+UlEQVR4nO3RoRHAQBDEsOu/6YR+B2s'+ // 'gIO4Z3919pMwDMCRtHoAhafMADEmbB2BI2jwAQ9LmARiSNg/AkLR5AI'+ // 'akzQMwJG0egCFp8wAMSZsHYEjaPABD0uYBGJI2D8CQtHkAhqTNAzAkbR'+ // '6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPwJC0eQCGpM0DMCRtHoAhafMADEm'+ // 'bB2BI2jwAQ9LmARiSNg/AkLR5AIakzQMwJG0egCFp8wAMSZsHYEjaPABD0'+ // 'uYBGJI2D8CQtHkAhqTNAzAkbR6AIWnzAAxJmwdgSNo8AEPS5gEYkjYPw'+ // 'JC0eQCGpM0DMCRtHsDjB5K06yueJFXJAAAAAElFTkSuQmCC'; var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red' }); canvas.add(cObj); if (!fabric.Canvas.supports('toDataURL')) { window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.'); } else { var objCanvas = cObj.canvas; cObj.toDataURL(); equal(objCanvas, cObj.canvas); } }); test('hasStateChanged', function() { var cObj = new fabric.Object(); ok(typeof cObj.hasStateChanged == 'function'); cObj.setupState(); ok(!cObj.hasStateChanged()); cObj.saveState(); cObj.set('left', 123).set('top', 456); ok(cObj.hasStateChanged()); }); test('saveState', function() { var cObj = new fabric.Object(); ok(typeof cObj.saveState == 'function'); cObj.setupState(); equal(cObj.saveState(), cObj, 'chainable'); cObj.set('left', 123).set('top', 456); cObj.saveState(); cObj.set('left', 223).set('top', 556); equal(cObj.originalState.left, 123); equal(cObj.originalState.top, 456); }); test('intersectsWithRectangle', function() { var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100 }); cObj.setCoords(); ok(typeof cObj.intersectsWithRect == 'function'); var point1 = new fabric.Point(110, 100), point2 = new fabric.Point(210, 200), point3 = new fabric.Point(0, 0), point4 = new fabric.Point(10, 10); ok(cObj.intersectsWithRect(point1, point2)); ok(!cObj.intersectsWithRect(point3, point4)); }); test('intersectsWithObject', function() { var cObj = new fabric.Object({ left: 50, top: 50, width: 100, height: 100 }); cObj.setCoords(); ok(typeof cObj.intersectsWithObject == 'function'); var cObj2 = new fabric.Object({ left: -150, top: -150, width: 200, height: 200 }); cObj2.setCoords(); ok(cObj.intersectsWithObject(cObj2)); ok(cObj2.intersectsWithObject(cObj)); var cObj3 = new fabric.Object({ left: 392.5, top: 339.5, width: 13, height: 33 }); cObj3.setCoords(); ok(!cObj.intersectsWithObject(cObj3)); ok(!cObj3.intersectsWithObject(cObj)); }); test('isContainedWithinRect', function() { var cObj = new fabric.Object({ left: 20, top: 20, width: 10, height: 10 }); cObj.setCoords(); ok(typeof cObj.isContainedWithinRect == 'function'); // fully contained ok(cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(100,100))); // only intersects ok(!cObj.isContainedWithinRect(new fabric.Point(10,10), new fabric.Point(25, 25))); // doesn't intersect ok(!cObj.isContainedWithinRect(new fabric.Point(100,100), new fabric.Point(110, 110))); }); test('isType', function() { var cObj = new fabric.Object(); ok(typeof cObj.isType == 'function'); ok(cObj.isType('object')); ok(!cObj.isType('rect')); cObj = new fabric.Rect(); ok(cObj.isType('rect')); ok(!cObj.isType('object')); }); test('toggle', function() { var object = new fabric.Object({ left: 100, top: 124, width: 210, height: 66 }); ok(typeof object.toggle == 'function'); object.set('flipX', false); equal(object.toggle('flipX'), object, 'should be chainable'); equal(object.get('flipX'), true); object.toggle('flipX'); equal(object.get('flipX'), false); object.set('left', 112.45); object.toggle('left'); equal(object.get('left'), 112.45, 'non boolean properties should not be affected'); }); test('_setLineDash', function() { var object = new fabric.Rect({ left: 100, top: 124, width: 210, height: 66, stroke: 'black', strokeWidth: 2}); ok(typeof object._setLineDash === 'function'); canvas.add(object); object.strokeDashArray = [3, 2, 1]; equal(object.strokeDashArray.length, 3, 'strokeDash array is odd'); canvas.renderAll(); equal(object.strokeDashArray.length, 6, 'strokeDash array now is even'); }); test('straighten', function() { var object = new fabric.Object({ left: 100, top: 124, width: 210, height: 66 }); ok(typeof object.straighten == 'function'); object.setAngle(123.456); object.straighten(); equal(object.get('angle'), 90); object.setAngle(97.111); object.straighten(); equal(object.get('angle'), 90); object.setAngle(3.45); object.straighten(); equal(object.get('angle'), 0); object.setAngle(-157); object.straighten(); equal(object.get('angle'), -180); object.setAngle(159); object.straighten(); equal(object.get('angle'), 180); object.setAngle(999); object.straighten(); equal(object.get('angle'), 270); }); asyncTest('fxStraighten', function() { var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); var onCompleteFired = false; var onComplete = function(){ onCompleteFired = true; }; var onChangeFired = false; var onChange = function(){ onChangeFired = true; }; var callbacks = { onComplete: onComplete, onChange: onChange }; ok(typeof object.fxStraighten == 'function'); equal(object.fxStraighten(callbacks), object, 'should be chainable'); equal(fabric.util.toFixed(object.get('angle'), 0), 43); setTimeout(function(){ ok(onCompleteFired); ok(onChangeFired); equal(object.get('angle'), 0, 'angle should be set to 0 by the end of animation'); equal(object.fxStraighten(), object, 'should work without callbacks'); start(); }, 1000); }); asyncTest('animate', function() { var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); ok(typeof object.animate == 'function'); object.animate('left', 40); ok(true, 'animate without options does not crash'); setTimeout(function() { equal(40, Math.round(object.getLeft())); start(); }, 1000); }); asyncTest('animate multiple properties', function() { var object = new fabric.Object({ left: 123, top: 124 }); object.animate({ left: 223, top: 224 }); setTimeout(function() { equal(223, Math.round(object.get('left'))); equal(224, Math.round(object.get('top'))); start(); }, 1000); }); asyncTest('animate multiple properties with callback', function() { var object = new fabric.Object({ left: 0, top: 0 }); var changedInvocations = 0; var completeInvocations = 0; object.animate({ left: 1, top: 1 }, { duration: 1, onChange: function() { changedInvocations++; }, onComplete: function() { completeInvocations++; } }); setTimeout(function() { equal(Math.round(object.get('left')), 1); equal(Math.round(object.get('top')), 1); //equal(changedInvocations, 2); equal(completeInvocations, 1); start(); }, 1000); }); asyncTest('animate with abort', function() { var object = new fabric.Object({ left: 123, top: 124 }); var context; object.animate({ left: 223, top: 224 }, { abort: function() { context = this; return true; } }); setTimeout(function() { equal(123, Math.round(object.get('left'))); equal(124, Math.round(object.get('top'))); equal(context, object, 'abort should be called in context of an object'); start(); }, 100); }); test('observable', function() { var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); var fooFired = false, barFired = false; object.on('foo', function(){ fooFired = true; }); object.on('bar', function(){ barFired = true; }); object.fire('foo'); ok(fooFired); ok(!barFired); object.fire('bar'); ok(fooFired); ok(barFired); var firedOptions; object.on('baz', function(options) { firedOptions = options; }); object.fire('baz', { param1: 'abrakadabra', param2: 3.1415 }); equal('abrakadabra', firedOptions.param1); equal(3.1415, firedOptions.param2); }); test('object:added', function() { var object = new fabric.Object(); var addedEventFired = false; object.on('added', function(){ addedEventFired = true; }); canvas.add(object); ok(addedEventFired); }); test('canvas reference', function() { var object = new fabric.Object(); var object2 = new fabric.Object(); canvas.add(object); canvas.insertAt(object2, 0); ok(object.canvas === canvas); ok(object2.canvas === canvas); }); test('remove', function() { var object = new fabric.Object(); ok(typeof object.remove == 'function'); canvas.add(object); equal(object.remove(), object, 'should be chainable'); equal(canvas.getObjects().length, 0); }); test('object:removed', function() { var object = new fabric.Object(); var removedEventFired = false; canvas.add(object); object.on('removed', function(){ removedEventFired = true; }); object.remove(); ok(removedEventFired); }); test('center', function() { var object = new fabric.Object(); object.strokeWidth = 0; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; ok(typeof object.center == 'function'); canvas.add(object); equal(object.center(), object, 'should be chainable'); equal(object.getCenterPoint().x, canvas.getWidth() / 2); equal(object.getCenterPoint().y, canvas.getHeight() / 2); canvas.setZoom(2); object.center(); equal(object.getCenterPoint().x, canvas.getWidth() / 2, 'object center.x is in canvas center when the canvas is transformed'); equal(object.getCenterPoint().y, canvas.getHeight() / 2, 'object center.y is in canvas center when the canvas is transformed'); }); test('centerH', function() { var object = new fabric.Object(); object.strokeWidth = 0; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; ok(typeof object.centerH == 'function'); var oldY = object.top; canvas.add(object); equal(object.centerH(), object, 'should be chainable'); equal(object.getCenterPoint().x, canvas.getWidth() / 2); equal(object.top, oldY, 'object top did not change'); canvas.setZoom(2); object.centerH(); equal(object.getCenterPoint().x, canvas.getWidth() / 2, 'object center.x is in canvas center when the canvas is transformed'); }); test('centerV', function() { var object = new fabric.Object(); object.strokeWidth = 0; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; ok(typeof object.centerV == 'function'); var oldX = object.left; canvas.add(object); equal(object.centerV(), object, 'should be chainable'); equal(object.left, oldX, 'object top did not change'); equal(object.getCenterPoint().y, canvas.getHeight() / 2); canvas.setZoom(2); object.centerV(); equal(object.getCenterPoint().y, canvas.getHeight() / 2, 'object center.y is in canvas center when the canvas is transformed'); }); test('viewportCenter', function() { var object = new fabric.Object(); object.strokeWidth = 0; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; ok(typeof object.viewportCenter == 'function'); canvas.add(object); equal(object.viewportCenter(), object, 'should be chainable'); equal(object.getCenterPoint().x, canvas.getWidth() / 2); equal(object.getCenterPoint().y, canvas.getHeight() / 2); canvas.setZoom(2); object.viewportCenter(); equal(object.getCenterPoint().x, canvas.getWidth() / (2 * canvas.getZoom())); equal(object.getCenterPoint().y, canvas.getHeight() / (2 * canvas.getZoom())); }); test('viewportCenterH', function() { var object = new fabric.Object(); object.strokeWidth = 0; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; ok(typeof object.viewportCenterH == 'function'); var oldY = object.top; canvas.add(object); equal(object.viewportCenterH(), object, 'should be chainable'); equal(object.getCenterPoint().x, canvas.getWidth() / 2); equal(object.top, oldY, 'object top did not change'); canvas.setZoom(2); object.viewportCenterH(); equal(object.getCenterPoint().x, canvas.getWidth() / (2 * canvas.getZoom())); equal(object.top, oldY, 'object top did not change'); }); test('viewportCenterV', function() { var object = new fabric.Object(); object.strokeWidth = 0; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; ok(typeof object.viewportCenterV == 'function'); var oldX = object.left; canvas.add(object); equal(object.viewportCenterV(), object, 'should be chainable'); equal(object.getCenterPoint().y, canvas.getHeight() / 2); equal(object.left, oldX, 'object left did not change'); canvas.setZoom(2); object.viewportCenterV(); equal(object.getCenterPoint().y, canvas.getHeight() / (2 * canvas.getZoom())); equal(object.left, oldX, 'object left did not change'); }); test('sendToBack', function() { var object = new fabric.Object(); ok(typeof object.sendToBack == 'function'); canvas.add(object); equal(object.sendToBack(), object, 'should be chainable'); }); test('bringToFront', function() { var object = new fabric.Object(); ok(typeof object.bringToFront == 'function'); canvas.add(object); equal(object.bringToFront(), object, 'should be chainable'); }); test('sendBackwards', function() { var object = new fabric.Object(); ok(typeof object.sendBackwards == 'function'); canvas.add(object); equal(object.sendBackwards(), object, 'should be chainable'); }); test('bringForward', function() { var object = new fabric.Object(); ok(typeof object.bringForward == 'function'); canvas.add(object); equal(object.bringForward(), object, 'should be chainable'); }); test('moveTo', function() { var object = new fabric.Object(); ok(typeof object.moveTo == 'function'); canvas.add(object); equal(object.moveTo(), object, 'should be chainable'); }); test('setGradient', function() { var object = new fabric.Object(); ok(typeof object.setGradient == 'function'); equal(object.setGradient('fill', { x1: 0, y1: 0, x2: 100, y2: 100, colorStops: { '0': 'rgb(255,0,0)', '1': 'rgb(0,128,0)' } }), object, 'should be chainable'); ok(typeof object.toObject().fill == 'object'); ok(object.fill instanceof fabric.Gradient); var fill = object.fill; equal(fill.type, 'linear'); equal(fill.coords.x1, 0); equal(fill.coords.y1, 0); equal(fill.coords.x2, 100); equal(fill.coords.y2, 100); equal(fill.colorStops[0].offset, 0); equal(fill.colorStops[1].offset, 1); equal(fill.colorStops[0].color, 'rgb(255,0,0)'); equal(fill.colorStops[1].color, 'rgb(0,128,0)'); }); test('setGradient with gradientTransform', function() { var object = new fabric.Object(); ok(typeof object.setGradient == 'function'); equal(object.setGradient('fill', { x1: 0, y1: 0, x2: 100, y2: 100, gradientTransform: [1, 0, 0, 4, 5, 5], colorStops: { '0': 'rgb(255,0,0)', '1': 'rgb(0,128,0)' } }), object, 'should be chainable'); ok(typeof object.toObject().fill == 'object'); ok(object.fill instanceof fabric.Gradient); var fill = object.fill; equal(fill.type, 'linear'); equal(fill.coords.x1, 0); equal(fill.coords.y1, 0); equal(fill.coords.x2, 100); equal(fill.coords.y2, 100); deepEqual(fill.gradientTransform, [1, 0, 0, 4, 5, 5]); equal(fill.colorStops[0].offset, 0); equal(fill.colorStops[1].offset, 1); equal(fill.colorStops[0].color, 'rgb(255,0,0)'); equal(fill.colorStops[1].color, 'rgb(0,128,0)'); }); asyncTest('setPatternFill', function() { var object = new fabric.Object(); ok(typeof object.setPatternFill == 'function'); createImageObject(function(img) { equal(object.setPatternFill({source: img}), object, 'should be chainable'); ok(typeof object.toObject().fill == 'object'); ok(object.fill instanceof fabric.Pattern); equal(object.fill.source, img); equal(object.fill.repeat, 'repeat'); equal(object.fill.offsetX, 0); equal(object.fill.offsetY, 0); equal(object.setPatternFill({source: img, repeat: 'repeat-y', offsetX: 100, offsetY: 50}), object, 'should be chainable'); ok(typeof object.fill == 'object'); ok(object.fill instanceof fabric.Pattern); equal(object.fill.source, img); equal(object.fill.repeat, 'repeat-y'); equal(object.fill.offsetX, 100); equal(object.fill.offsetY, 50); start(); }); }); test('setShadow', function() { var object = new fabric.Object(); ok(typeof object.setShadow == 'function'); equal(object.setShadow({ color: 'red', blur: 10, offsetX: 5, offsetY: 15 }), object, 'should be chainable'); ok(typeof object.toObject().shadow == 'object'); ok(object.shadow instanceof fabric.Shadow); equal(object.shadow.color, 'red'); equal(object.shadow.blur, 10); equal(object.shadow.offsetX, 5); equal(object.shadow.offsetY, 15); equal(object.setShadow(null), object, 'should be chainable'); ok(!(object.shadow instanceof fabric.Shadow)); equal(object.shadow, null); }); test('set shadow', function() { var object = new fabric.Object(); object.set('shadow', '10px 5px 0 #FF0000'); ok(object.shadow instanceof fabric.Shadow); equal(object.shadow.color, '#FF0000'); equal(object.shadow.blur, 0); equal(object.shadow.offsetX, 10); equal(object.shadow.offsetY, 5); object.set('shadow', null); ok(!(object.shadow instanceof fabric.Shadow)); equal(object.shadow, null); }); test('setColor', function(){ var object = new fabric.Object(); ok(typeof object.setColor == 'function'); equal(object.setColor('123456'), object, 'should be chainable'); equal(object.get('fill'), '123456'); }); test('intersectsWithRect', function() { var object = new fabric.Object({ left: 0, top: 0, width: 40, height: 50, angle: 160 }), point1 = new fabric.Point(-10, -10), point2 = new fabric.Point(20, 30), point3 = new fabric.Point(10, 15), point4 = new fabric.Point(30, 35), point5 = new fabric.Point(50, 60), point6 = new fabric.Point(70, 80); object.setCoords(); // object and area intersects equal(object.intersectsWithRect(point1, point2), true); // area is contained in object (no intersection) equal(object.intersectsWithRect(point3, point4), false); // area is outside of object (no intersection) equal(object.intersectsWithRect(point5, point6), false); }); test('intersectsWithObject', function() { var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 230, strokeWidth: 0 }), object1 = new fabric.Object({ left: 20, top: 30, width: 60, height: 30, angle: 10, strokeWidth: 0 }), object2 = new fabric.Object({ left: 25, top: 35, width: 20, height: 20, angle: 50, strokeWidth: 0 }), object3 = new fabric.Object({ left: 50, top: 50, width: 20, height: 20, angle: 0, strokeWidth: 0 }); object.set({ originX: 'center', originY: 'center' }).setCoords(); object1.set({ originX: 'center', originY: 'center' }).setCoords(); object2.set({ originX: 'center', originY: 'center' }).setCoords(); object3.set({ originX: 'center', originY: 'center' }).setCoords(); // object and object1 intersects equal(object.intersectsWithObject(object1), true); // object2 is contained in object (no intersection) equal(object.intersectsWithObject(object2), false); // object3 is outside of object (no intersection) equal(object.intersectsWithObject(object3), false); }); test('isContainedWithinObject', function() { var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 230 }), object1 = new fabric.Object({ left: 25, top: 35, width: 20, height: 20, angle: 50 }), object2 = new fabric.Object({ left: 20, top: 30, width: 60, height: 30, angle: 10 }), object3 = new fabric.Object({ left: 50, top: 50, width: 20, height: 20, angle: 0 }); object.set({ originX: 'center', originY: 'center' }).setCoords(); object1.set({ originX: 'center', originY: 'center' }).setCoords(); object2.set({ originX: 'center', originY: 'center' }).setCoords(); object3.set({ originX: 'center', originY: 'center' }).setCoords(); // object1 is fully contained within object equal(object1.isContainedWithinObject(object), true); // object2 intersects object (not fully contained) equal(object2.isContainedWithinObject(object), false); // object3 is outside of object (not fully contained) equal(object3.isContainedWithinObject(object), false); }); test('isContainedWithinRect', function() { var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160 }), point1 = new fabric.Point(0, 0), point2 = new fabric.Point(80, 80), point3 = new fabric.Point(0, 0), point4 = new fabric.Point(80, 60), point5 = new fabric.Point(80, 80), point6 = new fabric.Point(90, 90); object.set({ originX: 'center', originY: 'center' }).setCoords(); // area is contained in object (no intersection) equal(object.isContainedWithinRect(point1, point2), true); // object and area intersects equal(object.isContainedWithinRect(point3, point4), false); // area is outside of object (no intersection) equal(object.isContainedWithinRect(point5, point6), false); }); test('isContainedWithinRect', function() { var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160 }), point1 = new fabric.Point(0, 0), point2 = new fabric.Point(80, 80), point3 = new fabric.Point(0, 0), point4 = new fabric.Point(80, 60), point5 = new fabric.Point(80, 80), point6 = new fabric.Point(90, 90); object.set({ originX: 'center', originY: 'center' }).setCoords(); // area is contained in object (no intersection) equal(object.isContainedWithinRect(point1, point2), true); // object and area intersects equal(object.isContainedWithinRect(point3, point4), false); // area is outside of object (no intersection) equal(object.isContainedWithinRect(point5, point6), false); }); test('containsPoint', function() { var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160, strokeWidth: 0 }), point1 = new fabric.Point(30, 30), point2 = new fabric.Point(60, 30), point3 = new fabric.Point(45, 65), point4 = new fabric.Point(15, 40), point5 = new fabric.Point(30, 15); object.set({ originX: 'center', originY: 'center' }).setCoords(); // point1 is contained in object equal(object.containsPoint(point1), true); // point2 is outside of object (right) equal(object.containsPoint(point2), false); // point3 is outside of object (bottom) equal(object.containsPoint(point3), false); // point4 is outside of object (left) equal(object.containsPoint(point4), false); // point5 is outside of object (top) equal(object.containsPoint(point5), false); }); test('containsPoint with padding', function() { var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, angle: 160, padding: 5 }), point1 = new fabric.Point(30, 30), point2 = new fabric.Point(10, 20), point3 = new fabric.Point(65, 30), point4 = new fabric.Point(45, 75), point5 = new fabric.Point(10, 40), point6 = new fabric.Point(30, 5); object.set({ originX: 'center', originY: 'center' }).setCoords(); // point1 is contained in object equal(object.containsPoint(point1), true); // point2 is contained in object (padding area) equal(object.containsPoint(point2), true); // point2 is outside of object (right) equal(object.containsPoint(point3), false); // point3 is outside of object (bottom) equal(object.containsPoint(point4), false); // point4 is outside of object (left) equal(object.containsPoint(point5), false); // point5 is outside of object (top) equal(object.containsPoint(point6), false); }); test('clipTo', function() { var object = new fabric.Object({ left: 40, top: 40, width: 40, height: 50, clipTo: function(ctx) { ctx.arc(10, 10, 10, 0, Math.PI * 2, false) } }); equal(typeof object.clipTo, 'function'); var deserializedObject = new fabric.Object(JSON.parse(JSON.stringify(object))); equal(typeof deserializedObject.clipTo, 'function'); }); test('getObjectScale', function() { var object = new fabric.Object({ scaleX: 3, scaleY : 2}); var objectScale = object.getObjectScaling(); deepEqual(objectScale, {scaleX: object.scaleX, scaleY: object.scaleY}); }); test('getObjectScale in group', function() { var object = new fabric.Object({ scaleX: 3, scaleY : 2}); var group = new fabric.Group(); group.scaleX = 2; group.scaleY = 2; object.group = group; var objectScale = object.getObjectScaling(); deepEqual(objectScale, { scaleX: object.scaleX * group.scaleX, scaleY: object.scaleY * group.scaleY }); }); test('_setShadow', function(){ var el = fabric.document.createElement('canvas'); el.width = 600; el.height = 600; var canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.StaticCanvas(el); var context = canvas.contextContainer; var object = new fabric.Object({ scaleX: 1, scaleY : 1}); var group = new fabric.Group(); group.scaleX = 2; group.scaleY = 2; object.setShadow({ color: 'red', blur: 10, offsetX: 5, offsetY: 15 }); object._setShadow(context); equal(context.shadowOffsetX, object.shadow.offsetX); equal(context.shadowOffsetY, object.shadow.offsetY); equal(context.shadowBlur, object.shadow.blur); object.scaleX = 2; object.scaleY = 3; object._setShadow(context); equal(context.shadowOffsetX, object.shadow.offsetX * object.scaleX); equal(context.shadowOffsetY, object.shadow.offsetY * object.scaleY); equal(context.shadowBlur, object.shadow.blur * (object.scaleX + object.scaleY) / 2); object.group = group; object._setShadow(context); equal(context.shadowOffsetX, object.shadow.offsetX * object.scaleX * group.scaleX); equal(context.shadowOffsetY, object.shadow.offsetY * object.scaleY * group.scaleY); equal(context.shadowBlur, object.shadow.blur * (object.scaleX * group.scaleX + object.scaleY * group.scaleY) / 2); }); })();