You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2017/11/23 09:34:15 UTC
[3/4] openmeetings git commit: [OPENMEETINGS-1738] fabric.js version
is updated to 2.0rc1
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/7008a693/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
index a8931f7..236a9ce 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
@@ -1,8 +1,8 @@
/* Licensed MIT https://github.com/kangax/fabric.js/blob/master/LICENSE */
-/* build: `node build.js modules=ALL exclude=json,gestures minifier=uglifyjs` */
+/* 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: "1.7.20" };
+var fabric = fabric || { version: '2.0.0-rc.1' };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}
@@ -10,28 +10,24 @@ if (typeof exports !== 'undefined') {
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
fabric.document = document;
fabric.window = window;
- // ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)
- window.fabric = fabric;
}
else {
// assume we're running under node.js when document/window are not present
- fabric.document = require("jsdom")
+ 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")
- );
-
- if (fabric.document.createWindow) {
- fabric.window = fabric.document.createWindow();
- } else {
- fabric.window = fabric.document.parentWindow;
- }
+ 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;
+ DOMParser = require('xmldom').DOMParser;
}
/**
* True when in environment that supports touch events
* @type boolean
*/
-
fabric.isTouchSupported = 'ontouchstart' in fabric.window;
/**
@@ -54,7 +50,8 @@ 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_ */
@@ -97,6 +94,25 @@ fabric.minCacheSideLimit = 256;
fabric.charWidthsCache = { };
/**
+ * if webgl is enabled and available, textureSize will determine the size
+ * of the canvas backend
+ * @since 2.0.0
+ * @type Number
+ * @default
+ */
+fabric.textureSize = 2048;
+
+/**
+ * Enable webgl for filtering picture is available
+ * A filtering backend will be initialized, this will both take memory and
+ * time since a default 2048x2048 canvas will be created for the gl context
+ * @since 2.0.0
+ * @type Boolean
+ * @default
+ */
+fabric.enableGLFiltering = true;
+
+/**
* Device Pixel Ratio
* @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html
*/
@@ -104,6 +120,37 @@ 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)) {
+ console.log('max texture size: ' + fabric.maxTextureSize);
+ return (new fabric.WebglFilterBackend({ tileSize: fabric.textureSize }));
+ }
+ else if (fabric.Canvas2dFilterBackend) {
+ return (new fabric.Canvas2dFilterBackend());
+ }
+};
+
+
+if (typeof document !== 'undefined' && typeof window !== 'undefined') {
+ // ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)
+ window.fabric = fabric;
+}
(function() {
@@ -261,7 +308,7 @@ fabric.Collection = {
this._onObjectAdded(arguments[i]);
}
}
- this.renderOnAddRemove && this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -286,7 +333,7 @@ fabric.Collection = {
objects.splice(index, 0, object);
}
this._onObjectAdded && this._onObjectAdded(object);
- this.renderOnAddRemove && this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -311,7 +358,7 @@ fabric.Collection = {
}
}
- this.renderOnAddRemove && somethingRemoved && this.renderAll();
+ this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
return this;
},
@@ -642,11 +689,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,
@@ -745,6 +792,33 @@ fabric.CommonMethods = {
},
/**
+ * Returns array of attributes for given svg that fabric parses
+ * @memberOf fabric.util
+ * @param {String} type Type of svg element (eg. 'circle')
+ * @return {Array} string names of supported attributes
+ */
+ getSvgAttributes: function(type) {
+ var attributes = [
+ 'instantiated_by_use',
+ 'style',
+ 'id',
+ 'class'
+ ];
+ switch (type) {
+ case 'linearGradient':
+ attributes = attributes.concat(['x1', 'y1', 'x2', 'y2', 'gradientUnits', 'gradientTransform']);
+ break;
+ case 'radialGradient':
+ attributes = attributes.concat(['gradientUnits', 'gradientTransform', 'cx', 'cy', 'r', 'fx', 'fy', 'fr']);
+ break;
+ case 'stop':
+ attributes = attributes.concat(['offset', 'stop-color', 'stop-opacity']);
+ break;
+ }
+ return attributes;
+ },
+
+ /**
* Returns object of given namespace
* @memberOf fabric.util
* @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
@@ -783,11 +857,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);
@@ -803,10 +878,44 @@ 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
* @memberOf fabric.util
@@ -827,8 +936,7 @@ fabric.CommonMethods = {
var enlivenedObjects = [],
numLoadedObjects = 0,
- numTotalObjects = objects.length,
- forceAsync = true;
+ numTotalObjects = objects.length;
if (!numTotalObjects) {
callback && callback(enlivenedObjects);
@@ -846,7 +954,7 @@ fabric.CommonMethods = {
error || (enlivenedObjects[index] = obj);
reviver && reviver(o, obj, error);
onLoaded();
- }, forceAsync);
+ });
});
},
@@ -854,10 +962,8 @@ fabric.CommonMethods = {
* Create and wait for loading of patterns
* @static
* @memberOf fabric.util
- * @param {Array} objects Objects to enliven
+ * @param {Array} patterns Objects to enliven
* @param {Function} callback Callback to invoke when all objects are created
- * @param {String} namespace Namespace to get klass "Class" object from
- * @param {Function} reviver Method for further parsing of object elements,
* called after each fabric object created.
*/
enlivenPatterns: function(patterns, callback) {
@@ -899,13 +1005,26 @@ fabric.CommonMethods = {
* @param {Array} elements SVG elements to group
* @param {Object} [options] Options object
* @param {String} path Value to set sourcePath to
- * @return {fabric.Object|fabric.PathGroup}
+ * @return {fabric.Object|fabric.Group}
*/
groupSVGElements: function(elements, options, path) {
var object;
-
- object = new fabric.PathGroup(elements, options);
-
+ if (elements.length === 1) {
+ return elements[0];
+ }
+ if (options) {
+ if (options.width && options.height) {
+ options.centerPoint = {
+ x: options.width / 2,
+ y: options.height / 2
+ };
+ }
+ else {
+ delete options.width;
+ delete options.height;
+ }
+ }
+ object = new fabric.Group(elements, options);
if (typeof path !== 'undefined') {
object.sourcePath = path;
}
@@ -918,7 +1037,7 @@ fabric.CommonMethods = {
* @memberOf fabric.util
* @param {Object} source Source object
* @param {Object} destination Destination object
- * @return {Array} properties Propertie names to include
+ * @return {Array} properties Properties names to include
*/
populateWithProperties: function(source, destination, properties) {
if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
@@ -971,21 +1090,13 @@ fabric.CommonMethods = {
},
/**
- * Creates canvas element and initializes it via excanvas if necessary
+ * Creates canvas element
* @static
* @memberOf fabric.util
- * @param {CanvasElement} [canvasEl] optional canvas element to initialize;
- * when not given, element is created implicitly
* @return {CanvasElement} initialized canvas element
*/
- createCanvasElement: function(canvasEl) {
- canvasEl || (canvasEl = fabric.document.createElement('canvas'));
- /* eslint-disable camelcase */
- if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
- G_vmlCanvasManager.initElement(canvasEl);
- }
- /* eslint-enable camelcase */
- return canvasEl;
+ createCanvasElement: function() {
+ return fabric.document.createElement('canvas');
},
/**
@@ -995,45 +1106,13 @@ fabric.CommonMethods = {
* @return {HTMLImageElement} HTML image element
*/
createImage: function() {
- return fabric.isLikelyNode
- ? new (require('canvas').Image)()
- : fabric.document.createElement('img');
- },
-
- /**
- * Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
- * @static
- * @memberOf fabric.util
- * @param {Object} klass "Class" to create accessors for
- */
- createAccessors: function(klass) {
- var proto = klass.prototype, i, propName,
- capitalizedPropName, setterName, getterName;
-
- for (i = proto.stateProperties.length; i--; ) {
-
- propName = proto.stateProperties[i];
- capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
- setterName = 'set' + capitalizedPropName;
- getterName = 'get' + capitalizedPropName;
-
- // using `new Function` for better introspection
- if (!proto[getterName]) {
- proto[getterName] = (function(property) {
- return new Function('return this.get("' + property + '")');
- })(propName);
- }
- if (!proto[setterName]) {
- proto[setterName] = (function(property) {
- return new Function('value', 'return this.set("' + property + '", value)');
- })(propName);
- }
- }
+ return fabric.document.createElement('img');
},
/**
* @static
* @memberOf fabric.util
+ * @deprecated since 2.0.0
* @param {fabric.Object} receiver Object implementing `clipTo` method
* @param {CanvasRenderingContext2D} ctx Context to clip
*/
@@ -1102,7 +1181,7 @@ fabric.CommonMethods = {
target.skewY = 0;
target.flipX = false;
target.flipY = false;
- target.setAngle(0);
+ target.rotate(0);
},
/**
@@ -1207,7 +1286,6 @@ fabric.CommonMethods = {
* @memberOf fabric.util
* @param {Number} ar aspect ratio
* @param {Number} maximumArea Maximum area you want to achieve
- * @param {Number} maximumSide biggest side allowed
* @return {Object.x} Limited dimensions by X
* @return {Object.y} Limited dimensions by Y
*/
@@ -1219,9 +1297,16 @@ fabric.CommonMethods = {
capValue: function(min, value, max) {
return Math.max(min, Math.min(value, max));
+ },
+
+ findScaleToFit: function(source, destination) {
+ return Math.min(destination.width / source.width, destination.height / source.height);
+ },
+
+ findScaleToCover: function(source, destination) {
+ return Math.max(destination.width / source.width, destination.height / source.height);
}
};
-
})(typeof exports !== 'undefined' ? exports : this);
@@ -1490,172 +1575,6 @@ fabric.CommonMethods = {
var slice = Array.prototype.slice;
- /* _ES5_COMPAT_START_ */
-
- if (!Array.prototype.indexOf) {
- /**
- * Finds index of an element in an array
- * @param {*} searchElement
- * @return {Number}
- */
- Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
- if (this === void 0 || this === null) {
- throw new TypeError();
- }
- var t = Object(this), len = t.length >>> 0;
- if (len === 0) {
- return -1;
- }
- var n = 0;
- if (arguments.length > 0) {
- n = Number(arguments[1]);
- if (n !== n) { // shortcut for verifying if it's NaN
- n = 0;
- }
- else if (n !== 0 && n !== Number.POSITIVE_INFINITY && n !== Number.NEGATIVE_INFINITY) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
- }
- }
- if (n >= len) {
- return -1;
- }
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
- for (; k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
- }
- }
- return -1;
- };
- }
-
- if (!Array.prototype.forEach) {
- /**
- * Iterates an array, invoking callback for each element
- * @param {Function} fn Callback to invoke for each element
- * @param {Object} [context] Context to invoke callback in
- * @return {Array}
- */
- Array.prototype.forEach = function(fn, context) {
- for (var i = 0, len = this.length >>> 0; i < len; i++) {
- if (i in this) {
- fn.call(context, this[i], i, this);
- }
- }
- };
- }
-
- if (!Array.prototype.map) {
- /**
- * Returns a result of iterating over an array, invoking callback for each element
- * @param {Function} fn Callback to invoke for each element
- * @param {Object} [context] Context to invoke callback in
- * @return {Array}
- */
- Array.prototype.map = function(fn, context) {
- var result = [];
- for (var i = 0, len = this.length >>> 0; i < len; i++) {
- if (i in this) {
- result[i] = fn.call(context, this[i], i, this);
- }
- }
- return result;
- };
- }
-
- if (!Array.prototype.every) {
- /**
- * Returns true if a callback returns truthy value for all elements in an array
- * @param {Function} fn Callback to invoke for each element
- * @param {Object} [context] Context to invoke callback in
- * @return {Boolean}
- */
- Array.prototype.every = function(fn, context) {
- for (var i = 0, len = this.length >>> 0; i < len; i++) {
- if (i in this && !fn.call(context, this[i], i, this)) {
- return false;
- }
- }
- return true;
- };
- }
-
- if (!Array.prototype.some) {
- /**
- * Returns true if a callback returns truthy value for at least one element in an array
- * @param {Function} fn Callback to invoke for each element
- * @param {Object} [context] Context to invoke callback in
- * @return {Boolean}
- */
- Array.prototype.some = function(fn, context) {
- for (var i = 0, len = this.length >>> 0; i < len; i++) {
- if (i in this && fn.call(context, this[i], i, this)) {
- return true;
- }
- }
- return false;
- };
- }
-
- if (!Array.prototype.filter) {
- /**
- * Returns the result of iterating over elements in an array
- * @param {Function} fn Callback to invoke for each element
- * @param {Object} [context] Context to invoke callback in
- * @return {Array}
- */
- Array.prototype.filter = function(fn, context) {
- var result = [], val;
- for (var i = 0, len = this.length >>> 0; i < len; i++) {
- if (i in this) {
- val = this[i]; // in case fn mutates this
- if (fn.call(context, val, i, this)) {
- result.push(val);
- }
- }
- }
- return result;
- };
- }
-
- if (!Array.prototype.reduce) {
- /**
- * Returns "folded" (reduced) result of iterating over elements in an array
- * @param {Function} fn Callback to invoke for each element
- * @return {*}
- */
- Array.prototype.reduce = function(fn /*, initial*/) {
- var len = this.length >>> 0,
- i = 0,
- rv;
-
- if (arguments.length > 1) {
- rv = arguments[1];
- }
- else {
- do {
- if (i in this) {
- rv = this[i++];
- break;
- }
- // if array contains no values, no initial value to return
- if (++i >= len) {
- throw new TypeError();
- }
- }
- while (true);
- }
- for (; i < len; i++) {
- if (i in this) {
- rv = fn.call(null, rv, this[i], i, this);
- }
- }
- return rv;
- };
- }
-
- /* _ES5_COMPAT_END_ */
-
/**
* Invokes method on all items in a given array
* @memberOf fabric.util.array
@@ -1796,6 +1715,7 @@ fabric.CommonMethods = {
/**
* Creates an empty object and copies all enumerable properties of another object to it
* @memberOf fabric.util.object
+ * TODO: this function return an empty object if you try to clone null
* @param {Object} object Object to clone
* @return {Object}
*/
@@ -1808,26 +1728,12 @@ fabric.CommonMethods = {
extend: extend,
clone: clone
};
-
+ fabric.util.object.extend(fabric.util, fabric.Observable);
})();
(function() {
- /* _ES5_COMPAT_START_ */
- if (!String.prototype.trim) {
- /**
- * Trims a string (removing whitespace from the beginning and the end)
- * @function external:String#trim
- * @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim">String#trim on MDN</a>
- */
- String.prototype.trim = function () {
- // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
- return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
- };
- }
- /* _ES5_COMPAT_END_ */
-
/**
* Camelizes a string
* @memberOf fabric.util.string
@@ -1862,61 +1768,80 @@ fabric.CommonMethods = {
*/
function escapeXml(string) {
return string.replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(/</g, '<')
- .replace(/>/g, '>');
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(/</g, '<')
+ .replace(/>/g, '>');
}
/**
- * String utilities
- * @namespace fabric.util.string
+ * Divide a string in the user perceived single units
+ * @memberOf fabric.util.string
+ * @param {String} textstring String to escape
+ * @return {Array} array containing the graphemes
*/
- fabric.util.string = {
- camelize: camelize,
- capitalize: capitalize,
- escapeXml: escapeXml
- };
-})();
-
+ function graphemeSplit(textstring) {
+ var i = 0, chr, graphemes = [];
+ for (i = 0, chr; i < textstring.length; i++) {
+ if ((chr = getWholeChar(textstring, i)) === false) {
+ continue;
+ }
+ graphemes.push(chr);
+ }
+ return graphemes;
+ }
-/* _ES5_COMPAT_START_ */
-(function() {
+ // taken from mdn in the charAt doc page.
+ function getWholeChar(str, i) {
+ var code = str.charCodeAt(i);
- var slice = Array.prototype.slice,
- apply = Function.prototype.apply,
- Dummy = function() { };
+ if (isNaN(code)) {
+ return ''; // Position not found
+ }
+ if (code < 0xD800 || code > 0xDFFF) {
+ return str.charAt(i);
+ }
- if (!Function.prototype.bind) {
- /**
- * Cross-browser approximation of ES5 Function.prototype.bind (not fully spec conforming)
- * @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind">Function#bind on MDN</a>
- * @param {Object} thisArg Object to bind function to
- * @param {Any[]} Values to pass to a bound function
- * @return {Function}
- */
- Function.prototype.bind = function(thisArg) {
- var _this = this, args = slice.call(arguments, 1), bound;
- if (args.length) {
- bound = function() {
- return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
- };
+ // High surrogate (could change last hex to 0xDB7F to treat high private
+ // surrogates as single characters)
+ if (0xD800 <= code && code <= 0xDBFF) {
+ if (str.length <= (i + 1)) {
+ throw 'High surrogate without following low surrogate';
}
- else {
- /** @ignore */
- bound = function() {
- return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
- };
+ var next = str.charCodeAt(i + 1);
+ if (0xDC00 > next || next > 0xDFFF) {
+ throw 'High surrogate without following low surrogate';
}
- Dummy.prototype = this.prototype;
- bound.prototype = new Dummy();
+ return str.charAt(i) + str.charAt(i + 1);
+ }
+ // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
+ if (i === 0) {
+ throw 'Low surrogate without preceding high surrogate';
+ }
+ var prev = str.charCodeAt(i - 1);
- return bound;
- };
+ // (could change last hex to 0xDB7F to treat high private
+ // surrogates as single characters)
+ if (0xD800 > prev || prev > 0xDBFF) {
+ throw 'Low surrogate without preceding high surrogate';
+ }
+ // We can pass over low surrogates now as the second component
+ // in a pair which we have already processed
+ return false;
}
+
+ /**
+ * String utilities
+ * @namespace fabric.util.string
+ */
+ fabric.util.string = {
+ camelize: camelize,
+ capitalize: capitalize,
+ escapeXml: escapeXml,
+ graphemeSplit: graphemeSplit
+ };
})();
-/* _ES5_COMPAT_END_ */
(function() {
@@ -2241,14 +2166,11 @@ fabric.CommonMethods = {
}
var pointerX = function(event) {
- // looks like in IE (<9) clientX at certain point (apparently when mouseup fires on VML element)
- // is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
- // need to investigate later
- return (typeof event.clientX !== unknown ? event.clientX : 0);
+ return event.clientX;
},
pointerY = function(event) {
- return (typeof event.clientY !== unknown ? event.clientY : 0);
+ return event.clientY;
};
function _getPointer(event, pageProp, clientProp) {
@@ -2271,8 +2193,6 @@ fabric.CommonMethods = {
fabric.util.getPointer = getPointer;
- fabric.util.object.extend(fabric.util, fabric.Observable);
-
})();
@@ -2817,9 +2737,11 @@ if (typeof console !== 'undefined') {
fabric.window.oRequestAnimationFrame ||
fabric.window.msRequestAnimationFrame ||
function(callback) {
- fabric.window.setTimeout(callback, 1000 / 60);
+ return fabric.window.setTimeout(callback, 1000 / 60);
};
+ var _cancelAnimFrame = fabric.window.cancelAnimationFrame || fabric.window.clearTimeout;
+
/**
* requestAnimationFrame polyfill based on http://paulirish.com/2011/requestanimationframe-for-smart-animating/
* In order to get a precise start time, `requestAnimFrame` should be called as an entry into the method
@@ -2831,9 +2753,13 @@ if (typeof console !== 'undefined') {
return _requestAnimFrame.apply(fabric.window, arguments);
}
+ function cancelAnimFrame() {
+ return _cancelAnimFrame.apply(fabric.window, arguments);
+ }
+
fabric.util.animate = animate;
fabric.util.requestAnimFrame = requestAnimFrame;
-
+ fabric.util.cancelAnimFrame = cancelAnimFrame;
})();
@@ -2876,8 +2802,8 @@ if (typeof console !== 'undefined') {
byValue: endColor,
easing: function (currentTime, startValue, byValue, duration) {
var posValue = options.colorEasing
- ? options.colorEasing(currentTime, duration)
- : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
+ ? options.colorEasing(currentTime, duration)
+ : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
return calculateColor(startValue, byValue, posValue);
}
}));
@@ -3304,10 +3230,11 @@ if (typeof console !== 'undefined') {
parseUnit = fabric.util.parseUnit,
multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
- reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/i,
- reViewBoxTagNames = /^(symbol|image|marker|pattern|view|svg)$/i,
- reNotAllowedAncestors = /^(?:pattern|defs|symbol|metadata|clipPath|mask)$/i,
- reAllowedParents = /^(symbol|g|a|svg)$/i,
+ svgValidTagNames = ['path', 'circle', 'polygon', 'polyline', 'ellipse', 'rect', 'line',
+ 'image', 'text', 'linearGradient', 'radialGradient', 'stop'],
+ svgViewBoxElements = ['symbol', 'image', 'marker', 'pattern', 'view', 'svg'],
+ svgInvalidAncestors = ['pattern', 'defs', 'symbol', 'metadata', 'clipPath', 'mask', 'desc'],
+ svgValidParents = ['symbol', 'g', 'a', 'svg'],
attributesMap = {
cx: 'left',
@@ -3324,6 +3251,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',
@@ -3331,7 +3259,7 @@ if (typeof console !== 'undefined') {
'stroke-opacity': 'strokeOpacity',
'stroke-width': 'strokeWidth',
'text-decoration': 'textDecoration',
- 'text-anchor': 'originX',
+ 'text-anchor': 'textAnchor',
opacity: 'opacity'
},
@@ -3340,6 +3268,11 @@ if (typeof console !== 'undefined') {
fill: 'fillOpacity'
};
+ fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames);
+ fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements);
+ fabric.svgInvalidAncestorsRegEx = getSvgRegex(svgInvalidAncestors);
+ fabric.svgValidParentsRegEx = getSvgRegex(svgValidParents);
+
fabric.cssRules = { };
fabric.gradientDefs = { };
@@ -3378,7 +3311,7 @@ if (typeof console !== 'undefined') {
}
}
else if (attr === 'visible') {
- value = (value === 'none' || value === 'hidden') ? false : true;
+ value = value !== 'none' && value !== 'hidden';
// display=none on parent element always takes precedence over child element
if (parentAttributes && parentAttributes.visible === false) {
value = false;
@@ -3390,9 +3323,20 @@ if (typeof console !== 'undefined') {
value *= parentAttributes.opacity;
}
}
- else if (attr === 'originX' /* text-anchor */) {
+ 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);
}
@@ -3401,6 +3345,13 @@ if (typeof console !== 'undefined') {
}
/**
+ * @private
+ */
+ function getSvgRegex(arr) {
+ return new RegExp('^(' + arr.join('|') + ')\\b', 'i');
+ }
+
+ /**
* @private
* @param {Object} attributes Array of attributes to parse
*/
@@ -3432,8 +3383,8 @@ if (typeof console !== 'undefined') {
* @private
*/
function _getMultipleNodes(doc, nodeNames) {
- var nodeName, nodeArray = [], nodeList;
- for (var i = 0; i < nodeNames.length; i++) {
+ var nodeName, nodeArray = [], nodeList, i, len;
+ for (i = 0, len = nodeNames.length; i < len; i++) {
nodeName = nodeNames[i];
nodeList = doc.getElementsByTagName(nodeName);
nodeArray = nodeArray.concat(Array.prototype.slice.call(nodeList));
@@ -3678,7 +3629,7 @@ if (typeof console !== 'undefined') {
function selectorMatches(element, selector) {
var nodeName = element.nodeName,
classNames = element.getAttribute('class'),
- id = element.getAttribute('id'), matcher;
+ id = element.getAttribute('id'), matcher, i;
// i check if a selector matches slicing away part from it.
// if i get empty string i should match
matcher = new RegExp('^' + nodeName, 'i');
@@ -3689,7 +3640,7 @@ if (typeof console !== 'undefined') {
}
if (classNames && selector.length) {
classNames = classNames.split(' ');
- for (var i = classNames.length; i--;) {
+ for (i = classNames.length; i--;) {
matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i');
selector = selector.replace(matcher, '');
}
@@ -3707,8 +3658,8 @@ if (typeof console !== 'undefined') {
if (el) {
return el;
}
- var node, i, nodelist = doc.getElementsByTagName('*');
- for (i = 0; i < nodelist.length; i++) {
+ var node, i, len, nodelist = doc.getElementsByTagName('*');
+ for (i = 0, len = nodelist.length; i < len; i++) {
node = nodelist[i];
if (id === node.getAttribute('id')) {
return node;
@@ -3729,12 +3680,12 @@ if (typeof console !== 'undefined') {
y = el.getAttribute('y') || 0,
el2 = elementById(doc, xlink).cloneNode(true),
currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
- parentNode, oldLength = nodelist.length, attr, j, attrs, l;
+ parentNode, oldLength = nodelist.length, attr, j, attrs, len;
applyViewboxTransform(el2);
if (/^svg$/i.test(el2.nodeName)) {
var el3 = el2.ownerDocument.createElement('g');
- for (j = 0, attrs = el2.attributes, l = attrs.length; j < l; j++) {
+ for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) {
attr = attrs.item(j);
el3.setAttribute(attr.nodeName, attr.nodeValue);
}
@@ -3745,7 +3696,7 @@ if (typeof console !== 'undefined') {
el2 = el3;
}
- for (j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
+ for (j = 0, attrs = el.attributes, len = attrs.length; j < len; j++) {
attr = attrs.item(j);
if (attr.nodeName === 'x' || attr.nodeName === 'y' || attr.nodeName === 'xlink:href') {
continue;
@@ -3798,7 +3749,7 @@ if (typeof console !== 'undefined') {
x = element.getAttribute('x') || 0,
y = element.getAttribute('y') || 0,
preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '',
- missingViewBox = (!viewBoxAttr || !reViewBoxTagNames.test(element.nodeName)
+ missingViewBox = (!viewBoxAttr || !fabric.svgViewBoxElementsRegEx.test(element.nodeName)
|| !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))),
missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'),
toBeParsed = missingViewBox && missingDimAttr,
@@ -3902,7 +3853,7 @@ if (typeof console !== 'undefined') {
parseUseDirectives(doc);
- var svgUid = fabric.Object.__uid++,
+ var svgUid = fabric.Object.__uid++, i, len,
options = applyViewboxTransform(doc),
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
@@ -3913,7 +3864,7 @@ if (typeof console !== 'undefined') {
// https://github.com/ajaxorg/node-o3-xml/issues/21
descendants = doc.selectNodes('//*[name(.)!="svg"]');
var arr = [];
- for (var i = 0, len = descendants.length; i < len; i++) {
+ for (i = 0, len = descendants.length; i < len; i++) {
arr[i] = descendants[i];
}
descendants = arr;
@@ -3921,8 +3872,8 @@ if (typeof console !== 'undefined') {
var elements = descendants.filter(function(el) {
applyViewboxTransform(el);
- return reAllowedSVGTagNames.test(el.nodeName.replace('svg:', '')) &&
- !hasAncestorWithNodeName(el, reNotAllowedAncestors); // http://www.w3.org/TR/SVG/struct.html#DefsElement
+ return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) &&
+ !hasAncestorWithNodeName(el, fabric.svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement
});
if (!elements || (elements && !elements.length)) {
@@ -3933,9 +3884,9 @@ if (typeof console !== 'undefined') {
fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
// Precedence of rules: style > class > attribute
- fabric.parseElements(elements, function(instances) {
+ fabric.parseElements(elements, function(instances, elements) {
if (callback) {
- callback(instances, options);
+ callback(instances, options, elements, descendants);
}
}, clone(options), reviver, parsingOptions);
};
@@ -4049,7 +4000,7 @@ if (typeof console !== 'undefined') {
svgUid = element.getAttribute('svgUid');
}
// if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
- if (element.parentNode && reAllowedParents.test(element.parentNode.nodeName)) {
+ if (element.parentNode && fabric.svgValidParentsRegEx.test(element.parentNode.nodeName)) {
parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
}
fontSize = (parentAttributes && parentAttributes.fontSize ) ||
@@ -4077,7 +4028,7 @@ if (typeof console !== 'undefined') {
fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle);
}
var mergedAttrs = extend(parentAttributes, normalizedStyle);
- return reAllowedParents.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs);
+ return fabric.svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs);
},
/**
@@ -4138,9 +4089,7 @@ if (typeof console !== 'undefined') {
points = points.split(/\s+/);
var parsedPoints = [], i, len;
- i = 0;
- len = points.length;
- for (; i < len; i += 2) {
+ for (i = 0, len = points.length; i < len; i += 2) {
parsedPoints.push({
x: parseFloat(points[i]),
y: parseFloat(points[i + 1])
@@ -4164,11 +4113,11 @@ if (typeof console !== 'undefined') {
* @return {Object} CSS rules of this document
*/
getCSSRules: function(doc) {
- var styles = doc.getElementsByTagName('style'),
+ var styles = doc.getElementsByTagName('style'), i, len,
allRules = { }, rules;
// very crude parsing of style contents
- for (var i = 0, len = styles.length; i < len; i++) {
+ for (i = 0, len = styles.length; i < len; i++) {
// IE9 doesn't support textContent, but provides text instead.
var styleContents = styles[i].textContent || styles[i].text;
@@ -4179,13 +4128,14 @@ if (typeof console !== 'undefined') {
}
rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
rules = rules.map(function(rule) { return rule.trim(); });
+ // eslint-disable-next-line no-loop-func
rules.forEach(function(rule) {
var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
ruleObj = { }, declaration = match[2].trim(),
propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
- for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
+ for (i = 0, len = propertyValuePairs.length; i < len; i++) {
var pair = propertyValuePairs[i].split(/\s*:\s*/),
property = pair[0],
value = pair[1];
@@ -4240,8 +4190,8 @@ if (typeof console !== 'undefined') {
callback && callback(null);
}
- fabric.parseSVGDocument(xml.documentElement, function (results, _options) {
- callback && callback(results, _options);
+ fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) {
+ callback && callback(results, _options, elements, allElements);
}, reviver, options);
}
},
@@ -4271,8 +4221,8 @@ if (typeof console !== 'undefined') {
doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
}
- fabric.parseSVGDocument(doc.documentElement, function (results, _options) {
- callback(results, _options);
+ fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) {
+ callback(results, _options, elements, allElements);
}, reviver, options);
}
});
@@ -4323,24 +4273,19 @@ fabric.ElementsParser.prototype.createObject = function(el, index) {
};
fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
- if (klass.async) {
- klass.fromElement(el, this.createCallback(index, el), this.options);
- }
- else {
- var obj = klass.fromElement(el, this.options);
- this.resolveGradient(obj, 'fill');
- this.resolveGradient(obj, 'stroke');
- this.reviver && this.reviver(el, obj);
- this.instances[index] = obj;
- this.checkIfDone();
- }
+ klass.fromElement(el, this.createCallback(index, el), this.options);
};
fabric.ElementsParser.prototype.createCallback = function(index, el) {
var _this = this;
return function(obj) {
+ var _options;
_this.resolveGradient(obj, 'fill');
_this.resolveGradient(obj, 'stroke');
+ if (obj instanceof fabric.Image) {
+ _options = obj.parsePreserveAspectRatioAttribute(el);
+ }
+ obj._removeTransformMatrix(_options);
_this.reviver && _this.reviver(el, obj);
_this.instances[index] = obj;
_this.checkIfDone();
@@ -4366,7 +4311,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
// eslint-disable-next-line no-eq-null, eqeqeq
return el != null;
});
- this.callback(this.instances);
+ this.callback(this.instances, this.elements);
}
};
@@ -4491,7 +4436,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
},
/**
- * Miltiplies this point by a value and returns a new one
+ * Multiplies this point by a value and returns a new one
* TODO: rename in scalarMultiply in 2.0
* @param {Number} scalar
* @return {fabric.Point}
@@ -4501,7 +4446,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
},
/**
- * Miltiplies this point by a value
+ * Multiplies this point by a value
* TODO: rename in scalarMultiplyEquals in 2.0
* @param {Number} scalar
* @return {fabric.Point} thisArg
@@ -4812,9 +4757,9 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
fabric.Intersection.intersectLinePolygon = function(a1, a2, points) {
var result = new Intersection(),
length = points.length,
- b1, b2, inter;
+ b1, b2, inter, i;
- for (var i = 0; i < length; i++) {
+ for (i = 0; i < length; i++) {
b1 = points[i];
b2 = points[(i + 1) % length];
inter = Intersection.intersectLineLine(a1, a2, b1, b2);
@@ -4836,9 +4781,9 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
*/
fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
var result = new Intersection(),
- length = points1.length;
+ length = points1.length, i;
- for (var i = 0; i < length; i++) {
+ for (i = 0; i < length; i++) {
var a1 = points1[i],
a2 = points1[(i + 1) % length],
inter = Intersection.intersectLinePolygon(a1, a2, points2);
@@ -5147,9 +5092,9 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
alpha = this.getAlpha(),
otherAlpha = 0.5,
source = this.getSource(),
- otherSource = otherColor.getSource();
+ otherSource = otherColor.getSource(), i;
- for (var i = 0; i < 3; i++) {
+ for (i = 0; i < 3; i++) {
result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
}
@@ -5165,7 +5110,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @field
* @memberOf fabric.Color
*/
- // eslint-disable-next-line max-len
+ // eslint-disable-next-line max-len
fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*((?:\d*\.?\d+)?)\s*)?\)$/;
/**
@@ -5185,31 +5130,161 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
fabric.Color.reHex = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
/**
- * Map of the 17 basic color names with HEX code
+ * Map of the 148 color names with HEX code
* @static
* @field
* @memberOf fabric.Color
- * @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
+ * @see: https://www.w3.org/TR/css3-color/#svg-color
*/
fabric.Color.colorNameMap = {
- aqua: '#00FFFF',
- black: '#000000',
- blue: '#0000FF',
- fuchsia: '#FF00FF',
- gray: '#808080',
- grey: '#808080',
- green: '#008000',
- lime: '#00FF00',
- maroon: '#800000',
- navy: '#000080',
- olive: '#808000',
- orange: '#FFA500',
- purple: '#800080',
- red: '#FF0000',
- silver: '#C0C0C0',
- teal: '#008080',
- white: '#FFFFFF',
- yellow: '#FFFF00'
+ aliceblue: '#F0F8FF',
+ antiquewhite: '#FAEBD7',
+ aqua: '#00FFFF',
+ aquamarine: '#7FFFD4',
+ azure: '#F0FFFF',
+ beige: '#F5F5DC',
+ bisque: '#FFE4C4',
+ black: '#000000',
+ blanchedalmond: '#FFEBCD',
+ blue: '#0000FF',
+ blueviolet: '#8A2BE2',
+ brown: '#A52A2A',
+ burlywood: '#DEB887',
+ cadetblue: '#5F9EA0',
+ chartreuse: '#7FFF00',
+ chocolate: '#D2691E',
+ coral: '#FF7F50',
+ cornflowerblue: '#6495ED',
+ cornsilk: '#FFF8DC',
+ crimson: '#DC143C',
+ cyan: '#00FFFF',
+ darkblue: '#00008B',
+ darkcyan: '#008B8B',
+ darkgoldenrod: '#B8860B',
+ darkgray: '#A9A9A9',
+ darkgrey: '#A9A9A9',
+ darkgreen: '#006400',
+ darkkhaki: '#BDB76B',
+ darkmagenta: '#8B008B',
+ darkolivegreen: '#556B2F',
+ darkorange: '#FF8C00',
+ darkorchid: '#9932CC',
+ darkred: '#8B0000',
+ darksalmon: '#E9967A',
+ darkseagreen: '#8FBC8F',
+ darkslateblue: '#483D8B',
+ darkslategray: '#2F4F4F',
+ darkslategrey: '#2F4F4F',
+ darkturquoise: '#00CED1',
+ darkviolet: '#9400D3',
+ deeppink: '#FF1493',
+ deepskyblue: '#00BFFF',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1E90FF',
+ firebrick: '#B22222',
+ floralwhite: '#FFFAF0',
+ forestgreen: '#228B22',
+ fuchsia: '#FF00FF',
+ gainsboro: '#DCDCDC',
+ ghostwhite: '#F8F8FF',
+ gold: '#FFD700',
+ goldenrod: '#DAA520',
+ gray: '#808080',
+ grey: '#808080',
+ green: '#008000',
+ greenyellow: '#ADFF2F',
+ honeydew: '#F0FFF0',
+ hotpink: '#FF69B4',
+ indianred: '#CD5C5C',
+ indigo: '#4B0082',
+ ivory: '#FFFFF0',
+ khaki: '#F0E68C',
+ lavender: '#E6E6FA',
+ lavenderblush: '#FFF0F5',
+ lawngreen: '#7CFC00',
+ lemonchiffon: '#FFFACD',
+ lightblue: '#ADD8E6',
+ lightcoral: '#F08080',
+ lightcyan: '#E0FFFF',
+ lightgoldenrodyellow: '#FAFAD2',
+ lightgray: '#D3D3D3',
+ lightgrey: '#D3D3D3',
+ lightgreen: '#90EE90',
+ lightpink: '#FFB6C1',
+ lightsalmon: '#FFA07A',
+ lightseagreen: '#20B2AA',
+ lightskyblue: '#87CEFA',
+ lightslategray: '#778899',
+ lightslategrey: '#778899',
+ lightsteelblue: '#B0C4DE',
+ lightyellow: '#FFFFE0',
+ lime: '#00FF00',
+ limegreen: '#32CD32',
+ linen: '#FAF0E6',
+ magenta: '#FF00FF',
+ maroon: '#800000',
+ mediumaquamarine: '#66CDAA',
+ mediumblue: '#0000CD',
+ mediumorchid: '#BA55D3',
+ mediumpurple: '#9370DB',
+ mediumseagreen: '#3CB371',
+ mediumslateblue: '#7B68EE',
+ mediumspringgreen: '#00FA9A',
+ mediumturquoise: '#48D1CC',
+ mediumvioletred: '#C71585',
+ midnightblue: '#191970',
+ mintcream: '#F5FFFA',
+ mistyrose: '#FFE4E1',
+ moccasin: '#FFE4B5',
+ navajowhite: '#FFDEAD',
+ navy: '#000080',
+ oldlace: '#FDF5E6',
+ olive: '#808000',
+ olivedrab: '#6B8E23',
+ orange: '#FFA500',
+ orangered: '#FF4500',
+ orchid: '#DA70D6',
+ palegoldenrod: '#EEE8AA',
+ palegreen: '#98FB98',
+ paleturquoise: '#AFEEEE',
+ palevioletred: '#DB7093',
+ papayawhip: '#FFEFD5',
+ peachpuff: '#FFDAB9',
+ peru: '#CD853F',
+ pink: '#FFC0CB',
+ plum: '#DDA0DD',
+ powderblue: '#B0E0E6',
+ purple: '#800080',
+ rebeccapurple: '#663399',
+ red: '#FF0000',
+ rosybrown: '#BC8F8F',
+ royalblue: '#4169E1',
+ saddlebrown: '#8B4513',
+ salmon: '#FA8072',
+ sandybrown: '#F4A460',
+ seagreen: '#2E8B57',
+ seashell: '#FFF5EE',
+ sienna: '#A0522D',
+ silver: '#C0C0C0',
+ skyblue: '#87CEEB',
+ slateblue: '#6A5ACD',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#FFFAFA',
+ springgreen: '#00FF7F',
+ steelblue: '#4682B4',
+ tan: '#D2B48C',
+ teal: '#008080',
+ thistle: '#D8BFD8',
+ tomato: '#FF6347',
+ turquoise: '#40E0D0',
+ violet: '#EE82EE',
+ wheat: '#F5DEB3',
+ white: '#FFFFFF',
+ whitesmoke: '#F5F5F5',
+ yellow: '#FFFF00',
+ yellowgreen: '#9ACD32'
};
/**
@@ -5398,7 +5473,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
function getColorStop(el) {
var style = el.getAttribute('style'),
offset = el.getAttribute('offset') || 0,
- color, colorAlpha, opacity;
+ color, colorAlpha, opacity, i;
// convert percents to absolute values
offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
@@ -5410,7 +5485,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
keyValuePairs.pop();
}
- for (var i = keyValuePairs.length; i--; ) {
+ for (i = keyValuePairs.length; i--; ) {
var split = keyValuePairs[i].split(/\s*:\s*/),
key = split[0].trim(),
@@ -5565,22 +5640,24 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @return {String} SVG representation of an gradient (linear/radial)
*/
toSVG: function(object) {
- var coords = clone(this.coords, true),
+ var coords = clone(this.coords, true), i, len,
markup, commonAttributes, colorStops = clone(this.colorStops, true),
- needsSwap = coords.r1 > coords.r2;
+ needsSwap = coords.r1 > coords.r2,
+ offsetX = object.width / 2, offsetY = object.height / 2;
// colorStops must be sorted ascending
colorStops.sort(function(a, b) {
return a.offset - b.offset;
});
-
- if (!(object.group && object.group.type === 'path-group')) {
- for (var prop in coords) {
- if (prop === 'x1' || prop === 'x2') {
- coords[prop] += this.offsetX - object.width / 2;
- }
- else if (prop === 'y1' || prop === 'y2') {
- coords[prop] += this.offsetY - object.height / 2;
- }
+ if (object.type === 'path') {
+ offsetX -= object.pathOffset.x;
+ offsetY -= object.pathOffset.y;
+ }
+ for (var prop in coords) {
+ if (prop === 'x1' || prop === 'x2') {
+ coords[prop] += this.offsetX - offsetX;
+ }
+ else if (prop === 'y1' || prop === 'y2') {
+ coords[prop] += this.offsetY - offsetY;
}
}
@@ -5619,7 +5696,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
// svg goes from internal to external radius. if radius are inverted, swap color stops.
colorStops = colorStops.concat();
colorStops.reverse();
- for (var i = 0; i < colorStops.length; i++) {
+ for (i = 0, len = colorStops.length; i < len; i++) {
colorStops[i].offset = 1 - colorStops[i].offset;
}
}
@@ -5628,19 +5705,19 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
// i have to shift all colorStops and add new one in 0.
var maxRadius = Math.max(coords.r1, coords.r2),
percentageShift = minRadius / maxRadius;
- for (var i = 0; i < colorStops.length; i++) {
+ for (i = 0, len = colorStops.length; i < len; i++) {
colorStops[i].offset += percentageShift * (1 - colorStops[i].offset);
}
}
}
- for (var i = 0; i < colorStops.length; i++) {
+ for (i = 0, len = colorStops.length; i < len; i++) {
var colorStop = colorStops[i];
markup.push(
'<stop ',
- 'offset="', (colorStop.offset * 100) + '%',
- '" style="stop-color:', colorStop.color,
- (colorStop.opacity !== null ? ';stop-opacity: ' + colorStop.opacity : ';'),
+ 'offset="', (colorStop.offset * 100) + '%',
+ '" style="stop-color:', colorStop.color,
+ (colorStop.opacity !== null ? ';stop-opacity: ' + colorStop.opacity : ';'),
'"/>\n'
);
}
@@ -5654,27 +5731,15 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
/**
* Returns an instance of CanvasGradient
* @param {CanvasRenderingContext2D} ctx Context to render on
- * @param {Object} object
* @return {CanvasGradient}
*/
- toLive: function(ctx, object) {
- var gradient, prop, coords = fabric.util.object.clone(this.coords);
+ toLive: function(ctx) {
+ var gradient, coords = fabric.util.object.clone(this.coords), i, len;
if (!this.type) {
return;
}
- if (object.group && object.group.type === 'path-group') {
- for (prop in coords) {
- if (prop === 'x1' || prop === 'x2') {
- coords[prop] += -this.offsetX + object.width / 2;
- }
- else if (prop === 'y1' || prop === 'y2') {
- coords[prop] += -this.offsetY + object.height / 2;
- }
- }
- }
-
if (this.type === 'linear') {
gradient = ctx.createLinearGradient(
coords.x1, coords.y1, coords.x2, coords.y2);
@@ -5684,7 +5749,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
coords.x1, coords.y1, coords.r1, coords.x2, coords.y2, coords.r2);
}
- for (var i = 0, len = this.colorStops.length; i < len; i++) {
+ for (i = 0, len = this.colorStops.length; i < len; i++) {
var color = this.colorStops[i].color,
opacity = this.colorStops[i].opacity,
offset = this.colorStops[i].offset;
@@ -5752,7 +5817,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
gradientTransform = el.getAttribute('gradientTransform'),
colorStops = [],
- coords, ellipseMatrix;
+ coords, ellipseMatrix, i;
if (el.nodeName === 'linearGradient' || el.nodeName === 'LINEARGRADIENT') {
type = 'linear';
@@ -5768,7 +5833,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
coords = getRadialCoords(el);
}
- for (var i = colorStopEls.length; i--; ) {
+ for (i = colorStopEls.length; i--; ) {
colorStops.push(getColorStop(colorStopEls[i]));
}
@@ -5785,6 +5850,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
if (gradientTransform || ellipseMatrix !== '') {
gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix);
}
+
return gradient;
},
/* _FROM_SVG_END_ */
@@ -6091,7 +6157,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
/**
* Constructor
- * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetX properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px, "2px 2px 10px rgba(0,0,0,0.2)")
+ * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetY properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px")
* @return {fabric.Shadow} thisArg
*/
initialize: function(options) {
@@ -6256,7 +6322,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
*/
initialize: function(el, options) {
options || (options = { });
-
+ this.renderAndResetBound = this.renderAndReset.bind(this);
+ this.requestRenderAllBound = this.requestRenderAll.bind(this);
this._initStatic(el, options);
},
@@ -6314,9 +6381,12 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
stateful: false,
/**
- * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove} should also re-render canvas.
- * Disabling this option could give a great performance boost when adding/removing a lot of objects to/from canvas at once
- * (followed by a manual rendering after addition/deletion)
+ * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove},
+ * {@link fabric.StaticCanvas.moveTo}, {@link fabric.StaticCanvas.clear} and many more, should also re-render canvas.
+ * Disabling this option will not give a performance boost when adding/removing a lot of objects to/from canvas at once
+ * since the renders are quequed and executed one per frame.
+ * Disabling is suggested anyway and managing the renders of the app manually is not a big effort ( canvas.requestRenderAll() )
+ * Left default to true to do not break documentation and old app, fiddles.
* @type Boolean
* @default
*/
@@ -6325,6 +6395,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
/**
* Function that determines clipping of entire canvas area
* Being passed context as first argument. See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
+ * @deprecated since 2.0.0
* @type Function
* @default
*/
@@ -6383,6 +6454,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
/**
* When true, canvas is scaled by devicePixelRatio for better rendering on retina screens
+ * @type Boolean
+ * @default
*/
enableRetinaScaling: true,
@@ -6404,8 +6477,10 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* If One of the corner of the bounding box of the object is on the canvas
* the objects get rendered.
* @memberOf fabric.StaticCanvas.prototype
+ * @type Boolean
+ * @default
*/
- skipOffscreen: false,
+ skipOffscreen: true,
/**
* @private
@@ -6413,7 +6488,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @param {Object} [options] Options object
*/
_initStatic: function(el, options) {
- var cb = fabric.StaticCanvas.prototype.renderAll.bind(this);
+ var cb = this.requestRenderAllBound;
this._objects = [];
this._createLowerCanvas(el);
this._initOptions(options);
@@ -6580,9 +6655,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}
@@ -6682,14 +6757,14 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
/**
* @private
*/
- _createCanvasElement: function(canvasEl) {
- var element = fabric.util.createCanvasElement(canvasEl);
- if (!element.style) {
- element.style = { };
- }
+ _createCanvasElement: function() {
+ var element = fabric.util.createCanvasElement();
if (!element) {
throw CANVAS_INIT_ERROR;
}
+ if (!element.style) {
+ element.style = { };
+ }
if (typeof element.getContext === 'undefined') {
throw CANVAS_INIT_ERROR;
}
@@ -6725,7 +6800,13 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @param {HTMLElement} [canvasEl]
*/
_createLowerCanvas: function (canvasEl) {
- this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement(canvasEl);
+ // canvasEl === 'HTMLCanvasElement' does not work on jsdom/node
+ if (canvasEl && canvasEl.getContext) {
+ this.lowerCanvasEl = canvasEl;
+ }
+ else {
+ this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
+ }
fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
@@ -6811,7 +6892,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
this.calcOffset();
if (!options.cssOnly) {
- this.renderAll();
+ this.requestRenderAll();
}
return this;
@@ -6878,17 +6959,17 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @chainable true
*/
setViewportTransform: function (vpt) {
- var activeGroup = this._activeGroup, object, ignoreVpt = false, skipAbsolute = true;
+ var activeObject = this._activeObject, object, ignoreVpt = false, skipAbsolute = true, i, len;
this.viewportTransform = vpt;
- for (var i = 0, len = this._objects.length; i < len; i++) {
+ for (i = 0, len = this._objects.length; i < len; i++) {
object = this._objects[i];
object.group || object.setCoords(ignoreVpt, skipAbsolute);
}
- if (activeGroup) {
- activeGroup.setCoords(ignoreVpt, skipAbsolute);
+ if (activeObject && activeObject.type === 'activeSelection') {
+ activeObject.setCoords(ignoreVpt, skipAbsolute);
}
this.calcViewportBoundaries();
- this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -7015,7 +7096,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
}
this.clearContext(this.contextContainer);
this.fire('canvas:cleared');
- this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -7031,14 +7112,38 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
},
/**
- * Calculate the position of the 4 corner of canvas with current viewportTransform.
- * helps to determinate when an object is in the current rendering viewport using
- * object absolute coordinates ( aCoords )
+ * Function created to be instance bound at initialization
+ * used in requestAnimationFrame rendering
+ * @return {fabric.Canvas} instance
+ * @chainable
+ */
+ renderAndReset: function() {
+ this.isRendering = 0;
+ this.renderAll();
+ },
+
+ /**
+ * Append a renderAll request to next animation frame.
+ * a boolean flag will avoid appending more.
+ * @return {fabric.Canvas} instance
+ * @chainable
+ */
+ requestRenderAll: function () {
+ if (!this.isRendering) {
+ this.isRendering = fabric.util.requestAnimFrame(this.renderAndResetBound);
+ }
+ return this;
+ },
+
+ /**
+ * Calculate the position of the 4 corner of canvas with current viewportTransform.
+ * helps to determinate when an object is in the current rendering viewport using
+ * object absolute coordinates ( aCoords )
* @return {Object} points.tl
* @chainable
*/
calcViewportBoundaries: function() {
- var points = { }, width = this.getWidth(), height = this.getHeight(),
+ var points = { }, width = this.width, height = this.height,
iVpt = invertTransform(this.viewportTransform);
points.tl = transformPoint({ x: 0, y: 0 }, iVpt);
points.br = transformPoint({ x: width, y: height }, iVpt);
@@ -7056,6 +7161,11 @@ 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;
+ }
this.calcViewportBoundaries();
this.clearContext(ctx);
this.fire('before:render');
@@ -7066,7 +7176,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) {
@@ -7088,7 +7198,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @param {Array} objects to render
*/
_renderObjects: function(ctx, objects) {
- for (var i = 0, length = objects.length; i < length; ++i) {
+ var i, len;
+ for (i = 0, len = objects.length; i < len; ++i) {
objects[i] && objects[i].render(ctx);
}
},
@@ -7099,7 +7210,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)
@@ -7114,8 +7225,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();
@@ -7145,8 +7257,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
*/
getCenter: function () {
return {
- top: this.getHeight() / 2,
- left: this.getWidth() / 2
+ top: this.height / 2,
+ left: this.width / 2
};
},
@@ -7243,7 +7355,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
*/
_centerObject: function(object, center) {
object.setPositionByOrigin(center, 'center', 'center');
- this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -7280,6 +7392,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
_toObjectMethod: function (methodName, propertiesToInclude) {
var data = {
+ version: fabric.version,
objects: this._toObjects(methodName, propertiesToInclude)
};
@@ -7422,8 +7535,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
}
markup.push(
'<?xml version="1.0" encoding="', (options.encoding || 'UTF-8'), '" standalone="no" ?>\n',
- '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
- '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
+ '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
+ '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
);
},
@@ -7456,17 +7569,17 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
markup.push(
'<svg ',
- 'xmlns="http://www.w3.org/2000/svg" ',
- 'xmlns:xlink="http://www.w3.org/1999/xlink" ',
- 'version="1.1" ',
- 'width="', width, '" ',
- 'height="', height, '" ',
- viewBox,
- 'xml:space="preserve">\n',
+ 'xmlns="http://www.w3.org/2000/svg" ',
+ 'xmlns:xlink="http://www.w3.org/1999/xlink" ',
+ 'version="1.1" ',
+ 'width="', width, '" ',
+ 'height="', height, '" ',
+ viewBox,
+ 'xml:space="preserve">\n',
'<desc>Created with Fabric.js ', fabric.version, '</desc>\n',
'<defs>\n',
- this.createSVGFontFacesMarkup(),
- this.createSVGRefElementsMarkup(),
+ this.createSVGFontFacesMarkup(),
+ this.createSVGRefElementsMarkup(),
'</defs>\n'
);
},
@@ -7495,10 +7608,10 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
*/
createSVGFontFacesMarkup: function() {
var markup = '', fontList = { }, obj, fontFamily,
- style, row, rowIndex, _char, charIndex,
+ style, row, rowIndex, _char, charIndex, i, len,
fontPaths = fabric.fontPaths, objects = this.getObjects();
- for (var i = 0, len = objects.length; i < len; i++) {
+ for (i = 0, len = objects.length; i < len; i++) {
obj = objects[i];
fontFamily = obj.fontFamily;
if (obj.type.indexOf('text') === -1 || fontList[fontFamily] || !fontPaths[fontFamily]) {
@@ -7547,8 +7660,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @private
*/
_setSVGObjects: function(markup, reviver) {
- var instance;
- for (var i = 0, objects = this.getObjects(), len = objects.length; i < len; i++) {
+ var instance, i, len, objects = this.getObjects();
+ for (i = 0, len = objects.length; i < len; i++) {
instance = objects[i];
if (instance.excludeFromExport) {
continue;
@@ -7558,7 +7671,6 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
},
/**
- * push single object svg representation in the markup
* @private
*/
_setSVGObject: function(markup, instance, reviver) {
@@ -7586,25 +7698,25 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
var repeat = filler.repeat;
markup.push(
'<rect transform="translate(', this.width / 2, ',', this.height / 2, ')"',
- ' x="', filler.offsetX - this.width / 2, '" y="', filler.offsetY - this.height / 2, '" ',
- 'width="',
- (repeat === 'repeat-y' || repeat === 'no-repeat'
- ? filler.source.width
- : this.width),
- '" height="',
- (repeat === 'repeat-x' || repeat === 'no-repeat'
- ? filler.source.height
- : this.height),
- '" fill="url(#SVGID_' + filler.id + ')"',
+ ' x="', filler.offsetX - this.width / 2, '" y="', filler.offsetY - this.height / 2, '" ',
+ 'width="',
+ (repeat === 'repeat-y' || repeat === 'no-repeat'
+ ? filler.source.width
+ : this.width),
+ '" height="',
+ (repeat === 'repeat-x' || repeat === 'no-repeat'
+ ? filler.source.height
+ : this.height),
+ '" fill="url(#SVGID_' + filler.id + ')"',
'></rect>\n'
);
}
else {
markup.push(
'<rect x="0" y="0" ',
- 'width="', this.width,
- '" height="', this.height,
- '" fill="', this[property], '"',
+ 'width="', this.width,
+ '" height="', this.height,
+ '" fill="', this[property], '"',
'></rect>\n'
);
}
@@ -7622,10 +7734,10 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
if (!object) {
return this;
}
- var activeGroup = this._activeGroup,
+ var activeSelection = this._activeObject,
i, obj, objs;
- if (object === activeGroup) {
- objs = activeGroup._objects;
+ if (object === activeSelection && object.type === 'activeSelection') {
+ objs = activeSelection._objects;
for (i = objs.length; i--;) {
obj = objs[i];
removeFromArray(this._objects, obj);
@@ -7636,7 +7748,8 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
removeFromArray(this._objects, object);
this._objects.unshift(object);
}
- return this.renderAll && this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
},
/**
@@ -7650,10 +7763,10 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
if (!object) {
return this;
}
- var activeGroup = this._activeGroup,
+ var activeSelection = this._activeObject,
i, obj, objs;
- if (object === activeGroup) {
- objs = activeGroup._objects;
+ if (object === activeSelection && object.type === 'activeSelection') {
+ objs = activeSelection._objects;
for (i = 0; i < objs.length; i++) {
obj = objs[i];
removeFromArray(this._objects, obj);
@@ -7664,11 +7777,16 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
removeFromArray(this._objects, object);
this._objects.push(object);
}
- return this.renderAll && this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
},
/**
* Moves an object or a selection down in stack of drawn objects
+ * An optional paramter, intersecting allowes to move the object in behind
+ * the first intersecting object. Where intersection is calculated with
+ * bounding box. If no intersection is found, there will not be change in the
+ * stack.
* @param {fabric.Object} object Object to send
* @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
* @return {fabric.Canvas} thisArg
@@ -7678,12 +7796,11 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
if (!object) {
return this;
}
-
- var activeGroup = this._activeGroup,
+ var activeSelection = this._activeObject,
i, obj, idx, newIdx, objs, objsMoved = 0;
- if (object === activeGroup) {
- objs = activeGroup._objects;
+ if (object === activeSelection && object.type === 'activeSelection') {
+ objs = activeSelection._objects;
for (i = 0; i < objs.length; i++) {
obj = objs[i];
idx = this._objects.indexOf(obj);
@@ -7704,7 +7821,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
this._objects.splice(newIdx, 0, object);
}
}
- this.renderAll && this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -7712,13 +7829,13 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @private
*/
_findNewLowerIndex: function(object, idx, intersecting) {
- var newIdx;
+ var newIdx, i;
if (intersecting) {
newIdx = idx;
// traverse down the stack looking for the nearest intersecting object
- for (var i = idx - 1; i >= 0; --i) {
+ for (i = idx - 1; i >= 0; --i) {
var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
object.isContainedWithinObject(this._objects[i]) ||
@@ -7739,6 +7856,10 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
/**
* Moves an object or a selection up in stack of drawn objects
+ * An optional paramter, intersecting allowes to move the object in front
+ * of the first intersecting object. Where intersection is calculated with
+ * bounding box. If no intersection is found, there will not be change in the
+ * stack.
* @param {fabric.Object} object Object to send
* @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
* @return {fabric.Canvas} thisArg
@@ -7748,12 +7869,11 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
if (!object) {
return this;
}
-
- var activeGroup = this._activeGroup,
+ var activeSelection = this._activeObject,
i, obj, idx, newIdx, objs, objsMoved = 0;
- if (object === activeGroup) {
- objs = activeGroup._objects;
+ if (object === activeSelection && object.type === 'activeSelection') {
+ objs = activeSelection._objects;
for (i = objs.length; i--;) {
obj = objs[i];
idx = this._objects.indexOf(obj);
@@ -7774,7 +7894,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
this._objects.splice(newIdx, 0, object);
}
}
- this.renderAll && this.renderAll();
+ this.renderOnAddRemove && this.requestRenderAll();
return this;
},
@@ -7782,13 +7902,13 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
* @private
*/
_findNewUpperIndex: function(object, idx, intersecting) {
- var newIdx;
+ var newIdx, i, len;
if (intersecting) {
newIdx = idx;
// traverse up the stack looking for the nearest intersecting object
- for (var i = idx + 1; i < this._objects.length; ++i) {
+ for (i = idx + 1, len = this._objects.length; i < len; ++i) {
var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
object.isContainedWithinObject(this._objects[i]) ||
@@ -7817,7 +7937,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() {
moveTo: function (object, index) {
removeFromArray(this._objects, object);
this._objects.splice(index, 0, object);
- return this.renderAll && this.renderAll();
+ return this.renderOnAddRemove && this.requestRenderAll();
},
/**
@@ -7826,7 +7946,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;
},
@@ -7957,13 +8082,20 @@ 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
* @default
@@ -7987,10 +8119,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);
@@ -8136,7 +8268,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
@@ -8215,6 +8346,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,
});
@@ -8245,18 +8377,17 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
// whereas Chrome 10 renders nothing
- this.canvas.renderAll();
+ this.canvas.requestRenderAll();
return;
}
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.renderAll();
+
// fire event 'path' created
this.canvas.fire('path:created', { path: path });
@@ -8330,12 +8461,12 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
* Invoked on mouse up
*/
onMouseUp: function() {
- var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
+ var originalRenderOnAddRemove = this.canvas.renderOnAddRemove, i, len;
this.canvas.renderOnAddRemove = false;
var circles = [];
- for (var i = 0, len = this.points.length; i < len; i++) {
+ for (i = 0, len = this.points.length; i < len; i++) {
var point = this.points[i],
circle = new fabric.Circle({
radius: point.radius,
@@ -8359,7 +8490,7 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
this.canvas.clearContext(this.canvas.contextTop);
this._resetShadow();
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
- this.canvas.renderAll();
+ this.canvas.requestRenderAll();
},
/**
@@ -8370,11 +8501,11 @@ fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric
var pointerPoint = new fabric.Point(pointer.x, pointer.y),
circleRadius = fabric.util.getRandomInt(
- Math.max(0, this.width - 20), this.width + 20) / 2,
+ Math.max(0, this.width - 20), this.width + 20) / 2,
circleColor = new fabric.Color(this.color)
- .setAlpha(fabric.util.getRandomInt(0, 100) / 100)
- .toRgba();
+ .setAlpha(fabric.util.getRandomInt(0, 100) / 100)
+ .toRgba();
pointerPoint.radius = circleRadius;
pointerPoint.fill = circleColor;
@@ -8508,7 +8639,7 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
this.canvas.clearContext(this.canvas.contextTop);
this._resetShadow();
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
- this.canvas.renderAll();
+ this.canvas.requestRenderAll();
},
/**
@@ -8518,9 +8649,9 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
_getOptimizedRects: function(rects) {
// avoid creating duplicate rects at the same coordinates
- var uniqueRects = { }, key;
+ var uniqueRects = { }, key, i, len;
- for (var i = 0, len = rects.length; i < len; i++) {
+ for (i = 0, len = rects.length; i < len; i++) {
key = rects[i].left + '' + rects[i].top;
if (!uniqueRects[key]) {
uniqueRects[key] = rects[i];
@@ -8541,11 +8672,11 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
var ctx = this.canvas.contextTop;
ctx.fillStyle = this.color;
- var v = this.canvas.viewportTransform;
+ var v = this.canvas.viewportTransform, i, len;
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
- for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
+ for (i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
var point = this.sprayChunkPoints[i];
if (typeof point.opacity !== 'undefined') {
ctx.globalAlpha = point.opacity;
@@ -8561,9 +8692,9 @@ fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric
addSprayChunk: function(pointer) {
this.sprayChunkPoints = [];
- var x, y, width, radius = this.width / 2;
+ var x, y, width, radius = this.width / 2, i;
- for (var i = 0; i < this.density; i++) {
+ for (i = 0; i < this.density; i++) {
x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius);
y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius);
@@ -8673,14 +8804,16 @@ 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
+ * @fires selection:updated
* @fires selection:created
*
* @fires path:created
@@ -8689,6 +8822,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @fires mouse:up
* @fires mouse:over
* @fires mouse:out
+ * @fires mouse:dblclick
*
*/
fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
@@ -8701,7 +8835,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
*/
initialize: function(el, options) {
options || (options = { });
-
+ this.renderAndResetBound = this.renderAndReset.bind(this);
this._initStatic(el, options);
this._initInteractive();
this._createCacheCanvas();
@@ -8744,7 +8878,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
centeredRotation: false,
/**
- * Indicates which key enable centered Transfrom
+ * Indicates which key enable centered Transform
* values: 'altKey', 'shiftKey', 'ctrlKey'.
* If `null` or 'none' or any other string that is not a modifier key
* feature is disabled feature disabled.
@@ -8780,12 +8914,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',
@@ -8866,6 +9001,14 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
rotationCursor: 'crosshair',
/**
+ * Cursor value used for disabled elements ( corners with disabled action )
+ * @type String
+ * @since 2.0.0
+ * @default
+ */
+ notAllowedCursor: 'not-allowed',
+
+ /**
* Default element class that's given to wrapper (div) element of canvas
* @type String
* @default
@@ -8975,25 +9118,25 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* @return {Array} objects to render immediately and pushes the other in the activeGroup.
*/
_chooseObjectsToRender: function(
<TRUNCATED>