diff --git a/build.js b/build.js index de3932da4c6..b0ddde907a4 100644 --- a/build.js +++ b/build.js @@ -313,7 +313,8 @@ else { exec(mininfierCmd, function (error, output) { if (error) { - console.error('Minification failed using', minifier, 'with', mininfierCmd); + console.log('Minification failed using', minifier, 'with', mininfierCmd); + console.error(error); process.exit(1); } console.log('Minified using', minifier, 'to ' + distributionPath + 'fabric.min.js'); diff --git a/src/canvas.class.js b/src/canvas.class.js index 4cff4bc4611..01b603ebfb2 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -1573,7 +1573,7 @@ * @chainable */ dispose: function () { - this.callSuper('dispose'); + fabric.StaticCanvas.prototype.dispose.call(this); var wrapper = this.wrapperEl; this.removeListeners(); wrapper.removeChild(this.upperCanvasEl); diff --git a/src/mixins/itext.svg_export.js b/src/mixins/itext.svg_export.js index 09209c09212..89ef0b8e903 100644 --- a/src/mixins/itext.svg_export.js +++ b/src/mixins/itext.svg_export.js @@ -103,7 +103,7 @@ fabric.util.string.escapeXml(_char), '\n' ].join(''); - } + }, }); })(); /* _TO_SVG_END_ */ diff --git a/src/mixins/itext_key_behavior.mixin.js b/src/mixins/itext_key_behavior.mixin.js index 6689f9ff60b..82d28d1ff92 100644 --- a/src/mixins/itext_key_behavior.mixin.js +++ b/src/mixins/itext_key_behavior.mixin.js @@ -6,9 +6,15 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot initHiddenTextarea: function() { this.hiddenTextarea = fabric.document.createElement('textarea'); this.hiddenTextarea.setAttribute('autocapitalize', 'off'); + this.hiddenTextarea.setAttribute('autocorrect', 'off'); + this.hiddenTextarea.setAttribute('autocomplete', 'off'); + this.hiddenTextarea.setAttribute('spellcheck', 'false'); + this.hiddenTextarea.setAttribute('data-fabric-hiddentextarea', ''); + this.hiddenTextarea.setAttribute('wrap', 'off'); var style = this._calcTextareaPosition(); - this.hiddenTextarea.style.cssText = 'white-space: nowrap; position: absolute; top: ' + style.top + - '; left: ' + style.left + '; opacity: 0; width: 1px; height: 1px; z-index: -999;'; + this.hiddenTextarea.style.cssText = 'position: absolute; top: ' + style.top + + '; left: ' + style.left + '; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px;' + + ' line-height: 1px; paddingーtop: ' + style.fontSize + ';'; fabric.document.body.appendChild(this.hiddenTextarea); fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this)); diff --git a/src/shapes/group.class.js b/src/shapes/group.class.js index 4c1cdd76bd4..feee3abf0dd 100644 --- a/src/shapes/group.class.js +++ b/src/shapes/group.class.js @@ -627,8 +627,9 @@ */ fabric.Group.fromObject = function(object, callback) { fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) { - delete object.objects; - callback && callback(new fabric.Group(enlivenedObjects, object, true)); + var options = fabric.util.object.clone(object, true); + delete options.objects; + callback && callback(new fabric.Group(enlivenedObjects, options, true)); }); }; diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index 7c2a14bf5c2..1f85f694752 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -1859,7 +1859,12 @@ * @chainable */ remove: function() { - this.canvas && this.canvas.remove(this); + if (this.canvas) { + if (this.group && this.group === this.canvas._activeGroup) { + this.group.remove(this); + } + this.canvas.remove(this); + } return this; }, diff --git a/src/shapes/text.class.js b/src/shapes/text.class.js index fbe78a45deb..6ad2201d12f 100644 --- a/src/shapes/text.class.js +++ b/src/shapes/text.class.js @@ -932,7 +932,7 @@ '\t\n', textAndBg.textBgRects.join(''), - '\t\t 0 + objsMoved) { newIdx = idx - 1; removeFromArray(this._objects, obj); this._objects.splice(newIdx, 0, obj); } + objsMoved++; } } else { @@ -1530,19 +1532,21 @@ if (!object) { return this; } + var activeGroup = this._activeGroup, - i, obj, idx, newIdx, objs; + i, obj, idx, newIdx, objs, objsMoved = 0; if (object === activeGroup) { objs = activeGroup._objects; for (i = objs.length; i--;) { obj = objs[i]; idx = this._objects.indexOf(obj); - if (idx !== this._objects.length - 1) { + if (idx < this._objects.length - 1 - objsMoved) { newIdx = idx + 1; removeFromArray(this._objects, obj); this._objects.splice(newIdx, 0, obj); } + objsMoved++; } } else { diff --git a/src/util/dom_event.js b/src/util/dom_event.js index 72db326f0f1..4d40dd44766 100644 --- a/src/util/dom_event.js +++ b/src/util/dom_event.js @@ -82,17 +82,20 @@ /** @ignore */ addListener = function (element, eventName, handler, options) { // since ie10 or ie9 can use addEventListener but they do not support options, i need to check - element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options); + element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options); }; /** @ignore */ removeListener = function (element, eventName, handler, options) { - element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options); + element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options); }; } else if (shouldUseAttachEventDetachEvent) { /** @ignore */ addListener = function (element, eventName, handler) { + if (!element) { + return; + } var uid = getUniqueId(element); setElement(uid, element); if (!listeners[uid]) { @@ -108,6 +111,9 @@ }; /** @ignore */ removeListener = function (element, eventName, handler) { + if (!element) { + return; + } var uid = getUniqueId(element), listener; if (listeners[uid] && listeners[uid][eventName]) { for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) { @@ -123,6 +129,9 @@ else { /** @ignore */ addListener = function (element, eventName, handler) { + if (!element) { + return; + } var uid = getUniqueId(element); if (!handlers[uid]) { handlers[uid] = { }; @@ -139,6 +148,9 @@ }; /** @ignore */ removeListener = function (element, eventName, handler) { + if (!element) { + return; + } var uid = getUniqueId(element); if (handlers[uid] && handlers[uid][eventName]) { var handlersForEvent = handlers[uid][eventName]; diff --git a/test/unit/canvas.js b/test/unit/canvas.js index b9c52a0151b..063b9c00168 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -599,6 +599,17 @@ equal(canvas._objects[1], rect1, 'rect1 should be the new second'); equal(canvas._objects[2], rect2, 'rect2 should be the third object'); equal(canvas._objects[3], rect4, 'rect4 did not move'); + canvas.bringForward(group); + equal(canvas._objects[0], rect3, 'rect3 should be the new last'); + equal(canvas._objects[1], rect4, 'rect4 should be the new second'); + equal(canvas._objects[2], rect1, 'rect1 should be the third object'); + equal(canvas._objects[3], rect2, 'rect2 is the new top'); + canvas.bringForward(group); + canvas.bringForward(group); + equal(canvas._objects[0], rect3, 'rect3 should be the new last'); + equal(canvas._objects[1], rect4, 'rect4 should be the new second'); + equal(canvas._objects[2], rect1, 'rect1 is still third'); + equal(canvas._objects[3], rect2, 'rect2 is still new top'); }); test('activeGroup sendBackwards', function() { @@ -618,6 +629,17 @@ equal(canvas._objects[1], rect3, 'rect3 should be shifted down by 1'); equal(canvas._objects[2], rect4, 'rect4 should be shifted down by 1'); equal(canvas._objects[3], rect2, 'rect2 is the new top'); + canvas.sendBackwards(group); + equal(canvas._objects[0], rect3, 'rect3 is last'); + equal(canvas._objects[1], rect4, 'rect4 should be shifted down by 1'); + equal(canvas._objects[2], rect1, 'rect1 should be shifted down by 1'); + equal(canvas._objects[3], rect2, 'rect2 is still on top'); + canvas.sendBackwards(group); + canvas.sendBackwards(group); + equal(canvas._objects[0], rect3, 'rect3 is still last'); + equal(canvas._objects[1], rect4, 'rect4 should be steady'); + equal(canvas._objects[2], rect1, 'rect1 should be steady'); + equal(canvas._objects[3], rect2, 'rect2 is still on top'); }); test('toDataURL', function() { diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index e30cbd94c4d..d395efdfd84 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -897,6 +897,24 @@ equal(canvas.toObject().objects.length, 1, 'only one object gets exported'); }); + test('toObject excludeFromExport bgImage overlay', function() { + var rect = makeRect(), rect2 = makeRect(), rect3 = makeRect(); + canvas.clear(); + canvas.backgroundImage = rect; + canvas.overlayImage = rect2; + canvas.add(rect3); + var rectToObject = rect.toObject(); + var rect2ToObject = rect2.toObject(); + var canvasToObject = canvas.toObject(); + deepEqual(canvasToObject.backgroundImage, rectToObject, 'background exported'); + deepEqual(canvasToObject.overlayImage, rect2ToObject, 'overlay exported'); + rect.excludeFromExport = true; + rect2.excludeFromExport = true; + canvasToObject = canvas.toObject(); + equal(canvasToObject.backgroundImage, undefined, 'background not exported'); + equal(canvasToObject.overlayImage, undefined, 'overlay not exported'); + }); + test('toDatalessObject', function() { ok(typeof canvas.toDatalessObject == 'function'); diff --git a/test/unit/itext.js b/test/unit/itext.js index 1be5eb0d0ce..93db027a67f 100644 --- a/test/unit/itext.js +++ b/test/unit/itext.js @@ -814,4 +814,31 @@ notEqual(measuredBy_getWidthOfWords_preservedSpaces, measuredBy_getWidthOfWords_omittedSpaces); }); + test('space wrap attribute', function() { + var iText = new fabric.IText('test foo bar-baz\nqux'); + iText.enterEditing(); + equal(iText.hiddenTextarea.getAttribute('wrap'), 'off', 'HiddenTextarea needs wrap off attribute'); + }); + + // test('measuring width of words', function () { + // var ctx = canvas.getContext('2d'); + // var text = 'test foo bar'; + // var iText = new fabric.IText(text, { + // styles: { + // 0: { + // 9: { fontWeight: 'bold' }, + // 10: { fontWeight: 'bold' }, + // 11: { fontWeight: 'bold' }, + // } + // } + // }); + // + // var textSplitted = text.split(' '); + // var measuredBy_getWidthOfWords_preservedSpaces = iText._getWidthOfWords(ctx, textSplitted.join(' '), 0, 0); + // var measuredBy_getWidthOfWords_omittedSpaces = iText._getWidthOfWords(ctx, textSplitted.join(''), 0, 0); + // + // notEqual(measuredBy_getWidthOfWords_preservedSpaces, measuredBy_getWidthOfWords_omittedSpaces); + // }); + + })(); diff --git a/test/unit/text.js b/test/unit/text.js index 73399df09a8..731c9b51361 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -50,8 +50,8 @@ 'charSpacing': 0 }; - var TEXT_SVG = '\t\n\t\t\n\t\t\tx\n\t\t\n\t\n'; - var TEXT_SVG_JUSTIFIED = '\t\n\t\t\n\t\t\tx\n\t\t\ty\n\t\t\n\t\n'; + var TEXT_SVG = '\t\n\t\t\n\t\t\tx\n\t\t\n\t\n'; + var TEXT_SVG_JUSTIFIED = '\t\n\t\t\n\t\t\tx\n\t\t\ty\n\t\t\n\t\n'; test('constructor', function() { ok(fabric.Text); diff --git a/test/unit/util.js b/test/unit/util.js index bbde764ce9a..be14afd2abf 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -731,10 +731,14 @@ test('fabric.util.addListener', function() { ok(typeof fabric.util.addListener === 'function', 'fabric.util.addListener is a function'); + fabric.util.addListener(null, 'mouseup'); + ok(true, 'test did not throw on null element addListener'); }); test('fabric.util.removeListener', function() { ok(typeof fabric.util.removeListener === 'function', 'fabric.util.removeListener is a function'); + fabric.util.removeListener(null, 'mouseup'); + ok(true, 'test did not throw on null element removeListener'); }); test('fabric.util.drawDashedLine', function() {