diff --git a/.travis.yml b/.travis.yml index 3dc6f0a8..4d137d66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ node_js: script: 'npm run build && npm run test && npm run lint && npm run lint_tests' before_install: - sudo apt-get update -qq - - sudo apt-get install libc6 -qq + - sudo apt-get install -qq libc6 libgif-dev libpng-dev libjpeg8-dev libpango1.0-dev libcairo2-dev dist: trusty diff --git a/HEADER.js b/HEADER.js index d76f1994..d331fc77 100644 --- a/HEADER.js +++ b/HEADER.js @@ -61,7 +61,7 @@ fabric.DPI = 96; fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)'; fabric.fontPaths = { }; fabric.iMatrix = [1, 0, 0, 1, 0, 0]; -fabric.canvasModule = 'canvas-prebuilt'; +fabric.canvasModule = 'canvas'; /** * Cache Object for widths of chars in text rendering. */ diff --git a/package.json b/package.json index c45e0349..3bb76125 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "license": "MIT", "scripts": { "build": "node build.js modules=ALL exclude=json,gestures", - "build:watch": "onchange 'src/**/**' 'test/**/**' 'HEADER.js' 'lib/**/**' -- npm run build_export", + "build:watch": "onchange 'src/**/**' 'HEADER.js' 'lib/**/**' -- npm run build_export", "build_with_gestures": "node build.js modules=ALL exclude=json", "build_export": "npm run build && npm run export_dist_to_site", "test": "node test.js", @@ -47,7 +47,7 @@ "all": "npm run build && npm run test && npm run lint && npm run lint_tests && npm run export_dist_to_site && npm run export_tests_to_site" }, "optionalDependencies": { - "canvas-prebuilt": "1.6.5-prerelease.1", + "canvas": "1.6.x", "jsdom": "9.x.x", "xmldom": "0.1.x" }, diff --git a/src/canvas.class.js b/src/canvas.class.js index 71c25c59..c9ba06ed 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -1277,7 +1277,13 @@ _createUpperCanvas: function () { var lowerCanvasClass = this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/, ''); - this.upperCanvasEl = this._createCanvasElement(); + // there is no need to create a new upperCanvas element if we have already one. + if (this.upperCanvasEl) { + this.upperCanvasEl.className = ''; + } + else { + this.upperCanvasEl = this._createCanvasElement(); + } fabric.util.addClass(this.upperCanvasEl, 'upper-canvas ' + lowerCanvasClass); this.wrapperEl.appendChild(this.upperCanvasEl); diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 29eb2c59..a48cf3e8 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -40,7 +40,10 @@ * @private */ _initEventListeners: function () { - + // in case we initialized the class twice. This should not happen normally + // but in some kind of applications where the canvas element may be changed + // this is a workaround to having double listeners. + this.removeListeners(); this._bindEvents(); addListener(fabric.window, 'resize', this._onResize); @@ -70,6 +73,10 @@ * @private */ _bindEvents: function() { + if (this.eventsBinded) { + // for any reason we pass here twice we do not want to bind events twice. + return; + } this._onMouseDown = this._onMouseDown.bind(this); this._onMouseMove = this._onMouseMove.bind(this); this._onMouseUp = this._onMouseUp.bind(this); @@ -83,6 +90,7 @@ this._onMouseOut = this._onMouseOut.bind(this); this._onMouseEnter = this._onMouseEnter.bind(this); this._onContextMenu = this._onContextMenu.bind(this); + this.eventsBinded = true; }, /** diff --git a/test/unit/canvas.js b/test/unit/canvas.js index 1c37990a..60a98e21 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -1829,4 +1829,159 @@ canvas.__onMouseUp(e2); equal(isClick, false, 'moving the pointer, the click is false'); }); + + test('avoid multiple bindings', function() { + var el2 = fabric.document.createElement('canvas'); + var c = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + var eventsArray = [ + c._onMouseDown, + c._onMouseMove, + c._onMouseUp, + c._onResize, + c._onGesture, + c._onDrag, + c._onShake, + c._onLongPress, + c._onOrientationChange, + c._onMouseWheel, + c._onMouseOut, + c._onMouseEnter, + c._onContextMenu + ]; + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + var eventsArray2 = [ + c._onMouseDown, + c._onMouseMove, + c._onMouseUp, + c._onResize, + c._onGesture, + c._onDrag, + c._onShake, + c._onLongPress, + c._onOrientationChange, + c._onMouseWheel, + c._onMouseOut, + c._onMouseEnter, + c._onContextMenu + ]; + deepEqual(eventsArray, eventsArray2, 'after first initialize, functions do not change.'); + }); + + test('avoid multiple registration - mousedown', function() { + var el2 = fabric.document.createElement('canvas'); + var originalMouseDown = fabric.Canvas.prototype._onMouseDown; + var counter = 0; + fabric.Canvas.prototype._onMouseDown = function() { + counter++; + }; + var c = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + var event = fabric.document.createEvent('MouseEvent'); + event.initEvent('mousedown', true, true); + c.upperCanvasEl.dispatchEvent(event); + equal(counter, 1, 'listener executed once'); + fabric.Canvas.prototype._onMouseDown = originalMouseDown; + }); + + test('avoid multiple registration - mousemove', function() { + var el2 = fabric.document.createElement('canvas'); + var originalMouseMove = fabric.Canvas.prototype._onMouseMove; + var counter = 0; + fabric.Canvas.prototype._onMouseMove = function() { + counter++; + }; + var c = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + var event = fabric.document.createEvent('MouseEvent'); + event.initEvent('mousemove', true, true); + c.upperCanvasEl.dispatchEvent(event); + equal(counter, 1, 'listener executed once'); + fabric.Canvas.prototype._onMouseMove = originalMouseMove; + }); + + asyncTest('avoid multiple registration - mouseup', function() { + var el2 = fabric.document.createElement('canvas'); + var originalMouseUp = fabric.Canvas.prototype._onMouseUp; + var counter = 0; + fabric.Canvas.prototype._onMouseUp = function() { + counter++; + }; + var c = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + + // a mouse down is necessary to register mouse up. + var _event = fabric.document.createEvent('MouseEvent'); + _event.initEvent('mousedown', true, true); + c.upperCanvasEl.dispatchEvent(_event); + setTimeout(function() { + var event = fabric.document.createEvent('MouseEvent'); + event.initEvent('mouseup', true, true); + fabric.document.dispatchEvent(event); + equal(counter, 1, 'listener executed once'); + fabric.Canvas.prototype._onMouseUp = originalMouseUp; + start(); + }, 200); + }); + + test('avoid multiple registration - mouseout', function() { + var el2 = fabric.document.createElement('canvas'); + var originalMouseOut = fabric.Canvas.prototype._onMouseOut; + var counter = 0; + fabric.Canvas.prototype._onMouseOut = function() { + counter++; + }; + var c = this.canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + var event = fabric.document.createEvent('MouseEvent'); + event.initEvent('mouseout', true, true); + c.upperCanvasEl.dispatchEvent(event); + equal(counter, 1, 'listener executed once'); + fabric.Canvas.prototype._onMouseOut = originalMouseOut; + }); + + test('avoid multiple registration - mouseenter', function() { + var el2 = fabric.document.createElement('canvas'); + var originalMouseEnter = fabric.Canvas.prototype._onMouseEnter; + var counter = 0; + fabric.Canvas.prototype._onMouseEnter = function() { + counter++; + }; + var c = this.canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + var event = fabric.document.createEvent('MouseEvent'); + event.initEvent('mouseenter', true, true); + c.upperCanvasEl.dispatchEvent(event); + equal(counter, 1, 'listener executed once'); + fabric.Canvas.prototype._onMouseEnter = originalMouseEnter; + }); + + test('avoid multiple events on window', function() { + var el2 = fabric.document.createElement('canvas'); + var originalResize = fabric.Canvas.prototype._onResize; + var counter = 0; + fabric.Canvas.prototype._onResize = function() { + counter++; + }; + var c = this.canvas = fabric.isLikelyNode ? fabric.createCanvasForNode() : new fabric.Canvas(el2); + // initialize canvas more than once + c.initialize(el2); + c.initialize(el2); + var event = fabric.document.createEvent('UIEvents'); + event.initUIEvent('resize', true, false, fabric.window, 0); + fabric.window.dispatchEvent(event); + equal(counter, 1, 'listener on window executed once'); + fabric.Canvas.prototype._onResize = originalResize; + }); })();