From 065fdb8e66c9cda47ac21d292624780a6c75b8fe Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Wed, 20 Nov 2019 14:13:26 +0100 Subject: [PATCH 1/5] simple fix --- src/mixins/itext_click_behavior.mixin.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mixins/itext_click_behavior.mixin.js b/src/mixins/itext_click_behavior.mixin.js index c2a4f25846f..ec02c263fb3 100644 --- a/src/mixins/itext_click_behavior.mixin.js +++ b/src/mixins/itext_click_behavior.mixin.js @@ -110,6 +110,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot if (this === this.canvas._activeObject) { this.selected = true; } + else { + this.selected = false; + } }, /** From 49aadc9520ac315514bdbdf191d13eede0c7d820 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Thu, 21 Nov 2019 01:02:08 +0100 Subject: [PATCH 2/5] one liner --- dist/fabric.js | 74 ++++++++++++------------ src/mixins/itext_click_behavior.mixin.js | 7 +-- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/dist/fabric.js b/dist/fabric.js index e7c505b2a4d..55e841be8ea 100644 --- a/dist/fabric.js +++ b/dist/fabric.js @@ -74,6 +74,7 @@ fabric.SHARED_ATTRIBUTES = [ */ fabric.DPI = 96; fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)'; +fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig; fabric.fontPaths = { }; fabric.iMatrix = [1, 0, 0, 1, 0, 0]; @@ -2751,29 +2752,13 @@ fabric.CommonMethods = { * Wrapper around `console.log` (when available) * @param {*} [values] Values to log */ -fabric.log = function() { }; +fabric.log = console.log; /** * Wrapper around `console.warn` (when available) * @param {*} [values] Values to log as a warning */ -fabric.warn = function() { }; - -/* eslint-disable */ -if (typeof console !== 'undefined') { - - ['log', 'warn'].forEach(function(methodName) { - - if (typeof console[methodName] !== 'undefined' && - typeof console[methodName].apply === 'function') { - - fabric[methodName] = function() { - return console[methodName].apply(console, arguments); - }; - } - }); -} -/* eslint-enable */ +fabric.warn = console.warn; (function() { @@ -3383,7 +3368,9 @@ if (typeof console !== 'undefined') { colorAttributes = { stroke: 'strokeOpacity', fill: 'fillOpacity' - }; + }, + + fSize = 'font-size', cPath = 'clip-path'; fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames); fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements); @@ -4175,13 +4162,21 @@ if (typeof console !== 'undefined') { }, { }); // add values parsed from style, which take precedence over attributes // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes) - ownAttributes = extend(ownAttributes, - extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element))); - + var cssAttrs = extend( + getGlobalStylesForElement(element, svgUid), + fabric.parseStyleAttribute(element) + ); + ownAttributes = extend( + ownAttributes, + cssAttrs + ); + if (cssAttrs[cPath]) { + element.setAttribute(cPath, cssAttrs[cPath]); + } fontSize = parentFontSize = parentAttributes.fontSize || fabric.Text.DEFAULT_SVG_FONT_SIZE; - if (ownAttributes['font-size']) { + if (ownAttributes[fSize]) { // looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers. - ownAttributes['font-size'] = fontSize = parseUnit(ownAttributes['font-size'], parentFontSize); + ownAttributes[fSize] = fontSize = parseUnit(ownAttributes[fSize], parentFontSize); } var normalizedAttr, normalizedValue, normalizedStyle = {}; @@ -4397,7 +4392,7 @@ if (typeof console !== 'undefined') { })(typeof exports !== 'undefined' ? exports : this); -fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) { +fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions, doc) { this.elements = elements; this.callback = callback; this.options = options; @@ -4405,6 +4400,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp this.svgUid = (options && options.svgUid) || 0; this.parsingOptions = parsingOptions; this.regexUrl = /^url\(['"]?#([^'"]+)['"]?\)/g; + this.doc = doc; }; (function(proto) { @@ -4451,7 +4447,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp _options = obj.parsePreserveAspectRatioAttribute(el); } obj._removeTransformMatrix(_options); - _this.resolveClipPath(obj); + _this.resolveClipPath(obj, el); _this.reviver && _this.reviver(el, obj); _this.instances[index] = obj; _this.checkIfDone(); @@ -4459,12 +4455,13 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp }; proto.extractPropertyDefinition = function(obj, property, storage) { - var value = obj[property]; - if (!(/^url\(/).test(value)) { + var value = obj[property], regex = this.regexUrl; + if (!regex.test(value)) { return; } - var id = this.regexUrl.exec(value)[1]; - this.regexUrl.lastIndex = 0; + regex.lastIndex = 0; + var id = regex.exec(value)[1]; + regex.lastIndex = 0; return fabric[storage][this.svgUid][id]; }; @@ -4485,12 +4482,19 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp }; }; - proto.resolveClipPath = function(obj) { + proto.resolveClipPath = function(obj, usingElement) { var clipPath = this.extractPropertyDefinition(obj, 'clipPath', 'clipPaths'), element, klass, objTransformInv, container, gTransform, options; if (clipPath) { container = []; objTransformInv = fabric.util.invertTransform(obj.calcTransformMatrix()); + // move the clipPath tag as sibling to the real element that is using it + var clipPathTag = clipPath[0].parentNode; + var clipPathOwner = usingElement; + while (clipPathOwner.parentNode && clipPathOwner.getAttribute('clip-path') !== obj.clipPath) { + clipPathOwner = clipPathOwner.parentNode; + } + clipPathOwner.parentNode.appendChild(clipPathTag); for (var i = 0; i < clipPath.length; i++) { element = clipPath[i]; klass = this.findTag(element); @@ -4511,7 +4515,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp clipPath.calcTransformMatrix() ); if (clipPath.clipPath) { - this.resolveClipPath(clipPath); + this.resolveClipPath(clipPath, clipPathOwner); } var options = fabric.util.qrDecompose(gTransform); clipPath.flipX = false; @@ -18703,7 +18707,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot coords = [], currentPath, parsed, - re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig, + re = fabric.rePathCommand, match, coordsStr; @@ -28028,9 +28032,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) { return; } - if (this === this.canvas._activeObject) { - this.selected = true; - } + this.selected = this === this.canvas._activeObject; }, /** diff --git a/src/mixins/itext_click_behavior.mixin.js b/src/mixins/itext_click_behavior.mixin.js index ec02c263fb3..3ed72f912b1 100644 --- a/src/mixins/itext_click_behavior.mixin.js +++ b/src/mixins/itext_click_behavior.mixin.js @@ -107,12 +107,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) { return; } - if (this === this.canvas._activeObject) { - this.selected = true; - } - else { - this.selected = false; - } + this.selected = this === this.canvas._activeObject; }, /** From c27ff40f1f59c1ada8411e4fd4f3911aaa0ffa8f Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Thu, 21 Nov 2019 01:02:19 +0100 Subject: [PATCH 3/5] no build --- dist/fabric.js | 74 ++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/dist/fabric.js b/dist/fabric.js index 55e841be8ea..e7c505b2a4d 100644 --- a/dist/fabric.js +++ b/dist/fabric.js @@ -74,7 +74,6 @@ fabric.SHARED_ATTRIBUTES = [ */ fabric.DPI = 96; fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)'; -fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig; fabric.fontPaths = { }; fabric.iMatrix = [1, 0, 0, 1, 0, 0]; @@ -2752,13 +2751,29 @@ fabric.CommonMethods = { * Wrapper around `console.log` (when available) * @param {*} [values] Values to log */ -fabric.log = console.log; +fabric.log = function() { }; /** * Wrapper around `console.warn` (when available) * @param {*} [values] Values to log as a warning */ -fabric.warn = console.warn; +fabric.warn = function() { }; + +/* eslint-disable */ +if (typeof console !== 'undefined') { + + ['log', 'warn'].forEach(function(methodName) { + + if (typeof console[methodName] !== 'undefined' && + typeof console[methodName].apply === 'function') { + + fabric[methodName] = function() { + return console[methodName].apply(console, arguments); + }; + } + }); +} +/* eslint-enable */ (function() { @@ -3368,9 +3383,7 @@ fabric.warn = console.warn; colorAttributes = { stroke: 'strokeOpacity', fill: 'fillOpacity' - }, - - fSize = 'font-size', cPath = 'clip-path'; + }; fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames); fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements); @@ -4162,21 +4175,13 @@ fabric.warn = console.warn; }, { }); // add values parsed from style, which take precedence over attributes // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes) - var cssAttrs = extend( - getGlobalStylesForElement(element, svgUid), - fabric.parseStyleAttribute(element) - ); - ownAttributes = extend( - ownAttributes, - cssAttrs - ); - if (cssAttrs[cPath]) { - element.setAttribute(cPath, cssAttrs[cPath]); - } + ownAttributes = extend(ownAttributes, + extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element))); + fontSize = parentFontSize = parentAttributes.fontSize || fabric.Text.DEFAULT_SVG_FONT_SIZE; - if (ownAttributes[fSize]) { + if (ownAttributes['font-size']) { // looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers. - ownAttributes[fSize] = fontSize = parseUnit(ownAttributes[fSize], parentFontSize); + ownAttributes['font-size'] = fontSize = parseUnit(ownAttributes['font-size'], parentFontSize); } var normalizedAttr, normalizedValue, normalizedStyle = {}; @@ -4392,7 +4397,7 @@ fabric.warn = console.warn; })(typeof exports !== 'undefined' ? exports : this); -fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions, doc) { +fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) { this.elements = elements; this.callback = callback; this.options = options; @@ -4400,7 +4405,6 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp this.svgUid = (options && options.svgUid) || 0; this.parsingOptions = parsingOptions; this.regexUrl = /^url\(['"]?#([^'"]+)['"]?\)/g; - this.doc = doc; }; (function(proto) { @@ -4447,7 +4451,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp _options = obj.parsePreserveAspectRatioAttribute(el); } obj._removeTransformMatrix(_options); - _this.resolveClipPath(obj, el); + _this.resolveClipPath(obj); _this.reviver && _this.reviver(el, obj); _this.instances[index] = obj; _this.checkIfDone(); @@ -4455,13 +4459,12 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp }; proto.extractPropertyDefinition = function(obj, property, storage) { - var value = obj[property], regex = this.regexUrl; - if (!regex.test(value)) { + var value = obj[property]; + if (!(/^url\(/).test(value)) { return; } - regex.lastIndex = 0; - var id = regex.exec(value)[1]; - regex.lastIndex = 0; + var id = this.regexUrl.exec(value)[1]; + this.regexUrl.lastIndex = 0; return fabric[storage][this.svgUid][id]; }; @@ -4482,19 +4485,12 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp }; }; - proto.resolveClipPath = function(obj, usingElement) { + proto.resolveClipPath = function(obj) { var clipPath = this.extractPropertyDefinition(obj, 'clipPath', 'clipPaths'), element, klass, objTransformInv, container, gTransform, options; if (clipPath) { container = []; objTransformInv = fabric.util.invertTransform(obj.calcTransformMatrix()); - // move the clipPath tag as sibling to the real element that is using it - var clipPathTag = clipPath[0].parentNode; - var clipPathOwner = usingElement; - while (clipPathOwner.parentNode && clipPathOwner.getAttribute('clip-path') !== obj.clipPath) { - clipPathOwner = clipPathOwner.parentNode; - } - clipPathOwner.parentNode.appendChild(clipPathTag); for (var i = 0; i < clipPath.length; i++) { element = clipPath[i]; klass = this.findTag(element); @@ -4515,7 +4511,7 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp clipPath.calcTransformMatrix() ); if (clipPath.clipPath) { - this.resolveClipPath(clipPath, clipPathOwner); + this.resolveClipPath(clipPath); } var options = fabric.util.qrDecompose(gTransform); clipPath.flipX = false; @@ -18707,7 +18703,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot coords = [], currentPath, parsed, - re = fabric.rePathCommand, + re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig, match, coordsStr; @@ -28032,7 +28028,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) { return; } - this.selected = this === this.canvas._activeObject; + if (this === this.canvas._activeObject) { + this.selected = true; + } }, /** From dd1c38e28752c480662351e74bd128da1139d958 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Thu, 21 Nov 2019 02:03:39 +0100 Subject: [PATCH 4/5] more changes --- src/mixins/itext_click_behavior.mixin.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/mixins/itext_click_behavior.mixin.js b/src/mixins/itext_click_behavior.mixin.js index 3ed72f912b1..ca93f1df3b1 100644 --- a/src/mixins/itext_click_behavior.mixin.js +++ b/src/mixins/itext_click_behavior.mixin.js @@ -60,16 +60,26 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot this.initClicks(); }, + /** + * Default handler for double click, select a word + */ + doubleClickHandler: function(options) { + this.selectWord(this.getSelectionStartFromPointer(options.e)); + }, + + /** + * Default handler for triple click, select a line + */ + tripleClickHandler: function(options) { + this.selectLine(this.getSelectionStartFromPointer(options.e)); + }, + /** * Initializes double and triple click event handlers */ initClicks: function() { - this.on('mousedblclick', function(options) { - this.selectWord(this.getSelectionStartFromPointer(options.e)); - }); - this.on('tripleclick', function(options) { - this.selectLine(this.getSelectionStartFromPointer(options.e)); - }); + this.on('mousedblclick', this.doubleClickHandler); + this.on('tripleclick', this.tripleClickHandler); }, /** @@ -107,6 +117,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) { return; } + // we want to avoid that an object that was selected and then becomes unselectable, + // may trigger editing mode in some way. this.selected = this === this.canvas._activeObject; }, From 8ca6eab52ef9c2af43f338094ff55438b8a29c13 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 22 Nov 2019 14:22:53 +0100 Subject: [PATCH 5/5] 2 new tests --- test/unit/itext_click_behaviour.js | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/unit/itext_click_behaviour.js b/test/unit/itext_click_behaviour.js index e111f1ebd1d..b78eff0bb0c 100644 --- a/test/unit/itext_click_behaviour.js +++ b/test/unit/itext_click_behaviour.js @@ -15,6 +15,65 @@ var selection = iText._getNewSelectionStartFromOffset({ y: 1, x: 1000 }, 500, 520, index, jlen); assert.equal(selection, index, 'index value did not change'); }); + QUnit.test('doubleClickHandler', function(assert) { + var iText = new fabric.IText('test need some word\nsecond line'); + iText.canvas = canvas; + var eventData = { + which: 1, + target: canvas.upperCanvasEl, + clientX: 40, + clientY: 10 + }; + iText.doubleClickHandler({ + e: eventData + }); + assert.equal(iText.selectionStart, 0, 'dblClcik selection start is'); + assert.equal(iText.selectionEnd, 4, 'dblClcik selection end is'); + var eventData = { + which: 1, + target: canvas.upperCanvasEl, + clientX: 40, + clientY: 60 + }; + iText.doubleClickHandler({ + e: eventData + }); + assert.equal(iText.selectionStart, 19, 'second dblClcik selection start is'); + assert.equal(iText.selectionEnd, 26, 'second dblClcik selection end is'); + }); + QUnit.test('tripleClickHandler', function(assert) { + var iText = new fabric.IText('test need some word\nsecond line'); + iText.canvas = canvas; + var eventData = { + which: 1, + target: canvas.upperCanvasEl, + clientX: 40, + clientY: 10 + }; + iText.tripleClickHandler({ + e: eventData + }); + assert.equal(iText.selectionStart, 0, 'tripleClick selection start is'); + assert.equal(iText.selectionEnd, 19, 'tripleClick selection end is'); + var eventData = { + which: 1, + target: canvas.upperCanvasEl, + clientX: 40, + clientY: 60 + }; + iText.tripleClickHandler({ + e: eventData + }); + assert.equal(iText.selectionStart, 20, 'second tripleClick selection start is'); + assert.equal(iText.selectionEnd, 31, 'second tripleClick selection end is'); + }); + QUnit.test('_getNewSelectionStartFromOffset end of line', function(assert) { + var iText = new fabric.IText('test need some word\nsecond line'); + var index = 10; + var jlen = 20; + var selection = iText._getNewSelectionStartFromOffset({ y: 1, x: 1000 }, 500, 520, index, jlen); + assert.equal(selection, index, 'index value did not change'); + }); QUnit.test('_getNewSelectionStartFromOffset middle of line', function(assert) { var iText = new fabric.IText('test need some word\nsecond line'); var index = 10;