diff --git a/CHANGELOG.md b/CHANGELOG.md
index 710e0dbf8e4..440219741eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
**Version 2.0.0**
-
+ - rc1
+ - Remove node specific code [#4470](https://github.com/kangax/fabric.js/pull/4470)
+ - Improved Canvas.dispose code to leak less memory [#4471](https://github.com/kangax/fabric.js/pull/4471)
+ - Remove extra padding of cache when upper limited [#4467](https://github.com/kangax/fabric.js/pull/4467)
+ - Solved 2 perfomances problems with textbox [#4466](https://github.com/kangax/fabric.js/pull/4466) [#4465](https://github.com/kangax/fabric.js/pull/4465)
+ - Added justify-left justify-right and justify-center [#4437](https://github.com/kangax/fabric.js/pull/4437)
+ - Fix Group fromObject and subTargetCheck [#4454](https://github.com/kangax/fabric.js/pull/4454)
+ - Fix regression on IMG from SVG [#4450](https://github.com/kangax/fabric.js/pull/4450)
+ - Remove cache dimensions together with canvas [#4453](https://github.com/kangax/fabric.js/pull/4453)
+ - Fixed some fuzzyness cases for cache [#4452](https://github.com/kangax/fabric.js/pull/4452)
+ - Fixed resize filter for webgl [#4426](https://github.com/kangax/fabric.js/pull/4426)
+ - Stop searching target during a mouse move with a transform [#4442](https://github.com/kangax/fabric.js/pull/4442)
+ - safeguard shaders for non supported precisions [#4433](https://github.com/kangax/fabric.js/pull/4433)
+ - fix insert and remove style for edge cases [#4420](https://github.com/kangax/fabric.js/pull/4420)
+ - Fix object.move when in active selection [#4394](https://github.com/kangax/fabric.js/pull/4394)
+ - Memoize calcTransformMatrix function [#4418](https://github.com/kangax/fabric.js/pull/4418)
+ - Make _set flag object as dirty just when a real change happen[#4415](https://github.com/kangax/fabric.js/pull/4415)
+ - Add browserShadowBlurConstant to adjust shadowBlur value [#4413](https://github.com/kangax/fabric.js/pull/4413)
+ - Fix set element not clearing the cacheTexture. [#4410](https://github.com/kangax/fabric.js/pull/4410)
+ - Multi selection key can be configured with an array of keys. [#4363](https://github.com/kangax/fabric.js/pull/4363)
+ - fix fast type in text loosing some style. [#4339](https://github.com/kangax/fabric.js/pull/4339)
+ - fixed division by zero with lockscaling flip.
+ - added paintFirst ( paint-order with svg support ) [#4303](https://github.com/kangax/fabric.js/pull/4303)
- beta7
- added a build flag for not attaching fabric to window [#4199](https://github.com/kangax/fabric.js/pull/4199)
- removed .active property from objects [#4200](https://github.com/kangax/fabric.js/pull/4200)
diff --git a/HEADER.js b/HEADER.js
index 190f52db48d..a69e069d997 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: '2.0.0-beta7' };
+var fabric = fabric || { version: '2.0.0-rc.1' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index a085dc719d0..e2fd0127281 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -25,7 +25,7 @@ Remove the template from below and provide thoughtful commentary *and code sampl
## Version
-2.0 beta
+2.0
## Test Case
http://jsfiddle.net/fabricjs/Da7SP/
diff --git a/dist/fabric.js b/dist/fabric.js
index bc1d60e1059..3c10a68bc92 100644
--- a/dist/fabric.js
+++ b/dist/fabric.js
@@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL exclude=gestures,accessors minifier=uglifyjs` */
/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
-var fabric = fabric || { version: '2.0.0-beta7' };
+var fabric = fabric || { version: '2.0.0-rc.1' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -18,9 +18,9 @@ else {
{ features: {
FetchExternalResources: ['img']
}
- });
-
+ });
fabric.window = fabric.document.defaultView;
+ DOMParser = require('xmldom').DOMParser;
}
/**
@@ -49,7 +49,7 @@ fabric.SHARED_ATTRIBUTES = [
"stroke", "stroke-dasharray", "stroke-linecap",
"stroke-linejoin", "stroke-miterlimit",
"stroke-opacity", "stroke-width",
- "id",
+ "id", "paint-order",
"instantiated_by_use"
];
/* _FROM_SVG_END_ */
@@ -119,6 +119,21 @@ fabric.devicePixelRatio = fabric.window.devicePixelRatio ||
fabric.window.webkitDevicePixelRatio ||
fabric.window.mozDevicePixelRatio ||
1;
+/**
+ * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value,
+ * which is unitless and not rendered equally across browsers.
+ *
+ * Values that work quite well (as of October 2017) are:
+ * - Chrome: 1.5
+ * - Edge: 1.75
+ * - Firefox: 0.9
+ * - Safari: 0.95
+ *
+ * @since 2.0.0
+ * @type Number
+ * @default 1
+ */
+fabric.browserShadowBlurConstant = 1;
fabric.initFilterBackend = function() {
if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) {
@@ -673,11 +688,11 @@ fabric.CommonMethods = {
var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
minX = fabric.util.array.min(xPoints),
maxX = fabric.util.array.max(xPoints),
- width = Math.abs(minX - maxX),
+ width = maxX - minX,
yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
minY = fabric.util.array.min(yPoints),
maxY = fabric.util.array.max(yPoints),
- height = Math.abs(minY - maxY);
+ height = maxY - minY;
return {
left: minX,
@@ -841,11 +856,12 @@ fabric.CommonMethods = {
var img = fabric.util.createImage();
/** @ignore */
- img.onload = function () {
+ var onLoadCallback = function () {
callback && callback.call(context, img);
img = img.onload = img.onerror = null;
};
+ img.onload = onLoadCallback;
/** @ignore */
img.onerror = function() {
fabric.log('Error loading ' + img.src);
@@ -861,9 +877,43 @@ fabric.CommonMethods = {
img.crossOrigin = crossOrigin;
}
+ // IE10 / IE11-Fix: SVG contents from data: URI
+ // will only be available if the IMG is present
+ // in the DOM (and visible)
+ if (url.substring(0,14) === 'data:image/svg') {
+ img.onload = null;
+ fabric.util.loadImageInDom(img, onLoadCallback);
+ }
+
img.src = url;
},
+ /**
+ * Attaches SVG image with data: URL to the dom
+ * @memberOf fabric.util
+ * @param {Object} img Image object with data:image/svg src
+ * @param {Function} callback Callback; invoked with loaded image
+ * @return {Object} DOM element (div containing the SVG image)
+ */
+ loadImageInDom: function(img, onLoadCallback) {
+ var div = fabric.document.createElement('div');
+ div.style.width = div.style.height = '1px';
+ div.style.left = div.style.top = '-100%';
+ div.style.position = 'absolute';
+ div.appendChild(img);
+ fabric.document.querySelector('body').appendChild(div);
+ /**
+ * Wrap in function to:
+ * 1. Call existing callback
+ * 2. Cleanup DOM
+ */
+ img.onload = function () {
+ onLoadCallback();
+ div.parentNode.removeChild(div);
+ div = null;
+ };
+ },
+
/**
* Creates corresponding fabric instances from their object representations
* @static
@@ -3200,6 +3250,7 @@ if (typeof console !== 'undefined') {
'font-size': 'fontSize',
'font-style': 'fontStyle',
'font-weight': 'fontWeight',
+ 'paint-order': 'paintFirst',
'stroke-dasharray': 'strokeDashArray',
'stroke-linecap': 'strokeLineCap',
'stroke-linejoin': 'strokeLineJoin',
@@ -3274,6 +3325,17 @@ if (typeof console !== 'undefined') {
else if (attr === 'textAnchor' /* text-anchor */) {
value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
}
+ else if (attr === 'paintFirst') {
+ var fillIndex = value.indexOf('fill');
+ var strokeIndex = value.indexOf('stroke');
+ var value = 'fill';
+ if (fillIndex > -1 && strokeIndex > -1 && strokeIndex < fillIndex) {
+ value = 'stroke';
+ }
+ else if (fillIndex === -1 && strokeIndex > -1) {
+ value = 'stroke';
+ }
+ }
else {
parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
}
@@ -4216,12 +4278,13 @@ fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
fabric.ElementsParser.prototype.createCallback = function(index, el) {
var _this = this;
return function(obj) {
+ var _options;
_this.resolveGradient(obj, 'fill');
_this.resolveGradient(obj, 'stroke');
- obj._removeTransformMatrix();
if (obj instanceof fabric.Image) {
- obj.parsePreserveAspectRatioAttribute(el);
+ _options = obj.parsePreserveAspectRatioAttribute(el);
}
+ obj._removeTransformMatrix(_options);
_this.reviver && _this.reviver(el, obj);
_this.instances[index] = obj;
_this.checkIfDone();
@@ -6591,9 +6654,9 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
},
/**
- * Sets {@link fabric.StaticCanvas#overlayColor|background color} for this canvas
- * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set background color to
- * @param {Function} callback Callback to invoke when background color is set
+ * Sets {@link fabric.StaticCanvas#overlayColor|foreground color} for this canvas
+ * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set foreground color to
+ * @param {Function} callback Callback to invoke when foreground color is set
* @return {fabric.Canvas} thisArg
* @chainable
* @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
@@ -7097,6 +7160,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @chainable
*/
renderCanvas: function(ctx, objects) {
+ var v = this.viewportTransform;
if (this.isRendering) {
fabric.util.cancelAnimFrame(this.isRendering);
this.isRendering = 0;
@@ -7111,7 +7175,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
ctx.save();
//apply viewport transform once for all rendering process
- ctx.transform.apply(ctx, this.viewportTransform);
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
this._renderObjects(ctx, objects);
ctx.restore();
if (!this.controlsAboveOverlay && this.interactive) {
@@ -7145,7 +7209,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @param {string} property 'background' or 'overlay'
*/
_renderBackgroundOrOverlay: function(ctx, property) {
- var object = this[property + 'Color'];
+ var object = this[property + 'Color'], v;
if (object) {
ctx.fillStyle = object.toLive
? object.toLive(ctx, this)
@@ -7160,8 +7224,9 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
object = this[property + 'Image'];
if (object) {
if (this[property + 'Vpt']) {
+ v = this.viewportTransform;
ctx.save();
- ctx.transform.apply(ctx, this.viewportTransform);
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
}
object.render(ctx);
this[property + 'Vpt'] && ctx.restore();
@@ -7880,7 +7945,12 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @chainable
*/
dispose: function () {
- this.clear();
+ this._objects.length = 0;
+ this.backgroundImage = null;
+ this.overlayImage = null;
+ this._iTextInstances = null;
+ this.lowerCanvasEl = null;
+ this.cacheCanvasEl = null;
return this;
},
@@ -8011,12 +8081,19 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
strokeLineCap: 'round',
/**
- * Corner style of a brush (one of "bevil", "round", "miter")
+ * Corner style of a brush (one of "bevel", "round", "miter")
* @type String
* @default
*/
strokeLineJoin: 'round',
+ /**
+ * Maximum miter length (used for strokeLineJoin = "miter") of a brush's
+ * @type Number
+ * @default
+ */
+ strokeMiterLimit: 10,
+
/**
* Stroke Dash Array.
* @type Array
@@ -8041,10 +8118,10 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
*/
_setBrushStyles: function() {
var ctx = this.canvas.contextTop;
-
ctx.strokeStyle = this.color;
ctx.lineWidth = this.width;
ctx.lineCap = this.strokeLineCap;
+ ctx.miterLimit = this.strokeMiterLimit;
ctx.lineJoin = this.strokeLineJoin;
if (this.strokeDashArray && fabric.StaticCanvas.supports('setLineDash')) {
ctx.setLineDash(this.strokeDashArray);
@@ -8151,6 +8228,9 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @param {fabric.Point} point Point to be added to points array
*/
_addPoint: function(point) {
+ if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) {
+ return;
+ }
this._points.push(point);
},
@@ -8187,7 +8267,6 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
ctx.beginPath();
-
//if we only have 2 points in the path and they are the same
//it means that the user only clicked the canvas without moving the mouse
//then we should be drawing a dot. A path isn't drawn between two identical dots
@@ -8227,9 +8306,13 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
var path = [], i, width = this.width / 1000,
p1 = new fabric.Point(points[0].x, points[0].y),
p2 = new fabric.Point(points[1].x, points[1].y),
- len = points.length;
+ len = points.length, multSignX, multSignY, manyPoints = len > 2;
- path.push('M ', p1.x - width, ' ', p1.y, ' ');
+ if (manyPoints) {
+ multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1;
+ multSignY = points[2].y < p2.y ? -1 : points[2].y === p2.y ? 0 : 1;
+ }
+ path.push('M ', p1.x - multSignX * width, ' ', p1.y - multSignY * width, ' ');
for (i = 1; i < len; i++) {
if (!p1.eq(p2)) {
var midPoint = p1.midPointFrom(p2);
@@ -8243,7 +8326,11 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
p2 = points[i + 1];
}
}
- path.push('L ', p1.x + width, ' ', p1.y, ' ');
+ if (manyPoints) {
+ multSignX = p1.x > points[i - 2].x ? 1 : p1.x === points[i - 2].x ? 0 : -1;
+ multSignY = p1.y > points[i - 2].y ? 1 : p1.y === points[i - 2].y ? 0 : -1;
+ }
+ path.push('L ', p1.x + multSignX * width, ' ', p1.y + multSignY * width);
return path;
},
@@ -8258,6 +8345,7 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
stroke: this.color,
strokeWidth: this.width,
strokeLineCap: this.strokeLineCap,
+ strokeMiterLimit: this.strokeMiterLimit,
strokeLineJoin: this.strokeLineJoin,
strokeDashArray: this.strokeDashArray,
});
@@ -8293,13 +8381,12 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
}
var path = this.createPath(pathData);
-
+ this.canvas.clearContext(this.canvas.contextTop);
this.canvas.add(path);
+ this.canvas.renderAll();
path.setCoords();
-
- this.canvas.clearContext(this.canvas.contextTop);
this._resetShadow();
- this.canvas.requestRenderAll();
+
// fire event 'path' created
this.canvas.fire('path:created', { path: path });
@@ -8716,11 +8803,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @see {@link fabric.Canvas#initialize} for constructor definition
*
* @fires object:added
+ * @fires object:removed
* @fires object:modified
* @fires object:rotating
* @fires object:scaling
* @fires object:moving
- * @fires object:selected
+ * @fires object:selected this event is deprecated. use selection:created
*
* @fires before:selection:cleared
* @fires selection:cleared
@@ -8825,12 +8913,13 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
selection: true,
/**
- * Indicates which key enable multiple click selection
+ * Indicates which key or keys enable multiple click selection
+ * Pass value as a string or array of strings
* values: 'altKey', 'shiftKey', 'ctrlKey'.
- * If `null` or 'none' or any other string that is not a modifier key
- * feature is disabled feature disabled.
+ * If `null` or empty or containing any other string that is not a modifier key
+ * feature is disabled.
* @since 1.6.2
- * @type String
+ * @type String|Array
* @default
*/
selectionKey: 'shiftKey',
@@ -9179,19 +9268,22 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
isTargetTransparent: function (target, x, y) {
var ctx = this.contextCache,
- originalColor = target.selectionBackgroundColor;
+ originalColor = target.selectionBackgroundColor, v = this.viewportTransform;
- target.hasBorders = target.transparentCorners = false;
target.selectionBackgroundColor = '';
+ this.clearContext(ctx);
+
ctx.save();
- ctx.transform.apply(ctx, this.viewportTransform);
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
target.render(ctx);
ctx.restore();
target === this._activeObject && target._renderControls(ctx, {
hasBorders: false,
transparentCorners: false
+ }, {
+ hasBorders: false,
});
target.selectionBackgroundColor = originalColor;
@@ -9199,11 +9291,27 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var isTransparent = fabric.util.isTransparent(
ctx, x, y, this.targetFindTolerance);
- this.clearContext(ctx);
-
return isTransparent;
},
+ /**
+ * takes an event and determins if selection key has been pressed
+ * @private
+ * @param {Event} e Event object
+ */
+ _isSelectionKeyPressed: function(e) {
+ var selectionKeyPressed = false;
+
+ if (Object.prototype.toString.call(this.selectionKey) === '[object Array]') {
+ selectionKeyPressed = !!this.selectionKey.find(function(key) { return e[key] === true; });
+ }
+ else {
+ selectionKeyPressed = e[this.selectionKey];
+ }
+
+ return selectionKeyPressed;
+ },
+
/**
* @private
* @param {Event} e Event object
@@ -9212,6 +9320,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
_shouldClearSelection: function (e, target) {
var activeObjects = this.getActiveObjects(),
activeObject = this._activeObject;
+
return (
!target
||
@@ -9220,7 +9329,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
activeObjects.length > 1 &&
activeObjects.indexOf(target) === -1 &&
activeObject !== target &&
- !e[this.selectionKey])
+ !this._isSelectionKeyPressed(e))
||
(target && !target.evented)
||
@@ -9578,8 +9687,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var dist = localMouse.y + localMouse.x,
lastDist = _dim.y * transform.original.scaleY / target.scaleY +
_dim.x * transform.original.scaleX / target.scaleX,
- scaled, signX = localMouse.x / Math.abs(localMouse.x),
- signY = localMouse.y / Math.abs(localMouse.y);
+ scaled, signX = localMouse.x < 0 ? -1 : 1,
+ signY = localMouse.y < 0 ? -1 : 1;
// We use transform.scaleX/Y instead of target.scaleX/Y
// because the object may have a min scale and we'll loose the proportions
@@ -9685,7 +9794,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
curAngle = atan2(y - t.top, x - t.left),
angle = radiansToDegrees(curAngle - lastAngle + t.theta),
- hasRoated = true;
+ hasRotated = true;
if (t.target.snapAngle > 0) {
var snapAngle = t.target.snapAngle,
@@ -9708,13 +9817,13 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
angle %= 360;
if (t.target.angle === angle) {
- hasRoated = false;
+ hasRotated = false;
}
else {
t.target.angle = angle;
}
- return hasRoated;
+ return hasRotated;
},
/**
@@ -10094,6 +10203,53 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.callSuper('_onObjectRemoved', obj);
},
+ /**
+ * @private
+ * Compares the old activeObject with the current one and fires correct events
+ * @param {fabric.Object} obj old activeObject
+ */
+ _fireSelectionEvents: function(oldObjects, e) {
+ var somethingChanged = false, objects = this.getActiveObjects(),
+ added = [], removed = [], opt = { e: e };
+ oldObjects.forEach(function(oldObject) {
+ if (objects.indexOf(oldObject) === -1) {
+ somethingChanged = true;
+ oldObject.fire('deselected', opt);
+ removed.push(oldObject);
+ }
+ });
+ objects.forEach(function(object) {
+ if (oldObjects.indexOf(object) === -1) {
+ somethingChanged = true;
+ object.fire('selected', opt);
+ added.push(object);
+ }
+ });
+ if (oldObjects.length > 0 && objects.length > 0) {
+ opt.selected = added;
+ opt.deselected = removed;
+ // added for backward compatibility
+ opt.updated = added[0] || removed[0];
+ opt.target = this._activeObject;
+ somethingChanged && this.fire('selection:updated', opt);
+ }
+ else if (objects.length > 0) {
+ // deprecated event
+ if (objects.length === 1) {
+ opt.target = added[0];
+ this.fire('object:selected', opt);
+ }
+ opt.selected = added;
+ // added for backward compatibility
+ opt.target = this._activeObject;
+ this.fire('selection:created', opt);
+ }
+ else if (oldObjects.length > 0) {
+ opt.deselected = removed;
+ this.fire('selection:cleared', opt);
+ }
+ },
+
/**
* Sets given object as the only active object on canvas
* @param {fabric.Object} object Object to set as an active one
@@ -10102,15 +10258,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @chainable
*/
setActiveObject: function (object, e) {
- var currentActiveObject = this._activeObject;
- if (object === currentActiveObject) {
- return this;
- }
- if (this._setActiveObject(object, e)) {
- currentActiveObject && currentActiveObject.fire('deselected', { e: e });
- this.fire('object:selected', { target: object, e: e });
- object.fire('selected', { e: e });
- }
+ var currentActives = this.getActiveObjects();
+ this._setActiveObject(object, e);
+ this._fireSelectionEvents(currentActives, e);
return this;
},
@@ -10159,14 +10309,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @chainable
*/
discardActiveObject: function (e) {
- var activeObject = this._activeObject;
- if (activeObject) {
- this.fire('before:selection:cleared', { target: activeObject, e: e });
- if (this._discardActiveObject(e)) {
- this.fire('selection:cleared', { e: e, target: activeObject });
- activeObject.fire('deselected', { e: e });
- }
+ var currentActives = this.getActiveObjects();
+ if (currentActives.length) {
+ this.fire('before:selection:cleared', { target: currentActives[0], e: e });
}
+ this._discardActiveObject(e);
+ this._fireSelectionEvents(currentActives, e);
return this;
},
@@ -10176,7 +10324,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @chainable
*/
dispose: function () {
- fabric.StaticCanvas.prototype.dispose.call(this);
var wrapper = this.wrapperEl;
this.removeListeners();
wrapper.removeChild(this.upperCanvasEl);
@@ -10186,6 +10333,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
wrapper.parentNode.replaceChild(this.lowerCanvasEl, this.wrapperEl);
}
delete this.wrapperEl;
+ fabric.StaticCanvas.prototype.dispose.call(this);
return this;
},
@@ -10949,7 +11097,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
else {
this._transformObject(e);
}
- this._handleEvent(e, 'move', target ? target : null);
+ this._handleEvent(e, 'move', this._currentTransform ? null : target);
},
/**
@@ -11202,7 +11350,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
_shouldGroup: function(e, target) {
var activeObject = this._activeObject;
- return activeObject && e[this.selectionKey] && target && target.selectable && this.selection &&
+
+ return activeObject && this._isSelectionKeyPressed(e) && target && target.selectable && this.selection &&
(activeObject !== target || activeObject.type === 'activeSelection');
},
@@ -11236,33 +11385,31 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @private
*/
_updateActiveSelection: function(target, e) {
- var activeSelection = this._activeObject;
+ var activeSelection = this._activeObject,
+ currentActiveObjects = activeSelection._objects.slice(0);
if (activeSelection.contains(target)) {
activeSelection.removeWithUpdate(target);
- target.fire('deselected', { e: e });
this._hoveredTarget = target;
if (activeSelection.size() === 1) {
// activate last remaining object
- this.setActiveObject(activeSelection.item(0), e);
- return;
+ this._setActiveObject(activeSelection.item(0), e);
}
}
else {
activeSelection.addWithUpdate(target);
- target.fire('selected', { e: e });
this._hoveredTarget = activeSelection;
}
- this.fire('selection:updated', { target: activeSelection, e: e, updated: target });
+ this._fireSelectionEvents(currentActiveObjects, e);
},
/**
* @private
*/
_createActiveSelection: function(target, e) {
- var group = this._createGroup(target);
- this.setActiveObject(group, e);
- target.fire('selected', { e: e });
- this.fire('selection:created', { target: group, e: e });
+ var currentActives = this.getActiveObjects(), group = this._createGroup(target);
+ this._hoveredTarget = group;
+ this._setActiveObject(group, e);
+ this._fireSelectionEvents(currentActives, e);
},
/**
@@ -11298,11 +11445,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
aGroup = new fabric.ActiveSelection(group.reverse(), {
canvas: this
});
- group.forEach(function(object) {
- object.fire('selected', { e: e });
- });
this.setActiveObject(aGroup, e);
- this.fire('selection:created', { target: aGroup, e: e });
}
},
@@ -12259,6 +12402,13 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
__corner: 0,
+ /**
+ * Determins if the fill or the stroke is drawn first (one of "fill" or "stroke")
+ * @type String
+ * @default
+ */
+ paintFirst: 'fill',
+
/**
* List of properties to consider when checking if state
* of an object is changed (fabric.Object#hasStateChanged)
@@ -12269,7 +12419,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' +
- 'skewX skewY fillRule'
+ 'skewX skewY fillRule paintFirst'
).split(' '),
/**
@@ -12277,7 +12427,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @type Array
*/
cacheProperties: (
- 'fill stroke strokeWidth strokeDashArray width height' +
+ 'fill stroke strokeWidth strokeDashArray width height paintFirst' +
' strokeLineCap strokeLineJoin strokeMiterLimit backgroundColor'
).split(' '),
@@ -12319,25 +12469,30 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_limitCacheSize: function(dims) {
var perfLimitSizeTotal = fabric.perfLimitSizeTotal,
- maximumSide = fabric.cacheSideLimit,
width = dims.width, height = dims.height,
- ar = width / height, limitedDims = fabric.util.limitDimsByArea(ar, perfLimitSizeTotal, maximumSide),
- capValue = fabric.util.capValue, max = fabric.maxCacheSideLimit, min = fabric.minCacheSideLimit,
+ max = fabric.maxCacheSideLimit, min = fabric.minCacheSideLimit;
+ if (width <= max && height <= max && width * height <= perfLimitSizeTotal) {
+ if (width < min) {
+ dims.width = min;
+ }
+ if (height < min) {
+ dims.height = min;
+ }
+ return dims;
+ }
+ var ar = width / height, limitedDims = fabric.util.limitDimsByArea(ar, perfLimitSizeTotal),
+ capValue = fabric.util.capValue,
x = capValue(min, limitedDims.x, max),
y = capValue(min, limitedDims.y, max);
if (width > x) {
dims.zoomX /= width / x;
dims.width = x;
- }
- else if (width < min) {
- dims.width = min;
+ dims.capped = true;
}
if (height > y) {
dims.zoomY /= height / y;
dims.height = y;
- }
- else if (height < min) {
- dims.height = min;
+ dims.capped = true;
}
return dims;
},
@@ -12346,6 +12501,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* Return the dimension and the zoom level needed to create a cache canvas
* big enough to host the object to be cached.
* @private
+ * @param {Object} dim.x width of object to be cached
+ * @param {Object} dim.y height of object to be cached
* @return {Object}.width width of canvas
* @return {Object}.height height of canvas
* @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
@@ -12354,17 +12511,21 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
_getCacheCanvasDimensions: function() {
var zoom = this.canvas && this.canvas.getZoom() || 1,
objectScale = this.getObjectScaling(),
- dim = this._getNonTransformedDimensions(),
retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1,
+ dim = this._getNonTransformedDimensions(),
zoomX = objectScale.scaleX * zoom * retina,
zoomY = objectScale.scaleY * zoom * retina,
width = dim.x * zoomX,
height = dim.y * zoomY;
return {
+ // for sure this ALIASING_LIMIT is slightly crating problem
+ // in situation in wich the cache canvas gets an upper limit
width: width + ALIASING_LIMIT,
height: height + ALIASING_LIMIT,
zoomX: zoomX,
- zoomY: zoomY
+ zoomY: zoomY,
+ x: dim.x,
+ y: dim.y
};
},
@@ -12376,14 +12537,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_updateCacheCanvas: function() {
if (this.noScaleCache && this.canvas && this.canvas._currentTransform) {
- var action = this.canvas._currentTransform.action;
- if (action.slice && action.slice(0, 5) === 'scale') {
+ var target = this.canvas._currentTransform.target,
+ action = this.canvas._currentTransform.action;
+ if (this === target && action.slice && action.slice(0, 5) === 'scale') {
return false;
}
}
- var dims = this._limitCacheSize(this._getCacheCanvasDimensions()),
+ var canvas = this._cacheCanvas,
+ dims = this._limitCacheSize(this._getCacheCanvasDimensions()),
minCacheSize = fabric.minCacheSideLimit,
- width = dims.width, height = dims.height,
+ width = dims.width, height = dims.height, drawingWidth, drawingHeight,
zoomX = dims.zoomX, zoomY = dims.zoomY,
dimensionsChanged = width !== this.cacheWidth || height !== this.cacheHeight,
zoomChanged = this.zoomX !== zoomX || this.zoomY !== zoomY,
@@ -12396,22 +12559,24 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
sizeShrinking = (width < canvasWidth * 0.9 || height < canvasHeight * 0.9) &&
canvasWidth > minCacheSize && canvasHeight > minCacheSize;
shouldResizeCanvas = sizeGrowing || sizeShrinking;
- if (sizeGrowing) {
- additionalWidth = (width * 0.1) & ~1;
- additionalHeight = (height * 0.1) & ~1;
+ if (sizeGrowing && !dims.capped && (width > minCacheSize || height > minCacheSize)) {
+ additionalWidth = width * 0.1;
+ additionalHeight = height * 0.1;
}
}
if (shouldRedraw) {
if (shouldResizeCanvas) {
- this._cacheCanvas.width = Math.max(Math.ceil(width) + additionalWidth, minCacheSize);
- this._cacheCanvas.height = Math.max(Math.ceil(height) + additionalHeight, minCacheSize);
- this.cacheTranslationX = (width + additionalWidth) / 2;
- this.cacheTranslationY = (height + additionalHeight) / 2;
+ canvas.width = Math.ceil(width + additionalWidth);
+ canvas.height = Math.ceil(height + additionalHeight);
}
else {
this._cacheContext.setTransform(1, 0, 0, 1, 0, 0);
- this._cacheContext.clearRect(0, 0, this._cacheCanvas.width, this._cacheCanvas.height);
+ this._cacheContext.clearRect(0, 0, canvas.width, canvas.height);
}
+ drawingWidth = dims.x * zoomX / 2;
+ drawingHeight = dims.y * zoomY / 2;
+ this.cacheTranslationX = Math.round(canvas.width / 2 - drawingWidth) + drawingWidth;
+ this.cacheTranslationY = Math.round(canvas.height / 2 - drawingHeight) + drawingHeight;
this.cacheWidth = width;
this.cacheHeight = height;
this._cacheContext.translate(this.cacheTranslationX, this.cacheTranslationY);
@@ -12439,21 +12604,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Transforms context when rendering an object
* @param {CanvasRenderingContext2D} ctx Context
- * @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node
*/
- transform: function(ctx, fromLeft) {
+ transform: function(ctx) {
+ var m;
if (this.group && !this.group._transformDone) {
- this.group.transform(ctx);
+ m = this.calcTransformMatrix();
}
- var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint();
- ctx.translate(center.x, center.y);
- this.angle && ctx.rotate(degreesToRadians(this.angle));
- ctx.scale(
- this.scaleX * (this.flipX ? -1 : 1),
- this.scaleY * (this.flipY ? -1 : 1)
- );
- this.skewX && ctx.transform(1, 0, Math.tan(degreesToRadians(this.skewX)), 1, 0, 0);
- this.skewY && ctx.transform(1, Math.tan(degreesToRadians(this.skewY)), 0, 1, 0, 0);
+ else {
+ m = this.calcOwnMatrix();
+ }
+ ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
},
/**
@@ -12491,6 +12651,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
clipTo: this.clipTo && String(this.clipTo),
backgroundColor: this.backgroundColor,
fillRule: this.fillRule,
+ paintFirst: this.paintFirst,
globalCompositeOperation: this.globalCompositeOperation,
transformMatrix: this.transformMatrix ? this.transformMatrix.concat() : null,
skewX: toFixed(this.skewX, NUM_FRACTION_DIGITS),
@@ -12579,7 +12740,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {fabric.Object} thisArg
*/
_set: function(key, value) {
- var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY');
+ var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY'),
+ isChanged = this[key] !== value, groupNeedsUpdate = false;
if (shouldConstrainValue) {
value = this._constrainScale(value);
@@ -12601,15 +12763,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this[key] = value;
- if (this.cacheProperties.indexOf(key) > -1) {
- if (this.group) {
+ if (isChanged) {
+ groupNeedsUpdate = this.group && this.group.isOnACache();
+ if (this.cacheProperties.indexOf(key) > -1) {
+ this.dirty = true;
+ groupNeedsUpdate && this.group.set('dirty', true);
+ }
+ else if (groupNeedsUpdate && this.stateProperties.indexOf(key) > -1) {
this.group.set('dirty', true);
}
- this.dirty = true;
- }
-
- if (this.group && this.stateProperties.indexOf(key) > -1 && this.group.isOnACache()) {
- this.group.set('dirty', true);
}
return this;
@@ -12682,6 +12844,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this.drawCacheOnCanvas(ctx);
}
else {
+ this._removeCacheCanvas();
this.dirty = false;
this.drawObject(ctx);
if (this.objectCaching && this.statefullCache) {
@@ -12692,6 +12855,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
ctx.restore();
},
+ /**
+ * Remove cacheCanvas and its dimensions from the objects
+ */
+ _removeCacheCanvas: function() {
+ this._cacheCanvas = null;
+ this.cacheWidth = 0;
+ this.cacheHeight = 0;
+ },
+
/**
* When set to `true`, force the object to have its own cache, even if it is inside a group
* it may be needed when your object behave in a particular way on the cache and always needs
@@ -12701,6 +12873,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @returns false
*/
needsItsOwnCache: function() {
+ if (this.paintFirst === 'stroke' && typeof this.shadow === 'object') {
+ return true;
+ }
return false;
},
@@ -12901,7 +13076,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
multY *= fabric.devicePixelRatio;
}
ctx.shadowColor = this.shadow.color;
- ctx.shadowBlur = this.shadow.blur * (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
+ ctx.shadowBlur = this.shadow.blur * fabric.browserShadowBlurConstant *
+ (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
ctx.shadowOffsetX = this.shadow.offsetX * multX * scaling.scaleX;
ctx.shadowOffsetY = this.shadow.offsetY * multY * scaling.scaleY;
},
@@ -12928,16 +13104,31 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
if (!filler || !filler.toLive) {
return { offsetX: 0, offsetY: 0 };
}
- var transform = filler.gradientTransform || filler.patternTransform;
+ var t = filler.gradientTransform || filler.patternTransform;
var offsetX = -this.width / 2 + filler.offsetX || 0,
offsetY = -this.height / 2 + filler.offsetY || 0;
ctx.translate(offsetX, offsetY);
- if (transform) {
- ctx.transform.apply(ctx, transform);
+ if (t) {
+ ctx.transform(t[0], t[1], t[2], t[3], t[4], t[5]);
}
return { offsetX: offsetX, offsetY: offsetY };
},
+ /**
+ * @private
+ * @param {CanvasRenderingContext2D} ctx Context to render on
+ */
+ _renderPaintInOrder: function(ctx) {
+ if (this.paintFirst === 'stroke') {
+ this._renderStroke(ctx);
+ this._renderFill(ctx);
+ }
+ else {
+ this._renderFill(ctx);
+ this._renderStroke(ctx);
+ }
+ },
+
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@@ -13008,16 +13199,26 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* This function is an helper for svg import. it removes the transform matrix
* and set to object properties that fabricjs can handle
* @private
- * @chainable
+ * @param {Object} preserveAspectRatioOptions
* @return {thisArg}
*/
- _removeTransformMatrix: function() {
+ _removeTransformMatrix: function(preserveAspectRatioOptions) {
var center = this._findCenterFromElement();
if (this.transformMatrix) {
this._assignTransformMatrixProps();
center = fabric.util.transformPoint(center, this.transformMatrix);
}
this.transformMatrix = null;
+ if (preserveAspectRatioOptions) {
+ this.scaleX *= preserveAspectRatioOptions.scaleX;
+ this.scaleY *= preserveAspectRatioOptions.scaleY;
+ this.cropX = preserveAspectRatioOptions.cropX;
+ this.cropY = preserveAspectRatioOptions.cropY;
+ center.x += preserveAspectRatioOptions.offsetLeft;
+ center.y += preserveAspectRatioOptions.offsetTop;
+ this.width = preserveAspectRatioOptions.width;
+ this.height = preserveAspectRatioOptions.height;
+ }
this.setPositionByOrigin(center, 'center', 'center');
},
@@ -13732,6 +13933,16 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
aCoords: null,
+ /**
+ * storage for object transform matrix
+ */
+ ownMatrixCache: null,
+
+ /**
+ * storage for object full transform matrix
+ */
+ matrixCache: null,
+
/**
* return correct set of coordinates for intersection
*/
@@ -13942,24 +14153,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return xcount;
},
- /**
- * Returns width of an object's bounding rectangle
- * @deprecated since 1.0.4
- * @return {Number} width value
- */
- getBoundingRectWidth: function() {
- return this.getBoundingRect().width;
- },
-
- /**
- * Returns height of an object's bounding rectangle
- * @deprecated since 1.0.4
- * @return {Number} height value
- */
- getBoundingRectHeight: function() {
- return this.getBoundingRect().height;
- },
-
/**
* Returns coordinates of object's bounding rectangle (left, top, width, height)
* the box is intented as aligned to axis of canvas.
@@ -14024,24 +14217,26 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
* @param {Number} value New width value
+ * @param {Boolean} absolute ignore viewport
* @return {fabric.Object} thisArg
* @chainable
*/
- scaleToWidth: function(value) {
+ scaleToWidth: function(value, absolute) {
// adjust to bounding rect factor so that rotated shapes would fit as well
- var boundingRectFactor = this.getBoundingRect().width / this.getScaledWidth();
+ var boundingRectFactor = this.getBoundingRect(absolute).width / this.getScaledWidth();
return this.scale(value / this.width / boundingRectFactor);
},
/**
* Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
* @param {Number} value New height value
+ * @param {Boolean} absolute ignore viewport
* @return {fabric.Object} thisArg
* @chainable
*/
- scaleToHeight: function(value) {
+ scaleToHeight: function(value, absolute) {
// adjust to bounding rect factor so that rotated shapes would fit as well
- var boundingRectFactor = this.getBoundingRect().height / this.getScaledHeight();
+ var boundingRectFactor = this.getBoundingRect(absolute).height / this.getScaledHeight();
return this.scale(value / this.height / boundingRectFactor);
},
@@ -14055,8 +14250,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
vpt = this.getViewportTransform(),
dim = absolute ? this._getTransformedDimensions() : this._calculateCurrentDimensions(),
currentWidth = dim.x, currentHeight = dim.y,
- sinTh = Math.sin(theta),
- cosTh = Math.cos(theta),
+ sinTh = theta ? Math.sin(theta) : 0,
+ cosTh = theta ? Math.cos(theta) : 1,
_angle = currentWidth > 0 ? Math.atan(currentHeight / currentWidth) : 0,
_hypotenuse = (currentWidth / Math.cos(_angle)) / 2,
offsetX = Math.cos(_angle + theta) * _hypotenuse,
@@ -14143,6 +14338,15 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return fabric.iMatrix.concat();
},
+ transformMatrixKey: function(skipGroup) {
+ var sep = '_', prefix = '';
+ if (!skipGroup && this.group) {
+ prefix = this.group.transformMatrixKey(skipGroup) + sep;
+ };
+ return prefix + this.top + sep + this.left + sep + this.scaleX + sep + this.scaleY +
+ sep + this.skewX + sep + this.skewY + sep + this.angle + sep + this.flipX + sep + this.flipY;
+ },
+
/**
* calculate trasform Matrix that represent current transformation from
* object properties.
@@ -14150,22 +14354,38 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @return {Array} matrix Transform Matrix for the object
*/
calcTransformMatrix: function(skipGroup) {
- var center = this.getCenterPoint(),
- translateMatrix = [1, 0, 0, 1, center.x, center.y],
- rotateMatrix,
- dimensionMatrix = this._calcDimensionsTransformMatrix(this.skewX, this.skewY, true),
- matrix;
- if (this.group && !skipGroup) {
- matrix = multiplyMatrices(this.group.calcTransformMatrix(), translateMatrix);
+ if (skipGroup) {
+ return this.calcOwnMatrix();
}
- else {
- matrix = translateMatrix;
+ var key = this.transformMatrixKey(), cache = this.matrixCache || (this.matrixCache = {});
+ if (cache.key === key) {
+ return cache.value;
+ }
+ var matrix = this.calcOwnMatrix();
+ if (this.group) {
+ matrix = multiplyMatrices(this.group.calcTransformMatrix(), matrix);
+ }
+ cache.key = key;
+ cache.value = matrix;
+ return matrix;
+ },
+
+ calcOwnMatrix: function() {
+ var key = this.transformMatrixKey(true), cache = this.ownMatrixCache || (this.ownMatrixCache = {});
+ if (cache.key === key) {
+ return cache.value;
}
+ var center = this.getCenterPoint(),
+ matrix = [1, 0, 0, 1, center.x, center.y],
+ rotateMatrix,
+ dimensionMatrix = this._calcDimensionsTransformMatrix(this.skewX, this.skewY, true);
if (this.angle) {
rotateMatrix = this._calcRotateMatrix();
matrix = multiplyMatrices(matrix, rotateMatrix);
}
matrix = multiplyMatrices(matrix, dimensionMatrix);
+ cache.key = key;
+ cache.value = matrix;
return matrix;
},
@@ -14326,7 +14546,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @chainable
*/
moveTo: function(index) {
- if (this.group) {
+ if (this.group && this.group.type !== 'activeSelection') {
fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index);
}
else {
@@ -14404,15 +14624,18 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgSpanStyles: function(style, useWhiteSpace) {
- var strokeWidth = style.strokeWidth ? 'stroke-width: ' + style.strokeWidth + '; ' : '',
- fontFamily = style.fontFamily ? 'font-family: ' + style.fontFamily.replace(/"/g, '\'') + '; ' : '',
- fontSize = style.fontSize ? 'font-size: ' + style.fontSize + '; ' : '',
- fontStyle = style.fontStyle ? 'font-style: ' + style.fontStyle + '; ' : '',
- fontWeight = style.fontWeight ? 'font-weight: ' + style.fontWeight + '; ' : '',
+ var term = '; ',
+ strokeWidth = style.strokeWidth ? 'stroke-width: ' + style.strokeWidth + term : '',
+ fontFamily = style.fontFamily ? 'font-family: ' + style.fontFamily.replace(/"/g, '\'') + term : '',
+ fontSize = style.fontSize ? 'font-size: ' + style.fontSize + term : '',
+ fontStyle = style.fontStyle ? 'font-style: ' + style.fontStyle + term : '',
+ fontWeight = style.fontWeight ? 'font-weight: ' + style.fontWeight + term : '',
fill = style.fill ? getSvgColorString('fill', style.fill) : '',
stroke = style.stroke ? getSvgColorString('stroke', style.stroke) : '',
textDecoration = this.getSvgTextDecoration(style);
-
+ if (textDecoration) {
+ textDecoration = 'text-decoration: ' + textDecoration + term;
+ }
return [
stroke,
strokeWidth,
@@ -14428,8 +14651,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
getSvgTextDecoration: function(style) {
if ('overline' in style || 'underline' in style || 'linethrough' in style) {
- return 'text-decoration: ' + (style.overline ? 'overline ' : '') +
- (style.underline ? 'underline ' : '') + (style.linethrough ? 'line-through ' : '') + ';';
+ return (style.overline ? 'overline ' : '') +
+ (style.underline ? 'underline ' : '') + (style.linethrough ? 'line-through ' : '');
}
return '';
},
@@ -14534,6 +14757,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
markup.push(this.shadow.toSVG(this));
}
return markup;
+ },
+
+ addPaintOrder: function() {
+ return this.paintFirst !== 'fill' ? ' paint-order="' + this.paintFirst + '" ' : '';
}
});
})();
@@ -15301,14 +15528,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
- var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
- cacheProperties.push(
- 'x1',
- 'x2',
- 'y1',
- 'y2'
- );
-
/**
* Line class
* @class fabric.Line
@@ -15352,7 +15571,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
y2: 0,
- cacheProperties: cacheProperties,
+ cacheProperties: fabric.Object.prototype.cacheProperties.concat('x1', 'x2', 'y1', 'y2'),
/**
* Constructor
@@ -15655,11 +15874,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
- var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
- cacheProperties.push(
- 'radius'
- );
-
/**
* Circle class
* @class fabric.Circle
@@ -15696,7 +15910,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
endAngle: pi * 2,
- cacheProperties: cacheProperties,
+ cacheProperties: fabric.Object.prototype.cacheProperties.concat('radius'),
/**
* Constructor
@@ -15750,8 +15964,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'r="', this.radius,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
- ' ', this.getSvgTransformMatrix(),
- '"/>\n'
+ ' ', this.getSvgTransformMatrix(), '"',
+ this.addPaintOrder(),
+ '/>\n'
);
}
else {
@@ -15767,7 +15982,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
' 0 ', +largeFlag + ' 1', ' ' + endX + ' ' + endY,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
- ' ', this.getSvgTransformMatrix(),
+ ' ', this.getSvgTransformMatrix(), '"',
+ this.addPaintOrder(),
'"/>\n'
);
}
@@ -15782,13 +15998,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
_render: function(ctx) {
ctx.beginPath();
- ctx.arc(0,
+ ctx.arc(
+ 0,
0,
this.radius,
this.startAngle,
this.endAngle, false);
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
/**
@@ -15924,8 +16140,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.lineTo(widthBy2, heightBy2);
ctx.closePath();
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
/**
@@ -15964,8 +16179,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
''
+ '" transform="', this.getSvgTransform(), '"',
+ this.addPaintOrder(),
+ '/>'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@@ -16000,12 +16216,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
- var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
- cacheProperties.push(
- 'rx',
- 'ry'
- );
-
/**
* Ellipse class
* @class fabric.Ellipse
@@ -16036,7 +16246,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
ry: 0,
- cacheProperties: cacheProperties,
+ cacheProperties: fabric.Object.prototype.cacheProperties.concat('rx', 'ry'),
/**
* Constructor
@@ -16113,8 +16323,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'" ry="', this.ry,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
- this.getSvgTransformMatrix(),
- '"/>\n'
+ this.getSvgTransformMatrix(), '"',
+ this.addPaintOrder(),
+ '/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@@ -16137,8 +16348,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
piBy2,
false);
ctx.restore();
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
});
@@ -16198,12 +16408,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
- var stateProperties = fabric.Object.prototype.stateProperties.concat();
- stateProperties.push('rx', 'ry');
-
- var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
- cacheProperties.push('rx', 'ry');
-
/**
* Rectangle class
* @class fabric.Rect
@@ -16218,7 +16422,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* as well as for history (undo/redo) purposes
* @type Array
*/
- stateProperties: stateProperties,
+ stateProperties: fabric.Object.prototype.stateProperties.concat('rx', 'ry'),
/**
* Type of an object
@@ -16241,7 +16445,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
ry: 0,
- cacheProperties: cacheProperties,
+ cacheProperties: fabric.Object.prototype.cacheProperties.concat('rx', 'ry'),
/**
* Constructor
@@ -16305,8 +16509,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
ctx.closePath();
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
/**
@@ -16351,8 +16554,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
- this.getSvgTransformMatrix(),
- '"/>\n');
+ this.getSvgTransformMatrix(), '"',
+ this.addPaintOrder(),
+ '/>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@@ -16422,9 +16626,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
- var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
- cacheProperties.push('points');
-
/**
* Polyline class
* @class fabric.Polyline
@@ -16447,7 +16648,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
points: null,
- cacheProperties: cacheProperties,
+ cacheProperties: fabric.Object.prototype.cacheProperties.concat('points'),
/**
* Constructor
@@ -16547,8 +16748,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
- ' ', this.getSvgTransformMatrix(),
- '"/>\n'
+ ' ', this.getSvgTransformMatrix(), '"',
+ this.addPaintOrder(),
+ '/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@@ -16587,8 +16789,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (!this.commonRender(ctx)) {
return;
}
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
/**
@@ -16695,8 +16896,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
ctx.closePath();
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
/**
@@ -16785,12 +16985,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
return;
}
- var stateProperties = fabric.Object.prototype.stateProperties.concat();
- stateProperties.push('path');
-
- var cacheProperties = fabric.Object.prototype.cacheProperties.concat();
- cacheProperties.push('path', 'fillRule');
-
/**
* Path class
* @class fabric.Path
@@ -16814,9 +17008,9 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
path: null,
- cacheProperties: cacheProperties,
+ cacheProperties: fabric.Object.prototype.cacheProperties.concat('path', 'fillRule'),
- stateProperties: stateProperties,
+ stateProperties: fabric.Object.prototype.stateProperties.concat('path'),
/**
* Constructor
@@ -17186,8 +17380,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
_render: function(ctx) {
this._renderPathCommands(ctx);
- this._renderFill(ctx);
- this._renderStroke(ctx);
+ this._renderPaintInOrder(ctx);
},
/**
@@ -17247,6 +17440,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(), addTransform,
this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
+ this.addPaintOrder(),
'/>\n'
);
@@ -17779,10 +17973,24 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
delete options.centerPoint;
this.callSuper('initialize', options);
}
+ else {
+ this._updateObjectsACoords();
+ }
this.setCoords();
},
+ /**
+ * @private
+ * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change
+ */
+ _updateObjectsACoords: function() {
+ var ignoreZoom = true, skipAbsolute = true;
+ for (var i = this._objects.length; i--; ){
+ this._objects[i].setCoords(ignoreZoom, skipAbsolute);
+ }
+ },
+
/**
* @private
* @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change
@@ -18601,6 +18809,11 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @chainable
*/
setElement: function(element, options) {
+ var backend = fabric.filterBackend;
+ if (backend && backend.evictCachesForKey) {
+ backend.evictCachesForKey(this.cacheKey);
+ backend.evictCachesForKey(this.cacheKey + '_filtered');
+ }
this._element = element;
this._originalElement = element;
this._initConfig(options);
@@ -18701,9 +18914,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
if (this.resizeFilter) {
object.resizeFilter = this.resizeFilter.toObject();
}
- object.width /= this._filterScalingX;
- object.height /= this._filterScalingY;
-
return object;
},
@@ -18715,9 +18925,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2;
- markup.push(
- '\n',
- '\t\n');
+ var imageMarkup = ['\t\n'
- );
-
+ '">\n'];
+ if (this.paintFirst === 'fill') {
+ Array.prototype.push.apply(markup, imageMarkup);
+ }
if (this.stroke || this.strokeDashArray) {
var origFill = this.fill;
this.fill = null;
markup.push(
- ' -1) {
+ set: function(key, value) {
+ this.callSuper('set', key, value);
+ var needsDims = false;
+ if (typeof key === 'object') {
+ for (var _key in key) {
+ needsDims = needsDims || this._dimensionAffectingProps.indexOf(_key) !== -1;
+ }
+ }
+ else {
+ needsDims = this._dimensionAffectingProps.indexOf(key) !== -1;
+ }
+ if (needsDims) {
this.initDimensions();
this.setCoords();
}
+ return this;
},
/**
@@ -24929,7 +25358,6 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
*/
_render: function(ctx) {
this.callSuper('_render', ctx);
- this.ctx = ctx;
},
/**
@@ -24940,9 +25368,9 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
return;
}
if (this.canvas && this.canvas.contextTop) {
- var ctx = this.canvas.contextTop;
+ var ctx = this.canvas.contextTop, v = this.canvas.viewportTransform;
ctx.save();
- ctx.transform.apply(ctx, this.canvas.viewportTransform);
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
this.transform(ctx);
this.transformMatrix && ctx.transform.apply(ctx, this.transformMatrix);
this._clearTextArea(ctx);
@@ -24954,7 +25382,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* Renders cursor or selection (depending on what exists)
*/
renderCursorOrSelection: function() {
- if (!this.isEditing) {
+ if (!this.isEditing || !this.canvas) {
return;
}
var boundaries = this._getCursorBoundaries(), ctx;
@@ -24963,7 +25391,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
this.clearContextTop(true);
}
else {
- ctx = this.ctx;
+ ctx = this.canvas.contextContainer;
ctx.save();
}
if (this.selectionStart === this.selectionEnd) {
@@ -25079,6 +25507,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
var selectionStart = this.inCompositionMode ? this.hiddenTextarea.selectionStart : this.selectionStart,
selectionEnd = this.inCompositionMode ? this.hiddenTextarea.selectionEnd : this.selectionEnd,
+ isJustify = this.textAlign.indexOf('justify') !== -1,
start = this.get2DCursorLocation(selectionStart),
end = this.get2DCursorLocation(selectionEnd),
startLine = start.lineIndex,
@@ -25095,7 +25524,7 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
boxStart = this.__charBounds[startLine][startChar].left;
}
if (i >= startLine && i < endLine) {
- boxEnd = this.getLineWidth(i) || 5; // WTF is this 5?
+ boxEnd = isJustify && !this.isEndOfWrapping(i) ? this.width : this.getLineWidth(i) || 5; // WTF is this 5?
}
else if (i === endLine) {
if (endChar === 0) {
@@ -25338,7 +25767,8 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
* Aborts cursor animation and clears all timeouts
*/
abortCursorAnimation: function() {
- var shouldClear = this._currentTickState || this._currentTickCompleteState;
+ var shouldClear = this._currentTickState || this._currentTickCompleteState,
+ canvas = this.canvas;
this._currentTickState && this._currentTickState.abort();
this._currentTickCompleteState && this._currentTickCompleteState.abort();
@@ -25348,8 +25778,8 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
this._currentCursorOpacity = 0;
// to clear just itext area we need to transform the context
// it may not be worth it
- if (shouldClear) {
- this.canvas && this.canvas.clearContext(this.canvas.contextTop || this.ctx);
+ if (shouldClear && canvas) {
+ canvas.clearContext(canvas.contextTop || canvas.contextContainer);
}
},
@@ -25665,6 +26095,10 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
}
this.cursorOffsetCache = { };
this.text = this.hiddenTextarea.value;
+ if (this._shouldClearDimensionCache()) {
+ this.initDimensions();
+ this.setCoords();
+ }
var newSelection = this.fromStringToGraphemeSelection(
this.hiddenTextarea.selectionStart, this.hiddenTextarea.selectionEnd, this.hiddenTextarea.value);
this.selectionEnd = this.selectionStart = newSelection.selectionEnd;
@@ -25788,7 +26222,10 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
this.abortCursorAnimation();
this._restoreEditingProps();
this._currentCursorOpacity = 0;
-
+ if (this._shouldClearDimensionCache()) {
+ this.initDimensions();
+ this.setCoords();
+ }
this.fire('editing:exited');
isTextChanged && this.fire('modified');
if (this.canvas) {
@@ -26019,10 +26456,14 @@ fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
}
linesLenght && this.insertNewlineStyleObject(
cursorLoc.lineIndex, cursorLoc.charIndex + addedLines[0], linesLenght);
- for (var i = 1; i <= linesLenght; i++) {
+ for (var i = 1; i < linesLenght; i++) {
this.insertCharStyleObject(cursorLoc.lineIndex + i, 0, addedLines[i], copiedStyle);
copiedStyle = copiedStyle && copiedStyle.slice(addedLines[i] + 1);
}
+ // we use i outside the loop to get it like linesLength
+ if (addedLines[i] > 0) {
+ this.insertCharStyleObject(cursorLoc.lineIndex + i, 0, addedLines[i], copiedStyle);
+ }
},
/**
@@ -26445,7 +26886,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
nextCharCount = nextText.length,
removedText, insertedText,
charDiff = nextCharCount - charCount;
-
if (this.hiddenTextarea.value === '') {
this.styles = { };
this.updateFromTextArea();
@@ -26454,26 +26894,34 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.canvas.fire('text:changed', { target: this });
this.canvas.requestRenderAll();
}
+ return;
}
+ var textareaSelection = this.fromStringToGraphemeSelection(
+ this.hiddenTextarea.selectionStart,
+ this.hiddenTextarea.selectionEnd,
+ this.hiddenTextarea.value
+ );
+ var backDelete = this.selectionStart > textareaSelection.selectionStart;
+
if (this.selectionStart !== this.selectionEnd) {
removedText = this._text.slice(this.selectionStart, this.selectionEnd);
charDiff += this.selectionEnd - this.selectionStart;
}
else if (nextCharCount < charCount) {
- removedText = this._text.slice(this.selectionEnd + charDiff, this.selectionEnd);
+ if (backDelete) {
+ removedText = this._text.slice(this.selectionEnd + charDiff, this.selectionEnd);
+ }
+ else {
+ removedText = this._text.slice(this.selectionStart, this.selectionStart - charDiff);
+ }
}
- var textareaSelection = this.fromStringToGraphemeSelection(
- this.hiddenTextarea.selectionStart,
- this.hiddenTextarea.selectionEnd,
- this.hiddenTextarea.value
- );
insertedText = nextText.slice(textareaSelection.selectionEnd - charDiff, textareaSelection.selectionEnd);
if (removedText && removedText.length) {
if (this.selectionStart !== this.selectionEnd) {
this.removeStyleFromTo(this.selectionStart, this.selectionEnd);
}
- else if (this.selectionStart > textareaSelection.selectionStart) {
+ else if (backDelete) {
// detect differencies between forwardDelete and backDelete
this.removeStyleFromTo(this.selectionEnd - removedText.length, this.selectionEnd);
}
@@ -26899,10 +27347,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.setSelectionEnd(this.selectionStart);
this._removeExtraneousStyles();
-
+ if (this._shouldClearDimensionCache()) {
+ this.initDimensions();
+ this.setCoords();
+ }
this.canvas && this.canvas.requestRenderAll();
-
- this.setCoords();
this.fire('changed');
this.canvas && this.canvas.fire('text:changed', { target: this });
},
@@ -26974,8 +27423,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_wrapSVGTextAndBg: function(markup, textAndBg) {
var noShadow = true, filter = this.getSvgFilter(),
- style = filter === '' ? '' : ' style="' + filter + '"';
-
+ style = filter === '' ? '' : ' style="' + filter + '"',
+ textDecoration = this.getSvgTextDecoration(this);
markup.push(
'\t\n',
@@ -26985,8 +27434,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
(this.fontSize ? 'font-size="' + this.fontSize + '" ' : ''),
(this.fontStyle ? 'font-style="' + this.fontStyle + '" ' : ''),
(this.fontWeight ? 'font-weight="' + this.fontWeight + '" ' : ''),
- (this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ' : ''),
- 'style="', this.getSvgStyles(noShadow), '" >\n',
+ (textDecoration ? 'text-decoration="' + textDecoration + '" ' : ''),
+ 'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >\n',
textAndBg.textSpans.join(''),
'\t\t\n',
'\t\n'
@@ -27041,6 +27490,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
_setSVGTextLineText: function(textSpans, lineIndex, textLeftOffset, textTopOffset) {
// set proper line offset
var lineHeight = this.getHeightOfLine(lineIndex),
+ isJustify = this.textAlign.indexOf('justify') !== -1,
actualStyle,
nextStyle,
charsToRender = '',
@@ -27056,9 +27506,12 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
charBox = this.__charBounds[lineIndex][i];
if (boxWidth === 0) {
textLeftOffset += charBox.kernedWidth - charBox.width;
+ boxWidth += charBox.width;
+ }
+ else {
+ boxWidth += charBox.kernedWidth;
}
- boxWidth += charBox.kernedWidth;
- if (this.textAlign === 'justify' && !timeToRender) {
+ if (isJustify && !timeToRender) {
if (this._reSpaceAndTab.test(line[i])) {
timeToRender = true;
}
@@ -27067,7 +27520,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
// if we have charSpacing, we render char by char
actualStyle = actualStyle || this.getCompleteStyleDeclaration(lineIndex, i);
nextStyle = this.getCompleteStyleDeclaration(lineIndex, i + 1);
- timeToRender = this._hasStyleChanged(actualStyle, nextStyle);
+ timeToRender = this._hasStyleChangedForSvg(actualStyle, nextStyle);
}
if (timeToRender) {
style = this._getStyleDeclaration(lineIndex, i) || { };
@@ -27224,6 +27677,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
noScaleCache: false,
+ /**
+ * Properties which when set cause object to change dimensions
+ * @type Object
+ * @private
+ */
+ _dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat('width'),
+
/**
* Constructor. Some scaling related property values are forced. Visibility
* of controls is also fixed; only the rotation and width controls are
@@ -27233,11 +27693,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @return {fabric.Textbox} thisArg
*/
initialize: function(text, options) {
-
this.callSuper('initialize', text, options);
- this.ctx = this.objectCaching ? this._cacheContext : fabric.util.createCanvasElement().getContext('2d');
- // add width to this list of props that effect line wrapping.
- this._dimensionAffectingProps.push('width');
},
/**
@@ -27256,22 +27712,18 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
// clear dynamicMinWidth as it will be different after we re-wrap line
this.dynamicMinWidth = 0;
// wrap lines
- var newText = this._splitTextIntoLines(this.text);
- this.textLines = newText.lines;
- this._textLines = newText.graphemeLines;
- this._unwrappedTextLines = newText._unwrappedLines;
- this._text = newText.graphemeText;
- this._styleMap = this._generateStyleMap(newText);
+ this._styleMap = this._generateStyleMap(this._splitText());
// if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap
if (this.dynamicMinWidth > this.width) {
this._set('width', this.dynamicMinWidth);
}
- if (this.textAlign === 'justify') {
+ if (this.textAlign.indexOf('justify') !== -1) {
// once text is measured we need to make space fatter to make justified text.
this.enlargeSpaces();
}
// clear cache and re-calculate height
this.height = this.calcTextHeight();
+ this.saveState({ propertySet: '_dimensionAffectingProps' });
},
/**
@@ -27495,6 +27947,24 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return graphemeLines;
},
+ /**
+ * Detect if the text line is ended with an hard break
+ * text and itext do not have wrapping, return false
+ * @param {Number} lineIndex text to split
+ * @return {Boolean}
+ */
+ isEndOfWrapping: function(lineIndex) {
+ if (!this._styleMap[lineIndex + 1]) {
+ // is last line, return true;
+ return true;
+ }
+ if (this._styleMap[lineIndex + 1].line !== this._styleMap[lineIndex].line) {
+ // this is last line before a line break, return true;
+ return true;
+ }
+ return false;
+ },
+
/**
* Gets lines of text to render in the Textbox. This function calculates
* text wrapping on the fly every time it is called.
@@ -27584,216 +28054,3 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
});
})();
-
-(function() {
-
- if (typeof document !== 'undefined' && typeof window !== 'undefined') {
- return;
- }
-
- var DOMParser = require('xmldom').DOMParser,
- URL = require('url'),
- HTTP = require('http'),
- HTTPS = require('https'),
-
- Canvas = require(fabric.canvasModule),
- Image = require(fabric.canvasModule).Image;
-
- /** @private */
- function request(url, encoding, callback) {
- var oURL = URL.parse(url);
-
- // detect if http or https is used
- if ( !oURL.port ) {
- oURL.port = ( oURL.protocol.indexOf('https:') === 0 ) ? 443 : 80;
- }
-
- // assign request handler based on protocol
- var reqHandler = (oURL.protocol.indexOf('https:') === 0 ) ? HTTPS : HTTP,
- req = reqHandler.request({
- hostname: oURL.hostname,
- port: oURL.port,
- path: oURL.path,
- method: 'GET'
- }, function(response) {
- var body = '';
- if (encoding) {
- response.setEncoding(encoding);
- }
- response.on('end', function () {
- callback(body);
- });
- response.on('data', function (chunk) {
- if (response.statusCode === 200) {
- body += chunk;
- }
- });
- });
-
- req.on('error', function(err) {
- if (err.errno === process.ECONNREFUSED) {
- fabric.log('ECONNREFUSED: connection refused to ' + oURL.hostname + ':' + oURL.port);
- }
- else {
- fabric.log(err.message);
- }
- callback(null);
- });
-
- req.end();
- }
-
- /** @private */
- function requestFs(path, callback) {
- var fs = require('fs');
- fs.readFile(path, function (err, data) {
- if (err) {
- fabric.log(err);
- throw err;
- }
- else {
- callback(data);
- }
- });
- }
-
- fabric.util.loadImage = function(url, callback, context) {
- function createImageAndCallBack(data) {
- if (data) {
- img.src = new Buffer(data, 'binary');
- // preserving original url, which seems to be lost in node-canvas
- img._src = url;
- callback && callback.call(context, img);
- }
- else {
- img = null;
- callback && callback.call(context, null, true);
- }
- }
- var img = new Image();
- if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
- img.src = img._src = url;
- callback && callback.call(context, img);
- }
- else if (url && url.indexOf('http') !== 0) {
- requestFs(url, createImageAndCallBack);
- }
- else if (url) {
- request(url, 'binary', createImageAndCallBack);
- }
- else {
- callback && callback.call(context, url);
- }
- };
-
- fabric.loadSVGFromURL = function(url, callback, reviver) {
- url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
- if (url.indexOf('http') !== 0) {
- requestFs(url, function(body) {
- fabric.loadSVGFromString(body.toString(), callback, reviver);
- });
- }
- else {
- request(url, '', function(body) {
- fabric.loadSVGFromString(body, callback, reviver);
- });
- }
- };
-
- fabric.loadSVGFromString = function(string, callback, reviver) {
- var doc = new DOMParser().parseFromString(string);
- fabric.parseSVGDocument(doc.documentElement, function(results, options) {
- callback && callback(results, options);
- }, reviver);
- };
-
- fabric.util.getScript = function(url, callback) {
- request(url, '', function(body) {
- // eslint-disable-next-line no-eval
- eval(body);
- callback && callback();
- });
- };
-
- // fabric.util.createCanvasElement = function(_, width, height) {
- // return new Canvas(width, height);
- // }
-
- /**
- * Only available when running fabric on node.js
- * @param {Number} width Canvas width
- * @param {Number} height Canvas height
- * @param {Object} [options] Options to pass to FabricCanvas.
- * @param {Object} [nodeCanvasOptions] Options to pass to NodeCanvas.
- * @return {Object} wrapped canvas instance
- */
- fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
- nodeCanvasOptions = nodeCanvasOptions || options;
-
- var canvasEl = fabric.document.createElement('canvas'),
- nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions),
- nodeCacheCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);
-
- canvasEl.width = nodeCanvas.width;
- canvasEl.height = nodeCanvas.height;
- options = options || { };
- options.nodeCanvas = nodeCanvas;
- options.nodeCacheCanvas = nodeCacheCanvas;
- var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
- fabricCanvas = new FabricCanvas(canvasEl, options);
- fabricCanvas.nodeCanvas = nodeCanvas;
- fabricCanvas.nodeCacheCanvas = nodeCacheCanvas;
- fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
- fabricCanvas.contextCache = nodeCacheCanvas.getContext('2d');
- fabricCanvas.Font = Canvas.Font;
- return fabricCanvas;
- };
-
- var originaInitStatic = fabric.StaticCanvas.prototype._initStatic;
- fabric.StaticCanvas.prototype._initStatic = function(el, options) {
- el = el || fabric.document.createElement('canvas');
- this.nodeCanvas = new Canvas(el.width, el.height);
- this.nodeCacheCanvas = new Canvas(el.width, el.height);
- originaInitStatic.call(this, el, options);
- this.contextContainer = this.nodeCanvas.getContext('2d');
- this.contextCache = this.nodeCacheCanvas.getContext('2d');
- this.Font = Canvas.Font;
- };
-
- /** @ignore */
- fabric.StaticCanvas.prototype.createPNGStream = function() {
- return this.nodeCanvas.createPNGStream();
- };
-
- fabric.StaticCanvas.prototype.createJPEGStream = function(opts) {
- return this.nodeCanvas.createJPEGStream(opts);
- };
-
- fabric.StaticCanvas.prototype._initRetinaScaling = function() {
- if (!this._isRetinaScaling()) {
- return;
- }
-
- this.lowerCanvasEl.setAttribute('width', this.width * fabric.devicePixelRatio);
- this.lowerCanvasEl.setAttribute('height', this.height * fabric.devicePixelRatio);
- this.nodeCanvas.width = this.width * fabric.devicePixelRatio;
- this.nodeCanvas.height = this.height * fabric.devicePixelRatio;
- this.contextContainer.scale(fabric.devicePixelRatio, fabric.devicePixelRatio);
- return this;
- };
- if (fabric.Canvas) {
- fabric.Canvas.prototype._initRetinaScaling = fabric.StaticCanvas.prototype._initRetinaScaling;
- }
-
- var origSetBackstoreDimension = fabric.StaticCanvas.prototype._setBackstoreDimension;
- fabric.StaticCanvas.prototype._setBackstoreDimension = function(prop, value) {
- origSetBackstoreDimension.call(this, prop, value);
- this.nodeCanvas[prop] = value;
- return this;
- };
- if (fabric.Canvas) {
- fabric.Canvas.prototype._setBackstoreDimension = fabric.StaticCanvas.prototype._setBackstoreDimension;
- }
-
-})();
-
diff --git a/dist/fabric.min.js b/dist/fabric.min.js
index 5c294bb32f0..7f484ee26d0 100644
--- a/dist/fabric.min.js
+++ b/dist/fabric.min.js
@@ -1 +1 @@
-function copyGLTo2DDrawImage(t,e){var i=t.canvas,r=e.getContext("2d");r.translate(0,e.height),r.scale(1,-1);var n=i.height-e.height;r.drawImage(i,0,n,e.width,e.height,0,0,e.width,e.height)}function copyGLTo2DPutImageData(t,e){var i=e.getContext("2d"),r=e.width,n=e.height,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 c=new ImageData(a,r);i.putImageData(c,0,0)}var fabric=fabric||{version:"2.0.0-beta7"};"undefined"!=typeof exports&&(exports.fabric=fabric),"undefined"!=typeof document&&"undefined"!=typeof window?(fabric.document=document,fabric.window=window):(fabric.document=require("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"]}}),fabric.window=fabric.document.defaultView),fabric.isTouchSupported="ontouchstart"in fabric.window,fabric.isLikelyNode="undefined"!=typeof Buffer&&"undefined"==typeof window,fabric.SHARED_ATTRIBUTES=["display","transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","id","instantiated_by_use"],fabric.DPI=96,fabric.reNum="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",fabric.fontPaths={},fabric.iMatrix=[1,0,0,1,0,0],fabric.canvasModule="canvas",fabric.perfLimitSizeTotal=2097152,fabric.maxCacheSideLimit=4096,fabric.minCacheSideLimit=256,fabric.charWidthsCache={},fabric.textureSize=2048,fabric.enableGLFiltering=!0,fabric.devicePixelRatio=fabric.window.devicePixelRatio||fabric.window.webkitDevicePixelRatio||fabric.window.mozDevicePixelRatio||1,fabric.initFilterBackend=function(){return fabric.enableGLFiltering&&fabric.isWebglSupported&&fabric.isWebglSupported(fabric.textureSize)?(console.log("max texture size: "+fabric.maxTextureSize),new fabric.WebglFilterBackend({tileSize:fabric.textureSize})):fabric.Canvas2dFilterBackend?new fabric.Canvas2dFilterBackend:void 0},"undefined"!=typeof document&&"undefined"!=typeof window&&(window.fabric=fabric),function(){function t(t,e){if(this.__eventListeners[t]){var i=this.__eventListeners[t];e?i[i.indexOf(e)]=!1:fabric.util.array.fill(i,!1)}}function e(t,e){if(this.__eventListeners||(this.__eventListeners={}),1===arguments.length)for(var i in t)this.on(i,t[i]);else this.__eventListeners[t]||(this.__eventListeners[t]=[]),this.__eventListeners[t].push(e);return this}function i(e,i){if(this.__eventListeners){if(0===arguments.length)for(e in this.__eventListeners)t.call(this,e);else if(1===arguments.length&&"object"==typeof arguments[0])for(var r in e)t.call(this,r,e[r]);else t.call(this,e,i);return this}}function r(t,e){if(this.__eventListeners){var i=this.__eventListeners[t];if(i){for(var r=0,n=i.length;r-1},complexity:function(){return this.getObjects().reduce(function(t,e){return t+=e.complexity?e.complexity():0},0)}},fabric.CommonMethods={_setOptions:function(t){for(var e in t)this.set(e,t[e])},_initGradient:function(t,e){!t||!t.colorStops||t instanceof fabric.Gradient||this.set(e,new fabric.Gradient(t))},_initPattern:function(t,e,i){!t||!t.source||t instanceof fabric.Pattern?i&&i():this.set(e,new fabric.Pattern(t,i))},_initClipping:function(t){if(t.clipTo&&"string"==typeof t.clipTo){var e=fabric.util.getFunctionBody(t.clipTo);void 0!==e&&(this.clipTo=new Function("ctx",e))}},_setObject:function(t){for(var e in t)this._set(e,t[e])},set:function(t,e){return"object"==typeof t?this._setObject(t):"function"==typeof e&&"clipTo"!==t?this._set(t,e(this.get(t))):this._set(t,e),this},_set:function(t,e){this[t]=e},toggle:function(t){var e=this.get(t);return"boolean"==typeof e&&this.set(t,!e),this},get:function(t){return this[t]}},function(t){var e=Math.sqrt,i=Math.atan2,r=Math.pow,n=Math.abs,s=Math.PI/180;fabric.util={removeFromArray:function(t,e){var i=t.indexOf(e);return-1!==i&&t.splice(i,1),t},getRandomInt:function(t,e){return Math.floor(Math.random()*(e-t+1))+t},degreesToRadians:function(t){return t*s},radiansToDegrees:function(t){return t/s},rotatePoint:function(t,e,i){t.subtractEquals(e);var r=fabric.util.rotateVector(t,i);return new fabric.Point(r.x,r.y).addEquals(e)},rotateVector:function(t,e){var i=Math.sin(e),r=Math.cos(e);return{x:t.x*r-t.y*i,y:t.x*i+t.y*r}},transformPoint:function(t,e,i){return i?new fabric.Point(e[0]*t.x+e[2]*t.y,e[1]*t.x+e[3]*t.y):new fabric.Point(e[0]*t.x+e[2]*t.y+e[4],e[1]*t.x+e[3]*t.y+e[5])},makeBoundingBoxFromPoints:function(t){var e=[t[0].x,t[1].x,t[2].x,t[3].x],i=fabric.util.array.min(e),r=fabric.util.array.max(e),n=Math.abs(i-r),s=[t[0].y,t[1].y,t[2].y,t[3].y],o=fabric.util.array.min(s),a=fabric.util.array.max(s);return{left:i,top:o,width:n,height:Math.abs(o-a)}},invertTransform:function(t){var e=1/(t[0]*t[3]-t[1]*t[2]),i=[e*t[3],-e*t[1],-e*t[2],e*t[0]],r=fabric.util.transformPoint({x:t[4],y:t[5]},i,!0);return i[4]=-r.x,i[5]=-r.y,i},toFixed:function(t,e){return parseFloat(Number(t).toFixed(e))},parseUnit:function(t,e){var i=/\D{0,2}$/.exec(t),r=parseFloat(t);switch(e||(e=fabric.Text.DEFAULT_SVG_FONT_SIZE),i[0]){case"mm":return r*fabric.DPI/25.4;case"cm":return r*fabric.DPI/2.54;case"in":return r*fabric.DPI;case"pt":return r*fabric.DPI/72;case"pc":return r*fabric.DPI/72*12;case"em":return r*e;default:return r}},falseFunction:function(){return!1},getKlass:function(t,e){return t=fabric.util.string.camelize(t.charAt(0).toUpperCase()+t.slice(1)),fabric.util.resolveNamespace(e)[t]},getSvgAttributes:function(t){var e=["instantiated_by_use","style","id","class"];switch(t){case"linearGradient":e=e.concat(["x1","y1","x2","y2","gradientUnits","gradientTransform"]);break;case"radialGradient":e=e.concat(["gradientUnits","gradientTransform","cx","cy","r","fx","fy","fr"]);break;case"stop":e=e.concat(["offset","stop-color","stop-opacity"])}return e},resolveNamespace:function(e){if(!e)return fabric;var i,r=e.split("."),n=r.length,s=t||fabric.window;for(i=0;ir;)(r+=a[d++%f])>l&&(r=l),t[g?"lineTo":"moveTo"](r,0),g=!g;t.restore()},createCanvasElement:function(){return fabric.document.createElement("canvas")},createImage:function(){return fabric.document.createElement("img")},clipContext:function(t,e){e.save(),e.beginPath(),t.clipTo(e),e.clip()},multiplyTransformMatrices:function(t,e,i){return[t[0]*e[0]+t[2]*e[1],t[1]*e[0]+t[3]*e[1],t[0]*e[2]+t[2]*e[3],t[1]*e[2]+t[3]*e[3],i?0:t[0]*e[4]+t[2]*e[5]+t[4],i?0:t[1]*e[4]+t[3]*e[5]+t[5]]},qrDecompose:function(t){var n=i(t[1],t[0]),o=r(t[0],2)+r(t[1],2),a=e(o),c=(t[0]*t[3]-t[2]*t[1])/a,h=i(t[0]*t[2]+t[1]*t[3],o);return{angle:n/s,scaleX:a,scaleY:c,skewX:h/s,skewY:0,translateX:t[4],translateY:t[5]}},customTransformMatrix:function(t,e,i){var r=[1,0,n(Math.tan(i*s)),1],o=[n(t),0,0,n(e)];return fabric.util.multiplyTransformMatrices(o,r,!0)},resetObjectTransform:function(t){t.scaleX=1,t.scaleY=1,t.skewX=0,t.skewY=0,t.flipX=!1,t.flipY=!1,t.rotate(0)},getFunctionBody:function(t){return(String(t).match(/function[^{]*\{([\s\S]*)\}/)||{})[1]},isTransparent:function(t,e,i,r){r>0&&(e>r?e-=r:e=0,i>r?i-=r:i=0);var n,s,o=!0,a=t.getImageData(e,i,2*r||1,2*r||1),c=a.data.length;for(n=3;n0?P-=2*f:1===h&&P<0&&(P+=2*f);for(var M=Math.ceil(Math.abs(P/f*2)),F=[],L=P/M,I=8/3*Math.sin(L/4)*Math.sin(L/4)/Math.sin(L/2),R=A+L,B=0;B=n?s-n:2*Math.PI-(n-s)}function r(t,e,i,r,n,s,c,h){var l=a.call(arguments);if(o[l])return o[l];var u,f,d,g,p,v,m,b,_=Math.sqrt,y=Math.min,x=Math.max,C=Math.abs,S=[],w=[[],[]];f=6*t-12*i+6*n,u=-3*t+9*i-9*n+3*c,d=3*i-3*t;for(var T=0;T<2;++T)if(T>0&&(f=6*e-12*r+6*s,u=-3*e+9*r-9*s+3*h,d=3*r-3*e),C(u)<1e-12){if(C(f)<1e-12)continue;0<(g=-d/f)&&g<1&&S.push(g)}else(m=f*f-4*d*u)<0||(0<(p=(-f+(b=_(m)))/(2*u))&&p<1&&S.push(p),0<(v=(-f-b)/(2*u))&&v<1&&S.push(v));for(var O,k,D,E=S.length,j=E;E--;)O=(D=1-(g=S[E]))*D*D*t+3*D*D*g*i+3*D*g*g*n+g*g*g*c,w[0][E]=O,k=D*D*D*e+3*D*D*g*r+3*D*g*g*s+g*g*g*h,w[1][E]=k;w[0][j]=t,w[1][j]=e,w[0][j+1]=c,w[1][j+1]=h;var A=[{x:y.apply(null,w[0]),y:y.apply(null,w[1])},{x:x.apply(null,w[0]),y:x.apply(null,w[1])}];return o[l]=A,A}var n={},s={},o={},a=Array.prototype.join;fabric.util.drawArc=function(e,i,r,n){for(var s=n[0],o=n[1],a=n[2],c=n[3],h=n[4],l=[[],[],[],[]],u=t(n[5]-i,n[6]-r,s,o,c,h,a),f=0,d=u.length;f=e})}}}(),function(){function t(e,i,r){if(r)if(!fabric.isLikelyNode&&i instanceof Element)e=i;else if(i instanceof Array){e=[];for(var n=0,s=i.length;n57343)return t.charAt(e);if(55296<=i&&i<=56319){if(t.length<=e+1)throw"High surrogate without following low surrogate";var r=t.charCodeAt(e+1);if(56320>r||r>57343)throw"High surrogate without following low surrogate";return t.charAt(e)+t.charAt(e+1)}if(0===e)throw"Low surrogate without preceding high surrogate";var n=t.charCodeAt(e-1);if(55296>n||n>56319)throw"Low surrogate without preceding high surrogate";return!1}fabric.util.string={camelize:function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},capitalize:function(t,e){return t.charAt(0).toUpperCase()+(e?t.slice(1):t.slice(1).toLowerCase())},escapeXml:function(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")},graphemeSplit:function(e){var i,r=0,n=[];for(r=0,i;r1?e.apply(this,i.call(arguments,1)):e.call(this):console.log("tried to callSuper "+t+", method not found in prototype chain",this)}var i=Array.prototype.slice,r=function(){},n=function(){for(var t in{toString:1})if("toString"===t)return!1;return!0}(),s=function(t,e,i){for(var r in e)r in t.prototype&&"function"==typeof t.prototype[r]&&(e[r]+"").indexOf("callSuper")>-1?t.prototype[r]=function(t){return function(){var r=this.constructor.superclass;this.constructor.superclass=i;var n=e[t].apply(this,arguments);if(this.constructor.superclass=r,"initialize"!==t)return n}}(r):t.prototype[r]=e[r],n&&(e.toString!==Object.prototype.toString&&(t.prototype.toString=e.toString),e.valueOf!==Object.prototype.valueOf&&(t.prototype.valueOf=e.valueOf))};fabric.util.createClass=function(){function n(){this.initialize.apply(this,arguments)}var o=null,a=i.call(arguments,0);"function"==typeof a[0]&&(o=a.shift()),n.superclass=o,n.subclasses=[],o&&(t.prototype=o.prototype,n.prototype=new t,o.subclasses.push(n));for(var c=0,h=a.length;c=.9999?"":"alpha(opacity="+100*e+")",i.filter=i.filter.replace(r,e)):i.filter+=" alpha(opacity="+100*e+")",t}),fabric.util.setStyle=function(t,e){var i=t.style;if(!i)return t;if("string"==typeof e)return t.style.cssText+=";"+e,e.indexOf("opacity")>-1?n(t,e.match(/opacity:\s*(\d?\.?\d*)/)[1]):t;for(var r in e)"opacity"===r?n(t,e[r]):i["float"===r||"cssFloat"===r?void 0===i.styleFloat?"cssFloat":"styleFloat":r]=e[r];return t}}(),function(){function t(t,e){var i=fabric.document.createElement(t);for(var r in e)"class"===r?i.className=e[r]:"for"===r?i.htmlFor=e[r]:i.setAttribute(r,e[r]);return i}function e(t){for(var e=0,i=0,r=fabric.document.documentElement,n=fabric.document.body||{scrollLeft:0,scrollTop:0};t&&(t.parentNode||t.host)&&((t=t.parentNode||t.host)===fabric.document?(e=n.scrollLeft||r.scrollLeft||0,i=n.scrollTop||r.scrollTop||0):(e+=t.scrollLeft||0,i+=t.scrollTop||0),1!==t.nodeType||"fixed"!==fabric.util.getElementStyle(t,"position")););return{left:e,top:i}}var i,r=Array.prototype.slice,n=function(t){return r.call(t,0)};try{i=n(fabric.document.childNodes)instanceof Array}catch(t){}i||(n=function(t){for(var e=new Array(t.length),i=t.length;i--;)e[i]=t[i];return e});var s;s=fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?function(t,e){var i=fabric.document.defaultView.getComputedStyle(t,null);return i?i[e]:void 0}:function(t,e){var i=t.style[e];return!i&&t.currentStyle&&(i=t.currentStyle[e]),i},function(){var t=fabric.document.documentElement.style,e="userSelect"in t?"userSelect":"MozUserSelect"in t?"MozUserSelect":"WebkitUserSelect"in t?"WebkitUserSelect":"KhtmlUserSelect"in t?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=function(t){return void 0!==t.onselectstart&&(t.onselectstart=fabric.util.falseFunction),e?t.style[e]="none":"string"==typeof t.unselectable&&(t.unselectable="on"),t},fabric.util.makeElementSelectable=function(t){return void 0!==t.onselectstart&&(t.onselectstart=null),e?t.style[e]="":"string"==typeof t.unselectable&&(t.unselectable=""),t}}(),function(){fabric.util.getScript=function(t,e){var i=fabric.document.getElementsByTagName("head")[0],r=fabric.document.createElement("script"),n=!0;r.onload=r.onreadystatechange=function(t){if(n){if("string"==typeof this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)return;n=!1,e(t||fabric.window.event),r=r.onload=r.onreadystatechange=null}},r.src=t,i.appendChild(r)}}(),fabric.util.getById=function(t){return"string"==typeof t?fabric.document.getElementById(t):t},fabric.util.toArray=n,fabric.util.makeElement=t,fabric.util.addClass=function(t,e){t&&-1===(" "+t.className+" ").indexOf(" "+e+" ")&&(t.className+=(t.className?" ":"")+e)},fabric.util.wrapElement=function(e,i,r){return"string"==typeof i&&(i=t(i,r)),e.parentNode&&e.parentNode.replaceChild(i,e),i.appendChild(e),i},fabric.util.getScrollLeftTop=e,fabric.util.getElementOffset=function(t){var i,r,n=t&&t.ownerDocument,o={left:0,top:0},a={left:0,top:0},c={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!n)return a;for(var h in c)a[c[h]]+=parseInt(s(t,h),10)||0;return i=n.documentElement,void 0!==t.getBoundingClientRect&&(o=t.getBoundingClientRect()),r=e(t),{left:o.left+r.left-(i.clientLeft||0)+a.left,top:o.top+r.top-(i.clientTop||0)+a.top}},fabric.util.getElementStyle=s}(),function(){function t(t,e){return t+(/\?/.test(t)?"&":"?")+e}function e(){}var i=function(){for(var t=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0")},function(){return new XMLHttpRequest}],e=t.length;e--;)try{if(t[e]())return t[e]}catch(t){}}();fabric.util.request=function(r,n){n||(n={});var s=n.method?n.method.toUpperCase():"GET",o=n.onComplete||function(){},a=i(),c=n.body||n.parameters;return a.onreadystatechange=function(){4===a.readyState&&(o(a),a.onreadystatechange=e)},"GET"===s&&(c=null,"string"==typeof n.parameters&&(r=t(r,n.parameters))),a.open(s,r,!0),"POST"!==s&&"PUT"!==s||a.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),a.send(c),a}}(),fabric.log=function(){},fabric.warn=function(){},"undefined"!=typeof console&&["log","warn"].forEach(function(t){void 0!==console[t]&&"function"==typeof console[t].apply&&(fabric[t]=function(){return console[t].apply(console,arguments)})}),function(){function t(){return!1}function e(){return i.apply(fabric.window,arguments)}var i=fabric.window.requestAnimationFrame||fabric.window.webkitRequestAnimationFrame||fabric.window.mozRequestAnimationFrame||fabric.window.oRequestAnimationFrame||fabric.window.msRequestAnimationFrame||function(t){return fabric.window.setTimeout(t,1e3/60)},r=fabric.window.cancelAnimationFrame||fabric.window.clearTimeout;fabric.util.animate=function(i){e(function(r){i||(i={});var n,s=r||+new Date,o=i.duration||500,a=s+o,c=i.onChange||t,h=i.abort||t,l=i.onComplete||t,u=i.easing||function(t,e,i,r){return-i*Math.cos(t/r*(Math.PI/2))+i+e},f="startValue"in i?i.startValue:0,d="endValue"in i?i.endValue:100,g=i.byValue||d-f;i.onStart&&i.onStart(),function t(r){if(h())l(d,1,1);else{var p=(n=r||+new Date)>a?o:n-s,v=p/o,m=u(p,f,g,o),b=Math.abs((m-f)/g);c(m,b,v),n>a?i.onComplete&&i.onComplete():e(t)}}(s)})},fabric.util.requestAnimFrame=e,fabric.util.cancelAnimFrame=function(){return r.apply(fabric.window,arguments)}}(),function(){function t(t,e,i){var r="rgba("+parseInt(t[0]+i*(e[0]-t[0]),10)+","+parseInt(t[1]+i*(e[1]-t[1]),10)+","+parseInt(t[2]+i*(e[2]-t[2]),10);return r+=","+(t&&e?parseFloat(t[3]+i*(e[3]-t[3])):1),r+=")"}fabric.util.animateColor=function(e,i,r,n){var s=new fabric.Color(e).getSource(),o=new fabric.Color(i).getSource();n=n||{},fabric.util.animate(fabric.util.object.extend(n,{duration:r||500,startValue:s,endValue:o,byValue:o,easing:function(e,i,r,s){return t(i,r,n.colorEasing?n.colorEasing(e,s):1-Math.cos(e/s*(Math.PI/2)))}}))}}(),function(){function t(t,e,i,r){return ta?a:o),1===o&&1===a&&0===c&&0===h&&0===f&&0===d)return _;if((f||d)&&(x=" translate("+y(f)+" "+y(d)+") "),r=x+" matrix("+o+" 0 0 "+a+" "+c*o+" "+h*a+") ","svg"===t.nodeName){for(n=t.ownerDocument.createElement("g");t.firstChild;)n.appendChild(t.firstChild);t.appendChild(n)}else r=(n=t).getAttribute("transform")+r;return n.setAttribute("transform",r),_}function p(t,e){for(;t&&(t=t.parentNode);)if(t.nodeName&&e.test(t.nodeName.replace("svg:",""))&&!t.getAttribute("instantiated_by_use"))return!0;return!1}var v=t.fabric||(t.fabric={}),m=v.util.object.extend,b=v.util.object.clone,_=v.util.toFixed,y=v.util.parseUnit,x=v.util.multiplyTransformMatrices,C=["path","circle","polygon","polyline","ellipse","rect","line","image","text","linearGradient","radialGradient","stop"],S=["symbol","image","marker","pattern","view","svg"],w=["pattern","defs","symbol","metadata","clipPath","mask","desc"],T=["symbol","g","a","svg"],O={cx:"left",x:"left",r:"radius",cy:"top",y:"top",display:"visible",visibility:"visible",transform:"transformMatrix","fill-opacity":"fillOpacity","fill-rule":"fillRule","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","stroke-dasharray":"strokeDashArray","stroke-linecap":"strokeLineCap","stroke-linejoin":"strokeLineJoin","stroke-miterlimit":"strokeMiterLimit","stroke-opacity":"strokeOpacity","stroke-width":"strokeWidth","text-decoration":"textDecoration","text-anchor":"textAnchor",opacity:"opacity"},k={stroke:"strokeOpacity",fill:"fillOpacity"};v.svgValidTagNamesRegEx=r(C),v.svgViewBoxElementsRegEx=r(S),v.svgInvalidAncestorsRegEx=r(w),v.svgValidParentsRegEx=r(T),v.cssRules={},v.gradientDefs={},v.parseTransformAttribute=function(){function t(t,e){var i=Math.cos(e[0]),r=Math.sin(e[0]),n=0,s=0;3===e.length&&(n=e[1],s=e[2]),t[0]=i,t[1]=r,t[2]=-r,t[3]=i,t[4]=n-(i*n-r*s),t[5]=s-(r*n+i*s)}function e(t,e){var i=e[0],r=2===e.length?e[1]:e[0];t[0]=i,t[3]=r}function i(t,e,i){t[i]=Math.tan(v.util.degreesToRadians(e[0]))}function r(t,e){t[4]=e[0],2===e.length&&(t[5]=e[1])}var n=[1,0,0,1,0,0],s=v.reNum,o="(?:\\s+,?\\s*|,\\s*)",a="(?:"+("(?:(matrix)\\s*\\(\\s*("+s+")"+o+"("+s+")"+o+"("+s+")"+o+"("+s+")"+o+"("+s+")"+o+"("+s+")\\s*\\))")+"|"+("(?:(translate)\\s*\\(\\s*("+s+")(?:"+o+"("+s+"))?\\s*\\))")+"|"+("(?:(scale)\\s*\\(\\s*("+s+")(?:"+o+"("+s+"))?\\s*\\))")+"|"+("(?:(rotate)\\s*\\(\\s*("+s+")(?:"+o+"("+s+")"+o+"("+s+"))?\\s*\\))")+"|"+("(?:(skewX)\\s*\\(\\s*("+s+")\\s*\\))")+"|"+("(?:(skewY)\\s*\\(\\s*("+s+")\\s*\\))")+")",c="^\\s*(?:"+("(?:"+a+"(?:"+o+"*"+a+")*)")+"?)\\s*$",h=new RegExp(c),l=new RegExp(a,"g");return function(s){var o=n.concat(),c=[];if(!s||s&&!h.test(s))return o;s.replace(l,function(s){var h=new RegExp(a).exec(s).filter(function(t){return!!t}),l=h[1],u=h.slice(2).map(parseFloat);switch(l){case"translate":r(o,u);break;case"rotate":u[0]=v.util.degreesToRadians(u[0]),t(o,u);break;case"scale":e(o,u);break;case"skewX":i(o,u,2);break;case"skewY":i(o,u,1);break;case"matrix":o=u}c.push(o.concat()),o=n.concat()});for(var u=c[0];c.length>1;)c.shift(),u=v.util.multiplyTransformMatrices(u,c[0]);return u}}();var D=new RegExp("^\\s*("+v.reNum+"+)\\s*,?\\s*("+v.reNum+"+)\\s*,?\\s*("+v.reNum+"+)\\s*,?\\s*("+v.reNum+"+)\\s*$");v.parseSVGDocument=function(t,e,i,r){if(t){d(t);var n,s,o=v.Object.__uid++,a=g(t),c=v.util.toArray(t.getElementsByTagName("*"));if(a.crossOrigin=r&&r.crossOrigin,a.svgUid=o,0===c.length&&v.isLikelyNode){var h=[];for(n=0,s=(c=t.selectNodes('//*[name(.)!="svg"]')).length;n/i,""))),n&&n.documentElement||e&&e(null),v.parseSVGDocument(n.documentElement,function(t,i,r,n){e&&e(t,i,r,n)},i,r)}})},loadSVGFromString:function(t,e,i,r){t=t.trim();var n;if("undefined"!=typeof DOMParser){var s=new DOMParser;s&&s.parseFromString&&(n=s.parseFromString(t,"text/xml"))}else v.window.ActiveXObject&&((n=new ActiveXObject("Microsoft.XMLDOM")).async="false",n.loadXML(t.replace(//i,"")));v.parseSVGDocument(n.documentElement,function(t,i,r,n){e(t,i,r,n)},i,r)}})}("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},fabric.ElementsParser.prototype.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},fabric.ElementsParser.prototype.createObjects=function(){for(var t=0,e=this.elements.length;tt.x&&this.y>t.y},gte:function(t){return this.x>=t.x&&this.y>=t.y},lerp:function(t,i){return void 0===i&&(i=.5),i=Math.max(Math.min(1,i),0),new e(this.x+(t.x-this.x)*i,this.y+(t.y-this.y)*i)},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 e(Math.min(this.x,t.x),Math.min(this.y,t.y))},max:function(t){return new e(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 e(this.x,this.y)}})}("undefined"!=typeof exports?exports:this),function(t){"use strict";function e(t){this.status=t,this.points=[]}var i=t.fabric||(t.fabric={});i.Intersection?i.warn("fabric.Intersection is already defined"):(i.Intersection=e,i.Intersection.prototype={constructor:e,appendPoint:function(t){return this.points.push(t),this},appendPoints:function(t){return this.points=this.points.concat(t),this}},i.Intersection.intersectLineLine=function(t,r,n,s){var o,a=(s.x-n.x)*(t.y-n.y)-(s.y-n.y)*(t.x-n.x),c=(r.x-t.x)*(t.y-n.y)-(r.y-t.y)*(t.x-n.x),h=(s.y-n.y)*(r.x-t.x)-(s.x-n.x)*(r.y-t.y);if(0!==h){var l=a/h,u=c/h;0<=l&&l<=1&&0<=u&&u<=1?(o=new e("Intersection")).appendPoint(new i.Point(t.x+l*(r.x-t.x),t.y+l*(r.y-t.y))):o=new e}else o=new e(0===a||0===c?"Coincident":"Parallel");return o},i.Intersection.intersectLinePolygon=function(t,i,r){var n,s,o,a,c=new e,h=r.length;for(a=0;a0&&(c.status="Intersection"),c},i.Intersection.intersectPolygonPolygon=function(t,i){var r,n=new e,s=t.length;for(r=0;r0&&(n.status="Intersection"),n},i.Intersection.intersectPolygonRectangle=function(t,r,n){var s=r.min(n),o=r.max(n),a=new i.Point(o.x,s.y),c=new i.Point(s.x,o.y),h=e.intersectLinePolygon(s,a,t),l=e.intersectLinePolygon(a,o,t),u=e.intersectLinePolygon(o,c,t),f=e.intersectLinePolygon(c,s,t),d=new e;return d.appendPoints(h.points),d.appendPoints(l.points),d.appendPoints(u.points),d.appendPoints(f.points),d.points.length>0&&(d.status="Intersection"),d})}("undefined"!=typeof exports?exports:this),function(t){"use strict";function e(t){t?this._tryParsingColor(t):this.setSource([0,0,0,1])}function i(t,e,i){return i<0&&(i+=1),i>1&&(i-=1),i<1/6?t+6*(e-t)*i:i<.5?e:i<2/3?t+(e-t)*(2/3-i)*6:t}var r=t.fabric||(t.fabric={});r.Color?r.warn("fabric.Color is already defined."):(r.Color=e,r.Color.prototype={_tryParsingColor:function(t){var i;t in e.colorNameMap&&(t=e.colorNameMap[t]),"transparent"===t&&(i=[255,255,255,0]),i||(i=e.sourceFromHex(t)),i||(i=e.sourceFromRgb(t)),i||(i=e.sourceFromHsl(t)),i||(i=[0,0,0,1]),i&&this.setSource(i)},_rgbToHsl:function(t,e,i){t/=255,e/=255,i/=255;var n,s,o,a=r.util.array.max([t,e,i]),c=r.util.array.min([t,e,i]);if(o=(a+c)/2,a===c)n=s=0;else{var h=a-c;switch(s=o>.5?h/(2-a-c):h/(a+c),a){case t:n=(e-i)/h+(e1?1:o,s){var a=s.split(/\s*;\s*/);for(""===a[a.length-1]&&a.pop(),n=a.length;n--;){var c=a[n].split(/\s*:\s*/),h=c[0].trim(),l=c[1].trim();"stop-color"===h?e=l:"stop-opacity"===h&&(r=l)}}return e||(e=t.getAttribute("stop-color")||"rgb(0,0,0)"),r||(r=t.getAttribute("stop-opacity")),e=new fabric.Color(e),i=e.getAlpha(),r=isNaN(parseFloat(r))?1:parseFloat(r),r*=i,{offset:o,color:e.toRgb(),opacity:r}}function e(t){return{x1:t.getAttribute("x1")||0,y1:t.getAttribute("y1")||0,x2:t.getAttribute("x2")||"100%",y2:t.getAttribute("y2")||0}}function i(t){return{x1:t.getAttribute("fx")||t.getAttribute("cx")||"50%",y1:t.getAttribute("fy")||t.getAttribute("cy")||"50%",r1:0,x2:t.getAttribute("cx")||"50%",y2:t.getAttribute("cy")||"50%",r2:t.getAttribute("r")||"50%"}}function r(t,e,i){var r,n=0,s=1,o="";for(var a in e)"Infinity"===e[a]?e[a]=1:"-Infinity"===e[a]&&(e[a]=0),r=parseFloat(e[a],10),s="string"==typeof e[a]&&/^\d+%$/.test(e[a])?.01:1,"x1"===a||"x2"===a||"r2"===a?(s*="objectBoundingBox"===i?t.width:1,n="objectBoundingBox"===i?t.left||0:0):"y1"!==a&&"y2"!==a||(s*="objectBoundingBox"===i?t.height:1,n="objectBoundingBox"===i?t.top||0:0),e[a]=r*s+n;if("ellipse"===t.type&&null!==e.r2&&"objectBoundingBox"===i&&t.rx!==t.ry){var c=t.ry/t.rx;o=" scale(1, "+c+")",e.y1&&(e.y1/=c),e.y2&&(e.y2/=c)}return o}var n=fabric.util.object.clone;fabric.Gradient=fabric.util.createClass({offsetX:0,offsetY:0,initialize:function(t){t||(t={});var e={};this.id=fabric.Object.__uid++,this.type=t.type||"linear",e={x1:t.coords.x1||0,y1:t.coords.y1||0,x2:t.coords.x2||0,y2:t.coords.y2||0},"radial"===this.type&&(e.r1=t.coords.r1||0,e.r2=t.coords.r2||0),this.coords=e,this.colorStops=t.colorStops.slice(),t.gradientTransform&&(this.gradientTransform=t.gradientTransform),this.offsetX=t.offsetX||this.offsetX,this.offsetY=t.offsetY||this.offsetY},addColorStop:function(t){for(var e in t){var i=new fabric.Color(t[e]);this.colorStops.push({offset:parseFloat(e),color:i.toRgb(),opacity:i.getAlpha()})}return this},toObject:function(t){var e={type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY,gradientTransform:this.gradientTransform?this.gradientTransform.concat():this.gradientTransform};return fabric.util.populateWithProperties(this,e,t),e},toSVG:function(t){var e,i,r,s,o=n(this.coords,!0),a=n(this.colorStops,!0),c=o.r1>o.r2,h=t.width/2,l=t.height/2;a.sort(function(t,e){return t.offset-e.offset}),"path"===t.type&&(h-=t.pathOffset.x,l-=t.pathOffset.y);for(var u in o)"x1"===u||"x2"===u?o[u]+=this.offsetX-h:"y1"!==u&&"y2"!==u||(o[u]+=this.offsetY-l);if(s='id="SVGID_'+this.id+'" gradientUnits="userSpaceOnUse"',this.gradientTransform&&(s+=' gradientTransform="matrix('+this.gradientTransform.join(" ")+')" '),"linear"===this.type?r=["\n']:"radial"===this.type&&(r=["\n']),"radial"===this.type){if(c)for((a=a.concat()).reverse(),e=0,i=a.length;e0){var d=f/Math.max(o.r1,o.r2);for(e=0,i=a.length;e\n')}return r.push("linear"===this.type?"\n":"\n"),r.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 e=t.fabric||(t.fabric={}),i=e.util.toFixed;e.Shadow?e.warn("fabric.Shadow is already defined."):(e.Shadow=e.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(t){"string"==typeof t&&(t=this._parseShadow(t));for(var i in t)this[i]=t[i];this.id=e.Object.__uid++},_parseShadow:function(t){var i=t.trim(),r=e.Shadow.reOffsetsAndBlur.exec(i)||[];return{color:(i.replace(e.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)").trim(),offsetX:parseInt(r[1],10)||0,offsetY:parseInt(r[2],10)||0,blur:parseInt(r[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(t){var r=40,n=40,s=e.Object.NUM_FRACTION_DIGITS,o=e.util.rotateVector({x:this.offsetX,y:this.offsetY},e.util.degreesToRadians(-t.angle));return t.width&&t.height&&(r=100*i((Math.abs(o.x)+this.blur)/t.width,s)+20,n=100*i((Math.abs(o.y)+this.blur)/t.height,s)+20),t.flipX&&(o.x*=-1),t.flipY&&(o.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};var t={},i=e.Shadow.prototype;return["color","blur","offsetX","offsetY","affectStroke"].forEach(function(e){this[e]!==i[e]&&(t[e]=this[e])},this),t}}),e.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 t=fabric.util.object.extend,e=fabric.util.getElementOffset,i=fabric.util.removeFromArray,r=fabric.util.toFixed,n=fabric.util.transformPoint,s=fabric.util.invertTransform,o=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,_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=e(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(t,e,i,r){return"string"==typeof e?fabric.util.loadImage(e,function(e){e&&(this[t]=new fabric.Image(e,r)),i&&i(e)},this,r&&r.crossOrigin):(r&&e.setOptions(r),this[t]=e,i&&i(e)),this},__setBgOverlayColor:function(t,e,i){return this[t]=e,this._initGradient(e,t),this._initPattern(e,t,i),this},_createCanvasElement:function(){var t=fabric.util.createCanvasElement();if(!t)throw o;if(t.style||(t.style={}),void 0===t.getContext)throw o;return t},_initOptions:function(t){this._setOptions(t),this.width=this.width||parseInt(this.lowerCanvasEl.width,10)||0,this.height=this.height||parseInt(this.lowerCanvasEl.height,10)||0,this.lowerCanvasEl.style&&(this.lowerCanvasEl.width=this.width,this.lowerCanvasEl.height=this.height,this.lowerCanvasEl.style.width=this.width+"px",this.lowerCanvasEl.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;e=e||{};for(var r in t)i=t[r],e.cssOnly||(this._setBackstoreDimension(r,t[r]),i+="px"),e.backstoreOnly||this._setCssDimension(r,i);return 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"),i.join("")},_setSVGPreamble:function(t,e){e.suppressPreamble||t.push('\n','\n')},_setSVGHeader:function(t,e){var i,n=e.width||this.width,s=e.height||this.height,o='viewBox="0 0 '+this.width+" "+this.height+'" ',a=fabric.Object.NUM_FRACTION_DIGITS;e.viewBox?o='viewBox="'+e.viewBox.x+" "+e.viewBox.y+" "+e.viewBox.width+" "+e.viewBox.height+'" ':this.svgViewportTransformation&&(i=this.viewportTransform,o='viewBox="'+r(-i[4]/i[0],a)+" "+r(-i[5]/i[3],a)+" "+r(this.width/i[0],a)+" "+r(this.height/i[3],a)+'" '),t.push("