(function() {
// var emptyImageCanvasData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAH0CAYAAADL1t+KAAAH7ElEQVR4nO3VMQ0AMAzAsPInvYHoMS2yEeTLHADge/M6AADYM3QACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIMHQACDB0AAgwdAAIuMjH4b7osLFBAAAAAElFTkSuQmCC";
var CANVAS_SVG = '\n\n' +
'';
var CANVAS_SVG_VIEWBOX = '\n\n' +
'';
var PATH_JSON = '{"version":"' + fabric.version + '","objects": [{"type": "path", "version":"' + fabric.version + '", "originX": "left", "originY": "top", "left": 268, "top": 266, "width": 51, "height": 49,' +
' "fill": "rgb(0,0,0)", "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]]}], "background": "#ff5555", "overlay":"rgba(0,0,0,0.2)"}';
var PATH_WITHOUT_DEFAULTS_JSON = '{"version":"' + fabric.version + '","objects": [{"type": "path", "version":"' + fabric.version + '", "left": 268, "top": 266, "width": 51, "height": 49, "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]]}], "background": "#ff5555","overlay": "rgba(0,0,0,0.2)"}';
var PATH_DATALESS_JSON = '{"version":"' + fabric.version + '","objects":[{"type":"path","version":"' + fabric.version + '","originX":"left","originY":"top","left":100,"top":100,"width":200,"height":200,"fill":"rgb(0,0,0)",' +
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":4,' +
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,' +
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"sourcePath":"http://example.com/"}]}';
var RECT_JSON = '{"version":"' + fabric.version + '","objects":[{"type":"rect","version":"' + fabric.version + '","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",' +
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":4,' +
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,' +
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
var RECT_JSON_WITH_PADDING = '{"version":"' + fabric.version + '","objects":[{"type":"rect","version":"' + fabric.version + '","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":20,"fill":"rgb(0,0,0)",' +
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":4,' +
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,' +
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0,"padding":123,"foo":"bar"}]}';
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;
var REFERENCE_IMG_OBJECT = {
'version': fabric.version,
'type': 'image',
'originX': 'left',
'originY': 'top',
'left': 0,
'top': 0,
'width': IMG_WIDTH, // node-canvas doesn't seem to allow setting width/height on image objects
'height': IMG_HEIGHT, // or does it now?
'fill': 'rgb(0,0,0)',
'stroke': null,
'strokeWidth': 0,
'strokeDashArray': null,
'strokeLineCap': 'butt',
'strokeLineJoin': 'miter',
'strokeMiterLimit': 4,
'scaleX': 1,
'scaleY': 1,
'angle': 0,
'flipX': false,
'flipY': false,
'opacity': 1,
'src': IMG_SRC,
'shadow': null,
'visible': true,
'backgroundColor': '',
'clipTo': null,
'filters': [],
'fillRule': 'nonzero',
'paintFirst': 'fill',
'globalCompositeOperation': 'source-over',
'transformMatrix': null,
'crossOrigin': '',
'skewX': 0,
'skewY': 0,
'cropX': 0,
'cropY': 0
};
function _createImageElement() {
return fabric.document.createElement('img');
}
function _createImageObject(width, height, callback) {
var elImage = _createImageElement();
elImage.width = width;
elImage.height = height;
setSrc(elImage, IMG_SRC, function() {
callback(new fabric.Image(elImage));
});
}
function createImageObject(callback) {
return _createImageObject(IMG_WIDTH, IMG_HEIGHT, callback);
}
function setSrc(img, src, callback) {
img.onload = callback;
img.src = src;
}
function fixImageDimension(imgObj) {
// workaround for node-canvas sometimes producing images with width/height and sometimes not
if (imgObj.width === 0) {
imgObj.width = IMG_WIDTH;
}
if (imgObj.height === 0) {
imgObj.height = IMG_HEIGHT;
}
}
// force creation of static canvas
// TODO: fix this
var canvas = this.canvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 600, height: 600});
var canvas2 = this.canvas2 = new fabric.StaticCanvas(null, {enableRetinaScaling: false, width: 600, height: 600});
var lowerCanvasEl = canvas.lowerCanvasEl;
function makeRect(options) {
var defaultOptions = { width: 10, height: 10 };
return new fabric.Rect(fabric.util.object.extend(defaultOptions, options || { }));
}
QUnit.module('fabric.StaticCanvas', {
beforeEach: function() {
canvas.clear();
canvas.backgroundColor = fabric.StaticCanvas.prototype.backgroundColor;
canvas.backgroundImage = fabric.StaticCanvas.prototype.backgroundImage;
canvas.overlayColor = fabric.StaticCanvas.prototype.overlayColor;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
canvas.calcOffset();
}
});
QUnit.test('initialProperties', function(assert) {
assert.ok('backgroundColor' in canvas);
assert.ok('overlayColor' in canvas);
assert.ok('backgroundImage' in canvas);
assert.ok('overlayImage' in canvas);
assert.ok('clipTo' in canvas);
assert.ok('includeDefaultValues' in canvas);
assert.ok('stateful' in canvas);
assert.ok('renderOnAddRemove' in canvas);
assert.ok('controlsAboveOverlay' in canvas);
assert.ok('allowTouchScrolling' in canvas);
assert.ok('imageSmoothingEnabled' in canvas);
assert.ok('backgroundVpt' in canvas);
assert.ok('overlayVpt' in canvas);
assert.equal(canvas.includeDefaultValues, true);
assert.equal(canvas.stateful, false);
assert.equal(canvas.renderOnAddRemove, true);
assert.equal(canvas.controlsAboveOverlay, false);
assert.equal(canvas.allowTouchScrolling, false);
assert.equal(canvas.imageSmoothingEnabled, true);
assert.equal(canvas.backgroundVpt, true);
assert.equal(canvas.overlayVpt, true);
assert.notStrictEqual(canvas.viewportTransform, canvas2.viewportTransform);
});
QUnit.test('getObjects', function(assert) {
assert.ok(typeof canvas.getObjects === 'function', 'should respond to `getObjects` method');
assert.deepEqual([], canvas.getObjects(), 'should return empty array for `getObjects` when empty');
assert.equal(canvas.getObjects().length, 0, 'should have a 0 length when empty');
});
QUnit.test('getObjects with type', function(assert) {
var rect = new fabric.Rect({ width: 10, height: 20 });
var circle = new fabric.Circle({ radius: 30 });
canvas.add(rect, circle);
assert.equal(canvas.getObjects().length, 2, 'should have length=2 initially');
assert.deepEqual(canvas.getObjects('rect'), [rect], 'should return rect only');
assert.deepEqual(canvas.getObjects('circle'), [circle], 'should return circle only');
});
QUnit.test('getElement', function(assert) {
assert.ok(typeof canvas.getElement === 'function', 'should respond to `getElement` method');
assert.equal(canvas.getElement(), lowerCanvasEl, 'should return a proper element');
});
QUnit.test('item', function(assert) {
var rect = makeRect();
assert.ok(typeof canvas.item === 'function', 'should respond to item');
canvas.add(rect);
assert.equal(canvas.item(0), rect, 'should return proper item');
});
QUnit.test('calcOffset', function(assert) {
assert.ok(typeof canvas.calcOffset === 'function', 'should respond to `calcOffset`');
assert.equal(canvas.calcOffset(), canvas, 'should be chainable');
});
QUnit.test('add', function(assert) {
var rect1 = makeRect(),
rect2 = makeRect(),
rect3 = makeRect(),
rect4 = makeRect();
assert.ok(typeof canvas.add === 'function');
assert.equal(canvas.add(rect1), canvas, 'should be chainable');
assert.strictEqual(canvas.item(0), rect1);
canvas.add(rect2, rect3, rect4);
assert.equal(canvas.getObjects().length, 4, 'should support multiple arguments');
assert.strictEqual(canvas.item(1), rect2);
assert.strictEqual(canvas.item(2), rect3);
assert.strictEqual(canvas.item(3), rect4);
});
QUnit.test('add renderOnAddRemove disabled', function(assert) {
var rect = makeRect(),
originalRenderOnAddition,
renderAllCount = 0;
function countRenderAll() {
renderAllCount++;
}
originalRenderOnAddition = canvas.renderOnAddRemove;
canvas.renderOnAddRemove = false;
canvas.on('after:render', countRenderAll);
assert.equal(canvas.add(rect), canvas, 'should be chainable');
assert.equal(renderAllCount, 0);
assert.equal(canvas.item(0), rect);
canvas.add(makeRect(), makeRect(), makeRect());
assert.equal(canvas.getObjects().length, 4, 'should support multiple arguments');
assert.equal(renderAllCount, 0);
canvas.renderAll();
assert.equal(renderAllCount, 1);
canvas.off('after:render', countRenderAll);
canvas.renderOnAddRemove = originalRenderOnAddition;
});
QUnit.test('object:added', function(assert) {
var objectsAdded = [];
canvas.on('object:added', function(e) {
objectsAdded.push(e.target);
});
var rect = new fabric.Rect({ width: 10, height: 20 });
canvas.add(rect);
assert.deepEqual(objectsAdded[0], rect);
var circle1 = new fabric.Circle(),
circle2 = new fabric.Circle();
canvas.add(circle1, circle2);
assert.strictEqual(objectsAdded[1], circle1);
assert.strictEqual(objectsAdded[2], circle2);
var circle3 = new fabric.Circle();
canvas.insertAt(circle3, 2);
assert.strictEqual(objectsAdded[3], circle3);
});
QUnit.test('insertAt', function(assert) {
var rect1 = makeRect(),
rect2 = makeRect();
canvas.add(rect1, rect2);
assert.ok(typeof canvas.insertAt === 'function', 'should respond to `insertAt` method');
var rect = makeRect();
canvas.insertAt(rect, 1);
assert.strictEqual(canvas.item(1), rect);
canvas.insertAt(rect, 2);
assert.strictEqual(canvas.item(2), rect);
assert.equal(canvas.insertAt(rect, 2), canvas, 'should be chainable');
});
QUnit.test('insertAt renderOnAddRemove disabled', function(assert) {
var rect1 = makeRect(),
rect2 = makeRect(),
originalRenderOnAddition,
renderAllCount = 0;
function countRenderAll() {
renderAllCount++;
}
originalRenderOnAddition = canvas.renderOnAddRemove;
canvas.renderOnAddRemove = false;
canvas.on('after:render', countRenderAll);
canvas.add(rect1, rect2);
assert.equal(renderAllCount, 0);
var rect = makeRect();
canvas.insertAt(rect, 1);
assert.equal(renderAllCount, 0);
assert.strictEqual(canvas.item(1), rect);
canvas.insertAt(rect, 2);
assert.equal(renderAllCount, 0);
canvas.renderAll();
assert.equal(renderAllCount, 1);
canvas.off('after:render', countRenderAll);
canvas.renderOnAddRemove = originalRenderOnAddition;
});
QUnit.test('remove', function(assert) {
var rect1 = makeRect(),
rect2 = makeRect(),
rect3 = makeRect(),
rect4 = makeRect();
canvas.add(rect1, rect2, rect3, rect4);
assert.ok(typeof canvas.remove === 'function');
assert.equal(canvas.remove(rect1), canvas, 'should be chainable');
assert.strictEqual(canvas.item(0), rect2, 'should be second object');
canvas.remove(rect2, rect3);
assert.strictEqual(canvas.item(0), rect4);
canvas.remove(rect4);
assert.equal(canvas.isEmpty(), true, 'canvas should be empty');
});
QUnit.test('remove renderOnAddRemove disabled', function(assert) {
var rect1 = makeRect(),
rect2 = makeRect(),
originalRenderOnAddition,
renderAllCount = 0;
function countRenderAll() {
renderAllCount++;
}
originalRenderOnAddition = canvas.renderOnAddRemove;
canvas.renderOnAddRemove = false;
canvas.on('after:render', countRenderAll);
canvas.add(rect1, rect2);
assert.equal(renderAllCount, 0);
assert.equal(canvas.remove(rect1), canvas, 'should be chainable');
assert.equal(renderAllCount, 0);
assert.strictEqual(canvas.item(0), rect2, 'only second object should be left');
canvas.renderAll();
assert.equal(renderAllCount, 1);
canvas.off('after:render', countRenderAll);
canvas.renderOnAddRemove = originalRenderOnAddition;
});
QUnit.test('object:removed', function(assert) {
var objectsRemoved = [];
canvas.on('object:removed', function(e) {
objectsRemoved.push(e.target);
});
var rect = new fabric.Rect({ width: 10, height: 20 }),
circle1 = new fabric.Circle(),
circle2 = new fabric.Circle();
canvas.add(rect, circle1, circle2);
assert.strictEqual(canvas.item(0), rect);
assert.strictEqual(canvas.item(1), circle1);
assert.strictEqual(canvas.item(2), circle2);
canvas.remove(rect);
assert.strictEqual(objectsRemoved[0], rect);
canvas.remove(circle1, circle2);
assert.strictEqual(objectsRemoved[1], circle1);
assert.strictEqual(objectsRemoved[2], circle2);
assert.equal(canvas.isEmpty(), true, 'canvas should be empty');
});
QUnit.test('clearContext', function(assert) {
assert.ok(typeof canvas.clearContext === 'function');
assert.equal(canvas.clearContext(canvas.contextContainer), canvas, 'should be chainable');
});
QUnit.test('clear', function(assert) {
assert.ok(typeof canvas.clear === 'function');
var bg = new fabric.Rect({ width: 10, height: 20 });
canvas.backgroundColor = '#FF0000';
canvas.overlayColor = '#FF0000';
canvas.backgroundImage = bg;
canvas.overlayImage = bg;
assert.equal(canvas.clear(), canvas, 'should be chainable');
assert.equal(canvas.getObjects().length, 0, 'clear remove all objects');
assert.equal(canvas.backgroundColor, '', 'clear remove background color');
assert.equal(canvas.overlayColor, '', 'clear remove overlay color');
assert.equal(canvas.backgroundImage, null, 'clear remove bg image');
assert.equal(canvas.overlayImage, null, 'clear remove overlay image');
});
QUnit.test('renderAll', function(assert) {
assert.ok(typeof canvas.renderAll === 'function');
assert.equal(canvas, canvas.renderAll());
});
QUnit.test('toDataURL', function(assert) {
assert.ok(typeof canvas.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 rect = new fabric.Rect({width: 100, height: 100, fill: 'red', top: 0, left: 0});
canvas.add(rect);
var dataURL = canvas.toDataURL();
// don't compare actual data url, as it is often browser-dependent
// this.assertIdentical(emptyImageCanvasData, canvas.toDataURL('png'));
assert.equal(typeof dataURL, 'string');
assert.equal(dataURL.substring(0, 21), 'data:image/png;base64');
//we can just compare that the dataUrl generated differs from the dataURl of an empty canvas.
assert.equal(dataURL.substring(200, 210) !== 'AAAAAAAAAA', true);
}
});
QUnit.test('toDataURL with enableRetinaScaling: true and no multiplier', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: true });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width * fabric.devicePixelRatio, 'output width is bigger');
assert.equal(img.height, c.height * fabric.devicePixelRatio, 'output height is bigger');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL with enableRetinaScaling: true and multiplier = 1', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: true, multiplier: 1 });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width * fabric.devicePixelRatio, 'output width is bigger');
assert.equal(img.height, c.height * fabric.devicePixelRatio, 'output height is bigger');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL with enableRetinaScaling: true and multiplier = 3', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: true, multiplier: 3 });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width * fabric.devicePixelRatio * 3, 'output width is bigger by 6');
assert.equal(img.height, c.height * fabric.devicePixelRatio * 3, 'output height is bigger by 6');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL with enableRetinaScaling: false and no multiplier', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: false });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width, 'output width is not bigger');
assert.equal(img.height, c.height, 'output height is not bigger');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL with enableRetinaScaling: false and multiplier = 1', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: false, multiplier: 1 });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width, 'output width is not bigger');
assert.equal(img.height, c.height, 'output height is not bigger');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL with enableRetinaScaling: false and multiplier = 3', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: false, multiplier: 3 });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width * 3, 'output width is bigger by 3');
assert.equal(img.height, c.height * 3, 'output height is bigger by 3');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL with enableRetinaScaling: false', function(assert) {
var done = assert.async();
fabric.devicePixelRatio = 2;
var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 });
var dataUrl = c.toDataURL({ enableRetinaScaling: false });
var img = fabric.document.createElement('img');
img.onload = function() {
assert.equal(img.width, c.width, 'output width is bigger');
assert.equal(img.height, c.height, 'output height is bigger');
fabric.devicePixelRatio = 1;
done();
};
img.src = dataUrl;
});
QUnit.test('toDataURL jpg', function(assert) {
if (!fabric.Canvas.supports('toDataURL')) {
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
}
else {
try {
var dataURL = canvas.toDataURL({ format: 'jpg' });
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
}
// node-canvas does not support jpeg data urls
catch (err) {
assert.ok(true);
}
}
});
QUnit.test('toDataURL cropping', function(assert) {
var done = assert.async();
assert.ok(typeof canvas.toDataURL === 'function');
if (!fabric.Canvas.supports('toDataURL')) {
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
done();
}
else {
var croppingWidth = 75,
croppingHeight = 50,
dataURL = canvas.toDataURL({width: croppingWidth, height: croppingHeight});
fabric.Image.fromURL(dataURL, function (img) {
assert.equal(img.width, croppingWidth, 'Width of exported image should correspond to cropping width');
assert.equal(img.height, croppingHeight, 'Height of exported image should correspond to cropping height');
done();
});
}
});
QUnit.test('centerObjectH', function(assert) {
assert.ok(typeof canvas.centerObjectH === 'function');
var rect = makeRect({ left: 102, top: 202 });
canvas.add(rect);
assert.equal(canvas.centerObjectH(rect), canvas, 'should be chainable');
assert.equal(rect.getCenterPoint().x, canvas.width / 2, 'object\'s "center.y" property should correspond to canvas element\'s center');
canvas.setZoom(4);
assert.equal(rect.getCenterPoint().x, canvas.height / 2, 'object\'s "center.x" property should correspond to canvas element\'s center when canvas is transformed');
canvas.setZoom(1);
});
QUnit.test('centerObjectV', function(assert) {
assert.ok(typeof canvas.centerObjectV === 'function');
var rect = makeRect({ left: 102, top: 202 });
canvas.add(rect);
assert.equal(canvas.centerObjectV(rect), canvas, 'should be chainable');
assert.equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center');
canvas.setZoom(2);
assert.equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center when canvas is transformed');
});
QUnit.test('centerObject', function(assert) {
assert.ok(typeof canvas.centerObject === 'function');
var rect = makeRect({ left: 102, top: 202 });
canvas.add(rect);
assert.equal(canvas.centerObject(rect), canvas, 'should be chainable');
assert.equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center');
assert.equal(rect.getCenterPoint().x, canvas.height / 2, 'object\'s "center.x" property should correspond to canvas element\'s center');
canvas.setZoom(4);
assert.equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center when canvas is transformed');
assert.equal(rect.getCenterPoint().x, canvas.height / 2, 'object\'s "center.x" property should correspond to canvas element\'s center when canvas is transformed');
canvas.setZoom(1);
});
QUnit.test('viewportCenterObjectH', function(assert) {
assert.ok(typeof canvas.viewportCenterObjectH === 'function');
var rect = makeRect({ left: 102, top: 202 }), pan = 10;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
canvas.add(rect);
var oldY = rect.top;
assert.equal(canvas.viewportCenterObjectH(rect), canvas, 'should be chainable');
assert.equal(rect.getCenterPoint().x, canvas.width / 2, 'object\'s "center.x" property should correspond to canvas element\'s center when canvas is not transformed');
assert.equal(rect.top, oldY, 'object\'s "top" should not change');
canvas.setZoom(2);
canvas.viewportCenterObjectH(rect);
assert.equal(rect.getCenterPoint().x, canvas.width / (2 * canvas.getZoom()), 'object\'s "center.x" property should correspond to viewport center');
assert.equal(rect.top, oldY, 'object\'s "top" should not change');
canvas.absolutePan({x: pan, y: pan});
canvas.viewportCenterObjectH(rect);
assert.equal(rect.getCenterPoint().x, (canvas.width / 2 + pan) / canvas.getZoom(), 'object\'s "center.x" property should correspond to viewport center');
assert.equal(rect.top, oldY, 'object\'s "top" should not change');
});
QUnit.test('viewportCenterObjectV', function(assert) {
assert.ok(typeof canvas.viewportCenterObjectV === 'function');
var rect = makeRect({ left: 102, top: 202 }), pan = 10;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
canvas.add(rect);
var oldX = rect.left;
assert.equal(canvas.viewportCenterObjectV(rect), canvas, 'should be chainable');
assert.equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center when canvas is not transformed');
assert.equal(rect.left, oldX, 'x position did not change');
canvas.setZoom(2);
canvas.viewportCenterObjectV(rect);
assert.equal(rect.getCenterPoint().y, canvas.height / (2 * canvas.getZoom()), 'object\'s "center.y" property should correspond to viewport center');
assert.equal(rect.left, oldX, 'x position did not change');
canvas.absolutePan({x: pan, y: pan});
canvas.viewportCenterObjectV(rect);
assert.equal(rect.getCenterPoint().y, (canvas.height / 2 + pan) / canvas.getZoom(), 'object\'s "top" property should correspond to viewport center');
assert.equal(rect.left, oldX, 'x position did not change');
});
QUnit.test('viewportCenterObject', function(assert) {
assert.ok(typeof canvas.viewportCenterObject === 'function');
var rect = makeRect({ left: 102, top: 202 }), pan = 10;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
canvas.add(rect);
assert.equal(canvas.viewportCenterObject(rect), canvas, 'should be chainable');
assert.equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center when canvas is not transformed');
assert.equal(rect.getCenterPoint().x, canvas.width / 2, 'object\'s "center.x" property should correspond to canvas element\'s center when canvas is not transformed');
canvas.setZoom(2);
canvas.viewportCenterObject(rect);
assert.equal(rect.getCenterPoint().y, canvas.height / (2 * canvas.getZoom()), 'object\'s "center.y" property should correspond to viewport center');
assert.equal(rect.getCenterPoint().x, canvas.width / (2 * canvas.getZoom()), 'object\'s "center.x" property should correspond to viewport center');
canvas.absolutePan({x: pan, y: pan});
canvas.viewportCenterObject(rect);
assert.equal(rect.getCenterPoint().y, (canvas.height / 2 + pan) / canvas.getZoom(), 'object\'s "center.y" property should correspond to viewport center');
assert.equal(rect.getCenterPoint().x, (canvas.width / 2 + pan) / canvas.getZoom(), 'object\'s "center.x" property should correspond to viewport center');
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
});
QUnit.test('straightenObject', function(assert) {
assert.ok(typeof canvas.straightenObject === 'function');
var rect = makeRect({ angle: 10 });
canvas.add(rect);
assert.equal(canvas.straightenObject(rect), canvas, 'should be chainable');
assert.equal(rect.get('angle'), 0, 'angle should be coerced to 0 (from 10)');
rect.rotate('60');
canvas.straightenObject(rect);
assert.equal(rect.get('angle'), 90, 'angle should be coerced to 90 (from 60)');
rect.rotate('100');
canvas.straightenObject(rect);
assert.equal(rect.get('angle'), 90, 'angle should be coerced to 90 (from 100)');
});
QUnit.test('toSVG', function(assert) {
assert.ok(typeof canvas.toSVG === 'function');
canvas.clear();
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
var svg = canvas.toSVG();
assert.equal(svg, CANVAS_SVG);
});
QUnit.test('toSVG with different encoding (ISO-8859-1)', function(assert) {
assert.ok(typeof canvas.toSVG === 'function');
canvas.clear();
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
var svgDefaultEncoding = canvas.toSVG();
assert.ok(svg != svgDefaultEncoding);
assert.equal(svg, CANVAS_SVG.replace('encoding="UTF-8"', 'encoding="ISO-8859-1"'));
});
QUnit.test('toSVG without preamble', function(assert) {
assert.ok(typeof canvas.toSVG === 'function');
var withPreamble = canvas.toSVG();
var withoutPreamble = canvas.toSVG({suppressPreamble: true});
assert.ok(withPreamble != withoutPreamble);
assert.equal(withoutPreamble.slice(0, 4), '