diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 715697d6557..185c97fc25c 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -6,7 +6,7 @@ about: Create a report with a reproducible test case in a JSFIDDLE
## Version
-3.5.1
+3.6.0
## Test Case
http://jsfiddle.net/fabricjs/Da7SP/
diff --git a/.github/ISSUE_TEMPLATE/help-with-my-own-project.md b/.github/ISSUE_TEMPLATE/help-with-my-own-project.md
index 6e0e373c221..6a31fb6c8c0 100644
--- a/.github/ISSUE_TEMPLATE/help-with-my-own-project.md
+++ b/.github/ISSUE_TEMPLATE/help-with-my-own-project.md
@@ -4,7 +4,7 @@ about: Report a question you previously open on stack overflow with enough conte
---
-
diff --git a/.github/ISSUE_TEMPLATE/none-of-the-above.md b/.github/ISSUE_TEMPLATE/none-of-the-above.md
deleted file mode 100644
index 79b78f51863..00000000000
--- a/.github/ISSUE_TEMPLATE/none-of-the-above.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-name: None of the above
-about: Report an issue that does not fit in any category
-
----
-
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 824fcf52127..ef081f1609e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## [3.6.0]
+- fix: ISSUE-5512 better Clippath transform parsing in SVG [#5983](https://github.com/fabricjs/fabric.js/pull/5983)
+- fix: ISSUE-5984 Avoid enter editing in non selectable object [#5989](https://github.com/fabricjs/fabric.js/pull/5989)
+- Tweak to object._setLineDash to avoid cycles when nothing in array [#6000](https://github.com/fabricjs/fabric.js/pull/6000)
+- fix: ISSUE-5867 Fix the extra new line selection with empty line [#6011](https://github.com/fabricjs/fabric.js/pull/6011)
+- Improvement: Use SVG Namespace for SVG Elements [#5957](https://github.com/fabricjs/fabric.js/pull/5957)
+- Improvement: ISSUE-4115 - triggers in/out events for sub targets [#6013](https://github.com/fabricjs/fabric.js/pull/6013)
+- Improvement: Upper canvas retina scaling [#5938](https://github.com/fabricjs/fabric.js/pull/5938)
+
## [3.5.1]
- Fix for textbox non defined in scaleObject [#5896](https://github.com/fabricjs/fabric.js/pull/5896)
- Fix canvas pattern as background and exports [#5973](https://github.com/fabricjs/fabric.js/pull/5973)
diff --git a/HEADER.js b/HEADER.js
index e76e5bb7be7..7bfbf8c9122 100644
--- a/HEADER.js
+++ b/HEADER.js
@@ -1,6 +1,6 @@
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '3.5.1' };
+var fabric = fabric || { version: '3.6.0' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
diff --git a/dist/fabric.js b/dist/fabric.js
index e7c505b2a4d..7e743727ea3 100644
--- a/dist/fabric.js
+++ b/dist/fabric.js
@@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '3.5.1' };
+var fabric = fabric || { version: '3.6.0' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -74,8 +74,11 @@ fabric.SHARED_ATTRIBUTES = [
*/
fabric.DPI = 96;
fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)';
+fabric.rePathCommand = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/ig;
+fabric.reNonWord = /[ \n\.,;!\?\-]/;
fabric.fontPaths = { };
fabric.iMatrix = [1, 0, 0, 1, 0, 0];
+fabric.svgNS = 'http://www.w3.org/2000/svg';
/**
* Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.
@@ -2751,29 +2754,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 +3370,9 @@ if (typeof console !== 'undefined') {
colorAttributes = {
stroke: 'strokeOpacity',
fill: 'fillOpacity'
- };
+ },
+
+ fSize = 'font-size', cPath = 'clip-path';
fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames);
fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements);
@@ -3798,14 +3787,14 @@ if (typeof console !== 'undefined') {
y = el.getAttribute('y') || 0,
el2 = elementById(doc, xlink).cloneNode(true),
currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
- parentNode, oldLength = nodelist.length, attr, j, attrs, len;
+ parentNode, oldLength = nodelist.length, attr, j, attrs, len, namespace = fabric.svgNS;
applyViewboxTransform(el2);
if (/^svg$/i.test(el2.nodeName)) {
- var el3 = el2.ownerDocument.createElement('g');
+ var el3 = el2.ownerDocument.createElementNS(namespace, 'g');
for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) {
attr = attrs.item(j);
- el3.setAttribute(attr.nodeName, attr.nodeValue);
+ el3.setAttributeNS(namespace, attr.nodeName, attr.nodeValue);
}
// el2.firstChild != null
while (el2.firstChild) {
@@ -3950,7 +3939,7 @@ if (typeof console !== 'undefined') {
(minY * scaleY + heightDiff) + ') ';
parsedDim.viewboxTransform = fabric.parseTransformAttribute(matrix);
if (element.nodeName === 'svg') {
- el = element.ownerDocument.createElement('g');
+ el = element.ownerDocument.createElementNS(fabric.svgNS, 'g');
// element.firstChild != null
while (element.firstChild) {
el.appendChild(element.firstChild);
@@ -4175,13 +4164,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 +4394,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 +4402,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 +4449,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 +4457,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 +4484,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 +4517,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;
@@ -6876,12 +6882,20 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
if (!this._isRetinaScaling()) {
return;
}
- this.lowerCanvasEl.setAttribute('width', this.width * fabric.devicePixelRatio);
- this.lowerCanvasEl.setAttribute('height', this.height * fabric.devicePixelRatio);
+ var scaleRatio = fabric.devicePixelRatio;
+ this.__initRetinaScaling(scaleRatio, this.lowerCanvasEl, this.contextContainer);
+ if (this.upperCanvasEl) {
+ this.__initRetinaScaling(scaleRatio, this.upperCanvasEl, this.contextTop);
+ }
+ },
- this.contextContainer.scale(fabric.devicePixelRatio, fabric.devicePixelRatio);
+ __initRetinaScaling: function(scaleRatio, canvas, context) {
+ canvas.setAttribute('width', this.width * scaleRatio);
+ canvas.setAttribute('height', this.height * scaleRatio);
+ context.scale(scaleRatio, scaleRatio);
},
+
/**
* Calculates canvas element offset relative to the document
* This method is also attached as "resize" event handler of window
@@ -8578,13 +8592,18 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
return;
}
- var ctx = this.canvas.contextTop,
- zoom = this.canvas.getZoom();
+ var canvas = this.canvas,
+ shadow = this.shadow,
+ ctx = canvas.contextTop,
+ zoom = canvas.getZoom();
+ if (canvas && canvas._isRetinaScaling()) {
+ zoom *= fabric.devicePixelRatio;
+ }
- ctx.shadowColor = this.shadow.color;
- ctx.shadowBlur = this.shadow.blur * zoom;
- ctx.shadowOffsetX = this.shadow.offsetX * zoom;
- ctx.shadowOffsetY = this.shadow.offsetY * zoom;
+ ctx.shadowColor = shadow.color;
+ ctx.shadowBlur = shadow.blur * zoom;
+ ctx.shadowOffsetX = shadow.offsetX * zoom;
+ ctx.shadowOffsetY = shadow.offsetY * zoom;
},
needsFullRender: function() {
@@ -9651,6 +9670,26 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
fireMiddleClick: false,
+ /**
+ * Keep track of the subTargets for Mouse Events
+ * @type fabric.Object[]
+ */
+ targets: [],
+
+ /**
+ * Keep track of the hovered target
+ * @type fabric.Object
+ * @private
+ */
+ _hoveredTarget: null,
+
+ /**
+ * hold the list of nested targets hovered
+ * @type fabric.Object[]
+ * @private
+ */
+ _hoveredTargets: [],
+
/**
* @private
*/
@@ -10648,6 +10687,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
pointer = this.restorePointerVpt(pointer);
}
+ var retinaScaling = this.getRetinaScaling();
+ if (retinaScaling !== 1) {
+ pointer.x /= retinaScaling;
+ pointer.y /= retinaScaling;
+ }
+
if (boundsWidth === 0 || boundsHeight === 0) {
// If bounds are not available (i.e. not visible), do not apply scale.
cssScale = { width: 1, height: 1 };
@@ -10670,22 +10715,24 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @throws {CANVAS_INIT_ERROR} If canvas can not be initialized
*/
_createUpperCanvas: function () {
- var lowerCanvasClass = this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/, '');
+ var lowerCanvasClass = this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/, ''),
+ lowerCanvasEl = this.lowerCanvasEl, upperCanvasEl = this.upperCanvasEl;
// there is no need to create a new upperCanvas element if we have already one.
- if (this.upperCanvasEl) {
- this.upperCanvasEl.className = '';
+ if (upperCanvasEl) {
+ upperCanvasEl.className = '';
}
else {
- this.upperCanvasEl = this._createCanvasElement();
+ upperCanvasEl = this._createCanvasElement();
+ this.upperCanvasEl = upperCanvasEl;
}
- fabric.util.addClass(this.upperCanvasEl, 'upper-canvas ' + lowerCanvasClass);
+ fabric.util.addClass(upperCanvasEl, 'upper-canvas ' + lowerCanvasClass);
- this.wrapperEl.appendChild(this.upperCanvasEl);
+ this.wrapperEl.appendChild(upperCanvasEl);
- this._copyCanvasStyle(this.lowerCanvasEl, this.upperCanvasEl);
- this._applyCanvasStyle(this.upperCanvasEl);
- this.contextTop = this.upperCanvasEl.getContext('2d');
+ this._copyCanvasStyle(lowerCanvasEl, upperCanvasEl);
+ this._applyCanvasStyle(upperCanvasEl);
+ this.contextTop = upperCanvasEl.getContext('2d');
},
/**
@@ -10798,8 +10845,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.fire('selection:cleared', { target: obj });
obj.fire('deselected');
}
- if (this._hoveredTarget === obj) {
+ if (obj === this._hoveredTarget){
this._hoveredTarget = null;
+ this._hoveredTargets = [];
}
this.callSuper('_onObjectRemoved', obj);
},
@@ -11217,6 +11265,14 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.fire('mouse:out', { target: target, e: e });
this._hoveredTarget = null;
target && target.fire('mouseout', { e: e });
+
+ var _this = this;
+ this._hoveredTargets.forEach(function(_target){
+ _this.fire('mouse:out', { target: target, e: e });
+ _target && target.fire('mouseout', { e: e });
+ });
+ this._hoveredTargets = [];
+
if (this._iTextInstances) {
this._iTextInstances.forEach(function(obj) {
if (obj.isEditing) {
@@ -11240,6 +11296,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (!this.currentTransform && !this.findTarget(e)) {
this.fire('mouse:over', { target: null, e: e });
this._hoveredTarget = null;
+ this._hoveredTargets = [];
}
},
@@ -11865,13 +11922,26 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_fireOverOutEvents: function(target, e) {
+ var _hoveredTarget = this._hoveredTarget,
+ _hoveredTargets = this._hoveredTargets, targets = this.targets,
+ length = Math.max(_hoveredTargets.length, targets.length);
+
this.fireSyntheticInOutEvents(target, e, {
- targetName: '_hoveredTarget',
- canvasEvtOut: 'mouse:out',
+ oldTarget: _hoveredTarget,
evtOut: 'mouseout',
- canvasEvtIn: 'mouse:over',
+ canvasEvtOut: 'mouse:out',
evtIn: 'mouseover',
+ canvasEvtIn: 'mouse:over',
});
+ for (var i = 0; i < length; i++){
+ this.fireSyntheticInOutEvents(targets[i], e, {
+ oldTarget: _hoveredTargets[i],
+ evtOut: 'mouseout',
+ evtIn: 'mouseover',
+ });
+ }
+ this._hoveredTarget = target;
+ this._hoveredTargets = this.targets.concat();
},
/**
@@ -11881,11 +11951,23 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_fireEnterLeaveEvents: function(target, e) {
+ var _draggedoverTarget = this._draggedoverTarget,
+ _hoveredTargets = this._hoveredTargets, targets = this.targets,
+ length = Math.max(_hoveredTargets.length, targets.length);
+
this.fireSyntheticInOutEvents(target, e, {
- targetName: '_draggedoverTarget',
+ oldTarget: _draggedoverTarget,
evtOut: 'dragleave',
evtIn: 'dragenter',
});
+ for (var i = 0; i < length; i++) {
+ this.fireSyntheticInOutEvents(targets[i], e, {
+ oldTarget: _hoveredTargets[i],
+ evtOut: 'dragleave',
+ evtIn: 'dragenter',
+ });
+ }
+ this._draggedoverTarget = target;
},
/**
@@ -11901,12 +11983,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
fireSyntheticInOutEvents: function(target, e, config) {
- var inOpt, outOpt, oldTarget = this[config.targetName], outFires, inFires,
+ var inOpt, outOpt, oldTarget = config.oldTarget, outFires, inFires,
targetChanged = oldTarget !== target, canvasEvtIn = config.canvasEvtIn, canvasEvtOut = config.canvasEvtOut;
if (targetChanged) {
inOpt = { e: e, target: target, previousTarget: oldTarget };
outOpt = { e: e, target: oldTarget, nextTarget: target };
- this[config.targetName] = target;
}
inFires = target && targetChanged;
outFires = oldTarget && targetChanged;
@@ -12071,6 +12152,13 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
&& target._findTargetCorner(this.getPointer(e, true));
if (!corner) {
+ if (target.subTargetCheck){
+ // hoverCursor should come from top-most subTarget,
+ // so we walk the array backwards
+ this.targets.concat().reverse().map(function(_target){
+ hoverCursor = _target.hoverCursor || hoverCursor;
+ });
+ }
this.setCursor(hoverCursor);
}
else {
@@ -12190,6 +12278,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (activeSelection.contains(target)) {
activeSelection.removeWithUpdate(target);
this._hoveredTarget = target;
+ this._hoveredTargets = this.targets.concat();
if (activeSelection.size() === 1) {
// activate last remaining object
this._setActiveObject(activeSelection.item(0), e);
@@ -12198,6 +12287,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
else {
activeSelection.addWithUpdate(target);
this._hoveredTarget = activeSelection;
+ this._hoveredTargets = this.targets.concat();
}
this._fireSelectionEvents(currentActiveObjects, e);
},
@@ -12208,6 +12298,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_createActiveSelection: function(target, e) {
var currentActives = this.getActiveObjects(), group = this._createGroup(target);
this._hoveredTarget = group;
+ // ISSUE 4115: should we consider subTargets here?
+ // this._hoveredTargets = [];
+ // this._hoveredTargets = this.targets.concat();
this._setActiveObject(group, e);
this._fireSelectionEvents(currentActives, e);
},
@@ -14027,7 +14120,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {Function} alternative function to call if browser does not support lineDash
*/
_setLineDash: function(ctx, dashArray, alternative) {
- if (!dashArray) {
+ if (!dashArray || dashArray.length === 0) {
return;
}
// Spec requires the concatenation of two copies the dash list when the number of elements is odd
@@ -14774,7 +14867,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @type Number
*/
fabric.Object.__uid = 0;
-
})(typeof exports !== 'undefined' ? exports : this);
@@ -18703,7 +18795,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;
@@ -19153,7 +19245,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
strokeWidth: 0,
/**
- * Indicates if click events should also check for subtargets
+ * Indicates if click, mouseover, mouseout events & hoverCursor should also check for subtargets
* @type Boolean
* @default
*/
@@ -27276,15 +27368,17 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* @return {Number} Index of the beginning or end of a word
*/
searchWordBoundary: function(selectionStart, direction) {
- var index = this._reSpace.test(this._text[selectionStart]) ? selectionStart - 1 : selectionStart,
- _char = this._text[index],
- reNonWord = /[ \n\.,;!\?\-]/;
+ var text = this._text,
+ index = this._reSpace.test(text[selectionStart]) ? selectionStart - 1 : selectionStart,
+ _char = text[index],
+ // wrong
+ reNonWord = fabric.reNonWord;
- while (!reNonWord.test(_char) && index > 0 && index < this._text.length) {
+ while (!reNonWord.test(_char) && index > 0 && index < text.length) {
index += direction;
- _char = this._text[index];
+ _char = text[index];
}
- if (reNonWord.test(_char) && _char !== '\n') {
+ if (reNonWord.test(_char)) {
index += direction === 1 ? 0 : 1;
}
return index;
@@ -27525,9 +27619,10 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
x: boundaries.left + leftOffset,
y: boundaries.top + boundaries.topOffset + charHeight
},
+ retinaScaling = this.canvas.getRetinaScaling(),
upperCanvas = this.canvas.upperCanvasEl,
- upperCanvasWidth = upperCanvas.width,
- upperCanvasHeight = upperCanvas.height,
+ upperCanvasWidth = upperCanvas.width / retinaScaling,
+ upperCanvasHeight = upperCanvas.height / retinaScaling,
maxWidth = upperCanvasWidth - charHeight,
maxHeight = upperCanvasHeight - charHeight,
scaleX = upperCanvas.clientWidth / upperCanvasWidth,
@@ -27981,16 +28076,32 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.initClicks();
},
+ /**
+ * Default handler for double click, select a word
+ */
+ doubleClickHandler: function(options) {
+ if (!this.isEditing) {
+ return;
+ }
+ this.selectWord(this.getSelectionStartFromPointer(options.e));
+ },
+
+ /**
+ * Default handler for triple click, select a line
+ */
+ tripleClickHandler: function(options) {
+ if (!this.isEditing) {
+ return;
+ }
+ 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);
},
/**
@@ -28028,9 +28139,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;
}
- if (this === this.canvas._activeObject) {
- this.selected = true;
- }
+ // 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;
},
/**
diff --git a/dist/fabric.min.js b/dist/fabric.min.js
index 538961e1ff5..70ec1d3f827 100644
--- a/dist/fabric.min.js
+++ b/dist/fabric.min.js
@@ -1 +1 @@
-var fabric=fabric||{version:"3.5.0"};if("undefined"!=typeof exports?exports.fabric=fabric:"function"==typeof define&&define.amd&&define([],function(){return fabric}),"undefined"!=typeof document&&"undefined"!=typeof window)document instanceof("undefined"!=typeof HTMLDocument?HTMLDocument:Document)?fabric.document=document:fabric.document=document.implementation.createHTMLDocument(""),fabric.window=window;else{var jsdom=require("jsdom"),virtualWindow=new jsdom.JSDOM(decodeURIComponent("%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E"),{features:{FetchExternalResources:["img"]},resources:"usable"}).window;fabric.document=virtualWindow.document,fabric.jsdomImplForWrapper=require("jsdom/lib/jsdom/living/generated/utils").implForWrapper,fabric.nodeCanvas=require("jsdom/lib/jsdom/utils").Canvas,fabric.window=virtualWindow,DOMParser=fabric.window.DOMParser}function resizeCanvasIfNeeded(t){var e=t.targetCanvas,i=e.width,r=e.height,n=t.destinationWidth,s=t.destinationHeight;i===n&&r===s||(e.width=n,e.height=s)}function copyGLTo2DDrawImage(t,e){var i=t.canvas,r=e.targetCanvas,n=r.getContext("2d");n.translate(0,r.height),n.scale(1,-1);var s=i.height-r.height;n.drawImage(i,0,s,r.width,r.height,0,0,r.width,r.height)}function copyGLTo2DPutImageData(t,e){var i=e.targetCanvas.getContext("2d"),r=e.destinationWidth,n=e.destinationHeight,s=r*n*4,o=new Uint8Array(this.imageBuffer,0,s),a=new Uint8ClampedArray(this.imageBuffer,0,s);t.readPixels(0,0,r,n,t.RGBA,t.UNSIGNED_BYTE,o);var h=new ImageData(a,r,n);i.putImageData(h,0,0)}fabric.isTouchSupported="ontouchstart"in fabric.window||"ontouchstart"in fabric.document||fabric.window&&fabric.window.navigator&&0/g,">")},graphemeSplit:function(t){var e,i=0,r=[];for(i=0;i/i,"")));if(!e||!e.documentElement)return n&&n(null),!1;C.parseSVGDocument(e.documentElement,function(t,e,i,r){n&&n(t,e,i,r)},i,r)}})},loadSVGFromString:function(t,n,e,i){var r;if(t=t.trim(),void 0!==C.window.DOMParser){var s=new C.window.DOMParser;s&&s.parseFromString&&(r=s.parseFromString(t,"text/xml"))}else C.window.ActiveXObject&&((r=new ActiveXObject("Microsoft.XMLDOM")).async="false",r.loadXML(t.replace(//i,"")));C.parseSVGDocument(r.documentElement,function(t,e,i,r){n(t,e,i,r)},e,i)}})}("undefined"!=typeof exports?exports:this),fabric.ElementsParser=function(t,e,i,r,n){this.elements=t,this.callback=e,this.options=i,this.reviver=r,this.svgUid=i&&i.svgUid||0,this.parsingOptions=n,this.regexUrl=/^url\(['"]?#([^'"]+)['"]?\)/g},function(t){t.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},t.createObjects=function(){var i=this;this.elements.forEach(function(t,e){t.setAttribute("svgUid",i.svgUid),i.createObject(t,e)})},t.findTag=function(t){return fabric[fabric.util.string.capitalize(t.tagName.replace("svg:",""))]},t.createObject=function(t,e){var i=this.findTag(t);if(i&&i.fromElement)try{i.fromElement(t,this.createCallback(e,t),this.options)}catch(t){fabric.log(t)}else this.checkIfDone()},t.createCallback=function(i,r){var n=this;return function(t){var e;n.resolveGradient(t,r,"fill"),n.resolveGradient(t,r,"stroke"),t instanceof fabric.Image&&t._originalElement&&(e=t.parsePreserveAspectRatioAttribute(r)),t._removeTransformMatrix(e),n.resolveClipPath(t),n.reviver&&n.reviver(r,t),n.instances[i]=t,n.checkIfDone()}},t.extractPropertyDefinition=function(t,e,i){var r=t[e];if(/^url\(/.test(r)){var n=this.regexUrl.exec(r)[1];return this.regexUrl.lastIndex=0,fabric[i][this.svgUid][n]}},t.resolveGradient=function(t,e,i){var r=this.extractPropertyDefinition(t,i,"gradientDefs");if(r){var n=e.getAttribute(i+"-opacity"),s=fabric.Gradient.fromElement(r,t,n,this.options);t.set(i,s)}},t.createClipPathCallback=function(t,e){return function(t){t._removeTransformMatrix(),t.fillRule=t.clipRule,e.push(t)}},t.resolveClipPath=function(t){var e,i,r,n,s=this.extractPropertyDefinition(t,"clipPath","clipPaths");if(s){r=[],i=fabric.util.invertTransform(t.calcTransformMatrix());for(var o=0;ot.x&&this.y>t.y},gte:function(t){return this.x>=t.x&&this.y>=t.y},lerp:function(t,e){return void 0===e&&(e=.5),e=Math.max(Math.min(1,e),0),new i(this.x+(t.x-this.x)*e,this.y+(t.y-this.y)*e)},distanceFrom:function(t){var e=this.x-t.x,i=this.y-t.y;return Math.sqrt(e*e+i*i)},midPointFrom:function(t){return this.lerp(t)},min:function(t){return new i(Math.min(this.x,t.x),Math.min(this.y,t.y))},max:function(t){return new i(Math.max(this.x,t.x),Math.max(this.y,t.y))},toString:function(){return this.x+","+this.y},setXY:function(t,e){return this.x=t,this.y=e,this},setX:function(t){return this.x=t,this},setY:function(t){return this.y=t,this},setFromPoint:function(t){return this.x=t.x,this.y=t.y,this},swap:function(t){var e=this.x,i=this.y;this.x=t.x,this.y=t.y,t.x=e,t.y=i},clone:function(){return new i(this.x,this.y)}}}("undefined"!=typeof exports?exports:this),function(t){"use strict";var f=t.fabric||(t.fabric={});function d(t){this.status=t,this.points=[]}f.Intersection?f.warn("fabric.Intersection is already defined"):(f.Intersection=d,f.Intersection.prototype={constructor:d,appendPoint:function(t){return this.points.push(t),this},appendPoints:function(t){return this.points=this.points.concat(t),this}},f.Intersection.intersectLineLine=function(t,e,i,r){var n,s=(r.x-i.x)*(t.y-i.y)-(r.y-i.y)*(t.x-i.x),o=(e.x-t.x)*(t.y-i.y)-(e.y-t.y)*(t.x-i.x),a=(r.y-i.y)*(e.x-t.x)-(r.x-i.x)*(e.y-t.y);if(0!==a){var h=s/a,c=o/a;0<=h&&h<=1&&0<=c&&c<=1?(n=new d("Intersection")).appendPoint(new f.Point(t.x+h*(e.x-t.x),t.y+h*(e.y-t.y))):n=new d}else n=new d(0===s||0===o?"Coincident":"Parallel");return n},f.Intersection.intersectLinePolygon=function(t,e,i){var r,n,s,o,a=new d,h=i.length;for(o=0;oo.r2,c=this.gradientTransform?this.gradientTransform.concat():fabric.iMatrix.concat(),l=-this.offsetX,u=-this.offsetY,f=!!e.additionalTransform,d="pixels"===this.gradientUnits?"userSpaceOnUse":"objectBoundingBox";if(a.sort(function(t,e){return t.offset-e.offset}),"objectBoundingBox"===d?(l/=t.width,u/=t.height):(l+=t.width/2,u+=t.height/2),"path"===t.type&&(l-=t.pathOffset.x,u-=t.pathOffset.y),c[4]-=l,c[5]-=u,s='id="SVGID_'+this.id+'" gradientUnits="'+d+'"',s+=' gradientTransform="'+(f?e.additionalTransform+" ":"")+fabric.util.matrixToSVG(c)+'" ',"linear"===this.type?n=["\n']:"radial"===this.type&&(n=["\n']),"radial"===this.type){if(h)for((a=a.concat()).reverse(),i=0,r=a.length;i\n')}return n.push("linear"===this.type?"\n":"\n"),n.join("")},toLive:function(t){var e,i,r,n=fabric.util.object.clone(this.coords);if(this.type){for("linear"===this.type?e=t.createLinearGradient(n.x1,n.y1,n.x2,n.y2):"radial"===this.type&&(e=t.createRadialGradient(n.x1,n.y1,n.r1,n.x2,n.y2,n.r2)),i=0,r=this.colorStops.length;i\n\n\n'},setOptions:function(t){for(var e in t)this[e]=t[e]},toLive:function(t){var e="function"==typeof this.source?this.source():this.source;if(!e)return"";if(void 0!==e.src){if(!e.complete)return"";if(0===e.naturalWidth||0===e.naturalHeight)return""}return t.createPattern(e,this.repeat)}})}(),function(t){"use strict";var o=t.fabric||(t.fabric={}),a=o.util.toFixed;o.Shadow?o.warn("fabric.Shadow is already defined."):(o.Shadow=o.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,nonScaling:!1,initialize:function(t){for(var e in"string"==typeof t&&(t=this._parseShadow(t)),t)this[e]=t[e];this.id=o.Object.__uid++},_parseShadow:function(t){var e=t.trim(),i=o.Shadow.reOffsetsAndBlur.exec(e)||[];return{color:(e.replace(o.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)").trim(),offsetX:parseInt(i[1],10)||0,offsetY:parseInt(i[2],10)||0,blur:parseInt(i[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(t){var e=40,i=40,r=o.Object.NUM_FRACTION_DIGITS,n=o.util.rotateVector({x:this.offsetX,y:this.offsetY},o.util.degreesToRadians(-t.angle)),s=new o.Color(this.color);return t.width&&t.height&&(e=100*a((Math.abs(n.x)+this.blur)/t.width,r)+20,i=100*a((Math.abs(n.y)+this.blur)/t.height,r)+20),t.flipX&&(n.x*=-1),t.flipY&&(n.y*=-1),'\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\t\n\t\n\n'},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY,affectStroke:this.affectStroke,nonScaling:this.nonScaling};var e={},i=o.Shadow.prototype;return["color","blur","offsetX","offsetY","affectStroke","nonScaling"].forEach(function(t){this[t]!==i[t]&&(e[t]=this[t])},this),e}}),o.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/)}("undefined"!=typeof exports?exports:this),function(){"use strict";if(fabric.StaticCanvas)fabric.warn("fabric.StaticCanvas is already defined.");else{var n=fabric.util.object.extend,t=fabric.util.getElementOffset,c=fabric.util.removeFromArray,a=fabric.util.toFixed,s=fabric.util.transformPoint,o=fabric.util.invertTransform,i=fabric.util.getNodeCanvas,r=fabric.util.createCanvasElement,e=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass(fabric.CommonMethods,{initialize:function(t,e){e||(e={}),this.renderAndResetBound=this.renderAndReset.bind(this),this.requestRenderAllBound=this.requestRenderAll.bind(this),this._initStatic(t,e)},backgroundColor:"",backgroundImage:null,overlayColor:"",overlayImage:null,includeDefaultValues:!0,stateful:!1,renderOnAddRemove:!0,clipTo:null,controlsAboveOverlay:!1,allowTouchScrolling:!1,imageSmoothingEnabled:!0,viewportTransform:fabric.iMatrix.concat(),backgroundVpt:!0,overlayVpt:!0,onBeforeScaleRotate:function(){},enableRetinaScaling:!0,vptCoords:{},skipOffscreen:!0,clipPath:void 0,_initStatic:function(t,e){var i=this.requestRenderAllBound;this._objects=[],this._createLowerCanvas(t),this._initOptions(e),this._setImageSmoothing(),this.interactive||this._initRetinaScaling(),e.overlayImage&&this.setOverlayImage(e.overlayImage,i),e.backgroundImage&&this.setBackgroundImage(e.backgroundImage,i),e.backgroundColor&&this.setBackgroundColor(e.backgroundColor,i),e.overlayColor&&this.setOverlayColor(e.overlayColor,i),this.calcOffset()},_isRetinaScaling:function(){return 1!==fabric.devicePixelRatio&&this.enableRetinaScaling},getRetinaScaling:function(){return this._isRetinaScaling()?fabric.devicePixelRatio:1},_initRetinaScaling:function(){this._isRetinaScaling()&&(this.lowerCanvasEl.setAttribute("width",this.width*fabric.devicePixelRatio),this.lowerCanvasEl.setAttribute("height",this.height*fabric.devicePixelRatio),this.contextContainer.scale(fabric.devicePixelRatio,fabric.devicePixelRatio))},calcOffset:function(){return this._offset=t(this.lowerCanvasEl),this},setOverlayImage:function(t,e,i){return this.__setBgOverlayImage("overlayImage",t,e,i)},setBackgroundImage:function(t,e,i){return this.__setBgOverlayImage("backgroundImage",t,e,i)},setOverlayColor:function(t,e){return this.__setBgOverlayColor("overlayColor",t,e)},setBackgroundColor:function(t,e){return this.__setBgOverlayColor("backgroundColor",t,e)},_setImageSmoothing:function(){var t=this.getContext();t.imageSmoothingEnabled=t.imageSmoothingEnabled||t.webkitImageSmoothingEnabled||t.mozImageSmoothingEnabled||t.msImageSmoothingEnabled||t.oImageSmoothingEnabled,t.imageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(i,t,r,n){return"string"==typeof t?fabric.util.loadImage(t,function(t){if(t){var e=new fabric.Image(t,n);(this[i]=e).canvas=this}r&&r(t)},this,n&&n.crossOrigin):(n&&t.setOptions(n),(this[i]=t)&&(t.canvas=this),r&&r(t)),this},__setBgOverlayColor:function(t,e,i){return this[t]=e,this._initGradient(e,t),this._initPattern(e,t,i),this},_createCanvasElement:function(){var t=r();if(!t)throw e;if(t.style||(t.style={}),void 0===t.getContext)throw e;return t},_initOptions:function(t){var e=this.lowerCanvasEl;this._setOptions(t),this.width=this.width||parseInt(e.width,10)||0,this.height=this.height||parseInt(e.height,10)||0,this.lowerCanvasEl.style&&(e.width=this.width,e.height=this.height,e.style.width=this.width+"px",e.style.height=this.height+"px",this.viewportTransform=this.viewportTransform.slice())},_createLowerCanvas:function(t){t&&t.getContext?this.lowerCanvasEl=t:this.lowerCanvasEl=fabric.util.getById(t)||this._createCanvasElement(),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(t,e){return this.setDimensions({width:t},e)},setHeight:function(t,e){return this.setDimensions({height:t},e)},setDimensions:function(t,e){var i;for(var r in e=e||{},t)i=t[r],e.cssOnly||(this._setBackstoreDimension(r,t[r]),i+="px",this.hasLostContext=!0),e.backstoreOnly||this._setCssDimension(r,i);return this._isCurrentlyDrawing&&this.freeDrawingBrush&&this.freeDrawingBrush._setBrushStyles(),this._initRetinaScaling(),this._setImageSmoothing(),this.calcOffset(),e.cssOnly||this.requestRenderAll(),this},_setBackstoreDimension:function(t,e){return this.lowerCanvasEl[t]=e,this.upperCanvasEl&&(this.upperCanvasEl[t]=e),this.cacheCanvasEl&&(this.cacheCanvasEl[t]=e),this[t]=e,this},_setCssDimension:function(t,e){return this.lowerCanvasEl.style[t]=e,this.upperCanvasEl&&(this.upperCanvasEl.style[t]=e),this.wrapperEl&&(this.wrapperEl.style[t]=e),this},getZoom:function(){return this.viewportTransform[0]},setViewportTransform:function(t){var e,i,r,n=this._activeObject;for(this.viewportTransform=t,i=0,r=this._objects.length;i\n'),this._setSVGBgOverlayColor(i,"background"),this._setSVGBgOverlayImage(i,"backgroundImage",e),this._setSVGObjects(i,e),this.clipPath&&i.push("\n"),this._setSVGBgOverlayColor(i,"overlay"),this._setSVGBgOverlayImage(i,"overlayImage",e),i.push(""),i.join("")},_setSVGPreamble:function(t,e){e.suppressPreamble||t.push('\n','\n')},_setSVGHeader:function(t,e){var i,r=e.width||this.width,n=e.height||this.height,s='viewBox="0 0 '+this.width+" "+this.height+'" ',o=fabric.Object.NUM_FRACTION_DIGITS;e.viewBox?s='viewBox="'+e.viewBox.x+" "+e.viewBox.y+" "+e.viewBox.width+" "+e.viewBox.height+'" ':this.svgViewportTransformation&&(i=this.viewportTransform,s='viewBox="'+a(-i[4]/i[0],o)+" "+a(-i[5]/i[3],o)+" "+a(this.width/i[0],o)+" "+a(this.height/i[3],o)+'" '),t.push(""),i.join("")},_setSVGPreamble:function(t,e){e.suppressPreamble||t.push('\n','\n')},_setSVGHeader:function(t,e){var i,r=e.width||this.width,n=e.height||this.height,s='viewBox="0 0 '+this.width+" "+this.height+'" ',o=fabric.Object.NUM_FRACTION_DIGITS;e.viewBox?s='viewBox="'+e.viewBox.x+" "+e.viewBox.y+" "+e.viewBox.width+" "+e.viewBox.height+'" ':this.svgViewportTransformation&&(i=this.viewportTransform,s='viewBox="'+a(-i[4]/i[0],o)+" "+a(-i[5]/i[3],o)+" "+a(this.width/i[0],o)+" "+a(this.height/i[3],o)+'" '),t.push("