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 2018/04/10 05:20:15 UTC
[openmeetings] branch master updated: [OPENMEETINGS-1848] libraries
are updated
This is an automated email from the ASF dual-hosted git repository.
solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git
The following commit(s) were added to refs/heads/master by this push:
new 463a781 [OPENMEETINGS-1848] libraries are updated
463a781 is described below
commit 463a7815fe2c20a3fbee9fe37be21b437a094621
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Tue Apr 10 12:15:44 2018 +0700
[OPENMEETINGS-1848] libraries are updated
---
openmeetings-core/pom.xml | 4 +-
openmeetings-db/pom.xml | 2 +-
openmeetings-server/pom.xml | 2 +-
openmeetings-util/pom.xml | 1 +
.../org/apache/openmeetings/web/room/wb/MathJax.js | 88 +-
.../org/apache/openmeetings/web/room/wb/fabric.js | 41330 ++++++-------------
pom.xml | 18 +-
7 files changed, 13641 insertions(+), 27804 deletions(-)
diff --git a/openmeetings-core/pom.xml b/openmeetings-core/pom.xml
index ca9f300..8190ac6 100644
--- a/openmeetings-core/pom.xml
+++ b/openmeetings-core/pom.xml
@@ -30,7 +30,7 @@
<name>Openmeetings Core</name>
<description>Module for OpenMeetings core and red5 related classes and services</description>
<properties>
- <mail.version>1.6.0</mail.version>
+ <javax.mail.version>1.6.1</javax.mail.version>
<site.basedir>${project.parent.basedir}</site.basedir>
</properties>
<build>
@@ -84,7 +84,7 @@
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
- <version>${mail.version}</version>
+ <version>${javax.mail.version}</version>
</dependency>
<dependency>
<groupId>org.artofsolving.jodconverter</groupId>
diff --git a/openmeetings-db/pom.xml b/openmeetings-db/pom.xml
index a8f36b9..12d7362 100644
--- a/openmeetings-db/pom.xml
+++ b/openmeetings-db/pom.xml
@@ -35,7 +35,7 @@
<commons-dbcp.version>2.2.0</commons-dbcp.version>
<commons-pool.version>1.6</commons-pool.version> <!-- required by OpenJPA for clustering -->
<commons-pool2.version>2.5.0</commons-pool2.version>
- <postgresql.version>42.2.1</postgresql.version>
+ <postgresql.version>42.2.2</postgresql.version>
<site.basedir>${project.parent.basedir}</site.basedir>
</properties>
<dependencies>
diff --git a/openmeetings-server/pom.xml b/openmeetings-server/pom.xml
index 08c6fac..6c9e9e3 100644
--- a/openmeetings-server/pom.xml
+++ b/openmeetings-server/pom.xml
@@ -117,7 +117,7 @@
<id>prepare-web-server</id>
<activation>
<file>
- <missing>web-server/apache-tomcat-9.0.6.tar.gz</missing> <!-- TODO should be in-sync with global property -->
+ <missing>web-server/apache-tomcat-9.0.7.tar.gz</missing> <!-- TODO should be in-sync with global property -->
</file>
</activation>
<build>
diff --git a/openmeetings-util/pom.xml b/openmeetings-util/pom.xml
index 93fe9a9..ae663cf 100644
--- a/openmeetings-util/pom.xml
+++ b/openmeetings-util/pom.xml
@@ -140,6 +140,7 @@
<groupId>org.apache.pdfbox</groupId>
<artifactId>jbig2-imageio</artifactId>
<version>3.0.0</version>
+ <scope>compile</scope>
</dependency>
</dependencies>
</project>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js
index 5e51c82..6ae86c7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js
@@ -46,9 +46,9 @@ if (window.MathJax) {window.MathJax = {AuthorConfig: window.MathJax}}
// MathJax.isPacked = true; // This line is uncommented by the packer.
-MathJax.version = "2.7.3";
-MathJax.fileversion = "2.7.3";
-MathJax.cdnVersion = "2.7.3"; // specifies a revision to break caching
+MathJax.version = "2.7.4";
+MathJax.fileversion = "2.7.4";
+MathJax.cdnVersion = "2.7.4"; // specifies a revision to break caching
MathJax.cdnFileVersions = {}; // can be used to specify revisions for individual files
/**********************************************************/
@@ -2908,7 +2908,7 @@ MathJax.Hub.Startup = {
}
},{
id: "Jax",
- version: "2.7.3",
+ version: "2.7.4",
directory: ROOT+"/jax",
extensionDir: ROOT+"/extensions"
});
@@ -2954,7 +2954,7 @@ MathJax.Hub.Startup = {
}
},{
id: "InputJax",
- version: "2.7.3",
+ version: "2.7.4",
directory: JAX.directory+"/input",
extensionDir: JAX.extensionDir
});
@@ -2987,7 +2987,7 @@ MathJax.Hub.Startup = {
Remove: function (jax) {}
},{
id: "OutputJax",
- version: "2.7.3",
+ version: "2.7.4",
directory: JAX.directory+"/output",
extensionDir: JAX.extensionDir,
fontDir: ROOT+(BASE.isPacked?"":"/..")+"/fonts",
@@ -3071,7 +3071,7 @@ MathJax.Hub.Startup = {
}
},{
id: "ElementJax",
- version: "2.7.3",
+ version: "2.7.4",
directory: JAX.directory+"/element",
extensionDir: JAX.extensionDir,
ID: 0, // jax counter (for IDs)
@@ -3095,7 +3095,7 @@ MathJax.Hub.Startup = {
// Some "Fake" jax used to allow menu access for "Math Processing Error" messages
//
BASE.OutputJax.Error = {
- id: "Error", version: "2.7.3", config: {}, errors: 0,
+ id: "Error", version: "2.7.4", config: {}, errors: 0,
ContextMenu: function () {return BASE.Extension.MathEvents.Event.ContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
Mousedown: function () {return BASE.Extension.MathEvents.Event.AltContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
getJaxFromMath: function (math) {return (math.nextSibling.MathJax||{}).error},
@@ -3114,7 +3114,7 @@ MathJax.Hub.Startup = {
}
};
BASE.InputJax.Error = {
- id: "Error", version: "2.7.3", config: {},
+ id: "Error", version: "2.7.4", config: {},
sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"]
};
@@ -3394,7 +3394,7 @@ MathJax.ElementJax.mml = MathJax.ElementJax({
mimeType: "jax/mml"
},{
id: "mml",
- version: "2.7.3",
+ version: "2.7.4",
directory: MathJax.ElementJax.directory + "/mml",
extensionDir: MathJax.ElementJax.extensionDir + "/mml",
optableDir: MathJax.ElementJax.directory + "/mml/optable"
@@ -6783,7 +6783,7 @@ MathJax.ElementJax.mml.loadComplete("jax.js");
*/
(function (HUB,HTML,AJAX,CALLBACK,LOCALE,OUTPUT,INPUT) {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var EXTENSION = MathJax.Extension;
var ME = EXTENSION.MathEvents = {version: VERSION};
@@ -7404,7 +7404,7 @@ MathJax.ElementJax.mml.loadComplete("jax.js");
*/
MathJax.Extension.tex2jax = {
- version: "2.7.3",
+ version: "2.7.4",
config: {
inlineMath: [ // The start/stop pairs for in-line math
// ['$','$'], // (comment out any you don't want, or add your own, but
@@ -7533,22 +7533,29 @@ MathJax.Extension.tex2jax = {
scanText: function (element) {
if (element.nodeValue.replace(/\s+/,'') == '') {return element}
- var match, prev;
+ var match, prev, pos = 0, rescan;
this.search = {start: true};
this.pattern = this.start;
while (element) {
- this.pattern.lastIndex = 0;
+ rescan = null;
+ this.pattern.lastIndex = pos; pos = 0;
while (element && element.nodeName.toLowerCase() === '#text' &&
(match = this.pattern.exec(element.nodeValue))) {
if (this.search.start) {element = this.startMatch(match,element)}
else {element = this.endMatch(match,element)}
}
- if (this.search.matched) {element = this.encloseMath(element)}
+ if (this.search.matched) element = this.encloseMath(element);
+ else if (!this.search.start) rescan = this.search;
if (element) {
do {prev = element; element = element.nextSibling}
while (element && this.ignoreTags[element.nodeName.toLowerCase()] != null);
- if (!element || element.nodeName !== '#text')
- {return (this.search.close ? this.prevEndMatch() : prev)}
+ if (!element || element.nodeName !== '#text') {
+ if (!rescan) return (this.search.close ? this.prevEndMatch() : prev);
+ element = rescan.open;
+ pos = rescan.opos + rescan.olen + (rescan.blen || 0);
+ this.search = {start: true};
+ this.pattern = this.start;
+ }
}
}
return element;
@@ -7566,7 +7573,7 @@ MathJax.Extension.tex2jax = {
this.search = {
end: "\\end{"+match[1]+"}", mode: "; mode=display", pcount: 0,
open: element, olen: 0, opos: this.pattern.lastIndex - match[0].length,
- isBeginEnd: true
+ blen: match[1].length + 3, isBeginEnd: true
};
this.switchPattern(this.endPattern(this.search.end));
} else if (match[0].substr(0,4) === "\\ref" || match[0].substr(0,6) === "\\eqref") {
@@ -7717,7 +7724,7 @@ MathJax.Ajax.loadComplete("[MathJax]/extensions/tex2jax.js");
MathJax.InputJax.TeX = MathJax.InputJax({
id: "TeX",
- version: "2.7.3",
+ version: "2.7.4",
directory: MathJax.InputJax.directory + "/TeX",
extensionDir: MathJax.InputJax.extensionDir + "/TeX",
@@ -9209,12 +9216,12 @@ MathJax.InputJax.TeX.loadComplete("config.js");
Overset: function (name) {
var top = this.ParseArg(name), base = this.ParseArg(name);
- if (base.movablelimits) base.movablelimits = false;
+ base.movablelimits = false;
this.Push(MML.mover(base,top));
},
Underset: function (name) {
var bot = this.ParseArg(name), base = this.ParseArg(name);
- if (base.movablelimits) base.movablelimits = false;
+ base.movablelimits = false;
this.Push(MML.munder(base,bot));
},
@@ -10141,7 +10148,7 @@ MathJax.InputJax.TeX.loadComplete("config.js");
MathJax.OutputJax.SVG = MathJax.OutputJax({
id: "SVG",
- version: "2.7.3",
+ version: "2.7.4",
directory: MathJax.OutputJax.directory + "/SVG",
extensionDir: MathJax.OutputJax.extensionDir + "/SVG",
autoloadDir: MathJax.OutputJax.directory + "/SVG/autoload",
@@ -11184,8 +11191,8 @@ MathJax.OutputJax.SVG.loadComplete("config.js");
Check: function (data) {
var svg = data.toSVG(); this.svg.push(svg);
if (data.SVGcanStretch("Vertical")) {svg.mml = data}
- if (svg.h > this.sh) {this.sh = svg.h}
- if (svg.d > this.sd) {this.sd = svg.d}
+ if (svg.h + svg.y > this.sh) {this.sh = svg.h + svg.y}
+ if (svg.d - svg.y > this.sd) {this.sd = svg.d - svg.y}
},
Stretch: function () {
for (var i = 0, m = this.svg.length; i < m; i++)
@@ -11675,10 +11682,19 @@ MathJax.OutputJax.SVG.loadComplete("config.js");
return svg;
},
SVGautoload: function () {
+ this.constructor.Augment({toSVG: MML.mbase.SVGautoloadFail});
var file = SVG.autoloadDir+"/"+this.type+".js";
HUB.RestartAfter(AJAX.Require(file));
},
+ SVGautoloadFail: function () {
+ throw Error("SVG can't autoload '"+ this.type + "'");
+ },
+ SVGautoloadList: {},
SVGautoloadFile: function (name) {
+ if (MML.mbase.SVGautoloadList.hasOwnProperty(name)) {
+ throw Error("SVG can't autoload file '"+name+"'");
+ }
+ MML.mbase.SVGautoloadList[name] = true;
var file = SVG.autoloadDir+"/"+name+".js";
HUB.RestartAfter(AJAX.Require(file));
}
@@ -12182,7 +12198,7 @@ MathJax.OutputJax.SVG.loadComplete("config.js");
boxes[i] = this.SVGdataStretched(i,HW,D);
stretch[i] = (D != null || HW == null) && this.data[i].SVGcanStretch("Horizontal");
if (this.data[this.over] && values.accent) {
- boxes[i].h = Math.max(boxes[i].h,SVG.TeX.x_height); // min height of 1ex (#1706)
+ boxes[i].h = Math.max(boxes[i].h,scale*SVG.TeX.x_height); // min height of 1ex (#1706)
}
} else {
boxes[i] = this.data[i].toSVG(); boxes[i].x = 0; delete boxes[i].X;
@@ -12394,7 +12410,7 @@ MathJax.OutputJax.SVG.loadComplete("config.js");
// so if they are close to full width, make sure they aren't too big.
//
if (Math.abs(w-SVG.cwidth) < 10)
- style.maxWidth = SVG.Fixed(SVG.cwidth*SVG.em/1000);
+ style.maxWidth = SVG.Fixed(SVG.cwidth*SVG.em/1000) + "px";
//
// Add it to the MathJax span
//
@@ -12530,7 +12546,7 @@ MathJax.OutputJax.SVG.loadComplete("config.js");
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG,
BBOX = SVG.BBOX;
@@ -12920,7 +12936,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG,
BBOX = SVG.BBOX,
@@ -13027,7 +13043,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG;
@@ -13158,7 +13174,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG;
var BBOX = SVG.BBOX;
@@ -13251,7 +13267,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax["SVG"];
@@ -13453,7 +13469,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG,
BBOX = SVG.BBOX;
@@ -14175,7 +14191,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG,
BBOX = SVG.BBOX;
@@ -14409,7 +14425,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MML = MathJax.ElementJax.mml,
SVG = MathJax.OutputJax.SVG;
@@ -14466,7 +14482,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
(function (SVG,MML,AJAX,HUB) {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var MAIN = "MathJax_Main",
BOLD = "MathJax_Main-bold",
@@ -16079,7 +16095,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
*/
(function (SVG) {
- var VERSION = "2.7.3";
+ var VERSION = "2.7.4";
var DELIMITERS = SVG.FONTDATA.DELIMITERS;
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 98dbbd3..5e702ea 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
@@ -2,28168 +2,13988 @@
/* 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.1' };
-if (typeof exports !== 'undefined') {
- exports.fabric = fabric;
-}
+var fabric = fabric || {
+ version: "2.2.3"
+};
-if (typeof document !== 'undefined' && typeof window !== 'undefined') {
- fabric.document = document;
- fabric.window = window;
+if (typeof exports !== "undefined") {
+ exports.fabric = fabric;
+} else if (typeof define === "function" && define.amd) {
+ define([], function() {
+ return fabric;
+ });
}
-else {
- // assume we're running under node.js when document/window are not present
- 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.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
- fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
- fabric.window = fabric.document.defaultView;
- DOMParser = require('xmldom').DOMParser;
+
+if (typeof document !== "undefined" && typeof window !== "undefined") {
+ fabric.document = document;
+ fabric.window = window;
+} else {
+ 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.jsdomImplForWrapper = require("jsdom/lib/jsdom/living/generated/utils").implForWrapper;
+ fabric.nodeCanvas = require("jsdom/lib/jsdom/utils").Canvas;
+ 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;
+fabric.isTouchSupported = "ontouchstart" in fabric.window;
-/**
- * True when in environment that's probably Node.js
- * @type boolean
- */
-fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
- typeof window === 'undefined';
+fabric.isLikelyNode = typeof Buffer !== "undefined" && typeof window === "undefined";
-/* _FROM_SVG_START_ */
-/**
- * Attributes parsed from all SVG elements
- * @type array
- */
-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", "paint-order",
- "instantiated_by_use"
-];
-/* _FROM_SVG_END_ */
+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", "paint-order", "instantiated_by_use" ];
-/**
- * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
- */
fabric.DPI = 96;
-fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
-fabric.fontPaths = { };
-fabric.iMatrix = [1, 0, 0, 1, 0, 0];
-fabric.canvasModule = 'canvas';
-/**
- * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.
- * @since 1.7.14
- * @type Number
- * @default
- */
+fabric.reNum = "(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)";
+
+fabric.fontPaths = {};
+
+fabric.iMatrix = [ 1, 0, 0, 1, 0, 0 ];
+
+fabric.canvasModule = "canvas";
+
fabric.perfLimitSizeTotal = 2097152;
-/**
- * Pixel limit for cache canvases width or height. IE fixes the maximum at 5000
- * @since 1.7.14
- * @type Number
- * @default
- */
fabric.maxCacheSideLimit = 4096;
-/**
- * Lowest pixel limit for cache canvases, set at 256PX
- * @since 1.7.14
- * @type Number
- * @default
- */
fabric.minCacheSideLimit = 256;
-/**
- * Cache Object for widths of chars in text rendering.
- */
-fabric.charWidthsCache = { };
+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
- */
-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.devicePixelRatio = fabric.window.devicePixelRatio || fabric.window.webkitDevicePixelRatio || fabric.window.mozDevicePixelRatio || 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 (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;
+if (typeof document !== "undefined" && typeof window !== "undefined") {
+ window.fabric = fabric;
}
-
(function() {
-
- /**
- * @private
- * @param {String} eventName
- * @param {Function} handler
- */
- function _removeEventListener(eventName, handler) {
- if (!this.__eventListeners[eventName]) {
- return;
- }
- var eventListener = this.__eventListeners[eventName];
- if (handler) {
- eventListener[eventListener.indexOf(handler)] = false;
- }
- else {
- fabric.util.array.fill(eventListener, false);
- }
- }
-
- /**
- * Observes specified event
- * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
- * @memberOf fabric.Observable
- * @alias on
- * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
- * @param {Function} handler Function that receives a notification when an event of the specified type occurs
- * @return {Self} thisArg
- * @chainable
- */
- function observe(eventName, handler) {
- if (!this.__eventListeners) {
- this.__eventListeners = { };
- }
- // one object with key/value pairs was passed
- if (arguments.length === 1) {
- for (var prop in eventName) {
- this.on(prop, eventName[prop]);
- }
- }
- else {
- if (!this.__eventListeners[eventName]) {
- this.__eventListeners[eventName] = [];
- }
- this.__eventListeners[eventName].push(handler);
- }
- return this;
- }
-
- /**
- * Stops event observing for a particular event handler. Calling this method
- * without arguments removes all handlers for all events
- * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
- * @memberOf fabric.Observable
- * @alias off
- * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
- * @param {Function} handler Function to be deleted from EventListeners
- * @return {Self} thisArg
- * @chainable
- */
- function stopObserving(eventName, handler) {
- if (!this.__eventListeners) {
- return;
- }
-
- // remove all key/value pairs (event name -> event handler)
- if (arguments.length === 0) {
- for (eventName in this.__eventListeners) {
- _removeEventListener.call(this, eventName);
- }
- }
- // one object with key/value pairs was passed
- else if (arguments.length === 1 && typeof arguments[0] === 'object') {
- for (var prop in eventName) {
- _removeEventListener.call(this, prop, eventName[prop]);
- }
- }
- else {
- _removeEventListener.call(this, eventName, handler);
+ function _removeEventListener(eventName, handler) {
+ if (!this.__eventListeners[eventName]) {
+ return;
+ }
+ var eventListener = this.__eventListeners[eventName];
+ if (handler) {
+ eventListener[eventListener.indexOf(handler)] = false;
+ } else {
+ fabric.util.array.fill(eventListener, false);
+ }
}
- return this;
- }
-
- /**
- * Fires event with an optional options object
- * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
- * @memberOf fabric.Observable
- * @alias trigger
- * @param {String} eventName Event name to fire
- * @param {Object} [options] Options object
- * @return {Self} thisArg
- * @chainable
- */
- function fire(eventName, options) {
- if (!this.__eventListeners) {
- return;
+ function observe(eventName, handler) {
+ if (!this.__eventListeners) {
+ this.__eventListeners = {};
+ }
+ if (arguments.length === 1) {
+ for (var prop in eventName) {
+ this.on(prop, eventName[prop]);
+ }
+ } else {
+ if (!this.__eventListeners[eventName]) {
+ this.__eventListeners[eventName] = [];
+ }
+ this.__eventListeners[eventName].push(handler);
+ }
+ return this;
}
-
- var listenersForEvent = this.__eventListeners[eventName];
- if (!listenersForEvent) {
- return;
+ function stopObserving(eventName, handler) {
+ if (!this.__eventListeners) {
+ return;
+ }
+ if (arguments.length === 0) {
+ for (eventName in this.__eventListeners) {
+ _removeEventListener.call(this, eventName);
+ }
+ } else if (arguments.length === 1 && typeof arguments[0] === "object") {
+ for (var prop in eventName) {
+ _removeEventListener.call(this, prop, eventName[prop]);
+ }
+ } else {
+ _removeEventListener.call(this, eventName, handler);
+ }
+ return this;
}
-
- for (var i = 0, len = listenersForEvent.length; i < len; i++) {
- listenersForEvent[i] && listenersForEvent[i].call(this, options || { });
+ function fire(eventName, options) {
+ if (!this.__eventListeners) {
+ return;
+ }
+ var listenersForEvent = this.__eventListeners[eventName];
+ if (!listenersForEvent) {
+ return;
+ }
+ for (var i = 0, len = listenersForEvent.length; i < len; i++) {
+ listenersForEvent[i] && listenersForEvent[i].call(this, options || {});
+ }
+ this.__eventListeners[eventName] = listenersForEvent.filter(function(value) {
+ return value !== false;
+ });
+ return this;
}
- this.__eventListeners[eventName] = listenersForEvent.filter(function(value) {
- return value !== false;
- });
- return this;
- }
-
- /**
- * @namespace fabric.Observable
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#events}
- * @see {@link http://fabricjs.com/events|Events demo}
- */
- fabric.Observable = {
- observe: observe,
- stopObserving: stopObserving,
- fire: fire,
-
- on: observe,
- off: stopObserving,
- trigger: fire
- };
+ fabric.Observable = {
+ observe: observe,
+ stopObserving: stopObserving,
+ fire: fire,
+ on: observe,
+ off: stopObserving,
+ trigger: fire
+ };
})();
-
-/**
- * @namespace fabric.Collection
- */
fabric.Collection = {
-
- _objects: [],
-
- /**
- * Adds objects to collection, Canvas or Group, then renders canvas
- * (if `renderOnAddRemove` is not `false`).
- * in case of Group no changes to bounding box are made.
- * Objects should be instances of (or inherit from) fabric.Object
- * Use of this function is highly discouraged for groups.
- * you can add a bunch of objects with the add method but then you NEED
- * to run a addWithUpdate call for the Group class or position/bbox will be wrong.
- * @param {...fabric.Object} object Zero or more fabric instances
- * @return {Self} thisArg
- * @chainable
- */
- add: function () {
- this._objects.push.apply(this._objects, arguments);
- if (this._onObjectAdded) {
- for (var i = 0, length = arguments.length; i < length; i++) {
- this._onObjectAdded(arguments[i]);
- }
- }
- this.renderOnAddRemove && this.requestRenderAll();
- return this;
- },
-
- /**
- * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
- * An object should be an instance of (or inherit from) fabric.Object
- * Use of this function is highly discouraged for groups.
- * you can add a bunch of objects with the insertAt method but then you NEED
- * to run a addWithUpdate call for the Group class or position/bbox will be wrong.
- * @param {Object} object Object to insert
- * @param {Number} index Index to insert object at
- * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
- * @return {Self} thisArg
- * @chainable
- */
- insertAt: function (object, index, nonSplicing) {
- var objects = this.getObjects();
- if (nonSplicing) {
- objects[index] = object;
- }
- else {
- objects.splice(index, 0, object);
- }
- this._onObjectAdded && this._onObjectAdded(object);
- this.renderOnAddRemove && this.requestRenderAll();
- return this;
- },
-
- /**
- * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
- * @param {...fabric.Object} object Zero or more fabric instances
- * @return {Self} thisArg
- * @chainable
- */
- remove: function() {
- var objects = this.getObjects(),
- index, somethingRemoved = false;
-
- for (var i = 0, length = arguments.length; i < length; i++) {
- index = objects.indexOf(arguments[i]);
-
- // only call onObjectRemoved if an object was actually removed
- if (index !== -1) {
- somethingRemoved = true;
- objects.splice(index, 1);
- this._onObjectRemoved && this._onObjectRemoved(arguments[i]);
- }
- }
-
- this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
- return this;
- },
-
- /**
- * Executes given function for each object in this group
- * @param {Function} callback
- * Callback invoked with current object as first argument,
- * index - as second and an array of all objects - as third.
- * Callback is invoked in a context of Global Object (e.g. `window`)
- * when no `context` argument is given
- *
- * @param {Object} context Context (aka thisObject)
- * @return {Self} thisArg
- * @chainable
- */
- forEachObject: function(callback, context) {
- var objects = this.getObjects();
- for (var i = 0, len = objects.length; i < len; i++) {
- callback.call(context, objects[i], i, objects);
- }
- return this;
- },
-
- /**
- * Returns an array of children objects of this instance
- * Type parameter introduced in 1.3.10
- * @param {String} [type] When specified, only objects of this type are returned
- * @return {Array}
- */
- getObjects: function(type) {
- if (typeof type === 'undefined') {
- return this._objects;
- }
- return this._objects.filter(function(o) {
- return o.type === type;
- });
- },
-
- /**
- * Returns object at specified index
- * @param {Number} index
- * @return {Self} thisArg
- */
- item: function (index) {
- return this.getObjects()[index];
- },
-
- /**
- * Returns true if collection contains no objects
- * @return {Boolean} true if collection is empty
- */
- isEmpty: function () {
- return this.getObjects().length === 0;
- },
-
- /**
- * Returns a size of a collection (i.e: length of an array containing its objects)
- * @return {Number} Collection size
- */
- size: function() {
- return this.getObjects().length;
- },
-
- /**
- * Returns true if collection contains an object
- * @param {Object} object Object to check against
- * @return {Boolean} `true` if collection contains an object
- */
- contains: function(object) {
- return this.getObjects().indexOf(object) > -1;
- },
-
- /**
- * Returns number representation of a collection complexity
- * @return {Number} complexity
- */
- complexity: function () {
- return this.getObjects().reduce(function (memo, current) {
- memo += current.complexity ? current.complexity() : 0;
- return memo;
- }, 0);
- }
-};
-
-
-/**
- * @namespace fabric.CommonMethods
- */
-fabric.CommonMethods = {
-
- /**
- * Sets object's properties from options
- * @param {Object} [options] Options object
- */
- _setOptions: function(options) {
- for (var prop in options) {
- this.set(prop, options[prop]);
- }
- },
-
- /**
- * @private
- * @param {Object} [filler] Options object
- * @param {String} [property] property to set the Gradient to
- */
- _initGradient: function(filler, property) {
- if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
- this.set(property, new fabric.Gradient(filler));
- }
- },
-
- /**
- * @private
- * @param {Object} [filler] Options object
- * @param {String} [property] property to set the Pattern to
- * @param {Function} [callback] callback to invoke after pattern load
- */
- _initPattern: function(filler, property, callback) {
- if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
- this.set(property, new fabric.Pattern(filler, callback));
- }
- else {
- callback && callback();
- }
- },
-
- /**
- * @private
- * @param {Object} [options] Options object
- */
- _initClipping: function(options) {
- if (!options.clipTo || typeof options.clipTo !== 'string') {
- return;
- }
-
- var functionBody = fabric.util.getFunctionBody(options.clipTo);
- if (typeof functionBody !== 'undefined') {
- this.clipTo = new Function('ctx', functionBody);
- }
- },
-
- /**
- * @private
- */
- _setObject: function(obj) {
- for (var prop in obj) {
- this._set(prop, obj[prop]);
- }
- },
-
- /**
- * Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
- * @param {String|Object} key Property name or object (if object, iterate over the object properties)
- * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
- * @return {fabric.Object} thisArg
- * @chainable
- */
- set: function(key, value) {
- if (typeof key === 'object') {
- this._setObject(key);
- }
- else {
- if (typeof value === 'function' && key !== 'clipTo') {
- this._set(key, value(this.get(key)));
- }
- else {
- this._set(key, value);
- }
- }
- return this;
- },
-
- _set: function(key, value) {
- this[key] = value;
- },
-
- /**
- * Toggles specified property from `true` to `false` or from `false` to `true`
- * @param {String} property Property to toggle
- * @return {fabric.Object} thisArg
- * @chainable
- */
- toggle: function(property) {
- var value = this.get(property);
- if (typeof value === 'boolean') {
- this.set(property, !value);
- }
- return this;
- },
-
- /**
- * Basic getter
- * @param {String} property Property name
- * @return {*} value of a property
- */
- get: function(property) {
- return this[property];
- }
-};
-
-
-(function(global) {
-
- var sqrt = Math.sqrt,
- atan2 = Math.atan2,
- pow = Math.pow,
- abs = Math.abs,
- PiBy180 = Math.PI / 180;
-
- /**
- * @namespace fabric.util
- */
- fabric.util = {
-
- /**
- * Removes value from an array.
- * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
- * @static
- * @memberOf fabric.util
- * @param {Array} array
- * @param {*} value
- * @return {Array} original array
- */
- removeFromArray: function(array, value) {
- var idx = array.indexOf(value);
- if (idx !== -1) {
- array.splice(idx, 1);
- }
- return array;
- },
-
- /**
- * Returns random number between 2 specified ones.
- * @static
- * @memberOf fabric.util
- * @param {Number} min lower limit
- * @param {Number} max upper limit
- * @return {Number} random value (between min and max)
- */
- getRandomInt: function(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
- },
-
- /**
- * Transforms degrees to radians.
- * @static
- * @memberOf fabric.util
- * @param {Number} degrees value in degrees
- * @return {Number} value in radians
- */
- degreesToRadians: function(degrees) {
- return degrees * PiBy180;
- },
-
- /**
- * Transforms radians to degrees.
- * @static
- * @memberOf fabric.util
- * @param {Number} radians value in radians
- * @return {Number} value in degrees
- */
- radiansToDegrees: function(radians) {
- return radians / PiBy180;
- },
-
- /**
- * Rotates `point` around `origin` with `radians`
- * @static
- * @memberOf fabric.util
- * @param {fabric.Point} point The point to rotate
- * @param {fabric.Point} origin The origin of the rotation
- * @param {Number} radians The radians of the angle for the rotation
- * @return {fabric.Point} The new rotated point
- */
- rotatePoint: function(point, origin, radians) {
- point.subtractEquals(origin);
- var v = fabric.util.rotateVector(point, radians);
- return new fabric.Point(v.x, v.y).addEquals(origin);
- },
-
- /**
- * Rotates `vector` with `radians`
- * @static
- * @memberOf fabric.util
- * @param {Object} vector The vector to rotate (x and y)
- * @param {Number} radians The radians of the angle for the rotation
- * @return {Object} The new rotated point
- */
- rotateVector: function(vector, radians) {
- var sin = Math.sin(radians),
- cos = Math.cos(radians),
- rx = vector.x * cos - vector.y * sin,
- ry = vector.x * sin + vector.y * cos;
- return {
- x: rx,
- y: ry
- };
- },
-
- /**
- * Apply transform t to point p
- * @static
- * @memberOf fabric.util
- * @param {fabric.Point} p The point to transform
- * @param {Array} t The transform
- * @param {Boolean} [ignoreOffset] Indicates that the offset should not be applied
- * @return {fabric.Point} The transformed point
- */
- transformPoint: function(p, t, ignoreOffset) {
- if (ignoreOffset) {
- return new fabric.Point(
- t[0] * p.x + t[2] * p.y,
- t[1] * p.x + t[3] * p.y
- );
- }
- return new fabric.Point(
- t[0] * p.x + t[2] * p.y + t[4],
- t[1] * p.x + t[3] * p.y + t[5]
- );
- },
-
- /**
- * Returns coordinates of points's bounding rectangle (left, top, width, height)
- * @param {Array} points 4 points array
- * @return {Object} Object with left, top, width, height properties
- */
- makeBoundingBoxFromPoints: function(points) {
- 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 = 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 = maxY - minY;
-
- return {
- left: minX,
- top: minY,
- width: width,
- height: height
- };
- },
-
- /**
- * Invert transformation t
- * @static
- * @memberOf fabric.util
- * @param {Array} t The transform
- * @return {Array} The inverted transform
- */
- invertTransform: function(t) {
- var a = 1 / (t[0] * t[3] - t[1] * t[2]),
- r = [a * t[3], -a * t[1], -a * t[2], a * t[0]],
- o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r, true);
- r[4] = -o.x;
- r[5] = -o.y;
- return r;
- },
-
- /**
- * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
- * @static
- * @memberOf fabric.util
- * @param {Number|String} number number to operate on
- * @param {Number} fractionDigits number of fraction digits to "leave"
- * @return {Number}
- */
- toFixed: function(number, fractionDigits) {
- return parseFloat(Number(number).toFixed(fractionDigits));
- },
-
- /**
- * Converts from attribute value to pixel value if applicable.
- * Returns converted pixels or original value not converted.
- * @param {Number|String} value number to operate on
- * @param {Number} fontSize
- * @return {Number|String}
- */
- parseUnit: function(value, fontSize) {
- var unit = /\D{0,2}$/.exec(value),
- number = parseFloat(value);
- if (!fontSize) {
- fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
- }
- switch (unit[0]) {
- case 'mm':
- return number * fabric.DPI / 25.4;
-
- case 'cm':
- return number * fabric.DPI / 2.54;
-
- case 'in':
- return number * fabric.DPI;
-
- case 'pt':
- return number * fabric.DPI / 72; // or * 4 / 3
-
- case 'pc':
- return number * fabric.DPI / 72 * 12; // or * 16
-
- case 'em':
- return number * fontSize;
-
- default:
- return number;
- }
- },
-
- /**
- * Function which always returns `false`.
- * @static
- * @memberOf fabric.util
- * @return {Boolean}
- */
- falseFunction: function() {
- return false;
- },
-
- /**
- * Returns klass "Class" object of given namespace
- * @memberOf fabric.util
- * @param {String} type Type of object (eg. 'circle')
- * @param {String} namespace Namespace to get klass "Class" object from
- * @return {Object} klass "Class"
- */
- getKlass: function(type, namespace) {
- // capitalize first letter only
- type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
- return fabric.util.resolveNamespace(namespace)[type];
- },
-
- /**
- * 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'
- * @return {Object} Object for given namespace (default fabric)
- */
- resolveNamespace: function(namespace) {
- if (!namespace) {
- return fabric;
- }
-
- var parts = namespace.split('.'),
- len = parts.length, i,
- obj = global || fabric.window;
-
- for (i = 0; i < len; ++i) {
- obj = obj[parts[i]];
- }
-
- return obj;
- },
-
- /**
- * Loads image element from given url and passes it to a callback
- * @memberOf fabric.util
- * @param {String} url URL representing an image
- * @param {Function} callback Callback; invoked with loaded image
- * @param {*} [context] Context to invoke callback in
- * @param {Object} [crossOrigin] crossOrigin value to set image element to
- */
- loadImage: function(url, callback, context, crossOrigin) {
- if (!url) {
- callback && callback.call(context, url);
- return;
- }
-
- var img = fabric.util.createImage();
-
- /** @ignore */
- 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);
- callback && callback.call(context, null, true);
- img = img.onload = img.onerror = null;
- };
-
- // data-urls appear to be buggy with crossOrigin
- // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
- // see https://code.google.com/p/chromium/issues/detail?id=315152
- // https://bugzilla.mozilla.org/show_bug.cgi?id=935069
- if (url.indexOf('data') !== 0 && crossOrigin) {
- 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
- * @param {Array} objects 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.
- */
- enlivenObjects: function(objects, callback, namespace, reviver) {
- objects = objects || [];
-
- function onLoaded() {
- if (++numLoadedObjects === numTotalObjects) {
- callback && callback(enlivenedObjects);
+ _objects: [],
+ add: function() {
+ this._objects.push.apply(this._objects, arguments);
+ if (this._onObjectAdded) {
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ this._onObjectAdded(arguments[i]);
+ }
}
- }
-
- var enlivenedObjects = [],
- numLoadedObjects = 0,
- numTotalObjects = objects.length;
-
- if (!numTotalObjects) {
- callback && callback(enlivenedObjects);
- return;
- }
-
- objects.forEach(function (o, index) {
- // if sparse array
- if (!o || !o.type) {
- onLoaded();
- return;
- }
- var klass = fabric.util.getKlass(o.type, namespace);
- klass.fromObject(o, function (obj, error) {
- error || (enlivenedObjects[index] = obj);
- reviver && reviver(o, obj, error);
- onLoaded();
- });
- });
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
},
-
- /**
- * Create and wait for loading of patterns
- * @static
- * @memberOf fabric.util
- * @param {Array} patterns Objects to enliven
- * @param {Function} callback Callback to invoke when all objects are created
- * called after each fabric object created.
- */
- enlivenPatterns: function(patterns, callback) {
- patterns = patterns || [];
-
- function onLoaded() {
- if (++numLoadedPatterns === numPatterns) {
- callback && callback(enlivenedPatterns);
+ insertAt: function(object, index, nonSplicing) {
+ var objects = this.getObjects();
+ if (nonSplicing) {
+ objects[index] = object;
+ } else {
+ objects.splice(index, 0, object);
}
- }
-
- var enlivenedPatterns = [],
- numLoadedPatterns = 0,
- numPatterns = patterns.length;
-
- if (!numPatterns) {
- callback && callback(enlivenedPatterns);
- return;
- }
-
- patterns.forEach(function (p, index) {
- if (p && p.source) {
- new fabric.Pattern(p, function(pattern) {
- enlivenedPatterns[index] = pattern;
- onLoaded();
- });
- }
- else {
- enlivenedPatterns[index] = p;
- onLoaded();
- }
- });
- },
-
- /**
- * Groups SVG elements (usually those retrieved from SVG document)
- * @static
- * @memberOf fabric.util
- * @param {Array} elements SVG elements to group
- * @param {Object} [options] Options object
- * @param {String} path Value to set sourcePath to
- * @return {fabric.Object|fabric.Group}
- */
- groupSVGElements: function(elements, options, path) {
- var object;
- 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;
- }
- return object;
- },
-
- /**
- * Populates an object with properties of another object
- * @static
- * @memberOf fabric.util
- * @param {Object} source Source object
- * @param {Object} destination Destination object
- * @return {Array} properties Properties names to include
- */
- populateWithProperties: function(source, destination, properties) {
- if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
- for (var i = 0, len = properties.length; i < len; i++) {
- if (properties[i] in source) {
- destination[properties[i]] = source[properties[i]];
- }
- }
- }
- },
-
- /**
- * Draws a dashed line between two points
- *
- * This method is used to draw dashed line around selection area.
- * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
- *
- * @param {CanvasRenderingContext2D} ctx context
- * @param {Number} x start x coordinate
- * @param {Number} y start y coordinate
- * @param {Number} x2 end x coordinate
- * @param {Number} y2 end y coordinate
- * @param {Array} da dash array pattern
- */
- drawDashedLine: function(ctx, x, y, x2, y2, da) {
- var dx = x2 - x,
- dy = y2 - y,
- len = sqrt(dx * dx + dy * dy),
- rot = atan2(dy, dx),
- dc = da.length,
- di = 0,
- draw = true;
-
- ctx.save();
- ctx.translate(x, y);
- ctx.moveTo(0, 0);
- ctx.rotate(rot);
-
- x = 0;
- while (len > x) {
- x += da[di++ % dc];
- if (x > len) {
- x = len;
- }
- ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
- draw = !draw;
- }
-
- ctx.restore();
+ this._onObjectAdded && this._onObjectAdded(object);
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
},
-
- /**
- * Creates canvas element
- * @static
- * @memberOf fabric.util
- * @return {CanvasElement} initialized canvas element
- */
- createCanvasElement: function() {
- return fabric.document.createElement('canvas');
+ remove: function() {
+ var objects = this.getObjects(), index, somethingRemoved = false;
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ index = objects.indexOf(arguments[i]);
+ if (index !== -1) {
+ somethingRemoved = true;
+ objects.splice(index, 1);
+ this._onObjectRemoved && this._onObjectRemoved(arguments[i]);
+ }
+ }
+ this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
+ return this;
},
-
- /**
- * Creates image element (works on client and node)
- * @static
- * @memberOf fabric.util
- * @return {HTMLImageElement} HTML image element
- */
- createImage: function() {
- return fabric.document.createElement('img');
+ forEachObject: function(callback, context) {
+ var objects = this.getObjects();
+ for (var i = 0, len = objects.length; i < len; i++) {
+ callback.call(context, objects[i], i, objects);
+ }
+ return this;
},
-
- /**
- * @static
- * @memberOf fabric.util
- * @deprecated since 2.0.0
- * @param {fabric.Object} receiver Object implementing `clipTo` method
- * @param {CanvasRenderingContext2D} ctx Context to clip
- */
- clipContext: function(receiver, ctx) {
- ctx.save();
- ctx.beginPath();
- receiver.clipTo(ctx);
- ctx.clip();
+ getObjects: function(type) {
+ if (typeof type === "undefined") {
+ return this._objects;
+ }
+ return this._objects.filter(function(o) {
+ return o.type === type;
+ });
},
-
- /**
- * Multiply matrix A by matrix B to nest transformations
- * @static
- * @memberOf fabric.util
- * @param {Array} a First transformMatrix
- * @param {Array} b Second transformMatrix
- * @param {Boolean} is2x2 flag to multiply matrices as 2x2 matrices
- * @return {Array} The product of the two transform matrices
- */
- multiplyTransformMatrices: function(a, b, is2x2) {
- // Matrix multiply a * b
- return [
- a[0] * b[0] + a[2] * b[1],
- a[1] * b[0] + a[3] * b[1],
- a[0] * b[2] + a[2] * b[3],
- a[1] * b[2] + a[3] * b[3],
- is2x2 ? 0 : a[0] * b[4] + a[2] * b[5] + a[4],
- is2x2 ? 0 : a[1] * b[4] + a[3] * b[5] + a[5]
- ];
+ item: function(index) {
+ return this.getObjects()[index];
},
-
- /**
- * Decomposes standard 2x2 matrix into transform componentes
- * @static
- * @memberOf fabric.util
- * @param {Array} a transformMatrix
- * @return {Object} Components of transform
- */
- qrDecompose: function(a) {
- var angle = atan2(a[1], a[0]),
- denom = pow(a[0], 2) + pow(a[1], 2),
- scaleX = sqrt(denom),
- scaleY = (a[0] * a[3] - a[2] * a [1]) / scaleX,
- skewX = atan2(a[0] * a[2] + a[1] * a [3], denom);
- return {
- angle: angle / PiBy180,
- scaleX: scaleX,
- scaleY: scaleY,
- skewX: skewX / PiBy180,
- skewY: 0,
- translateX: a[4],
- translateY: a[5]
- };
+ isEmpty: function() {
+ return this.getObjects().length === 0;
},
-
- customTransformMatrix: function(scaleX, scaleY, skewX) {
- var skewMatrixX = [1, 0, abs(Math.tan(skewX * PiBy180)), 1],
- scaleMatrix = [abs(scaleX), 0, 0, abs(scaleY)];
- return fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true);
+ size: function() {
+ return this.getObjects().length;
},
-
- resetObjectTransform: function (target) {
- target.scaleX = 1;
- target.scaleY = 1;
- target.skewX = 0;
- target.skewY = 0;
- target.flipX = false;
- target.flipY = false;
- target.rotate(0);
+ contains: function(object) {
+ return this.getObjects().indexOf(object) > -1;
},
+ complexity: function() {
+ return this.getObjects().reduce(function(memo, current) {
+ memo += current.complexity ? current.complexity() : 0;
+ return memo;
+ }, 0);
+ }
+};
- /**
- * Returns string representation of function body
- * @param {Function} fn Function to get body of
- * @return {String} Function body
- */
- getFunctionBody: function(fn) {
- return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
+fabric.CommonMethods = {
+ _setOptions: function(options) {
+ for (var prop in options) {
+ this.set(prop, options[prop]);
+ }
},
-
- /**
- * Returns true if context has transparent pixel
- * at specified location (taking tolerance into account)
- * @param {CanvasRenderingContext2D} ctx context
- * @param {Number} x x coordinate
- * @param {Number} y y coordinate
- * @param {Number} tolerance Tolerance
- */
- isTransparent: function(ctx, x, y, tolerance) {
-
- // If tolerance is > 0 adjust start coords to take into account.
- // If moves off Canvas fix to 0
- if (tolerance > 0) {
- if (x > tolerance) {
- x -= tolerance;
+ _initGradient: function(filler, property) {
+ if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
+ this.set(property, new fabric.Gradient(filler));
}
- else {
- x = 0;
+ },
+ _initPattern: function(filler, property, callback) {
+ if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
+ this.set(property, new fabric.Pattern(filler, callback));
+ } else {
+ callback && callback();
}
- if (y > tolerance) {
- y -= tolerance;
+ },
+ _initClipping: function(options) {
+ if (!options.clipTo || typeof options.clipTo !== "string") {
+ return;
}
- else {
- y = 0;
+ var functionBody = fabric.util.getFunctionBody(options.clipTo);
+ if (typeof functionBody !== "undefined") {
+ this.clipTo = new Function("ctx", functionBody);
}
- }
-
- var _isTransparent = true, i, temp,
- imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1),
- l = imageData.data.length;
-
- // Split image data - for tolerance > 1, pixelDataSize = 4;
- for (i = 3; i < l; i += 4) {
- temp = imageData.data[i];
- _isTransparent = temp <= 0;
- if (_isTransparent === false) {
- break; // Stop if colour found
- }
- }
-
- imageData = null;
-
- return _isTransparent;
- },
-
- /**
- * Parse preserveAspectRatio attribute from element
- * @param {string} attribute to be parsed
- * @return {Object} an object containing align and meetOrSlice attribute
- */
- parsePreserveAspectRatioAttribute: function(attribute) {
- var meetOrSlice = 'meet', alignX = 'Mid', alignY = 'Mid',
- aspectRatioAttrs = attribute.split(' '), align;
-
- if (aspectRatioAttrs && aspectRatioAttrs.length) {
- meetOrSlice = aspectRatioAttrs.pop();
- if (meetOrSlice !== 'meet' && meetOrSlice !== 'slice') {
- align = meetOrSlice;
- meetOrSlice = 'meet';
- }
- else if (aspectRatioAttrs.length) {
- align = aspectRatioAttrs.pop();
- }
- }
- //divide align in alignX and alignY
- alignX = align !== 'none' ? align.slice(1, 4) : 'none';
- alignY = align !== 'none' ? align.slice(5, 8) : 'none';
- return {
- meetOrSlice: meetOrSlice,
- alignX: alignX,
- alignY: alignY
- };
},
-
- /**
- * Clear char widths cache for a font family.
- * @memberOf fabric.util
- * @param {String} [fontFamily] font family to clear
- */
- clearFabricFontCache: function(fontFamily) {
- if (!fontFamily) {
- fabric.charWidthsCache = { };
- }
- else if (fabric.charWidthsCache[fontFamily]) {
- delete fabric.charWidthsCache[fontFamily];
- }
+ _setObject: function(obj) {
+ for (var prop in obj) {
+ this._set(prop, obj[prop]);
+ }
},
-
- /**
- * Clear char widths cache for a font family.
- * @memberOf fabric.util
- * @param {Number} ar aspect ratio
- * @param {Number} maximumArea Maximum area you want to achieve
- * @return {Object.x} Limited dimensions by X
- * @return {Object.y} Limited dimensions by Y
- */
- limitDimsByArea: function(ar, maximumArea) {
- var roughWidth = Math.sqrt(maximumArea * ar),
- perfLimitSizeY = Math.floor(maximumArea / roughWidth);
- return { x: Math.floor(roughWidth), y: perfLimitSizeY };
+ set: function(key, value) {
+ if (typeof key === "object") {
+ this._setObject(key);
+ } else {
+ if (typeof value === "function" && key !== "clipTo") {
+ this._set(key, value(this.get(key)));
+ } else {
+ this._set(key, value);
+ }
+ }
+ return this;
},
-
- capValue: function(min, value, max) {
- return Math.max(min, Math.min(value, max));
+ _set: function(key, value) {
+ this[key] = value;
},
-
- findScaleToFit: function(source, destination) {
- return Math.min(destination.width / source.width, destination.height / source.height);
+ toggle: function(property) {
+ var value = this.get(property);
+ if (typeof value === "boolean") {
+ this.set(property, !value);
+ }
+ return this;
},
-
- findScaleToCover: function(source, destination) {
- return Math.max(destination.width / source.width, destination.height / source.height);
+ get: function(property) {
+ return this[property];
}
- };
-})(typeof exports !== 'undefined' ? exports : this);
+};
+(function(global) {
+ var sqrt = Math.sqrt, atan2 = Math.atan2, pow = Math.pow, abs = Math.abs, PiBy180 = Math.PI / 180, PiBy2 = Math.PI / 2;
+ fabric.util = {
+ cos: function(angle) {
+ if (angle === 0) {
+ return 1;
+ }
+ if (angle < 0) {
+ angle = -angle;
+ }
+ var angleSlice = angle / PiBy2;
+ switch (angleSlice) {
+ case 1:
+ case 3:
+ return 0;
+
+ case 2:
+ return -1;
+ }
+ return Math.cos(angle);
+ },
+ sin: function(angle) {
+ if (angle === 0) {
+ return 0;
+ }
+ var angleSlice = angle / PiBy2, sign = 1;
+ if (angle < 0) {
+ sign = -1;
+ }
+ switch (angleSlice) {
+ case 1:
+ return sign;
-(function() {
+ case 2:
+ return 0;
- var arcToSegmentsCache = { },
- segmentToBezierCache = { },
- boundsOfCurveCache = { },
- _join = Array.prototype.join;
+ case 3:
+ return -sign;
+ }
+ return Math.sin(angle);
+ },
+ removeFromArray: function(array, value) {
+ var idx = array.indexOf(value);
+ if (idx !== -1) {
+ array.splice(idx, 1);
+ }
+ return array;
+ },
+ getRandomInt: function(min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ },
+ degreesToRadians: function(degrees) {
+ return degrees * PiBy180;
+ },
+ radiansToDegrees: function(radians) {
+ return radians / PiBy180;
+ },
+ rotatePoint: function(point, origin, radians) {
+ point.subtractEquals(origin);
+ var v = fabric.util.rotateVector(point, radians);
+ return new fabric.Point(v.x, v.y).addEquals(origin);
+ },
+ rotateVector: function(vector, radians) {
+ var sin = fabric.util.sin(radians), cos = fabric.util.cos(radians), rx = vector.x * cos - vector.y * sin, ry = vector.x * sin + vector.y * cos;
+ return {
+ x: rx,
+ y: ry
+ };
+ },
+ transformPoint: function(p, t, ignoreOffset) {
+ if (ignoreOffset) {
+ return new fabric.Point(t[0] * p.x + t[2] * p.y, t[1] * p.x + t[3] * p.y);
+ }
+ return new fabric.Point(t[0] * p.x + t[2] * p.y + t[4], t[1] * p.x + t[3] * p.y + t[5]);
+ },
+ makeBoundingBoxFromPoints: function(points) {
+ 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 = 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 = maxY - minY;
+ return {
+ left: minX,
+ top: minY,
+ width: width,
+ height: height
+ };
+ },
+ invertTransform: function(t) {
+ var a = 1 / (t[0] * t[3] - t[1] * t[2]), r = [ a * t[3], -a * t[1], -a * t[2], a * t[0] ], o = fabric.util.transformPoint({
+ x: t[4],
+ y: t[5]
+ }, r, true);
+ r[4] = -o.x;
+ r[5] = -o.y;
+ return r;
+ },
+ toFixed: function(number, fractionDigits) {
+ return parseFloat(Number(number).toFixed(fractionDigits));
+ },
+ parseUnit: function(value, fontSize) {
+ var unit = /\D{0,2}$/.exec(value), number = parseFloat(value);
+ if (!fontSize) {
+ fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ }
+ switch (unit[0]) {
+ case "mm":
+ return number * fabric.DPI / 25.4;
- /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
- * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
- * http://mozilla.org/MPL/2.0/
- */
- function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
- var argsString = _join.call(arguments);
- if (arcToSegmentsCache[argsString]) {
- return arcToSegmentsCache[argsString];
- }
+ case "cm":
+ return number * fabric.DPI / 2.54;
- var PI = Math.PI, th = rotateX * PI / 180,
- sinTh = Math.sin(th),
- cosTh = Math.cos(th),
- fromX = 0, fromY = 0;
+ case "in":
+ return number * fabric.DPI;
- rx = Math.abs(rx);
- ry = Math.abs(ry);
+ case "pt":
+ return number * fabric.DPI / 72;
- var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
- py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
- rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
- pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
- root = 0;
+ case "pc":
+ return number * fabric.DPI / 72 * 12;
- if (pl < 0) {
- var s = Math.sqrt(1 - pl / (rx2 * ry2));
- rx *= s;
- ry *= s;
- }
- else {
- root = (large === sweep ? -1.0 : 1.0) *
- Math.sqrt( pl / (rx2 * py2 + ry2 * px2));
- }
-
- var cx = root * rx * py / ry,
- cy = -root * ry * px / rx,
- cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
- cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
- mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
- dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
-
- if (sweep === 0 && dtheta > 0) {
- dtheta -= 2 * PI;
- }
- else if (sweep === 1 && dtheta < 0) {
- dtheta += 2 * PI;
- }
-
- // Convert into cubic bezier segments <= 90deg
- var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
- result = [], mDelta = dtheta / segments,
- mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
- th3 = mTheta + mDelta;
-
- for (var i = 0; i < segments; i++) {
- result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
- fromX = result[i][4];
- fromY = result[i][5];
- mTheta = th3;
- th3 += mDelta;
- }
- arcToSegmentsCache[argsString] = result;
- return result;
- }
-
- function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
- var argsString2 = _join.call(arguments);
- if (segmentToBezierCache[argsString2]) {
- return segmentToBezierCache[argsString2];
- }
+ case "em":
+ return number * fontSize;
- var costh2 = Math.cos(th2),
- sinth2 = Math.sin(th2),
- costh3 = Math.cos(th3),
- sinth3 = Math.sin(th3),
- toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
- toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
- cp1X = fromX + mT * ( -cosTh * rx * sinth2 - sinTh * ry * costh2),
- cp1Y = fromY + mT * ( -sinTh * rx * sinth2 + cosTh * ry * costh2),
- cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
- cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
+ default:
+ return number;
+ }
+ },
+ falseFunction: function() {
+ return false;
+ },
+ getKlass: function(type, namespace) {
+ type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
+ return fabric.util.resolveNamespace(namespace)[type];
+ },
+ 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;
+ },
+ resolveNamespace: function(namespace) {
+ if (!namespace) {
+ return fabric;
+ }
+ var parts = namespace.split("."), len = parts.length, i, obj = global || fabric.window;
+ for (i = 0; i < len; ++i) {
+ obj = obj[parts[i]];
+ }
+ return obj;
+ },
+ loadImage: function(url, callback, context, crossOrigin) {
+ if (!url) {
+ callback && callback.call(context, url);
+ return;
+ }
+ var img = fabric.util.createImage();
+ var onLoadCallback = function() {
+ callback && callback.call(context, img);
+ img = img.onload = img.onerror = null;
+ };
+ img.onload = onLoadCallback;
+ img.onerror = function() {
+ fabric.log("Error loading " + img.src);
+ callback && callback.call(context, null, true);
+ img = img.onload = img.onerror = null;
+ };
+ if (url.indexOf("data") !== 0 && crossOrigin) {
+ img.crossOrigin = crossOrigin;
+ }
+ if (url.substring(0, 14) === "data:image/svg") {
+ img.onload = null;
+ fabric.util.loadImageInDom(img, onLoadCallback);
+ }
+ img.src = url;
+ },
+ 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);
+ img.onload = function() {
+ onLoadCallback();
+ div.parentNode.removeChild(div);
+ div = null;
+ };
+ },
+ enlivenObjects: function(objects, callback, namespace, reviver) {
+ objects = objects || [];
+ function onLoaded() {
+ if (++numLoadedObjects === numTotalObjects) {
+ callback && callback(enlivenedObjects);
+ }
+ }
+ var enlivenedObjects = [], numLoadedObjects = 0, numTotalObjects = objects.length;
+ if (!numTotalObjects) {
+ callback && callback(enlivenedObjects);
+ return;
+ }
+ objects.forEach(function(o, index) {
+ if (!o || !o.type) {
+ onLoaded();
+ return;
+ }
+ var klass = fabric.util.getKlass(o.type, namespace);
+ klass.fromObject(o, function(obj, error) {
+ error || (enlivenedObjects[index] = obj);
+ reviver && reviver(o, obj, error);
+ onLoaded();
+ });
+ });
+ },
+ enlivenPatterns: function(patterns, callback) {
+ patterns = patterns || [];
+ function onLoaded() {
+ if (++numLoadedPatterns === numPatterns) {
+ callback && callback(enlivenedPatterns);
+ }
+ }
+ var enlivenedPatterns = [], numLoadedPatterns = 0, numPatterns = patterns.length;
+ if (!numPatterns) {
+ callback && callback(enlivenedPatterns);
+ return;
+ }
+ patterns.forEach(function(p, index) {
+ if (p && p.source) {
+ new fabric.Pattern(p, function(pattern) {
+ enlivenedPatterns[index] = pattern;
+ onLoaded();
+ });
+ } else {
+ enlivenedPatterns[index] = p;
+ onLoaded();
+ }
+ });
+ },
+ groupSVGElements: function(elements, options, path) {
+ var object;
+ 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;
+ }
+ return object;
+ },
+ populateWithProperties: function(source, destination, properties) {
+ if (properties && Object.prototype.toString.call(properties) === "[object Array]") {
+ for (var i = 0, len = properties.length; i < len; i++) {
+ if (properties[i] in source) {
+ destination[properties[i]] = source[properties[i]];
+ }
+ }
+ }
+ },
+ drawDashedLine: function(ctx, x, y, x2, y2, da) {
+ var dx = x2 - x, dy = y2 - y, len = sqrt(dx * dx + dy * dy), rot = atan2(dy, dx), dc = da.length, di = 0, draw = true;
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.moveTo(0, 0);
+ ctx.rotate(rot);
+ x = 0;
+ while (len > x) {
+ x += da[di++ % dc];
+ if (x > len) {
+ x = len;
+ }
+ ctx[draw ? "lineTo" : "moveTo"](x, 0);
+ draw = !draw;
+ }
+ ctx.restore();
+ },
+ createCanvasElement: function() {
+ return fabric.document.createElement("canvas");
+ },
+ createImage: function() {
+ return fabric.document.createElement("img");
+ },
+ clipContext: function(receiver, ctx) {
+ ctx.save();
+ ctx.beginPath();
+ receiver.clipTo(ctx);
+ ctx.clip();
+ },
+ multiplyTransformMatrices: function(a, b, is2x2) {
+ return [ a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], is2x2 ? 0 : a[0] * b[4] + a[2] * b[5] + a[4], is2x2 ? 0 : a[1] * b[4] + a[3] * b[5] + a[5] ];
+ },
+ qrDecompose: function(a) {
+ var angle = atan2(a[1], a[0]), denom = pow(a[0], 2) + pow(a[1], 2), scaleX = sqrt(denom), scaleY = (a[0] * a[3] - a[2] * a[1]) / scaleX, skewX = atan2(a[0] * a[2] + a[1] * a[3], denom);
+ return {
+ angle: angle / PiBy180,
+ scaleX: scaleX,
+ scaleY: scaleY,
+ skewX: skewX / PiBy180,
+ skewY: 0,
+ translateX: a[4],
+ translateY: a[5]
+ };
+ },
+ customTransformMatrix: function(scaleX, scaleY, skewX) {
+ var skewMatrixX = [ 1, 0, abs(Math.tan(skewX * PiBy180)), 1 ], scaleMatrix = [ abs(scaleX), 0, 0, abs(scaleY) ];
+ return fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true);
+ },
+ resetObjectTransform: function(target) {
+ target.scaleX = 1;
+ target.scaleY = 1;
+ target.skewX = 0;
+ target.skewY = 0;
+ target.flipX = false;
+ target.flipY = false;
+ target.rotate(0);
+ },
+ getFunctionBody: function(fn) {
+ return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
+ },
+ isTransparent: function(ctx, x, y, tolerance) {
+ if (tolerance > 0) {
+ if (x > tolerance) {
+ x -= tolerance;
+ } else {
+ x = 0;
+ }
+ if (y > tolerance) {
+ y -= tolerance;
+ } else {
+ y = 0;
+ }
+ }
+ var _isTransparent = true, i, temp, imageData = ctx.getImageData(x, y, tolerance * 2 || 1, tolerance * 2 || 1), l = imageData.data.length;
+ for (i = 3; i < l; i += 4) {
+ temp = imageData.data[i];
+ _isTransparent = temp <= 0;
+ if (_isTransparent === false) {
+ break;
+ }
+ }
+ imageData = null;
+ return _isTransparent;
+ },
+ parsePreserveAspectRatioAttribute: function(attribute) {
+ var meetOrSlice = "meet", alignX = "Mid", alignY = "Mid", aspectRatioAttrs = attribute.split(" "), align;
+ if (aspectRatioAttrs && aspectRatioAttrs.length) {
+ meetOrSlice = aspectRatioAttrs.pop();
+ if (meetOrSlice !== "meet" && meetOrSlice !== "slice") {
+ align = meetOrSlice;
+ meetOrSlice = "meet";
+ } else if (aspectRatioAttrs.length) {
+ align = aspectRatioAttrs.pop();
+ }
+ }
+ alignX = align !== "none" ? align.slice(1, 4) : "none";
+ alignY = align !== "none" ? align.slice(5, 8) : "none";
+ return {
+ meetOrSlice: meetOrSlice,
+ alignX: alignX,
+ alignY: alignY
+ };
+ },
+ clearFabricFontCache: function(fontFamily) {
+ if (!fontFamily) {
+ fabric.charWidthsCache = {};
+ } else if (fabric.charWidthsCache[fontFamily]) {
+ delete fabric.charWidthsCache[fontFamily];
+ }
+ },
+ limitDimsByArea: function(ar, maximumArea) {
+ var roughWidth = Math.sqrt(maximumArea * ar), perfLimitSizeY = Math.floor(maximumArea / roughWidth);
+ return {
+ x: Math.floor(roughWidth),
+ y: perfLimitSizeY
+ };
+ },
+ 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);
- segmentToBezierCache[argsString2] = [
- cp1X, cp1Y,
- cp2X, cp2Y,
- toX, toY
- ];
- return segmentToBezierCache[argsString2];
- }
+(function() {
+ var arcToSegmentsCache = {}, segmentToBezierCache = {}, boundsOfCurveCache = {}, _join = Array.prototype.join;
+ function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
+ var argsString = _join.call(arguments);
+ if (arcToSegmentsCache[argsString]) {
+ return arcToSegmentsCache[argsString];
+ }
+ var PI = Math.PI, th = rotateX * PI / 180, sinTh = fabric.util.sin(th), cosTh = fabric.util.cos(th), fromX = 0, fromY = 0;
+ rx = Math.abs(rx);
+ ry = Math.abs(ry);
+ var px = -cosTh * toX * .5 - sinTh * toY * .5, py = -cosTh * toY * .5 + sinTh * toX * .5, rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px, pl = rx2 * ry2 - rx2 * py2 - ry2 * px2, root = 0;
+ if (pl < 0) {
+ var s = Math.sqrt(1 - pl / (rx2 * ry2));
+ rx *= s;
+ ry *= s;
+ } else {
+ root = (large === sweep ? -1 : 1) * Math.sqrt(pl / (rx2 * py2 + ry2 * px2));
+ }
+ var cx = root * rx * py / ry, cy = -root * ry * px / rx, cx1 = cosTh * cx - sinTh * cy + toX * .5, cy1 = sinTh * cx + cosTh * cy + toY * .5, mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry), dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
+ if (sweep === 0 && dtheta > 0) {
+ dtheta -= 2 * PI;
+ } else if (sweep === 1 && dtheta < 0) {
+ dtheta += 2 * PI;
+ }
+ var segments = Math.ceil(Math.abs(dtheta / PI * 2)), result = [], mDelta = dtheta / segments, mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2), th3 = mTheta + mDelta;
+ for (var i = 0; i < segments; i++) {
+ result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
+ fromX = result[i][4];
+ fromY = result[i][5];
+ mTheta = th3;
+ th3 += mDelta;
+ }
+ arcToSegmentsCache[argsString] = result;
+ return result;
+ }
+ function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
+ var argsString2 = _join.call(arguments);
+ if (segmentToBezierCache[argsString2]) {
+ return segmentToBezierCache[argsString2];
+ }
+ var costh2 = fabric.util.cos(th2), sinth2 = fabric.util.sin(th2), costh3 = fabric.util.cos(th3), sinth3 = fabric.util.sin(th3), toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1, toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1, cp1X = fromX + mT * (-cosTh * rx * sinth2 - sinTh * ry * costh2), cp1Y = fromY + mT * (-sinTh * rx * sinth2 + cosTh * ry * costh2), cp2X = toX + mT * (cosTh * rx * sinth3 + sinTh * ry * costh3), cp2Y = toY + mT * (sinTh * rx * sinth3 - cosTh * ry [...]
+ segmentToBezierCache[argsString2] = [ cp1X, cp1Y, cp2X, cp2Y, toX, toY ];
+ return segmentToBezierCache[argsString2];
+ }
+ function calcVectorAngle(ux, uy, vx, vy) {
+ var ta = Math.atan2(uy, ux), tb = Math.atan2(vy, vx);
+ if (tb >= ta) {
+ return tb - ta;
+ } else {
+ return 2 * Math.PI - (ta - tb);
+ }
+ }
+ fabric.util.drawArc = function(ctx, fx, fy, coords) {
+ var rx = coords[0], ry = coords[1], rot = coords[2], large = coords[3], sweep = coords[4], tx = coords[5], ty = coords[6], segs = [ [], [], [], [] ], segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
+ for (var i = 0, len = segsNorm.length; i < len; i++) {
+ segs[i][0] = segsNorm[i][0] + fx;
+ segs[i][1] = segsNorm[i][1] + fy;
+ segs[i][2] = segsNorm[i][2] + fx;
+ segs[i][3] = segsNorm[i][3] + fy;
+ segs[i][4] = segsNorm[i][4] + fx;
+ segs[i][5] = segsNorm[i][5] + fy;
+ ctx.bezierCurveTo.apply(ctx, segs[i]);
+ }
+ };
+ fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
+ var fromX = 0, fromY = 0, bound, bounds = [], segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
+ for (var i = 0, len = segs.length; i < len; i++) {
+ bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
+ bounds.push({
+ x: bound[0].x + fx,
+ y: bound[0].y + fy
+ });
+ bounds.push({
+ x: bound[1].x + fx,
+ y: bound[1].y + fy
+ });
+ fromX = segs[i][4];
+ fromY = segs[i][5];
+ }
+ return bounds;
+ };
+ function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
+ var argsString = _join.call(arguments);
+ if (boundsOfCurveCache[argsString]) {
+ return boundsOfCurveCache[argsString];
+ }
+ var sqrt = Math.sqrt, min = Math.min, max = Math.max, abs = Math.abs, tvalues = [], bounds = [ [], [] ], a, b, c, t, t1, t2, b2ac, sqrtb2ac;
+ b = 6 * x0 - 12 * x1 + 6 * x2;
+ a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
+ c = 3 * x1 - 3 * x0;
+ for (var i = 0; i < 2; ++i) {
+ if (i > 0) {
+ b = 6 * y0 - 12 * y1 + 6 * y2;
+ a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
+ c = 3 * y1 - 3 * y0;
+ }
+ if (abs(a) < 1e-12) {
+ if (abs(b) < 1e-12) {
+ continue;
+ }
+ t = -c / b;
+ if (0 < t && t < 1) {
+ tvalues.push(t);
+ }
+ continue;
+ }
+ b2ac = b * b - 4 * c * a;
+ if (b2ac < 0) {
+ continue;
+ }
+ sqrtb2ac = sqrt(b2ac);
+ t1 = (-b + sqrtb2ac) / (2 * a);
+ if (0 < t1 && t1 < 1) {
+ tvalues.push(t1);
+ }
+ t2 = (-b - sqrtb2ac) / (2 * a);
+ if (0 < t2 && t2 < 1) {
+ tvalues.push(t2);
+ }
+ }
+ var x, y, j = tvalues.length, jlen = j, mt;
+ while (j--) {
+ t = tvalues[j];
+ mt = 1 - t;
+ x = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;
+ bounds[0][j] = x;
+ y = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;
+ bounds[1][j] = y;
+ }
+ bounds[0][jlen] = x0;
+ bounds[1][jlen] = y0;
+ bounds[0][jlen + 1] = x3;
+ bounds[1][jlen + 1] = y3;
+ var result = [ {
+ x: min.apply(null, bounds[0]),
+ y: min.apply(null, bounds[1])
+ }, {
+ x: max.apply(null, bounds[0]),
+ y: max.apply(null, bounds[1])
+ } ];
+ boundsOfCurveCache[argsString] = result;
+ return result;
+ }
+ fabric.util.getBoundsOfCurve = getBoundsOfCurve;
+})();
- /*
- * Private
- */
- function calcVectorAngle(ux, uy, vx, vy) {
- var ta = Math.atan2(uy, ux),
- tb = Math.atan2(vy, vx);
- if (tb >= ta) {
- return tb - ta;
+(function() {
+ var slice = Array.prototype.slice;
+ function invoke(array, method) {
+ var args = slice.call(arguments, 2), result = [];
+ for (var i = 0, len = array.length; i < len; i++) {
+ result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
+ }
+ return result;
}
- else {
- return 2 * Math.PI - (ta - tb);
+ function max(array, byProperty) {
+ return find(array, byProperty, function(value1, value2) {
+ return value1 >= value2;
+ });
}
- }
-
- /**
- * Draws arc
- * @param {CanvasRenderingContext2D} ctx
- * @param {Number} fx
- * @param {Number} fy
- * @param {Array} coords
- */
- fabric.util.drawArc = function(ctx, fx, fy, coords) {
- var rx = coords[0],
- ry = coords[1],
- rot = coords[2],
- large = coords[3],
- sweep = coords[4],
- tx = coords[5],
- ty = coords[6],
- segs = [[], [], [], []],
- segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
-
- for (var i = 0, len = segsNorm.length; i < len; i++) {
- segs[i][0] = segsNorm[i][0] + fx;
- segs[i][1] = segsNorm[i][1] + fy;
- segs[i][2] = segsNorm[i][2] + fx;
- segs[i][3] = segsNorm[i][3] + fy;
- segs[i][4] = segsNorm[i][4] + fx;
- segs[i][5] = segsNorm[i][5] + fy;
- ctx.bezierCurveTo.apply(ctx, segs[i]);
+ function min(array, byProperty) {
+ return find(array, byProperty, function(value1, value2) {
+ return value1 < value2;
+ });
}
- };
-
- /**
- * Calculate bounding box of a elliptic-arc
- * @param {Number} fx start point of arc
- * @param {Number} fy
- * @param {Number} rx horizontal radius
- * @param {Number} ry vertical radius
- * @param {Number} rot angle of horizontal axe
- * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
- * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
- * @param {Number} tx end point of arc
- * @param {Number} ty
- */
- fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
-
- var fromX = 0, fromY = 0, bound, bounds = [],
- segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
-
- for (var i = 0, len = segs.length; i < len; i++) {
- bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
- bounds.push({ x: bound[0].x + fx, y: bound[0].y + fy });
- bounds.push({ x: bound[1].x + fx, y: bound[1].y + fy });
- fromX = segs[i][4];
- fromY = segs[i][5];
+ function fill(array, value) {
+ var k = array.length;
+ while (k--) {
+ array[k] = value;
+ }
+ return array;
}
- return bounds;
- };
-
- /**
- * Calculate bounding box of a beziercurve
- * @param {Number} x0 starting point
- * @param {Number} y0
- * @param {Number} x1 first control point
- * @param {Number} y1
- * @param {Number} x2 secondo control point
- * @param {Number} y2
- * @param {Number} x3 end of beizer
- * @param {Number} y3
- */
- // taken from http://jsbin.com/ivomiq/56/edit no credits available for that.
- function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
- var argsString = _join.call(arguments);
- if (boundsOfCurveCache[argsString]) {
- return boundsOfCurveCache[argsString];
+ function find(array, byProperty, condition) {
+ if (!array || array.length === 0) {
+ return;
+ }
+ var i = array.length - 1, result = byProperty ? array[i][byProperty] : array[i];
+ if (byProperty) {
+ while (i--) {
+ if (condition(array[i][byProperty], result)) {
+ result = array[i][byProperty];
+ }
+ }
+ } else {
+ while (i--) {
+ if (condition(array[i], result)) {
+ result = array[i];
+ }
+ }
+ }
+ return result;
}
+ fabric.util.array = {
+ fill: fill,
+ invoke: invoke,
+ min: min,
+ max: max
+ };
+})();
- var sqrt = Math.sqrt,
- min = Math.min, max = Math.max,
- abs = Math.abs, tvalues = [],
- bounds = [[], []],
- a, b, c, t, t1, t2, b2ac, sqrtb2ac;
-
- b = 6 * x0 - 12 * x1 + 6 * x2;
- a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
- c = 3 * x1 - 3 * x0;
-
- for (var i = 0; i < 2; ++i) {
- if (i > 0) {
- b = 6 * y0 - 12 * y1 + 6 * y2;
- a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
- c = 3 * y1 - 3 * y0;
- }
-
- if (abs(a) < 1e-12) {
- if (abs(b) < 1e-12) {
- continue;
- }
- t = -c / b;
- if (0 < t && t < 1) {
- tvalues.push(t);
- }
- continue;
- }
- b2ac = b * b - 4 * c * a;
- if (b2ac < 0) {
- continue;
- }
- sqrtb2ac = sqrt(b2ac);
- t1 = (-b + sqrtb2ac) / (2 * a);
- if (0 < t1 && t1 < 1) {
- tvalues.push(t1);
- }
- t2 = (-b - sqrtb2ac) / (2 * a);
- if (0 < t2 && t2 < 1) {
- tvalues.push(t2);
- }
+(function() {
+ function extend(destination, source, deep) {
+ if (deep) {
+ if (!fabric.isLikelyNode && source instanceof Element) {
+ destination = source;
+ } else if (source instanceof Array) {
+ destination = [];
+ for (var i = 0, len = source.length; i < len; i++) {
+ destination[i] = extend({}, source[i], deep);
+ }
+ } else if (source && typeof source === "object") {
+ for (var property in source) {
+ if (source.hasOwnProperty(property)) {
+ destination[property] = extend({}, source[property], deep);
+ }
+ }
+ } else {
+ destination = source;
+ }
+ } else {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ }
+ return destination;
}
-
- var x, y, j = tvalues.length, jlen = j, mt;
- while (j--) {
- t = tvalues[j];
- mt = 1 - t;
- x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
- bounds[0][j] = x;
-
- y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
- bounds[1][j] = y;
+ function clone(object, deep) {
+ return extend({}, object, deep);
}
-
- bounds[0][jlen] = x0;
- bounds[1][jlen] = y0;
- bounds[0][jlen + 1] = x3;
- bounds[1][jlen + 1] = y3;
- var result = [
- {
- x: min.apply(null, bounds[0]),
- y: min.apply(null, bounds[1])
- },
- {
- x: max.apply(null, bounds[0]),
- y: max.apply(null, bounds[1])
- }
- ];
- boundsOfCurveCache[argsString] = result;
- return result;
- }
-
- fabric.util.getBoundsOfCurve = getBoundsOfCurve;
-
+ fabric.util.object = {
+ extend: extend,
+ clone: clone
+ };
+ fabric.util.object.extend(fabric.util, fabric.Observable);
})();
-
(function() {
-
- var slice = Array.prototype.slice;
-
- /**
- * Invokes method on all items in a given array
- * @memberOf fabric.util.array
- * @param {Array} array Array to iterate over
- * @param {String} method Name of a method to invoke
- * @return {Array}
- */
- function invoke(array, method) {
- var args = slice.call(arguments, 2), result = [];
- for (var i = 0, len = array.length; i < len; i++) {
- result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
+ function camelize(string) {
+ return string.replace(/-+(.)?/g, function(match, character) {
+ return character ? character.toUpperCase() : "";
+ });
}
- return result;
- }
-
- /**
- * Finds maximum value in array (not necessarily "first" one)
- * @memberOf fabric.util.array
- * @param {Array} array Array to iterate over
- * @param {String} byProperty
- * @return {*}
- */
- function max(array, byProperty) {
- return find(array, byProperty, function(value1, value2) {
- return value1 >= value2;
- });
- }
-
- /**
- * Finds minimum value in array (not necessarily "first" one)
- * @memberOf fabric.util.array
- * @param {Array} array Array to iterate over
- * @param {String} byProperty
- * @return {*}
- */
- function min(array, byProperty) {
- return find(array, byProperty, function(value1, value2) {
- return value1 < value2;
- });
- }
-
- /**
- * @private
- */
- function fill(array, value) {
- var k = array.length;
- while (k--) {
- array[k] = value;
+ function capitalize(string, firstLetterOnly) {
+ return string.charAt(0).toUpperCase() + (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
}
- return array;
- }
-
- /**
- * @private
- */
- function find(array, byProperty, condition) {
- if (!array || array.length === 0) {
- return;
+ function escapeXml(string) {
+ return string.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
}
-
- var i = array.length - 1,
- result = byProperty ? array[i][byProperty] : array[i];
- if (byProperty) {
- while (i--) {
- if (condition(array[i][byProperty], result)) {
- result = array[i][byProperty];
- }
- }
+ 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;
}
- else {
- while (i--) {
- if (condition(array[i], result)) {
- result = array[i];
+ function getWholeChar(str, i) {
+ var code = str.charCodeAt(i);
+ if (isNaN(code)) {
+ return "";
+ }
+ if (code < 55296 || code > 57343) {
+ return str.charAt(i);
}
- }
+ if (55296 <= code && code <= 56319) {
+ if (str.length <= i + 1) {
+ throw "High surrogate without following low surrogate";
+ }
+ var next = str.charCodeAt(i + 1);
+ if (56320 > next || next > 57343) {
+ throw "High surrogate without following low surrogate";
+ }
+ return str.charAt(i) + str.charAt(i + 1);
+ }
+ if (i === 0) {
+ throw "Low surrogate without preceding high surrogate";
+ }
+ var prev = str.charCodeAt(i - 1);
+ if (55296 > prev || prev > 56319) {
+ throw "Low surrogate without preceding high surrogate";
+ }
+ return false;
}
- return result;
- }
-
- /**
- * @namespace fabric.util.array
- */
- fabric.util.array = {
- fill: fill,
- invoke: invoke,
- min: min,
- max: max
- };
-
+ fabric.util.string = {
+ camelize: camelize,
+ capitalize: capitalize,
+ escapeXml: escapeXml,
+ graphemeSplit: graphemeSplit
+ };
})();
-
(function() {
- /**
- * Copies all enumerable properties of one js object to another
- * Does not clone or extend fabric.Object subclasses.
- * @memberOf fabric.util.object
- * @param {Object} destination Where to copy to
- * @param {Object} source Where to copy from
- * @return {Object}
- */
-
- function extend(destination, source, deep) {
- // JScript DontEnum bug is not taken care of
- // the deep clone is for internal use, is not meant to avoid
- // javascript traps or cloning html element or self referenced objects.
- if (deep) {
- if (!fabric.isLikelyNode && source instanceof Element) {
- // avoid cloning deep images, canvases,
- destination = source;
- }
- else if (source instanceof Array) {
- destination = [];
- for (var i = 0, len = source.length; i < len; i++) {
- destination[i] = extend({ }, source[i], deep);
- }
- }
- else if (source && typeof source === 'object') {
+ var slice = Array.prototype.slice, emptyFunction = function() {}, IS_DONTENUM_BUGGY = function() {
+ for (var p in {
+ toString: 1
+ }) {
+ if (p === "toString") {
+ return false;
+ }
+ }
+ return true;
+ }(), addMethods = function(klass, source, parent) {
for (var property in source) {
- if (source.hasOwnProperty(property)) {
- destination[property] = extend({ }, source[property], deep);
- }
- }
- }
- else {
- // this sounds odd for an extend but is ok for recursive use
- destination = source;
- }
+ if (property in klass.prototype && typeof klass.prototype[property] === "function" && (source[property] + "").indexOf("callSuper") > -1) {
+ klass.prototype[property] = function(property) {
+ return function() {
+ var superclass = this.constructor.superclass;
+ this.constructor.superclass = parent;
+ var returnValue = source[property].apply(this, arguments);
+ this.constructor.superclass = superclass;
+ if (property !== "initialize") {
+ return returnValue;
+ }
+ };
+ }(property);
+ } else {
+ klass.prototype[property] = source[property];
+ }
+ if (IS_DONTENUM_BUGGY) {
+ if (source.toString !== Object.prototype.toString) {
+ klass.prototype.toString = source.toString;
+ }
+ if (source.valueOf !== Object.prototype.valueOf) {
+ klass.prototype.valueOf = source.valueOf;
+ }
+ }
+ }
+ };
+ function Subclass() {}
+ function callSuper(methodName) {
+ var parentMethod = null, _this = this;
+ while (_this.constructor.superclass) {
+ var superClassMethod = _this.constructor.superclass.prototype[methodName];
+ if (_this[methodName] !== superClassMethod) {
+ parentMethod = superClassMethod;
+ break;
+ }
+ _this = _this.constructor.superclass.prototype;
+ }
+ if (!parentMethod) {
+ return console.log("tried to callSuper " + methodName + ", method not found in prototype chain", this);
+ }
+ return arguments.length > 1 ? parentMethod.apply(this, slice.call(arguments, 1)) : parentMethod.call(this);
}
- else {
- for (var property in source) {
- destination[property] = source[property];
- }
+ function createClass() {
+ var parent = null, properties = slice.call(arguments, 0);
+ if (typeof properties[0] === "function") {
+ parent = properties.shift();
+ }
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+ klass.superclass = parent;
+ klass.subclasses = [];
+ if (parent) {
+ Subclass.prototype = parent.prototype;
+ klass.prototype = new Subclass();
+ parent.subclasses.push(klass);
+ }
+ for (var i = 0, length = properties.length; i < length; i++) {
+ addMethods(klass, properties[i], parent);
+ }
+ if (!klass.prototype.initialize) {
+ klass.prototype.initialize = emptyFunction;
+ }
+ klass.prototype.constructor = klass;
+ klass.prototype.callSuper = callSuper;
+ return klass;
}
- return destination;
- }
-
- /**
- * 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}
- */
- function clone(object, deep) {
- return extend({ }, object, deep);
- }
-
- /** @namespace fabric.util.object */
- fabric.util.object = {
- extend: extend,
- clone: clone
- };
- fabric.util.object.extend(fabric.util, fabric.Observable);
+ fabric.util.createClass = createClass;
})();
-
(function() {
-
- /**
- * Camelizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to camelize
- * @return {String} Camelized version of a string
- */
- function camelize(string) {
- return string.replace(/-+(.)?/g, function(match, character) {
- return character ? character.toUpperCase() : '';
- });
- }
-
- /**
- * Capitalizes a string
- * @memberOf fabric.util.string
- * @param {String} string String to capitalize
- * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
- * and other letters stay untouched, if false first letter is capitalized
- * and other letters are converted to lowercase.
- * @return {String} Capitalized version of a string
- */
- function capitalize(string, firstLetterOnly) {
- return string.charAt(0).toUpperCase() +
- (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
- }
-
- /**
- * Escapes XML in a string
- * @memberOf fabric.util.string
- * @param {String} string String to escape
- * @return {String} Escaped version of a string
- */
- function escapeXml(string) {
- return string.replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(/</g, '<')
- .replace(/>/g, '>');
- }
-
- /**
- * 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
- */
- 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);
+ var unknown = "unknown";
+ function areHostMethods(object) {
+ var methodNames = Array.prototype.slice.call(arguments, 1), t, i, len = methodNames.length;
+ for (i = 0; i < len; i++) {
+ t = typeof object[methodNames[i]];
+ if (!/^(?:function|object|unknown)$/.test(t)) {
+ return false;
+ }
+ }
+ return true;
}
- return graphemes;
- }
-
- // taken from mdn in the charAt doc page.
- function getWholeChar(str, i) {
- var code = str.charCodeAt(i);
-
- if (isNaN(code)) {
- return ''; // Position not found
+ var getElement, setElement, getUniqueId = function() {
+ var uid = 0;
+ return function(element) {
+ return element.__uniqueID || (element.__uniqueID = "uniqueID__" + uid++);
+ };
+ }();
+ (function() {
+ var elements = {};
+ getElement = function(uid) {
+ return elements[uid];
+ };
+ setElement = function(uid, element) {
+ elements[uid] = element;
+ };
+ })();
+ function createListener(uid, handler) {
+ return {
+ handler: handler,
+ wrappedHandler: createWrappedHandler(uid, handler)
+ };
+ }
+ function createWrappedHandler(uid, handler) {
+ return function(e) {
+ handler.call(getElement(uid), e || fabric.window.event);
+ };
}
- if (code < 0xD800 || code > 0xDFFF) {
- return str.charAt(i);
+ function createDispatcher(uid, eventName) {
+ return function(e) {
+ if (handlers[uid] && handlers[uid][eventName]) {
+ var handlersForEvent = handlers[uid][eventName];
+ for (var i = 0, len = handlersForEvent.length; i < len; i++) {
+ handlersForEvent[i].call(this, e || fabric.window.event);
+ }
+ }
+ };
}
-
- // 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';
- }
- var next = str.charCodeAt(i + 1);
- if (0xDC00 > next || next > 0xDFFF) {
- throw 'High surrogate without following low surrogate';
- }
- return str.charAt(i) + str.charAt(i + 1);
+ var shouldUseAddListenerRemoveListener = areHostMethods(fabric.document.documentElement, "addEventListener", "removeEventListener") && areHostMethods(fabric.window, "addEventListener", "removeEventListener"), shouldUseAttachEventDetachEvent = areHostMethods(fabric.document.documentElement, "attachEvent", "detachEvent") && areHostMethods(fabric.window, "attachEvent", "detachEvent"), listeners = {}, handlers = {}, addListener, removeListener;
+ if (shouldUseAddListenerRemoveListener) {
+ addListener = function(element, eventName, handler, options) {
+ element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
+ };
+ removeListener = function(element, eventName, handler, options) {
+ element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
+ };
+ } else if (shouldUseAttachEventDetachEvent) {
+ addListener = function(element, eventName, handler) {
+ if (!element) {
+ return;
+ }
+ var uid = getUniqueId(element);
+ setElement(uid, element);
+ if (!listeners[uid]) {
+ listeners[uid] = {};
+ }
+ if (!listeners[uid][eventName]) {
+ listeners[uid][eventName] = [];
+ }
+ var listener = createListener(uid, handler);
+ listeners[uid][eventName].push(listener);
+ element.attachEvent("on" + eventName, listener.wrappedHandler);
+ };
+ removeListener = function(element, eventName, handler) {
+ if (!element) {
+ return;
+ }
+ var uid = getUniqueId(element), listener;
+ if (listeners[uid] && listeners[uid][eventName]) {
+ for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
+ listener = listeners[uid][eventName][i];
+ if (listener && listener.handler === handler) {
+ element.detachEvent("on" + eventName, listener.wrappedHandler);
+ listeners[uid][eventName][i] = null;
+ }
+ }
+ }
+ };
+ } else {
+ addListener = function(element, eventName, handler) {
+ if (!element) {
+ return;
+ }
+ var uid = getUniqueId(element);
+ if (!handlers[uid]) {
+ handlers[uid] = {};
+ }
+ if (!handlers[uid][eventName]) {
+ handlers[uid][eventName] = [];
+ var existingHandler = element["on" + eventName];
+ if (existingHandler) {
+ handlers[uid][eventName].push(existingHandler);
+ }
+ element["on" + eventName] = createDispatcher(uid, eventName);
+ }
+ handlers[uid][eventName].push(handler);
+ };
+ removeListener = function(element, eventName, handler) {
+ if (!element) {
+ return;
+ }
+ var uid = getUniqueId(element);
+ if (handlers[uid] && handlers[uid][eventName]) {
+ var handlersForEvent = handlers[uid][eventName];
+ for (var i = 0, len = handlersForEvent.length; i < len; i++) {
+ if (handlersForEvent[i] === handler) {
+ handlersForEvent.splice(i, 1);
+ }
+ }
+ }
+ };
}
- // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
- if (i === 0) {
- throw 'Low surrogate without preceding high surrogate';
+ fabric.util.addListener = addListener;
+ fabric.util.removeListener = removeListener;
+ function getPointer(event) {
+ event || (event = fabric.window.event);
+ var element = event.target || (typeof event.srcElement !== unknown ? event.srcElement : null), scroll = fabric.util.getScrollLeftTop(element);
+ return {
+ x: pointerX(event) + scroll.left,
+ y: pointerY(event) + scroll.top
+ };
}
- var prev = str.charCodeAt(i - 1);
-
- // (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';
+ var pointerX = function(event) {
+ return event.clientX;
+ }, pointerY = function(event) {
+ return event.clientY;
+ };
+ function _getPointer(event, pageProp, clientProp) {
+ var touchProp = event.type === "touchend" ? "changedTouches" : "touches";
+ return event[touchProp] && event[touchProp][0] ? event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]) || event[clientProp] : event[clientProp];
}
- // 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
- };
+ if (fabric.isTouchSupported) {
+ pointerX = function(event) {
+ return _getPointer(event, "pageX", "clientX");
+ };
+ pointerY = function(event) {
+ return _getPointer(event, "pageY", "clientY");
+ };
+ }
+ fabric.util.getPointer = getPointer;
})();
-
(function() {
-
- var slice = Array.prototype.slice, emptyFunction = function() { },
-
- IS_DONTENUM_BUGGY = (function() {
- for (var p in { toString: 1 }) {
- if (p === 'toString') {
- return false;
- }
+ function setStyle(element, styles) {
+ var elementStyle = element.style;
+ if (!elementStyle) {
+ return element;
+ }
+ if (typeof styles === "string") {
+ element.style.cssText += ";" + styles;
+ return styles.indexOf("opacity") > -1 ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles) {
+ if (property === "opacity") {
+ setOpacity(element, styles[property]);
+ } else {
+ var normalizedProperty = property === "float" || property === "cssFloat" ? typeof elementStyle.styleFloat === "undefined" ? "cssFloat" : "styleFloat" : property;
+ elementStyle[normalizedProperty] = styles[property];
+ }
}
- return true;
- })(),
-
- /** @ignore */
- addMethods = function(klass, source, parent) {
- for (var property in source) {
-
- if (property in klass.prototype &&
- typeof klass.prototype[property] === 'function' &&
- (source[property] + '').indexOf('callSuper') > -1) {
-
- klass.prototype[property] = (function(property) {
- return function() {
-
- var superclass = this.constructor.superclass;
- this.constructor.superclass = parent;
- var returnValue = source[property].apply(this, arguments);
- this.constructor.superclass = superclass;
-
- if (property !== 'initialize') {
- return returnValue;
- }
- };
- })(property);
- }
- else {
- klass.prototype[property] = source[property];
- }
-
- if (IS_DONTENUM_BUGGY) {
- if (source.toString !== Object.prototype.toString) {
- klass.prototype.toString = source.toString;
+ return element;
+ }
+ var parseEl = fabric.document.createElement("div"), supportsOpacity = typeof parseEl.style.opacity === "string", supportsFilters = typeof parseEl.style.filter === "string", reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/, setOpacity = function(element) {
+ return element;
+ };
+ if (supportsOpacity) {
+ setOpacity = function(element, value) {
+ element.style.opacity = value;
+ return element;
+ };
+ } else if (supportsFilters) {
+ setOpacity = function(element, value) {
+ var es = element.style;
+ if (element.currentStyle && !element.currentStyle.hasLayout) {
+ es.zoom = 1;
}
- if (source.valueOf !== Object.prototype.valueOf) {
- klass.prototype.valueOf = source.valueOf;
+ if (reOpacity.test(es.filter)) {
+ value = value >= .9999 ? "" : "alpha(opacity=" + value * 100 + ")";
+ es.filter = es.filter.replace(reOpacity, value);
+ } else {
+ es.filter += " alpha(opacity=" + value * 100 + ")";
}
- }
- }
- };
-
- function Subclass() { }
-
- function callSuper(methodName) {
- var parentMethod = null,
- _this = this;
-
- // climb prototype chain to find method not equal to callee's method
- while (_this.constructor.superclass) {
- var superClassMethod = _this.constructor.superclass.prototype[methodName];
- if (_this[methodName] !== superClassMethod) {
- parentMethod = superClassMethod;
- break;
- }
- // eslint-disable-next-line
- _this = _this.constructor.superclass.prototype;
+ return element;
+ };
}
+ fabric.util.setStyle = setStyle;
+})();
- if (!parentMethod) {
- return console.log('tried to callSuper ' + methodName + ', method not found in prototype chain', this);
+(function() {
+ var _slice = Array.prototype.slice;
+ function getById(id) {
+ return typeof id === "string" ? fabric.document.getElementById(id) : id;
}
-
- return (arguments.length > 1)
- ? parentMethod.apply(this, slice.call(arguments, 1))
- : parentMethod.call(this);
- }
-
- /**
- * Helper for creation of "classes".
- * @memberOf fabric.util
- * @param {Function} [parent] optional "Class" to inherit from
- * @param {Object} [properties] Properties shared by all instances of this class
- * (be careful modifying objects defined here as this would affect all instances)
- */
- function createClass() {
- var parent = null,
- properties = slice.call(arguments, 0);
-
- if (typeof properties[0] === 'function') {
- parent = properties.shift();
+ var sliceCanConvertNodelists, toArray = function(arrayLike) {
+ return _slice.call(arrayLike, 0);
+ };
+ try {
+ sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
+ } catch (err) {}
+ if (!sliceCanConvertNodelists) {
+ toArray = function(arrayLike) {
+ var arr = new Array(arrayLike.length), i = arrayLike.length;
+ while (i--) {
+ arr[i] = arrayLike[i];
+ }
+ return arr;
+ };
}
- function klass() {
- this.initialize.apply(this, arguments);
+ function makeElement(tagName, attributes) {
+ var el = fabric.document.createElement(tagName);
+ for (var prop in attributes) {
+ if (prop === "class") {
+ el.className = attributes[prop];
+ } else if (prop === "for") {
+ el.htmlFor = attributes[prop];
+ } else {
+ el.setAttribute(prop, attributes[prop]);
+ }
+ }
+ return el;
}
-
- klass.superclass = parent;
- klass.subclasses = [];
-
- if (parent) {
- Subclass.prototype = parent.prototype;
- klass.prototype = new Subclass();
- parent.subclasses.push(klass);
+ function addClass(element, className) {
+ if (element && (" " + element.className + " ").indexOf(" " + className + " ") === -1) {
+ element.className += (element.className ? " " : "") + className;
+ }
}
- for (var i = 0, length = properties.length; i < length; i++) {
- addMethods(klass, properties[i], parent);
- }
- if (!klass.prototype.initialize) {
- klass.prototype.initialize = emptyFunction;
- }
- klass.prototype.constructor = klass;
- klass.prototype.callSuper = callSuper;
- return klass;
- }
-
- fabric.util.createClass = createClass;
-})();
-
-
-(function () {
-
- var unknown = 'unknown';
-
- /* EVENT HANDLING */
-
- function areHostMethods(object) {
- var methodNames = Array.prototype.slice.call(arguments, 1),
- t, i, len = methodNames.length;
- for (i = 0; i < len; i++) {
- t = typeof object[methodNames[i]];
- if (!(/^(?:function|object|unknown)$/).test(t)) {
- return false;
- }
+ function wrapElement(element, wrapper, attributes) {
+ if (typeof wrapper === "string") {
+ wrapper = makeElement(wrapper, attributes);
+ }
+ if (element.parentNode) {
+ element.parentNode.replaceChild(wrapper, element);
+ }
+ wrapper.appendChild(element);
+ return wrapper;
}
- return true;
- }
-
- /** @ignore */
- var getElement,
- setElement,
- getUniqueId = (function () {
- var uid = 0;
- return function (element) {
- return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
+ function getScrollLeftTop(element) {
+ var left = 0, top = 0, docElement = fabric.document.documentElement, body = fabric.document.body || {
+ scrollLeft: 0,
+ scrollTop: 0
+ };
+ while (element && (element.parentNode || element.host)) {
+ element = element.parentNode || element.host;
+ if (element === fabric.document) {
+ left = body.scrollLeft || docElement.scrollLeft || 0;
+ top = body.scrollTop || docElement.scrollTop || 0;
+ } else {
+ left += element.scrollLeft || 0;
+ top += element.scrollTop || 0;
+ }
+ if (element.nodeType === 1 && element.style.position === "fixed") {
+ break;
+ }
+ }
+ return {
+ left: left,
+ top: top
};
- })();
-
- (function () {
- var elements = { };
- /** @ignore */
- getElement = function (uid) {
- return elements[uid];
- };
- /** @ignore */
- setElement = function (uid, element) {
- elements[uid] = element;
- };
- })();
-
- function createListener(uid, handler) {
- return {
- handler: handler,
- wrappedHandler: createWrappedHandler(uid, handler)
- };
- }
-
- function createWrappedHandler(uid, handler) {
- return function (e) {
- handler.call(getElement(uid), e || fabric.window.event);
- };
- }
-
- function createDispatcher(uid, eventName) {
- return function (e) {
- if (handlers[uid] && handlers[uid][eventName]) {
- var handlersForEvent = handlers[uid][eventName];
- for (var i = 0, len = handlersForEvent.length; i < len; i++) {
- handlersForEvent[i].call(this, e || fabric.window.event);
- }
- }
- };
- }
-
- var shouldUseAddListenerRemoveListener = (
- areHostMethods(fabric.document.documentElement, 'addEventListener', 'removeEventListener') &&
- areHostMethods(fabric.window, 'addEventListener', 'removeEventListener')),
-
- shouldUseAttachEventDetachEvent = (
- areHostMethods(fabric.document.documentElement, 'attachEvent', 'detachEvent') &&
- areHostMethods(fabric.window, 'attachEvent', 'detachEvent')),
-
- // IE branch
- listeners = { },
-
- // DOM L0 branch
- handlers = { },
-
- addListener, removeListener;
-
- if (shouldUseAddListenerRemoveListener) {
- /** @ignore */
- addListener = function (element, eventName, handler, options) {
- // since ie10 or ie9 can use addEventListener but they do not support options, i need to check
- element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler, options) {
- element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
- };
- }
-
- else if (shouldUseAttachEventDetachEvent) {
- /** @ignore */
- addListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element);
- setElement(uid, element);
- if (!listeners[uid]) {
- listeners[uid] = { };
- }
- if (!listeners[uid][eventName]) {
- listeners[uid][eventName] = [];
-
- }
- var listener = createListener(uid, handler);
- listeners[uid][eventName].push(listener);
- element.attachEvent('on' + eventName, listener.wrappedHandler);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element), listener;
- if (listeners[uid] && listeners[uid][eventName]) {
- for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
- listener = listeners[uid][eventName][i];
- if (listener && listener.handler === handler) {
- element.detachEvent('on' + eventName, listener.wrappedHandler);
- listeners[uid][eventName][i] = null;
- }
- }
- }
- };
- }
- else {
- /** @ignore */
- addListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element);
- if (!handlers[uid]) {
- handlers[uid] = { };
- }
- if (!handlers[uid][eventName]) {
- handlers[uid][eventName] = [];
- var existingHandler = element['on' + eventName];
- if (existingHandler) {
- handlers[uid][eventName].push(existingHandler);
- }
- element['on' + eventName] = createDispatcher(uid, eventName);
- }
- handlers[uid][eventName].push(handler);
- };
- /** @ignore */
- removeListener = function (element, eventName, handler) {
- if (!element) {
- return;
- }
- var uid = getUniqueId(element);
- if (handlers[uid] && handlers[uid][eventName]) {
- var handlersForEvent = handlers[uid][eventName];
- for (var i = 0, len = handlersForEvent.length; i < len; i++) {
- if (handlersForEvent[i] === handler) {
- handlersForEvent.splice(i, 1);
- }
- }
- }
- };
- }
-
- /**
- * Adds an event listener to an element
- * @function
- * @memberOf fabric.util
- * @param {HTMLElement} element
- * @param {String} eventName
- * @param {Function} handler
- */
- fabric.util.addListener = addListener;
-
- /**
- * Removes an event listener from an element
- * @function
- * @memberOf fabric.util
- * @param {HTMLElement} element
- * @param {String} eventName
- * @param {Function} handler
- */
- fabric.util.removeListener = removeListener;
-
- /**
- * Cross-browser wrapper for getting event's coordinates
- * @memberOf fabric.util
- * @param {Event} event Event object
- */
- function getPointer(event) {
- event || (event = fabric.window.event);
-
- var element = event.target ||
- (typeof event.srcElement !== unknown ? event.srcElement : null),
-
- scroll = fabric.util.getScrollLeftTop(element);
-
- return {
- x: pointerX(event) + scroll.left,
- y: pointerY(event) + scroll.top
- };
- }
-
- var pointerX = function(event) {
- return event.clientX;
- },
-
- pointerY = function(event) {
- return event.clientY;
- };
-
- function _getPointer(event, pageProp, clientProp) {
- var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches';
-
- return (event[touchProp] && event[touchProp][0]
- ? (event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]))
- || event[clientProp]
- : event[clientProp]);
- }
-
- if (fabric.isTouchSupported) {
- pointerX = function(event) {
- return _getPointer(event, 'pageX', 'clientX');
- };
- pointerY = function(event) {
- return _getPointer(event, 'pageY', 'clientY');
- };
- }
-
- fabric.util.getPointer = getPointer;
-
-})();
-
-
-(function () {
-
- /**
- * Cross-browser wrapper for setting element's style
- * @memberOf fabric.util
- * @param {HTMLElement} element
- * @param {Object} styles
- * @return {HTMLElement} Element that was passed as a first argument
- */
- function setStyle(element, styles) {
- var elementStyle = element.style;
- if (!elementStyle) {
- return element;
}
- if (typeof styles === 'string') {
- element.style.cssText += ';' + styles;
- return styles.indexOf('opacity') > -1
- ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
- : element;
+ function getElementOffset(element) {
+ var docElem, doc = element && element.ownerDocument, box = {
+ left: 0,
+ top: 0
+ }, offset = {
+ left: 0,
+ top: 0
+ }, scrollLeftTop, offsetAttributes = {
+ borderLeftWidth: "left",
+ borderTopWidth: "top",
+ paddingLeft: "left",
+ paddingTop: "top"
+ };
+ if (!doc) {
+ return offset;
+ }
+ for (var attr in offsetAttributes) {
+ offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
+ }
+ docElem = doc.documentElement;
+ if (typeof element.getBoundingClientRect !== "undefined") {
+ box = element.getBoundingClientRect();
+ }
+ scrollLeftTop = getScrollLeftTop(element);
+ return {
+ left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
+ top: box.top + scrollLeftTop.top - (docElem.clientTop || 0) + offset.top
+ };
}
- for (var property in styles) {
- if (property === 'opacity') {
- setOpacity(element, styles[property]);
- }
- else {
- var normalizedProperty = (property === 'float' || property === 'cssFloat')
- ? (typeof elementStyle.styleFloat === 'undefined' ? 'cssFloat' : 'styleFloat')
- : property;
- elementStyle[normalizedProperty] = styles[property];
- }
+ var getElementStyle;
+ if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
+ getElementStyle = function(element, attr) {
+ var style = fabric.document.defaultView.getComputedStyle(element, null);
+ return style ? style[attr] : undefined;
+ };
+ } else {
+ getElementStyle = function(element, attr) {
+ var value = element.style[attr];
+ if (!value && element.currentStyle) {
+ value = element.currentStyle[attr];
+ }
+ return value;
+ };
}
- return element;
- }
-
- var parseEl = fabric.document.createElement('div'),
- supportsOpacity = typeof parseEl.style.opacity === 'string',
- supportsFilters = typeof parseEl.style.filter === 'string',
- reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,
-
- /** @ignore */
- setOpacity = function (element) { return element; };
-
- if (supportsOpacity) {
- /** @ignore */
- setOpacity = function(element, value) {
- element.style.opacity = value;
- return element;
- };
- }
- else if (supportsFilters) {
- /** @ignore */
- setOpacity = function(element, value) {
- var es = element.style;
- if (element.currentStyle && !element.currentStyle.hasLayout) {
- es.zoom = 1;
- }
- if (reOpacity.test(es.filter)) {
- value = value >= 0.9999 ? '' : ('alpha(opacity=' + (value * 100) + ')');
- es.filter = es.filter.replace(reOpacity, value);
- }
- else {
- es.filter += ' alpha(opacity=' + (value * 100) + ')';
- }
- return element;
- };
- }
-
- fabric.util.setStyle = setStyle;
-
+ (function() {
+ var style = fabric.document.documentElement.style, selectProp = "userSelect" in style ? "userSelect" : "MozUserSelect" in style ? "MozUserSelect" : "WebkitUserSelect" in style ? "WebkitUserSelect" : "KhtmlUserSelect" in style ? "KhtmlUserSelect" : "";
+ function makeElementUnselectable(element) {
+ if (typeof element.onselectstart !== "undefined") {
+ element.onselectstart = fabric.util.falseFunction;
+ }
+ if (selectProp) {
+ element.style[selectProp] = "none";
+ } else if (typeof element.unselectable === "string") {
+ element.unselectable = "on";
+ }
+ return element;
+ }
+ function makeElementSelectable(element) {
+ if (typeof element.onselectstart !== "undefined") {
+ element.onselectstart = null;
+ }
+ if (selectProp) {
+ element.style[selectProp] = "";
+ } else if (typeof element.unselectable === "string") {
+ element.unselectable = "";
+ }
+ return element;
+ }
+ fabric.util.makeElementUnselectable = makeElementUnselectable;
+ fabric.util.makeElementSelectable = makeElementSelectable;
+ })();
+ (function() {
+ function getScript(url, callback) {
+ var headEl = fabric.document.getElementsByTagName("head")[0], scriptEl = fabric.document.createElement("script"), loading = true;
+ scriptEl.onload = scriptEl.onreadystatechange = function(e) {
+ if (loading) {
+ if (typeof this.readyState === "string" && this.readyState !== "loaded" && this.readyState !== "complete") {
+ return;
+ }
+ loading = false;
+ callback(e || fabric.window.event);
+ scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
+ }
+ };
+ scriptEl.src = url;
+ headEl.appendChild(scriptEl);
+ }
+ fabric.util.getScript = getScript;
+ })();
+ function getNodeCanvas(element) {
+ var impl = fabric.jsdomImplForWrapper(element);
+ return impl._canvas || impl._image;
+ }
+ fabric.util.getById = getById;
+ fabric.util.toArray = toArray;
+ fabric.util.makeElement = makeElement;
+ fabric.util.addClass = addClass;
+ fabric.util.wrapElement = wrapElement;
+ fabric.util.getScrollLeftTop = getScrollLeftTop;
+ fabric.util.getElementOffset = getElementOffset;
+ fabric.util.getElementStyle = getElementStyle;
+ fabric.util.getNodeCanvas = getNodeCanvas;
})();
-
(function() {
-
- var _slice = Array.prototype.slice;
-
- /**
- * Takes id and returns an element with that id (if one exists in a document)
- * @memberOf fabric.util
- * @param {String|HTMLElement} id
- * @return {HTMLElement|null}
- */
- function getById(id) {
- return typeof id === 'string' ? fabric.document.getElementById(id) : id;
- }
-
- var sliceCanConvertNodelists,
- /**
- * Converts an array-like object (e.g. arguments or NodeList) to an array
- * @memberOf fabric.util
- * @param {Object} arrayLike
- * @return {Array}
- */
- toArray = function(arrayLike) {
- return _slice.call(arrayLike, 0);
- };
-
- try {
- sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
- }
- catch (err) { }
-
- if (!sliceCanConvertNodelists) {
- toArray = function(arrayLike) {
- var arr = new Array(arrayLike.length), i = arrayLike.length;
- while (i--) {
- arr[i] = arrayLike[i];
- }
- return arr;
- };
- }
-
- /**
- * Creates specified element with specified attributes
- * @memberOf fabric.util
- * @param {String} tagName Type of an element to create
- * @param {Object} [attributes] Attributes to set on an element
- * @return {HTMLElement} Newly created element
- */
- function makeElement(tagName, attributes) {
- var el = fabric.document.createElement(tagName);
- for (var prop in attributes) {
- if (prop === 'class') {
- el.className = attributes[prop];
- }
- else if (prop === 'for') {
- el.htmlFor = attributes[prop];
- }
- else {
- el.setAttribute(prop, attributes[prop]);
- }
- }
- return el;
- }
-
- /**
- * Adds class to an element
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to add class to
- * @param {String} className Class to add to an element
- */
- function addClass(element, className) {
- if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
- element.className += (element.className ? ' ' : '') + className;
- }
- }
-
- /**
- * Wraps element with another element
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to wrap
- * @param {HTMLElement|String} wrapper Element to wrap with
- * @param {Object} [attributes] Attributes to set on a wrapper
- * @return {HTMLElement} wrapper
- */
- function wrapElement(element, wrapper, attributes) {
- if (typeof wrapper === 'string') {
- wrapper = makeElement(wrapper, attributes);
- }
- if (element.parentNode) {
- element.parentNode.replaceChild(wrapper, element);
- }
- wrapper.appendChild(element);
- return wrapper;
- }
-
- /**
- * Returns element scroll offsets
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to operate on
- * @return {Object} Object with left/top values
- */
- function getScrollLeftTop(element) {
-
- var left = 0,
- top = 0,
- docElement = fabric.document.documentElement,
- body = fabric.document.body || {
- scrollLeft: 0, scrollTop: 0
+ function addParamToUrl(url, param) {
+ return url + (/\?/.test(url) ? "&" : "?") + param;
+ }
+ var makeXHR = function() {
+ var factories = [ function() {
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ }, function() {
+ return new ActiveXObject("Msxml2.XMLHTTP");
+ }, function() {
+ return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+ }, function() {
+ return new XMLHttpRequest();
+ } ];
+ for (var i = factories.length; i--; ) {
+ try {
+ var req = factories[i]();
+ if (req) {
+ return factories[i];
+ }
+ } catch (err) {}
+ }
+ }();
+ function emptyFn() {}
+ function request(url, options) {
+ options || (options = {});
+ var method = options.method ? options.method.toUpperCase() : "GET", onComplete = options.onComplete || function() {}, xhr = makeXHR(), body = options.body || options.parameters;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4) {
+ onComplete(xhr);
+ xhr.onreadystatechange = emptyFn;
+ }
};
-
- // While loop checks (and then sets element to) .parentNode OR .host
- // to account for ShadowDOM. We still want to traverse up out of ShadowDOM,
- // but the .parentNode of a root ShadowDOM node will always be null, instead
- // it should be accessed through .host. See http://stackoverflow.com/a/24765528/4383938
- while (element && (element.parentNode || element.host)) {
-
- // Set element to element parent, or 'host' in case of ShadowDOM
- element = element.parentNode || element.host;
-
- if (element === fabric.document) {
- left = body.scrollLeft || docElement.scrollLeft || 0;
- top = body.scrollTop || docElement.scrollTop || 0;
- }
- else {
- left += element.scrollLeft || 0;
- top += element.scrollTop || 0;
- }
-
- if (element.nodeType === 1 && element.style.position === 'fixed') {
- break;
- }
+ if (method === "GET") {
+ body = null;
+ if (typeof options.parameters === "string") {
+ url = addParamToUrl(url, options.parameters);
+ }
+ }
+ xhr.open(method, url, true);
+ if (method === "POST" || method === "PUT") {
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ }
+ xhr.send(body);
+ return xhr;
}
+ fabric.util.request = request;
+})();
- return { left: left, top: top };
- }
+fabric.log = function() {};
- /**
- * Returns offset for a given element
- * @function
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to get offset for
- * @return {Object} Object with "left" and "top" properties
- */
- function getElementOffset(element) {
- var docElem,
- doc = element && element.ownerDocument,
- box = { left: 0, top: 0 },
- offset = { left: 0, top: 0 },
- scrollLeftTop,
- offsetAttributes = {
- borderLeftWidth: 'left',
- borderTopWidth: 'top',
- paddingLeft: 'left',
- paddingTop: 'top'
- };
+fabric.warn = function() {};
- if (!doc) {
- return offset;
- }
+if (typeof console !== "undefined") {
+ [ "log", "warn" ].forEach(function(methodName) {
+ if (typeof console[methodName] !== "undefined" && typeof console[methodName].apply === "function") {
+ fabric[methodName] = function() {
+ return console[methodName].apply(console, arguments);
+ };
+ }
+ });
+}
- for (var attr in offsetAttributes) {
- offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
+(function() {
+ function noop() {
+ return false;
}
-
- docElem = doc.documentElement;
- if ( typeof element.getBoundingClientRect !== 'undefined' ) {
- box = element.getBoundingClientRect();
+ function animate(options) {
+ requestAnimFrame(function(timestamp) {
+ options || (options = {});
+ var start = timestamp || +new Date(), duration = options.duration || 500, finish = start + duration, time, onChange = options.onChange || noop, abort = options.abort || noop, onComplete = options.onComplete || noop, easing = options.easing || function(t, b, c, d) {
+ return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
+ }, startValue = "startValue" in options ? options.startValue : 0, endValue = "endValue" in options ? options.endValue : 100, byValue = options.byValue || endValue - startValue;
+ options.onStart && options.onStart();
+ (function tick(ticktime) {
+ if (abort()) {
+ onComplete(endValue, 1, 1);
+ return;
+ }
+ time = ticktime || +new Date();
+ var currentTime = time > finish ? duration : time - start, timePerc = currentTime / duration, current = easing(currentTime, startValue, byValue, duration), valuePerc = Math.abs((current - startValue) / byValue);
+ onChange(current, valuePerc, timePerc);
+ if (time > finish) {
+ options.onComplete && options.onComplete();
+ return;
+ }
+ requestAnimFrame(tick);
+ })(start);
+ });
}
-
- scrollLeftTop = getScrollLeftTop(element);
-
- return {
- left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
- top: box.top + scrollLeftTop.top - (docElem.clientTop || 0) + offset.top
- };
- }
-
- /**
- * Returns style attribute value of a given element
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to get style attribute for
- * @param {String} attr Style attribute to get for element
- * @return {String} Style attribute value of the given element.
- */
- var getElementStyle;
- if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
- getElementStyle = function(element, attr) {
- var style = fabric.document.defaultView.getComputedStyle(element, null);
- return style ? style[attr] : undefined;
+ var _requestAnimFrame = fabric.window.requestAnimationFrame || fabric.window.webkitRequestAnimationFrame || fabric.window.mozRequestAnimationFrame || fabric.window.oRequestAnimationFrame || fabric.window.msRequestAnimationFrame || function(callback) {
+ return fabric.window.setTimeout(callback, 1e3 / 60);
};
- }
- else {
- getElementStyle = function(element, attr) {
- var value = element.style[attr];
- if (!value && element.currentStyle) {
- value = element.currentStyle[attr];
- }
- return value;
- };
- }
-
- (function () {
- var style = fabric.document.documentElement.style,
- selectProp = 'userSelect' in style
- ? 'userSelect'
- : 'MozUserSelect' in style
- ? 'MozUserSelect'
- : 'WebkitUserSelect' in style
- ? 'WebkitUserSelect'
- : 'KhtmlUserSelect' in style
- ? 'KhtmlUserSelect'
- : '';
-
- /**
- * Makes element unselectable
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to make unselectable
- * @return {HTMLElement} Element that was passed in
- */
- function makeElementUnselectable(element) {
- if (typeof element.onselectstart !== 'undefined') {
- element.onselectstart = fabric.util.falseFunction;
- }
- if (selectProp) {
- element.style[selectProp] = 'none';
- }
- else if (typeof element.unselectable === 'string') {
- element.unselectable = 'on';
- }
- return element;
- }
-
- /**
- * Makes element selectable
- * @memberOf fabric.util
- * @param {HTMLElement} element Element to make selectable
- * @return {HTMLElement} Element that was passed in
- */
- function makeElementSelectable(element) {
- if (typeof element.onselectstart !== 'undefined') {
- element.onselectstart = null;
- }
- if (selectProp) {
- element.style[selectProp] = '';
- }
- else if (typeof element.unselectable === 'string') {
- element.unselectable = '';
- }
- return element;
+ var _cancelAnimFrame = fabric.window.cancelAnimationFrame || fabric.window.clearTimeout;
+ function requestAnimFrame() {
+ return _requestAnimFrame.apply(fabric.window, arguments);
}
-
- fabric.util.makeElementUnselectable = makeElementUnselectable;
- fabric.util.makeElementSelectable = makeElementSelectable;
- })();
-
- (function() {
-
- /**
- * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
- * @memberOf fabric.util
- * @param {String} url URL of a script to load
- * @param {Function} callback Callback to execute when script is finished loading
- */
- function getScript(url, callback) {
- var headEl = fabric.document.getElementsByTagName('head')[0],
- scriptEl = fabric.document.createElement('script'),
- loading = true;
-
- /** @ignore */
- scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
- if (loading) {
- if (typeof this.readyState === 'string' &&
- this.readyState !== 'loaded' &&
- this.readyState !== 'complete') {
- return;
- }
- loading = false;
- callback(e || fabric.window.event);
- scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
- }
- };
- scriptEl.src = url;
- headEl.appendChild(scriptEl);
- // causes issue in Opera
- // headEl.removeChild(scriptEl);
+ function cancelAnimFrame() {
+ return _cancelAnimFrame.apply(fabric.window, arguments);
}
-
- fabric.util.getScript = getScript;
- })();
-
- function getNodeCanvas(element) {
- var impl = fabric.jsdomImplForWrapper(element);
- return impl._canvas || impl._image;
- };
-
- fabric.util.getById = getById;
- fabric.util.toArray = toArray;
- fabric.util.makeElement = makeElement;
- fabric.util.addClass = addClass;
- fabric.util.wrapElement = wrapElement;
- fabric.util.getScrollLeftTop = getScrollLeftTop;
- fabric.util.getElementOffset = getElementOffset;
- fabric.util.getElementStyle = getElementStyle;
- fabric.util.getNodeCanvas = getNodeCanvas;
-
+ fabric.util.animate = animate;
+ fabric.util.requestAnimFrame = requestAnimFrame;
+ fabric.util.cancelAnimFrame = cancelAnimFrame;
})();
-
(function() {
-
- function addParamToUrl(url, param) {
- return url + (/\?/.test(url) ? '&' : '?') + param;
- }
-
- var makeXHR = (function() {
- var factories = [
- function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
- function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
- function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
- function() { return new XMLHttpRequest(); }
- ];
- for (var i = factories.length; i--; ) {
- try {
- var req = factories[i]();
- if (req) {
- return factories[i];
- }
- }
- catch (err) { }
- }
- })();
-
- function emptyFn() { }
-
- /**
- * Cross-browser abstraction for sending XMLHttpRequest
- * @memberOf fabric.util
- * @param {String} url URL to send XMLHttpRequest to
- * @param {Object} [options] Options object
- * @param {String} [options.method="GET"]
- * @param {String} [options.parameters] parameters to append to url in GET or in body
- * @param {String} [options.body] body to send with POST or PUT request
- * @param {Function} options.onComplete Callback to invoke when request is completed
- * @return {XMLHttpRequest} request
- */
- function request(url, options) {
-
- options || (options = { });
-
- var method = options.method ? options.method.toUpperCase() : 'GET',
- onComplete = options.onComplete || function() { },
- xhr = makeXHR(),
- body = options.body || options.parameters;
-
- /** @ignore */
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- onComplete(xhr);
- xhr.onreadystatechange = emptyFn;
- }
- };
-
- if (method === 'GET') {
- body = null;
- if (typeof options.parameters === 'string') {
- url = addParamToUrl(url, options.parameters);
- }
- }
-
- xhr.open(method, url, true);
-
- if (method === 'POST' || method === 'PUT') {
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- }
-
- xhr.send(body);
- return xhr;
- }
-
- fabric.util.request = request;
-})();
-
-
-/**
- * Wrapper around `console.log` (when available)
- * @param {*} [values] Values to log
- */
-fabric.log = function() { };
-
-/**
- * Wrapper around `console.warn` (when available)
- * @param {*} [values] Values to log as a warning
- */
-fabric.warn = function() { };
-
-/* eslint-disable */
-if (typeof console !== 'undefined') {
-
- ['log', 'warn'].forEach(function(methodName) {
-
- if (typeof console[methodName] !== 'undefined' &&
- typeof console[methodName].apply === 'function') {
-
- fabric[methodName] = function() {
- return console[methodName].apply(console, arguments);
- };
+ function calculateColor(begin, end, pos) {
+ var color = "rgba(" + parseInt(begin[0] + pos * (end[0] - begin[0]), 10) + "," + parseInt(begin[1] + pos * (end[1] - begin[1]), 10) + "," + parseInt(begin[2] + pos * (end[2] - begin[2]), 10);
+ color += "," + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
+ color += ")";
+ return color;
+ }
+ function animateColor(fromColor, toColor, duration, options) {
+ var startColor = new fabric.Color(fromColor).getSource(), endColor = new fabric.Color(toColor).getSource();
+ options = options || {};
+ fabric.util.animate(fabric.util.object.extend(options, {
+ duration: duration || 500,
+ startValue: startColor,
+ endValue: endColor,
+ byValue: endColor,
+ easing: function(currentTime, startValue, byValue, duration) {
+ var posValue = options.colorEasing ? options.colorEasing(currentTime, duration) : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
+ return calculateColor(startValue, byValue, posValue);
+ }
+ }));
}
- });
-}
-/* eslint-enable */
-
-
-(function() {
-
- function noop() {
- return false;
- }
-
- /**
- * Changes value from one to another within certain period of time, invoking callbacks as value is being changed.
- * @memberOf fabric.util
- * @param {Object} [options] Animation options
- * @param {Function} [options.onChange] Callback; invoked on every value change
- * @param {Function} [options.onComplete] Callback; invoked when value change is completed
- * @param {Number} [options.startValue=0] Starting value
- * @param {Number} [options.endValue=100] Ending value
- * @param {Number} [options.byValue=100] Value to modify the property by
- * @param {Function} [options.easing] Easing function
- * @param {Number} [options.duration=500] Duration of change (in ms)
- */
- function animate(options) {
-
- requestAnimFrame(function(timestamp) {
- options || (options = { });
-
- var start = timestamp || +new Date(),
- duration = options.duration || 500,
- finish = start + duration, time,
- onChange = options.onChange || noop,
- abort = options.abort || noop,
- onComplete = options.onComplete || noop,
- easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
- startValue = 'startValue' in options ? options.startValue : 0,
- endValue = 'endValue' in options ? options.endValue : 100,
- byValue = options.byValue || endValue - startValue;
-
- options.onStart && options.onStart();
-
- (function tick(ticktime) {
- if (abort()) {
- onComplete(endValue, 1, 1);
- return;
- }
- time = ticktime || +new Date();
- var currentTime = time > finish ? duration : (time - start),
- timePerc = currentTime / duration,
- current = easing(currentTime, startValue, byValue, duration),
- valuePerc = Math.abs((current - startValue) / byValue);
- onChange(current, valuePerc, timePerc);
- if (time > finish) {
- options.onComplete && options.onComplete();
- return;
- }
- requestAnimFrame(tick);
- })(start);
- });
-
- }
-
- var _requestAnimFrame = fabric.window.requestAnimationFrame ||
- fabric.window.webkitRequestAnimationFrame ||
- fabric.window.mozRequestAnimationFrame ||
- fabric.window.oRequestAnimationFrame ||
- fabric.window.msRequestAnimationFrame ||
- function(callback) {
- 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
- * @memberOf fabric.util
- * @param {Function} callback Callback to invoke
- * @param {DOMElement} element optional Element to associate with animation
- */
- function requestAnimFrame() {
- 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;
-})();
-
-
-(function() {
- // Calculate an in-between color. Returns a "rgba()" string.
- // Credit: Edwin Martin <ed...@bitstorm.org>
- // http://www.bitstorm.org/jquery/color-animation/jquery.animate-colors.js
- function calculateColor(begin, end, pos) {
- var color = 'rgba('
- + parseInt((begin[0] + pos * (end[0] - begin[0])), 10) + ','
- + parseInt((begin[1] + pos * (end[1] - begin[1])), 10) + ','
- + parseInt((begin[2] + pos * (end[2] - begin[2])), 10);
-
- color += ',' + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
- color += ')';
- return color;
- }
-
- /**
- * Changes the color from one to another within certain period of time, invoking callbacks as value is being changed.
- * @memberOf fabric.util
- * @param {String} fromColor The starting color in hex or rgb(a) format.
- * @param {String} toColor The starting color in hex or rgb(a) format.
- * @param {Number} [duration] Duration of change (in ms).
- * @param {Object} [options] Animation options
- * @param {Function} [options.onChange] Callback; invoked on every value change
- * @param {Function} [options.onComplete] Callback; invoked when value change is completed
- * @param {Function} [options.colorEasing] Easing function. Note that this function only take two arguments (currentTime, duration). Thus the regular animation easing functions cannot be used.
- */
- function animateColor(fromColor, toColor, duration, options) {
- var startColor = new fabric.Color(fromColor).getSource(),
- endColor = new fabric.Color(toColor).getSource();
-
- options = options || {};
-
- fabric.util.animate(fabric.util.object.extend(options, {
- duration: duration || 500,
- startValue: startColor,
- endValue: endColor,
- byValue: endColor,
- easing: function (currentTime, startValue, byValue, duration) {
- var posValue = options.colorEasing
- ? options.colorEasing(currentTime, duration)
- : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
- return calculateColor(startValue, byValue, posValue);
- }
- }));
- }
-
- fabric.util.animateColor = animateColor;
-
+ fabric.util.animateColor = animateColor;
})();
-
(function() {
-
- function normalize(a, c, p, s) {
- if (a < Math.abs(c)) {
- a = c;
- s = p / 4;
+ function normalize(a, c, p, s) {
+ if (a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ } else {
+ if (c === 0 && a === 0) {
+ s = p / (2 * Math.PI) * Math.asin(1);
+ } else {
+ s = p / (2 * Math.PI) * Math.asin(c / a);
+ }
+ }
+ return {
+ a: a,
+ c: c,
+ p: p,
+ s: s
+ };
}
- else {
- //handle the 0/0 case:
- if (c === 0 && a === 0) {
- s = p / (2 * Math.PI) * Math.asin(1);
- }
- else {
- s = p / (2 * Math.PI) * Math.asin(c / a);
- }
+ function elastic(opts, t, d) {
+ return opts.a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p);
}
- return { a: a, c: c, p: p, s: s };
- }
-
- function elastic(opts, t, d) {
- return opts.a *
- Math.pow(2, 10 * (t -= 1)) *
- Math.sin( (t * d - opts.s) * (2 * Math.PI) / opts.p );
- }
-
- /**
- * Cubic easing out
- * @memberOf fabric.util.ease
- */
- function easeOutCubic(t, b, c, d) {
- return c * ((t = t / d - 1) * t * t + 1) + b;
- }
-
- /**
- * Cubic easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutCubic(t, b, c, d) {
- t /= d / 2;
- if (t < 1) {
- return c / 2 * t * t * t + b;
+ function easeOutCubic(t, b, c, d) {
+ return c * ((t = t / d - 1) * t * t + 1) + b;
}
- return c / 2 * ((t -= 2) * t * t + 2) + b;
- }
-
- /**
- * Quartic easing in
- * @memberOf fabric.util.ease
- */
- function easeInQuart(t, b, c, d) {
- return c * (t /= d) * t * t * t + b;
- }
-
- /**
- * Quartic easing out
- * @memberOf fabric.util.ease
- */
- function easeOutQuart(t, b, c, d) {
- return -c * ((t = t / d - 1) * t * t * t - 1) + b;
- }
-
- /**
- * Quartic easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutQuart(t, b, c, d) {
- t /= d / 2;
- if (t < 1) {
- return c / 2 * t * t * t * t + b;
+ function easeInOutCubic(t, b, c, d) {
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t + 2) + b;
}
- return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
- }
-
- /**
- * Quintic easing in
- * @memberOf fabric.util.ease
- */
- function easeInQuint(t, b, c, d) {
- return c * (t /= d) * t * t * t * t + b;
- }
-
- /**
- * Quintic easing out
- * @memberOf fabric.util.ease
- */
- function easeOutQuint(t, b, c, d) {
- return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
- }
-
- /**
- * Quintic easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutQuint(t, b, c, d) {
- t /= d / 2;
- if (t < 1) {
- return c / 2 * t * t * t * t * t + b;
+ function easeInQuart(t, b, c, d) {
+ return c * (t /= d) * t * t * t + b;
}
- return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
- }
-
- /**
- * Sinusoidal easing in
- * @memberOf fabric.util.ease
- */
- function easeInSine(t, b, c, d) {
- return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
- }
-
- /**
- * Sinusoidal easing out
- * @memberOf fabric.util.ease
- */
- function easeOutSine(t, b, c, d) {
- return c * Math.sin(t / d * (Math.PI / 2)) + b;
- }
-
- /**
- * Sinusoidal easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutSine(t, b, c, d) {
- return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
- }
-
- /**
- * Exponential easing in
- * @memberOf fabric.util.ease
- */
- function easeInExpo(t, b, c, d) {
- return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
- }
-
- /**
- * Exponential easing out
- * @memberOf fabric.util.ease
- */
- function easeOutExpo(t, b, c, d) {
- return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
- }
-
- /**
- * Exponential easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutExpo(t, b, c, d) {
- if (t === 0) {
- return b;
+ function easeOutQuart(t, b, c, d) {
+ return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
- if (t === d) {
- return b + c;
+ function easeInOutQuart(t, b, c, d) {
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t + b;
+ }
+ return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
- t /= d / 2;
- if (t < 1) {
- return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
+ function easeInQuint(t, b, c, d) {
+ return c * (t /= d) * t * t * t * t + b;
}
- return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
- }
-
- /**
- * Circular easing in
- * @memberOf fabric.util.ease
- */
- function easeInCirc(t, b, c, d) {
- return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
- }
-
- /**
- * Circular easing out
- * @memberOf fabric.util.ease
- */
- function easeOutCirc(t, b, c, d) {
- return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
- }
-
- /**
- * Circular easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutCirc(t, b, c, d) {
- t /= d / 2;
- if (t < 1) {
- return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
+ function easeOutQuint(t, b, c, d) {
+ return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
}
- return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
- }
-
- /**
- * Elastic easing in
- * @memberOf fabric.util.ease
- */
- function easeInElastic(t, b, c, d) {
- var s = 1.70158, p = 0, a = c;
- if (t === 0) {
- return b;
+ function easeInOutQuint(t, b, c, d) {
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t * t * t * t + b;
+ }
+ return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
- t /= d;
- if (t === 1) {
- return b + c;
+ function easeInSine(t, b, c, d) {
+ return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
}
- if (!p) {
- p = d * 0.3;
+ function easeOutSine(t, b, c, d) {
+ return c * Math.sin(t / d * (Math.PI / 2)) + b;
}
- var opts = normalize(a, c, p, s);
- return -elastic(opts, t, d) + b;
- }
-
- /**
- * Elastic easing out
- * @memberOf fabric.util.ease
- */
- function easeOutElastic(t, b, c, d) {
- var s = 1.70158, p = 0, a = c;
- if (t === 0) {
- return b;
+ function easeInOutSine(t, b, c, d) {
+ return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
}
- t /= d;
- if (t === 1) {
- return b + c;
+ function easeInExpo(t, b, c, d) {
+ return t === 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
}
- if (!p) {
- p = d * 0.3;
+ function easeOutExpo(t, b, c, d) {
+ return t === d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}
- var opts = normalize(a, c, p, s);
- return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
- }
-
- /**
- * Elastic easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutElastic(t, b, c, d) {
- var s = 1.70158, p = 0, a = c;
- if (t === 0) {
- return b;
+ function easeInOutExpo(t, b, c, d) {
+ if (t === 0) {
+ return b;
+ }
+ if (t === d) {
+ return b + c;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
+ }
+ return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
- t /= d / 2;
- if (t === 2) {
- return b + c;
+ function easeInCirc(t, b, c, d) {
+ return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
}
- if (!p) {
- p = d * (0.3 * 1.5);
+ function easeOutCirc(t, b, c, d) {
+ return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
}
- var opts = normalize(a, c, p, s);
- if (t < 1) {
- return -0.5 * elastic(opts, t, d) + b;
+ function easeInOutCirc(t, b, c, d) {
+ t /= d / 2;
+ if (t < 1) {
+ return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
+ }
+ return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
}
- return opts.a * Math.pow(2, -10 * (t -= 1)) *
- Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
- }
-
- /**
- * Backwards easing in
- * @memberOf fabric.util.ease
- */
- function easeInBack(t, b, c, d, s) {
- if (s === undefined) {
- s = 1.70158;
+ function easeInElastic(t, b, c, d) {
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
+ t /= d;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * .3;
+ }
+ var opts = normalize(a, c, p, s);
+ return -elastic(opts, t, d) + b;
}
- return c * (t /= d) * t * ((s + 1) * t - s) + b;
- }
-
- /**
- * Backwards easing out
- * @memberOf fabric.util.ease
- */
- function easeOutBack(t, b, c, d, s) {
- if (s === undefined) {
- s = 1.70158;
+ function easeOutElastic(t, b, c, d) {
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
+ t /= d;
+ if (t === 1) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * .3;
+ }
+ var opts = normalize(a, c, p, s);
+ return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p) + opts.c + b;
}
- return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
- }
-
- /**
- * Backwards easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutBack(t, b, c, d, s) {
- if (s === undefined) {
- s = 1.70158;
+ function easeInOutElastic(t, b, c, d) {
+ var s = 1.70158, p = 0, a = c;
+ if (t === 0) {
+ return b;
+ }
+ t /= d / 2;
+ if (t === 2) {
+ return b + c;
+ }
+ if (!p) {
+ p = d * (.3 * 1.5);
+ }
+ var opts = normalize(a, c, p, s);
+ if (t < 1) {
+ return -.5 * elastic(opts, t, d) + b;
+ }
+ return opts.a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p) * .5 + opts.c + b;
}
- t /= d / 2;
- if (t < 1) {
- return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
+ function easeInBack(t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * (t /= d) * t * ((s + 1) * t - s) + b;
}
- return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
- }
-
- /**
- * Bouncing easing in
- * @memberOf fabric.util.ease
- */
- function easeInBounce(t, b, c, d) {
- return c - easeOutBounce (d - t, 0, c, d) + b;
- }
-
- /**
- * Bouncing easing out
- * @memberOf fabric.util.ease
- */
- function easeOutBounce(t, b, c, d) {
- if ((t /= d) < (1 / 2.75)) {
- return c * (7.5625 * t * t) + b;
+ function easeOutBack(t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
}
- else if (t < (2 / 2.75)) {
- return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
+ function easeInOutBack(t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * (t * t * (((s *= 1.525) + 1) * t - s)) + b;
+ }
+ return c / 2 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b;
}
- else if (t < (2.5 / 2.75)) {
- return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
+ function easeInBounce(t, b, c, d) {
+ return c - easeOutBounce(d - t, 0, c, d) + b;
}
- else {
- return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
+ function easeOutBounce(t, b, c, d) {
+ if ((t /= d) < 1 / 2.75) {
+ return c * (7.5625 * t * t) + b;
+ } else if (t < 2 / 2.75) {
+ return c * (7.5625 * (t -= 1.5 / 2.75) * t + .75) + b;
+ } else if (t < 2.5 / 2.75) {
+ return c * (7.5625 * (t -= 2.25 / 2.75) * t + .9375) + b;
+ } else {
+ return c * (7.5625 * (t -= 2.625 / 2.75) * t + .984375) + b;
+ }
}
- }
-
- /**
- * Bouncing easing in and out
- * @memberOf fabric.util.ease
- */
- function easeInOutBounce(t, b, c, d) {
- if (t < d / 2) {
- return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
+ function easeInOutBounce(t, b, c, d) {
+ if (t < d / 2) {
+ return easeInBounce(t * 2, 0, c, d) * .5 + b;
+ }
+ return easeOutBounce(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
}
- return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
- }
-
- /**
- * Easing functions
- * See <a href="http://gizma.com/easing/">Easing Equations by Robert Penner</a>
- * @namespace fabric.util.ease
- */
- fabric.util.ease = {
-
- /**
- * Quadratic easing in
- * @memberOf fabric.util.ease
- */
- easeInQuad: function(t, b, c, d) {
- return c * (t /= d) * t + b;
- },
-
- /**
- * Quadratic easing out
- * @memberOf fabric.util.ease
- */
- easeOutQuad: function(t, b, c, d) {
- return -c * (t /= d) * (t - 2) + b;
- },
-
- /**
- * Quadratic easing in and out
- * @memberOf fabric.util.ease
- */
- easeInOutQuad: function(t, b, c, d) {
- t /= (d / 2);
- if (t < 1) {
- return c / 2 * t * t + b;
- }
- return -c / 2 * ((--t) * (t - 2) - 1) + b;
- },
-
- /**
- * Cubic easing in
- * @memberOf fabric.util.ease
- */
- easeInCubic: function(t, b, c, d) {
- return c * (t /= d) * t * t + b;
- },
-
- easeOutCubic: easeOutCubic,
- easeInOutCubic: easeInOutCubic,
- easeInQuart: easeInQuart,
- easeOutQuart: easeOutQuart,
- easeInOutQuart: easeInOutQuart,
- easeInQuint: easeInQuint,
- easeOutQuint: easeOutQuint,
- easeInOutQuint: easeInOutQuint,
- easeInSine: easeInSine,
- easeOutSine: easeOutSine,
- easeInOutSine: easeInOutSine,
- easeInExpo: easeInExpo,
- easeOutExpo: easeOutExpo,
- easeInOutExpo: easeInOutExpo,
- easeInCirc: easeInCirc,
- easeOutCirc: easeOutCirc,
- easeInOutCirc: easeInOutCirc,
- easeInElastic: easeInElastic,
- easeOutElastic: easeOutElastic,
- easeInOutElastic: easeInOutElastic,
- easeInBack: easeInBack,
- easeOutBack: easeOutBack,
- easeInOutBack: easeInOutBack,
- easeInBounce: easeInBounce,
- easeOutBounce: easeOutBounce,
- easeInOutBounce: easeInOutBounce
- };
-
+ fabric.util.ease = {
+ easeInQuad: function(t, b, c, d) {
+ return c * (t /= d) * t + b;
+ },
+ easeOutQuad: function(t, b, c, d) {
+ return -c * (t /= d) * (t - 2) + b;
+ },
+ easeInOutQuad: function(t, b, c, d) {
+ t /= d / 2;
+ if (t < 1) {
+ return c / 2 * t * t + b;
+ }
+ return -c / 2 * (--t * (t - 2) - 1) + b;
+ },
+ easeInCubic: function(t, b, c, d) {
+ return c * (t /= d) * t * t + b;
+ },
+ easeOutCubic: easeOutCubic,
+ easeInOutCubic: easeInOutCubic,
+ easeInQuart: easeInQuart,
+ easeOutQuart: easeOutQuart,
+ easeInOutQuart: easeInOutQuart,
+ easeInQuint: easeInQuint,
+ easeOutQuint: easeOutQuint,
+ easeInOutQuint: easeInOutQuint,
+ easeInSine: easeInSine,
+ easeOutSine: easeOutSine,
+ easeInOutSine: easeInOutSine,
+ easeInExpo: easeInExpo,
+ easeOutExpo: easeOutExpo,
+ easeInOutExpo: easeInOutExpo,
+ easeInCirc: easeInCirc,
+ easeOutCirc: easeOutCirc,
+ easeInOutCirc: easeInOutCirc,
+ easeInElastic: easeInElastic,
+ easeOutElastic: easeOutElastic,
+ easeInOutElastic: easeInOutElastic,
+ easeInBack: easeInBack,
+ easeOutBack: easeOutBack,
+ easeInOutBack: easeInOutBack,
+ easeInBounce: easeInBounce,
+ easeOutBounce: easeOutBounce,
+ easeInOutBounce: easeInOutBounce
+ };
})();
-
(function(global) {
-
- 'use strict';
-
- /**
- * @name fabric
- * @namespace
- */
-
- var fabric = global.fabric || (global.fabric = { }),
- extend = fabric.util.object.extend,
- clone = fabric.util.object.clone,
- toFixed = fabric.util.toFixed,
- parseUnit = fabric.util.parseUnit,
- multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
-
- 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',
- 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',
- 'paint-order': 'paintFirst',
- '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'
- },
-
- colorAttributes = {
- stroke: 'strokeOpacity',
- fill: 'fillOpacity'
- };
-
- fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames);
- fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements);
- fabric.svgInvalidAncestorsRegEx = getSvgRegex(svgInvalidAncestors);
- fabric.svgValidParentsRegEx = getSvgRegex(svgValidParents);
-
- fabric.cssRules = { };
- fabric.gradientDefs = { };
-
- function normalizeAttr(attr) {
- // transform attribute names
- if (attr in attributesMap) {
- return attributesMap[attr];
+ "use strict";
+ var fabric = global.fabric || (global.fabric = {}), extend = fabric.util.object.extend, clone = fabric.util.object.clone, toFixed = fabric.util.toFixed, parseUnit = fabric.util.parseUnit, multiplyTransformMatrices = fabric.util.multiplyTransformMatrices, svgValidTagNames = [ "path", "circle", "polygon", "polyline", "ellipse", "rect", "line", "image", "text", "linearGradient", "radialGradient", "stop" ], svgViewBoxElements = [ "symbol", "image", "marker", "pattern", "view", "svg" ], s [...]
+ 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",
+ "letter-spacing": "charSpacing",
+ "paint-order": "paintFirst",
+ "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"
+ }, colorAttributes = {
+ stroke: "strokeOpacity",
+ fill: "fillOpacity"
+ };
+ fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames);
+ fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements);
+ fabric.svgInvalidAncestorsRegEx = getSvgRegex(svgInvalidAncestors);
+ fabric.svgValidParentsRegEx = getSvgRegex(svgValidParents);
+ fabric.cssRules = {};
+ fabric.gradientDefs = {};
+ function normalizeAttr(attr) {
+ if (attr in attributesMap) {
+ return attributesMap[attr];
+ }
+ return attr;
+ }
+ function normalizeValue(attr, value, parentAttributes, fontSize) {
+ var isArray = Object.prototype.toString.call(value) === "[object Array]", parsed;
+ if ((attr === "fill" || attr === "stroke") && value === "none") {
+ value = "";
+ } else if (attr === "strokeDashArray") {
+ if (value === "none") {
+ value = null;
+ } else {
+ value = value.replace(/,/g, " ").split(/\s+/).map(function(n) {
+ return parseFloat(n);
+ });
+ }
+ } else if (attr === "transformMatrix") {
+ if (parentAttributes && parentAttributes.transformMatrix) {
+ value = multiplyTransformMatrices(parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
+ } else {
+ value = fabric.parseTransformAttribute(value);
+ }
+ } else if (attr === "visible") {
+ value = value !== "none" && value !== "hidden";
+ if (parentAttributes && parentAttributes.visible === false) {
+ value = false;
+ }
+ } else if (attr === "opacity") {
+ value = parseFloat(value);
+ if (parentAttributes && typeof parentAttributes.opacity !== "undefined") {
+ value *= parentAttributes.opacity;
+ }
+ } else if (attr === "textAnchor") {
+ value = value === "start" ? "left" : value === "end" ? "right" : "center";
+ } else if (attr === "charSpacing") {
+ parsed = parseUnit(value, fontSize) / fontSize * 1e3;
+ } 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);
+ }
+ return !isArray && isNaN(parsed) ? value : parsed;
}
- return attr;
- }
-
- function normalizeValue(attr, value, parentAttributes, fontSize) {
- var isArray = Object.prototype.toString.call(value) === '[object Array]',
- parsed;
-
- if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
- value = '';
+ function getSvgRegex(arr) {
+ return new RegExp("^(" + arr.join("|") + ")\\b", "i");
}
- else if (attr === 'strokeDashArray') {
- if (value === 'none') {
- value = null;
- }
- else {
- value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
- return parseFloat(n);
+ function _setStrokeFillOpacity(attributes) {
+ for (var attr in colorAttributes) {
+ if (typeof attributes[colorAttributes[attr]] === "undefined" || attributes[attr] === "") {
+ continue;
+ }
+ if (typeof attributes[attr] === "undefined") {
+ if (!fabric.Object.prototype[attr]) {
+ continue;
+ }
+ attributes[attr] = fabric.Object.prototype[attr];
+ }
+ if (attributes[attr].indexOf("url(") === 0) {
+ continue;
+ }
+ var color = new fabric.Color(attributes[attr]);
+ attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
+ }
+ return attributes;
+ }
+ function _getMultipleNodes(doc, nodeNames) {
+ 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));
+ }
+ return nodeArray;
+ }
+ fabric.parseTransformAttribute = function() {
+ function rotateMatrix(matrix, args) {
+ var cos = fabric.util.cos(args[0]), sin = fabric.util.sin(args[0]), x = 0, y = 0;
+ if (args.length === 3) {
+ x = args[1];
+ y = args[2];
+ }
+ matrix[0] = cos;
+ matrix[1] = sin;
+ matrix[2] = -sin;
+ matrix[3] = cos;
+ matrix[4] = x - (cos * x - sin * y);
+ matrix[5] = y - (sin * x + cos * y);
+ }
+ function scaleMatrix(matrix, args) {
+ var multiplierX = args[0], multiplierY = args.length === 2 ? args[1] : args[0];
+ matrix[0] = multiplierX;
+ matrix[3] = multiplierY;
+ }
+ function skewMatrix(matrix, args, pos) {
+ matrix[pos] = Math.tan(fabric.util.degreesToRadians(args[0]));
+ }
+ function translateMatrix(matrix, args) {
+ matrix[4] = args[0];
+ if (args.length === 2) {
+ matrix[5] = args[1];
+ }
+ }
+ var iMatrix = [ 1, 0, 0, 1, 0, 0 ], number = fabric.reNum, commaWsp = "(?:\\s+,?\\s*|,\\s*)", skewX = "(?:(skewX)\\s*\\(\\s*(" + number + ")\\s*\\))", skewY = "(?:(skewY)\\s*\\(\\s*(" + number + ")\\s*\\))", rotate = "(?:(rotate)\\s*\\(\\s*(" + number + ")(?:" + commaWsp + "(" + number + ")" + commaWsp + "(" + number + "))?\\s*\\))", scale = "(?:(scale)\\s*\\(\\s*(" + number + ")(?:" + commaWsp + "(" + number + "))?\\s*\\))", translate = "(?:(translate)\\s*\\(\\s*(" + number + ") [...]
+ return function(attributeValue) {
+ var matrix = iMatrix.concat(), matrices = [];
+ if (!attributeValue || attributeValue && !reTransformList.test(attributeValue)) {
+ return matrix;
+ }
+ attributeValue.replace(reTransform, function(match) {
+ var m = new RegExp(transform).exec(match).filter(function(match) {
+ return !!match;
+ }), operation = m[1], args = m.slice(2).map(parseFloat);
+ switch (operation) {
+ case "translate":
+ translateMatrix(matrix, args);
+ break;
+
+ case "rotate":
+ args[0] = fabric.util.degreesToRadians(args[0]);
+ rotateMatrix(matrix, args);
+ break;
+
+ case "scale":
+ scaleMatrix(matrix, args);
+ break;
+
+ case "skewX":
+ skewMatrix(matrix, args, 2);
+ break;
+
+ case "skewY":
+ skewMatrix(matrix, args, 1);
+ break;
+
+ case "matrix":
+ matrix = args;
+ break;
+ }
+ matrices.push(matrix.concat());
+ matrix = iMatrix.concat();
+ });
+ var combinedMatrix = matrices[0];
+ while (matrices.length > 1) {
+ matrices.shift();
+ combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
+ }
+ return combinedMatrix;
+ };
+ }();
+ function parseStyleString(style, oStyle) {
+ var attr, value;
+ style.replace(/;\s*$/, "").split(";").forEach(function(chunk) {
+ var pair = chunk.split(":");
+ attr = pair[0].trim().toLowerCase();
+ value = pair[1].trim();
+ oStyle[attr] = value;
});
- }
}
- else if (attr === 'transformMatrix') {
- if (parentAttributes && parentAttributes.transformMatrix) {
- value = multiplyTransformMatrices(
- parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
- }
- else {
- value = fabric.parseTransformAttribute(value);
- }
- }
- else if (attr === 'visible') {
- value = value !== 'none' && value !== 'hidden';
- // display=none on parent element always takes precedence over child element
- if (parentAttributes && parentAttributes.visible === false) {
- value = false;
- }
- }
- else if (attr === 'opacity') {
- value = parseFloat(value);
- if (parentAttributes && typeof parentAttributes.opacity !== 'undefined') {
- value *= parentAttributes.opacity;
- }
+ function parseStyleObject(style, oStyle) {
+ var attr, value;
+ for (var prop in style) {
+ if (typeof style[prop] === "undefined") {
+ continue;
+ }
+ attr = prop.toLowerCase();
+ value = style[prop];
+ oStyle[attr] = value;
+ }
}
- else if (attr === 'textAnchor' /* text-anchor */) {
- value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
+ function getGlobalStylesForElement(element, svgUid) {
+ var styles = {};
+ for (var rule in fabric.cssRules[svgUid]) {
+ if (elementMatchesRule(element, rule.split(" "))) {
+ for (var property in fabric.cssRules[svgUid][rule]) {
+ styles[property] = fabric.cssRules[svgUid][rule][property];
+ }
+ }
+ }
+ return styles;
}
- 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';
- }
+ function elementMatchesRule(element, selectors) {
+ var firstMatching, parentMatching = true;
+ firstMatching = selectorMatches(element, selectors.pop());
+ if (firstMatching && selectors.length) {
+ parentMatching = doesSomeParentMatch(element, selectors);
+ }
+ return firstMatching && parentMatching && selectors.length === 0;
}
- else {
- parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
+ function doesSomeParentMatch(element, selectors) {
+ var selector, parentMatching = true;
+ while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
+ if (parentMatching) {
+ selector = selectors.pop();
+ }
+ element = element.parentNode;
+ parentMatching = selectorMatches(element, selector);
+ }
+ return selectors.length === 0;
+ }
+ function selectorMatches(element, selector) {
+ var nodeName = element.nodeName, classNames = element.getAttribute("class"), id = element.getAttribute("id"), matcher, i;
+ matcher = new RegExp("^" + nodeName, "i");
+ selector = selector.replace(matcher, "");
+ if (id && selector.length) {
+ matcher = new RegExp("#" + id + "(?![a-zA-Z\\-]+)", "i");
+ selector = selector.replace(matcher, "");
+ }
+ if (classNames && selector.length) {
+ classNames = classNames.split(" ");
+ for (i = classNames.length; i--; ) {
+ matcher = new RegExp("\\." + classNames[i] + "(?![a-zA-Z\\-]+)", "i");
+ selector = selector.replace(matcher, "");
+ }
+ }
+ return selector.length === 0;
}
-
- return (!isArray && isNaN(parsed) ? value : parsed);
- }
-
- /**
- * @private
- */
- function getSvgRegex(arr) {
- return new RegExp('^(' + arr.join('|') + ')\\b', 'i');
- }
-
- /**
- * @private
- * @param {Object} attributes Array of attributes to parse
- */
- function _setStrokeFillOpacity(attributes) {
- for (var attr in colorAttributes) {
-
- if (typeof attributes[colorAttributes[attr]] === 'undefined' || attributes[attr] === '') {
- continue;
- }
-
- if (typeof attributes[attr] === 'undefined') {
- if (!fabric.Object.prototype[attr]) {
- continue;
+ function elementById(doc, id) {
+ var el;
+ doc.getElementById && (el = doc.getElementById(id));
+ if (el) {
+ return el;
+ }
+ 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;
+ }
}
- attributes[attr] = fabric.Object.prototype[attr];
- }
-
- if (attributes[attr].indexOf('url(') === 0) {
- continue;
- }
-
- var color = new fabric.Color(attributes[attr]);
- attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
}
- return attributes;
- }
-
- /**
- * @private
- */
- function _getMultipleNodes(doc, nodeNames) {
- 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));
+ function parseUseDirectives(doc) {
+ var nodelist = _getMultipleNodes(doc, [ "use", "svg:use" ]), i = 0;
+ while (nodelist.length && i < nodelist.length) {
+ var el = nodelist[i], xlink = el.getAttribute("xlink:href").substr(1), x = el.getAttribute("x") || 0, y = el.getAttribute("y") || 0, el2 = elementById(doc, xlink).cloneNode(true), currentTrans = (el2.getAttribute("transform") || "") + " translate(" + x + ", " + y + ")", parentNode, oldLength = nodelist.length, attr, j, attrs, len;
+ applyViewboxTransform(el2);
+ if (/^svg$/i.test(el2.nodeName)) {
+ var el3 = el2.ownerDocument.createElement("g");
+ for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) {
+ attr = attrs.item(j);
+ el3.setAttribute(attr.nodeName, attr.nodeValue);
+ }
+ while (el2.firstChild) {
+ el3.appendChild(el2.firstChild);
+ }
+ el2 = el3;
+ }
+ 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;
+ }
+ if (attr.nodeName === "transform") {
+ currentTrans = attr.nodeValue + " " + currentTrans;
+ } else {
+ el2.setAttribute(attr.nodeName, attr.nodeValue);
+ }
+ }
+ el2.setAttribute("transform", currentTrans);
+ el2.setAttribute("instantiated_by_use", "1");
+ el2.removeAttribute("id");
+ parentNode = el.parentNode;
+ parentNode.replaceChild(el2, el);
+ if (nodelist.length === oldLength) {
+ i++;
+ }
+ }
}
- return nodeArray;
- }
-
- /**
- * Parses "transform" attribute, returning an array of values
- * @static
- * @function
- * @memberOf fabric
- * @param {String} attributeValue String containing attribute value
- * @return {Array} Array of 6 elements representing transformation matrix
- */
- fabric.parseTransformAttribute = (function() {
- function rotateMatrix(matrix, args) {
- var cos = Math.cos(args[0]), sin = Math.sin(args[0]),
- x = 0, y = 0;
- if (args.length === 3) {
- x = args[1];
- y = args[2];
- }
-
- matrix[0] = cos;
- matrix[1] = sin;
- matrix[2] = -sin;
- matrix[3] = cos;
- matrix[4] = x - (cos * x - sin * y);
- matrix[5] = y - (sin * x + cos * y);
+ var reViewBoxAttrValue = new RegExp("^" + "\\s*(" + fabric.reNum + "+)\\s*,?" + "\\s*(" + fabric.reNum + "+)\\s*,?" + "\\s*(" + fabric.reNum + "+)\\s*,?" + "\\s*(" + fabric.reNum + "+)\\s*" + "$");
+ function applyViewboxTransform(element) {
+ var viewBoxAttr = element.getAttribute("viewBox"), scaleX = 1, scaleY = 1, minX = 0, minY = 0, viewBoxWidth, viewBoxHeight, matrix, el, widthAttr = element.getAttribute("width"), heightAttr = element.getAttribute("height"), x = element.getAttribute("x") || 0, y = element.getAttribute("y") || 0, preserveAspectRatio = element.getAttribute("preserveAspectRatio") || "", missingViewBox = !viewBoxAttr || !fabric.svgViewBoxElementsRegEx.test(element.nodeName) || !(viewBoxAttr = viewBoxA [...]
+ parsedDim.width = 0;
+ parsedDim.height = 0;
+ parsedDim.toBeParsed = toBeParsed;
+ if (toBeParsed) {
+ return parsedDim;
+ }
+ if (missingViewBox) {
+ parsedDim.width = parseUnit(widthAttr);
+ parsedDim.height = parseUnit(heightAttr);
+ return parsedDim;
+ }
+ minX = -parseFloat(viewBoxAttr[1]);
+ minY = -parseFloat(viewBoxAttr[2]);
+ viewBoxWidth = parseFloat(viewBoxAttr[3]);
+ viewBoxHeight = parseFloat(viewBoxAttr[4]);
+ if (!missingDimAttr) {
+ parsedDim.width = parseUnit(widthAttr);
+ parsedDim.height = parseUnit(heightAttr);
+ scaleX = parsedDim.width / viewBoxWidth;
+ scaleY = parsedDim.height / viewBoxHeight;
+ } else {
+ parsedDim.width = viewBoxWidth;
+ parsedDim.height = viewBoxHeight;
+ }
+ preserveAspectRatio = fabric.util.parsePreserveAspectRatioAttribute(preserveAspectRatio);
+ if (preserveAspectRatio.alignX !== "none") {
+ scaleY = scaleX = scaleX > scaleY ? scaleY : scaleX;
+ }
+ if (scaleX === 1 && scaleY === 1 && minX === 0 && minY === 0 && x === 0 && y === 0) {
+ return parsedDim;
+ }
+ if (x || y) {
+ translateMatrix = " translate(" + parseUnit(x) + " " + parseUnit(y) + ") ";
+ }
+ matrix = translateMatrix + " matrix(" + scaleX + " 0" + " 0 " + scaleY + " " + minX * scaleX + " " + minY * scaleY + ") ";
+ if (element.nodeName === "svg") {
+ el = element.ownerDocument.createElement("g");
+ while (element.firstChild) {
+ el.appendChild(element.firstChild);
+ }
+ element.appendChild(el);
+ } else {
+ el = element;
+ matrix = el.getAttribute("transform") + matrix;
+ }
+ el.setAttribute("transform", matrix);
+ return parsedDim;
+ }
+ function hasAncestorWithNodeName(element, nodeName) {
+ while (element && (element = element.parentNode)) {
+ if (element.nodeName && nodeName.test(element.nodeName.replace("svg:", "")) && !element.getAttribute("instantiated_by_use")) {
+ return true;
+ }
+ }
+ return false;
}
+ fabric.parseSVGDocument = function(doc, callback, reviver, parsingOptions) {
+ if (!doc) {
+ return;
+ }
+ parseUseDirectives(doc);
+ var svgUid = fabric.Object.__uid++, i, len, options = applyViewboxTransform(doc), descendants = fabric.util.toArray(doc.getElementsByTagName("*"));
+ options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
+ options.svgUid = svgUid;
+ if (descendants.length === 0 && fabric.isLikelyNode) {
+ descendants = doc.selectNodes('//*[name(.)!="svg"]');
+ var arr = [];
+ for (i = 0, len = descendants.length; i < len; i++) {
+ arr[i] = descendants[i];
+ }
+ descendants = arr;
+ }
+ var elements = descendants.filter(function(el) {
+ applyViewboxTransform(el);
+ return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace("svg:", "")) && !hasAncestorWithNodeName(el, fabric.svgInvalidAncestorsRegEx);
+ });
+ if (!elements || elements && !elements.length) {
+ callback && callback([], {});
+ return;
+ }
+ fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
+ fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
+ fabric.parseElements(elements, function(instances, elements) {
+ if (callback) {
+ callback(instances, options, elements, descendants);
+ }
+ }, clone(options), reviver, parsingOptions);
+ };
+ var reFontDeclaration = new RegExp("(normal|italic)?\\s*(normal|small-caps)?\\s*" + "(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(" + fabric.reNum + "(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|" + fabric.reNum + "))?\\s+(.*)");
+ extend(fabric, {
+ parseFontDeclaration: function(value, oStyle) {
+ var match = value.match(reFontDeclaration);
+ if (!match) {
+ return;
+ }
+ var fontStyle = match[1], fontWeight = match[3], fontSize = match[4], lineHeight = match[5], fontFamily = match[6];
+ if (fontStyle) {
+ oStyle.fontStyle = fontStyle;
+ }
+ if (fontWeight) {
+ oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
+ }
+ if (fontSize) {
+ oStyle.fontSize = parseUnit(fontSize);
+ }
+ if (fontFamily) {
+ oStyle.fontFamily = fontFamily;
+ }
+ if (lineHeight) {
+ oStyle.lineHeight = lineHeight === "normal" ? 1 : lineHeight;
+ }
+ },
+ getGradientDefs: function(doc) {
+ var tagArray = [ "linearGradient", "radialGradient", "svg:linearGradient", "svg:radialGradient" ], elList = _getMultipleNodes(doc, tagArray), el, j = 0, id, xlink, gradientDefs = {}, idsToXlinkMap = {};
+ j = elList.length;
+ while (j--) {
+ el = elList[j];
+ xlink = el.getAttribute("xlink:href");
+ id = el.getAttribute("id");
+ if (xlink) {
+ idsToXlinkMap[id] = xlink.substr(1);
+ }
+ gradientDefs[id] = el;
+ }
+ for (id in idsToXlinkMap) {
+ var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
+ el = gradientDefs[id];
+ while (el2.firstChild) {
+ el.appendChild(el2.firstChild);
+ }
+ }
+ return gradientDefs;
+ },
+ parseAttributes: function(element, attributes, svgUid) {
+ if (!element) {
+ return;
+ }
+ var value, parentAttributes = {}, fontSize;
+ if (typeof svgUid === "undefined") {
+ svgUid = element.getAttribute("svgUid");
+ }
+ if (element.parentNode && fabric.svgValidParentsRegEx.test(element.parentNode.nodeName)) {
+ parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
+ }
+ fontSize = parentAttributes && parentAttributes.fontSize || element.getAttribute("font-size") || fabric.Text.DEFAULT_SVG_FONT_SIZE;
+ var ownAttributes = attributes.reduce(function(memo, attr) {
+ value = element.getAttribute(attr);
+ if (value) {
+ memo[attr] = value;
+ }
+ return memo;
+ }, {});
+ ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
+ var normalizedAttr, normalizedValue, normalizedStyle = {};
+ for (var attr in ownAttributes) {
+ normalizedAttr = normalizeAttr(attr);
+ normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize);
+ normalizedStyle[normalizedAttr] = normalizedValue;
+ }
+ if (normalizedStyle && normalizedStyle.font) {
+ fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle);
+ }
+ var mergedAttrs = extend(parentAttributes, normalizedStyle);
+ return fabric.svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs);
+ },
+ parseElements: function(elements, callback, options, reviver, parsingOptions) {
+ new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse();
+ },
+ parseStyleAttribute: function(element) {
+ var oStyle = {}, style = element.getAttribute("style");
+ if (!style) {
+ return oStyle;
+ }
+ if (typeof style === "string") {
+ parseStyleString(style, oStyle);
+ } else {
+ parseStyleObject(style, oStyle);
+ }
+ return oStyle;
+ },
+ parsePointsAttribute: function(points) {
+ if (!points) {
+ return null;
+ }
+ points = points.replace(/,/g, " ").trim();
+ points = points.split(/\s+/);
+ var parsedPoints = [], i, len;
+ for (i = 0, len = points.length; i < len; i += 2) {
+ parsedPoints.push({
+ x: parseFloat(points[i]),
+ y: parseFloat(points[i + 1])
+ });
+ }
+ return parsedPoints;
+ },
+ getCSSRules: function(doc) {
+ var styles = doc.getElementsByTagName("style"), i, len, allRules = {}, rules;
+ for (i = 0, len = styles.length; i < len; i++) {
+ var styleContents = styles[i].textContent || styles[i].text;
+ styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, "");
+ if (styleContents.trim() === "") {
+ continue;
+ }
+ rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
+ rules = rules.map(function(rule) {
+ return rule.trim();
+ });
+ rules.forEach(function(rule) {
+ var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/), ruleObj = {}, declaration = match[2].trim(), propertyValuePairs = declaration.replace(/;$/, "").split(/\s*;\s*/);
+ for (i = 0, len = propertyValuePairs.length; i < len; i++) {
+ var pair = propertyValuePairs[i].split(/\s*:\s*/), property = pair[0], value = pair[1];
+ ruleObj[property] = value;
+ }
+ rule = match[1];
+ rule.split(",").forEach(function(_rule) {
+ _rule = _rule.replace(/^svg/i, "").trim();
+ if (_rule === "") {
+ return;
+ }
+ if (allRules[_rule]) {
+ fabric.util.object.extend(allRules[_rule], ruleObj);
+ } else {
+ allRules[_rule] = fabric.util.object.clone(ruleObj);
+ }
+ });
+ });
+ }
+ return allRules;
+ },
+ loadSVGFromURL: function(url, callback, reviver, options) {
+ url = url.replace(/^\n\s*/, "").trim();
+ new fabric.util.request(url, {
+ method: "get",
+ onComplete: onComplete
+ });
+ function onComplete(r) {
+ var xml = r.responseXML;
+ if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
+ xml = new ActiveXObject("Microsoft.XMLDOM");
+ xml.async = "false";
+ xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ""));
+ }
+ if (!xml || !xml.documentElement) {
+ callback && callback(null);
+ }
+ fabric.parseSVGDocument(xml.documentElement, function(results, _options, elements, allElements) {
+ callback && callback(results, _options, elements, allElements);
+ }, reviver, options);
+ }
+ },
+ loadSVGFromString: function(string, callback, reviver, options) {
+ string = string.trim();
+ var doc;
+ if (typeof DOMParser !== "undefined") {
+ var parser = new DOMParser();
+ if (parser && parser.parseFromString) {
+ doc = parser.parseFromString(string, "text/xml");
+ }
+ } else if (fabric.window.ActiveXObject) {
+ doc = new ActiveXObject("Microsoft.XMLDOM");
+ doc.async = "false";
+ doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ""));
+ }
+ fabric.parseSVGDocument(doc.documentElement, function(results, _options, elements, allElements) {
+ callback(results, _options, elements, allElements);
+ }, reviver, options);
+ }
+ });
+})(typeof exports !== "undefined" ? exports : this);
- function scaleMatrix(matrix, args) {
- var multiplierX = args[0],
- multiplierY = (args.length === 2) ? args[1] : args[0];
-
- matrix[0] = multiplierX;
- matrix[3] = multiplierY;
- }
+fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) {
+ this.elements = elements;
+ this.callback = callback;
+ this.options = options;
+ this.reviver = reviver;
+ this.svgUid = options && options.svgUid || 0;
+ this.parsingOptions = parsingOptions;
+ this.regexUrl = /^url\(['"]?#([^'"]+)['"]?\)/g;
+};
- function skewMatrix(matrix, args, pos) {
- matrix[pos] = Math.tan(fabric.util.degreesToRadians(args[0]));
- }
+fabric.ElementsParser.prototype.parse = function() {
+ this.instances = new Array(this.elements.length);
+ this.numElements = this.elements.length;
+ this.createObjects();
+};
- function translateMatrix(matrix, args) {
- matrix[4] = args[0];
- if (args.length === 2) {
- matrix[5] = args[1];
- }
+fabric.ElementsParser.prototype.createObjects = function() {
+ for (var i = 0, len = this.elements.length; i < len; i++) {
+ this.elements[i].setAttribute("svgUid", this.svgUid);
+ (function(_obj, i) {
+ setTimeout(function() {
+ _obj.createObject(_obj.elements[i], i);
+ }, 0);
+ })(this, i);
}
+};
- // identity matrix
- var iMatrix = [
- 1, // a
- 0, // b
- 0, // c
- 1, // d
- 0, // e
- 0 // f
- ],
-
- // == begin transform regexp
- number = fabric.reNum,
-
- commaWsp = '(?:\\s+,?\\s*|,\\s*)',
-
- skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
-
- skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
-
- rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
- commaWsp + '(' + number + ')' +
- commaWsp + '(' + number + '))?\\s*\\))',
-
- scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
- commaWsp + '(' + number + '))?\\s*\\))',
-
- translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
- commaWsp + '(' + number + '))?\\s*\\))',
-
- matrix = '(?:(matrix)\\s*\\(\\s*' +
- '(' + number + ')' + commaWsp +
- '(' + number + ')' + commaWsp +
- '(' + number + ')' + commaWsp +
- '(' + number + ')' + commaWsp +
- '(' + number + ')' + commaWsp +
- '(' + number + ')' +
- '\\s*\\))',
-
- transform = '(?:' +
- matrix + '|' +
- translate + '|' +
- scale + '|' +
- rotate + '|' +
- skewX + '|' +
- skewY +
- ')',
-
- transforms = '(?:' + transform + '(?:' + commaWsp + '*' + transform + ')*' + ')',
-
- transformList = '^\\s*(?:' + transforms + '?)\\s*$',
-
- // http://www.w3.org/TR/SVG/coords.html#TransformAttribute
- reTransformList = new RegExp(transformList),
- // == end transform regexp
-
- reTransform = new RegExp(transform, 'g');
-
- return function(attributeValue) {
-
- // start with identity matrix
- var matrix = iMatrix.concat(),
- matrices = [];
-
- // return if no argument was given or
- // an argument does not match transform attribute regexp
- if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) {
- return matrix;
- }
-
- attributeValue.replace(reTransform, function(match) {
-
- var m = new RegExp(transform).exec(match).filter(function (match) {
- // match !== '' && match != null
- return (!!match);
- }),
- operation = m[1],
- args = m.slice(2).map(parseFloat);
-
- switch (operation) {
- case 'translate':
- translateMatrix(matrix, args);
- break;
- case 'rotate':
- args[0] = fabric.util.degreesToRadians(args[0]);
- rotateMatrix(matrix, args);
- break;
- case 'scale':
- scaleMatrix(matrix, args);
- break;
- case 'skewX':
- skewMatrix(matrix, args, 2);
- break;
- case 'skewY':
- skewMatrix(matrix, args, 1);
- break;
- case 'matrix':
- matrix = args;
- break;
+fabric.ElementsParser.prototype.createObject = function(el, index) {
+ var klass = fabric[fabric.util.string.capitalize(el.tagName.replace("svg:", ""))];
+ if (klass && klass.fromElement) {
+ try {
+ this._createObject(klass, el, index);
+ } catch (err) {
+ fabric.log(err);
}
+ } else {
+ this.checkIfDone();
+ }
+};
- // snapshot current matrix into matrices array
- matrices.push(matrix.concat());
- // reset
- matrix = iMatrix.concat();
- });
+fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
+ klass.fromElement(el, this.createCallback(index, el), this.options);
+};
- var combinedMatrix = matrices[0];
- while (matrices.length > 1) {
- matrices.shift();
- combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
- }
- return combinedMatrix;
+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();
};
- })();
-
- /**
- * @private
- */
- function parseStyleString(style, oStyle) {
- var attr, value;
- style.replace(/;\s*$/, '').split(';').forEach(function (chunk) {
- var pair = chunk.split(':');
-
- attr = pair[0].trim().toLowerCase();
- value = pair[1].trim();
-
- oStyle[attr] = value;
- });
- }
-
- /**
- * @private
- */
- function parseStyleObject(style, oStyle) {
- var attr, value;
- for (var prop in style) {
- if (typeof style[prop] === 'undefined') {
- continue;
- }
-
- attr = prop.toLowerCase();
- value = style[prop];
-
- oStyle[attr] = value;
- }
- }
+};
- /**
- * @private
- */
- function getGlobalStylesForElement(element, svgUid) {
- var styles = { };
- for (var rule in fabric.cssRules[svgUid]) {
- if (elementMatchesRule(element, rule.split(' '))) {
- for (var property in fabric.cssRules[svgUid][rule]) {
- styles[property] = fabric.cssRules[svgUid][rule][property];
- }
- }
+fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
+ var instanceFillValue = obj[property];
+ if (!/^url\(/.test(instanceFillValue)) {
+ return;
}
- return styles;
- }
-
- /**
- * @private
- */
- function elementMatchesRule(element, selectors) {
- var firstMatching, parentMatching = true;
- //start from rightmost selector.
- firstMatching = selectorMatches(element, selectors.pop());
- if (firstMatching && selectors.length) {
- parentMatching = doesSomeParentMatch(element, selectors);
+ var gradientId = this.regexUrl.exec(instanceFillValue)[1];
+ this.regexUrl.lastIndex = 0;
+ if (fabric.gradientDefs[this.svgUid][gradientId]) {
+ obj.set(property, fabric.Gradient.fromElement(fabric.gradientDefs[this.svgUid][gradientId], obj));
}
- return firstMatching && parentMatching && (selectors.length === 0);
- }
+};
- function doesSomeParentMatch(element, selectors) {
- var selector, parentMatching = true;
- while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
- if (parentMatching) {
- selector = selectors.pop();
- }
- element = element.parentNode;
- parentMatching = selectorMatches(element, selector);
+fabric.ElementsParser.prototype.checkIfDone = function() {
+ if (--this.numElements === 0) {
+ this.instances = this.instances.filter(function(el) {
+ return el != null;
+ });
+ this.callback(this.instances, this.elements);
}
- return selectors.length === 0;
- }
+};
- /**
- * @private
- */
- function selectorMatches(element, selector) {
- var nodeName = element.nodeName,
- classNames = element.getAttribute('class'),
- 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');
- selector = selector.replace(matcher, '');
- if (id && selector.length) {
- matcher = new RegExp('#' + id + '(?![a-zA-Z\\-]+)', 'i');
- selector = selector.replace(matcher, '');
+(function(global) {
+ "use strict";
+ var fabric = global.fabric || (global.fabric = {});
+ if (fabric.Point) {
+ fabric.warn("fabric.Point is already defined");
+ return;
}
- if (classNames && selector.length) {
- classNames = classNames.split(' ');
- for (i = classNames.length; i--;) {
- matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i');
- selector = selector.replace(matcher, '');
- }
+ fabric.Point = Point;
+ function Point(x, y) {
+ this.x = x;
+ this.y = y;
}
- return selector.length === 0;
- }
+ Point.prototype = {
+ type: "point",
+ constructor: Point,
+ add: function(that) {
+ return new Point(this.x + that.x, this.y + that.y);
+ },
+ addEquals: function(that) {
+ this.x += that.x;
+ this.y += that.y;
+ return this;
+ },
+ scalarAdd: function(scalar) {
+ return new Point(this.x + scalar, this.y + scalar);
+ },
+ scalarAddEquals: function(scalar) {
+ this.x += scalar;
+ this.y += scalar;
+ return this;
+ },
+ subtract: function(that) {
+ return new Point(this.x - that.x, this.y - that.y);
+ },
+ subtractEquals: function(that) {
+ this.x -= that.x;
+ this.y -= that.y;
+ return this;
+ },
+ scalarSubtract: function(scalar) {
+ return new Point(this.x - scalar, this.y - scalar);
+ },
+ scalarSubtractEquals: function(scalar) {
+ this.x -= scalar;
+ this.y -= scalar;
+ return this;
+ },
+ multiply: function(scalar) {
+ return new Point(this.x * scalar, this.y * scalar);
+ },
+ multiplyEquals: function(scalar) {
+ this.x *= scalar;
+ this.y *= scalar;
+ return this;
+ },
+ divide: function(scalar) {
+ return new Point(this.x / scalar, this.y / scalar);
+ },
+ divideEquals: function(scalar) {
+ this.x /= scalar;
+ this.y /= scalar;
+ return this;
+ },
+ eq: function(that) {
+ return this.x === that.x && this.y === that.y;
+ },
+ lt: function(that) {
+ return this.x < that.x && this.y < that.y;
+ },
+ lte: function(that) {
+ return this.x <= that.x && this.y <= that.y;
+ },
+ gt: function(that) {
+ return this.x > that.x && this.y > that.y;
+ },
+ gte: function(that) {
+ return this.x >= that.x && this.y >= that.y;
+ },
+ lerp: function(that, t) {
+ if (typeof t === "undefined") {
+ t = .5;
+ }
+ t = Math.max(Math.min(1, t), 0);
+ return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
+ },
+ distanceFrom: function(that) {
+ var dx = this.x - that.x, dy = this.y - that.y;
+ return Math.sqrt(dx * dx + dy * dy);
+ },
+ midPointFrom: function(that) {
+ return this.lerp(that);
+ },
+ min: function(that) {
+ return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
+ },
+ max: function(that) {
+ return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
+ },
+ toString: function() {
+ return this.x + "," + this.y;
+ },
+ setXY: function(x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ },
+ setX: function(x) {
+ this.x = x;
+ return this;
+ },
+ setY: function(y) {
+ this.y = y;
+ return this;
+ },
+ setFromPoint: function(that) {
+ this.x = that.x;
+ this.y = that.y;
+ return this;
+ },
+ swap: function(that) {
+ var x = this.x, y = this.y;
+ this.x = that.x;
+ this.y = that.y;
+ that.x = x;
+ that.y = y;
+ },
+ clone: function() {
+ return new Point(this.x, this.y);
+ }
+ };
+})(typeof exports !== "undefined" ? exports : this);
- /**
- * @private
- * to support IE8 missing getElementById on SVGdocument
- */
- function elementById(doc, id) {
- var el;
- doc.getElementById && (el = doc.getElementById(id));
- if (el) {
- return el;
+(function(global) {
+ "use strict";
+ var fabric = global.fabric || (global.fabric = {});
+ if (fabric.Intersection) {
+ fabric.warn("fabric.Intersection is already defined");
+ return;
}
- 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;
- }
+ function Intersection(status) {
+ this.status = status;
+ this.points = [];
}
- }
-
- /**
- * @private
- */
- function parseUseDirectives(doc) {
- var nodelist = _getMultipleNodes(doc, ['use', 'svg:use']), i = 0;
-
- while (nodelist.length && i < nodelist.length) {
- var el = nodelist[i],
- xlink = el.getAttribute('xlink:href').substr(1),
- x = el.getAttribute('x') || 0,
- y = el.getAttribute('y') || 0,
- el2 = elementById(doc, xlink).cloneNode(true),
- currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
- parentNode, oldLength = nodelist.length, attr, j, attrs, len;
-
- applyViewboxTransform(el2);
- if (/^svg$/i.test(el2.nodeName)) {
- var el3 = el2.ownerDocument.createElement('g');
- for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) {
- attr = attrs.item(j);
- el3.setAttribute(attr.nodeName, attr.nodeValue);
- }
- // el2.firstChild != null
- while (el2.firstChild) {
- el3.appendChild(el2.firstChild);
- }
- el2 = el3;
- }
-
- 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;
+ fabric.Intersection = Intersection;
+ fabric.Intersection.prototype = {
+ constructor: Intersection,
+ appendPoint: function(point) {
+ this.points.push(point);
+ return this;
+ },
+ appendPoints: function(points) {
+ this.points = this.points.concat(points);
+ return this;
}
-
- if (attr.nodeName === 'transform') {
- currentTrans = attr.nodeValue + ' ' + currentTrans;
+ };
+ fabric.Intersection.intersectLineLine = function(a1, a2, b1, b2) {
+ var result, uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x), uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
+ if (uB !== 0) {
+ var ua = uaT / uB, ub = ubT / uB;
+ if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
+ result = new Intersection("Intersection");
+ result.appendPoint(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
+ } else {
+ result = new Intersection();
+ }
+ } else {
+ if (uaT === 0 || ubT === 0) {
+ result = new Intersection("Coincident");
+ } else {
+ result = new Intersection("Parallel");
+ }
}
- else {
- el2.setAttribute(attr.nodeName, attr.nodeValue);
+ return result;
+ };
+ fabric.Intersection.intersectLinePolygon = function(a1, a2, points) {
+ var result = new Intersection(), length = points.length, b1, b2, inter, i;
+ for (i = 0; i < length; i++) {
+ b1 = points[i];
+ b2 = points[(i + 1) % length];
+ inter = Intersection.intersectLineLine(a1, a2, b1, b2);
+ result.appendPoints(inter.points);
+ }
+ if (result.points.length > 0) {
+ result.status = "Intersection";
+ }
+ return result;
+ };
+ fabric.Intersection.intersectPolygonPolygon = function(points1, points2) {
+ var result = new Intersection(), length = points1.length, i;
+ for (i = 0; i < length; i++) {
+ var a1 = points1[i], a2 = points1[(i + 1) % length], inter = Intersection.intersectLinePolygon(a1, a2, points2);
+ result.appendPoints(inter.points);
}
- }
-
- el2.setAttribute('transform', currentTrans);
- el2.setAttribute('instantiated_by_use', '1');
- el2.removeAttribute('id');
- parentNode = el.parentNode;
- parentNode.replaceChild(el2, el);
- // some browsers do not shorten nodelist after replaceChild (IE8)
- if (nodelist.length === oldLength) {
- i++;
- }
- }
- }
-
- // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
- // matches, e.g.: +14.56e-12, etc.
- var reViewBoxAttrValue = new RegExp(
- '^' +
- '\\s*(' + fabric.reNum + '+)\\s*,?' +
- '\\s*(' + fabric.reNum + '+)\\s*,?' +
- '\\s*(' + fabric.reNum + '+)\\s*,?' +
- '\\s*(' + fabric.reNum + '+)\\s*' +
- '$'
- );
-
- /**
- * Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements
- */
- function applyViewboxTransform(element) {
-
- var viewBoxAttr = element.getAttribute('viewBox'),
- scaleX = 1,
- scaleY = 1,
- minX = 0,
- minY = 0,
- viewBoxWidth, viewBoxHeight, matrix, el,
- widthAttr = element.getAttribute('width'),
- heightAttr = element.getAttribute('height'),
- x = element.getAttribute('x') || 0,
- y = element.getAttribute('y') || 0,
- preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '',
- missingViewBox = (!viewBoxAttr || !fabric.svgViewBoxElementsRegEx.test(element.nodeName)
- || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))),
- missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'),
- toBeParsed = missingViewBox && missingDimAttr,
- parsedDim = { }, translateMatrix = '';
-
- parsedDim.width = 0;
- parsedDim.height = 0;
- parsedDim.toBeParsed = toBeParsed;
-
- if (toBeParsed) {
- return parsedDim;
- }
-
- if (missingViewBox) {
- parsedDim.width = parseUnit(widthAttr);
- parsedDim.height = parseUnit(heightAttr);
- return parsedDim;
- }
-
- minX = -parseFloat(viewBoxAttr[1]);
- minY = -parseFloat(viewBoxAttr[2]);
- viewBoxWidth = parseFloat(viewBoxAttr[3]);
- viewBoxHeight = parseFloat(viewBoxAttr[4]);
-
- if (!missingDimAttr) {
- parsedDim.width = parseUnit(widthAttr);
- parsedDim.height = parseUnit(heightAttr);
- scaleX = parsedDim.width / viewBoxWidth;
- scaleY = parsedDim.height / viewBoxHeight;
- }
- else {
- parsedDim.width = viewBoxWidth;
- parsedDim.height = viewBoxHeight;
- }
-
- // default is to preserve aspect ratio
- preserveAspectRatio = fabric.util.parsePreserveAspectRatioAttribute(preserveAspectRatio);
- if (preserveAspectRatio.alignX !== 'none') {
- //translate all container for the effect of Mid, Min, Max
- scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX);
- }
-
- if (scaleX === 1 && scaleY === 1 && minX === 0 && minY === 0 && x === 0 && y === 0) {
- return parsedDim;
- }
-
- if (x || y) {
- translateMatrix = ' translate(' + parseUnit(x) + ' ' + parseUnit(y) + ') ';
- }
-
- matrix = translateMatrix + ' matrix(' + scaleX +
- ' 0' +
- ' 0 ' +
- scaleY + ' ' +
- (minX * scaleX) + ' ' +
- (minY * scaleY) + ') ';
+ if (result.points.length > 0) {
+ result.status = "Intersection";
+ }
+ return result;
+ };
+ fabric.Intersection.intersectPolygonRectangle = function(points, r1, r2) {
+ var min = r1.min(r2), max = r1.max(r2), topRight = new fabric.Point(max.x, min.y), bottomLeft = new fabric.Point(min.x, max.y), inter1 = Intersection.intersectLinePolygon(min, topRight, points), inter2 = Intersection.intersectLinePolygon(topRight, max, points), inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points), inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points), result = new Intersection();
+ result.appendPoints(inter1.points);
+ result.appendPoints(inter2.points);
+ result.appendPoints(inter3.points);
+ result.appendPoints(inter4.points);
+ if (result.points.length > 0) {
+ result.status = "Intersection";
+ }
+ return result;
+ };
+})(typeof exports !== "undefined" ? exports : this);
- if (element.nodeName === 'svg') {
- el = element.ownerDocument.createElement('g');
- // element.firstChild != null
- while (element.firstChild) {
- el.appendChild(element.firstChild);
- }
- element.appendChild(el);
+(function(global) {
+ "use strict";
+ var fabric = global.fabric || (global.fabric = {});
+ if (fabric.Color) {
+ fabric.warn("fabric.Color is already defined.");
+ return;
}
- else {
- el = element;
- matrix = el.getAttribute('transform') + matrix;
+ function Color(color) {
+ if (!color) {
+ this.setSource([ 0, 0, 0, 1 ]);
+ } else {
+ this._tryParsingColor(color);
+ }
}
-
- el.setAttribute('transform', matrix);
- return parsedDim;
- }
-
- function hasAncestorWithNodeName(element, nodeName) {
- while (element && (element = element.parentNode)) {
- if (element.nodeName && nodeName.test(element.nodeName.replace('svg:', ''))
- && !element.getAttribute('instantiated_by_use')) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
- * @static
- * @function
- * @memberOf fabric
- * @param {SVGDocument} doc SVG document to parse
- * @param {Function} callback Callback to call when parsing is finished;
- * It's being passed an array of elements (parsed from a document).
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- * @param {Object} [parsingOptions] options for parsing document
- * @param {String} [parsingOptions.crossOrigin] crossOrigin settings
- */
- fabric.parseSVGDocument = function(doc, callback, reviver, parsingOptions) {
- if (!doc) {
- return;
- }
-
- parseUseDirectives(doc);
-
- var svgUid = fabric.Object.__uid++, i, len,
- options = applyViewboxTransform(doc),
- descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
- options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
- options.svgUid = svgUid;
-
- if (descendants.length === 0 && fabric.isLikelyNode) {
- // we're likely in node, where "o3-xml" library fails to gEBTN("*")
- // https://github.com/ajaxorg/node-o3-xml/issues/21
- descendants = doc.selectNodes('//*[name(.)!="svg"]');
- var arr = [];
- for (i = 0, len = descendants.length; i < len; i++) {
- arr[i] = descendants[i];
- }
- descendants = arr;
- }
-
- var elements = descendants.filter(function(el) {
- applyViewboxTransform(el);
- 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)) {
- callback && callback([], {});
- return;
+ fabric.Color = Color;
+ fabric.Color.prototype = {
+ _tryParsingColor: function(color) {
+ var source;
+ if (color in Color.colorNameMap) {
+ color = Color.colorNameMap[color];
+ }
+ if (color === "transparent") {
+ source = [ 255, 255, 255, 0 ];
+ }
+ if (!source) {
+ source = Color.sourceFromHex(color);
+ }
+ if (!source) {
+ source = Color.sourceFromRgb(color);
+ }
+ if (!source) {
+ source = Color.sourceFromHsl(color);
+ }
+ if (!source) {
+ source = [ 0, 0, 0, 1 ];
+ }
+ if (source) {
+ this.setSource(source);
+ }
+ },
+ _rgbToHsl: function(r, g, b) {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+ var h, s, l, max = fabric.util.array.max([ r, g, b ]), min = fabric.util.array.min([ r, g, b ]);
+ l = (max + min) / 2;
+ if (max === min) {
+ h = s = 0;
+ } else {
+ var d = max - min;
+ s = l > .5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+
+ case g:
+ h = (b - r) / d + 2;
+ break;
+
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+ return [ Math.round(h * 360), Math.round(s * 100), Math.round(l * 100) ];
+ },
+ getSource: function() {
+ return this._source;
+ },
+ setSource: function(source) {
+ this._source = source;
+ },
+ toRgb: function() {
+ var source = this.getSource();
+ return "rgb(" + source[0] + "," + source[1] + "," + source[2] + ")";
+ },
+ toRgba: function() {
+ var source = this.getSource();
+ return "rgba(" + source[0] + "," + source[1] + "," + source[2] + "," + source[3] + ")";
+ },
+ toHsl: function() {
+ var source = this.getSource(), hsl = this._rgbToHsl(source[0], source[1], source[2]);
+ return "hsl(" + hsl[0] + "," + hsl[1] + "%," + hsl[2] + "%)";
+ },
+ toHsla: function() {
+ var source = this.getSource(), hsl = this._rgbToHsl(source[0], source[1], source[2]);
+ return "hsla(" + hsl[0] + "," + hsl[1] + "%," + hsl[2] + "%," + source[3] + ")";
+ },
+ toHex: function() {
+ var source = this.getSource(), r, g, b;
+ r = source[0].toString(16);
+ r = r.length === 1 ? "0" + r : r;
+ g = source[1].toString(16);
+ g = g.length === 1 ? "0" + g : g;
+ b = source[2].toString(16);
+ b = b.length === 1 ? "0" + b : b;
+ return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
+ },
+ toHexa: function() {
+ var source = this.getSource(), a;
+ a = Math.round(source[3] * 255);
+ a = a.toString(16);
+ a = a.length === 1 ? "0" + a : a;
+ return this.toHex() + a.toUpperCase();
+ },
+ getAlpha: function() {
+ return this.getSource()[3];
+ },
+ setAlpha: function(alpha) {
+ var source = this.getSource();
+ source[3] = alpha;
+ this.setSource(source);
+ return this;
+ },
+ toGrayscale: function() {
+ var source = this.getSource(), average = parseInt((source[0] * .3 + source[1] * .59 + source[2] * .11).toFixed(0), 10), currentAlpha = source[3];
+ this.setSource([ average, average, average, currentAlpha ]);
+ return this;
+ },
+ toBlackWhite: function(threshold) {
+ var source = this.getSource(), average = (source[0] * .3 + source[1] * .59 + source[2] * .11).toFixed(0), currentAlpha = source[3];
+ threshold = threshold || 127;
+ average = Number(average) < Number(threshold) ? 0 : 255;
+ this.setSource([ average, average, average, currentAlpha ]);
+ return this;
+ },
+ overlayWith: function(otherColor) {
+ if (!(otherColor instanceof Color)) {
+ otherColor = new Color(otherColor);
+ }
+ var result = [], alpha = this.getAlpha(), otherAlpha = .5, source = this.getSource(), otherSource = otherColor.getSource(), i;
+ for (i = 0; i < 3; i++) {
+ result.push(Math.round(source[i] * (1 - otherAlpha) + otherSource[i] * otherAlpha));
+ }
+ result[3] = alpha;
+ this.setSource(result);
+ return this;
+ }
+ };
+ 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*)?\)$/;
+ fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
+ fabric.Color.reHex = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
+ fabric.Color.colorNameMap = {
+ 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"
+ };
+ function hue2rgb(p, q, t) {
+ if (t < 0) {
+ t += 1;
+ }
+ if (t > 1) {
+ t -= 1;
+ }
+ if (t < 1 / 6) {
+ return p + (q - p) * 6 * t;
+ }
+ if (t < 1 / 2) {
+ return q;
+ }
+ if (t < 2 / 3) {
+ return p + (q - p) * (2 / 3 - t) * 6;
+ }
+ return p;
}
-
- fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
- fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
- // Precedence of rules: style > class > attribute
- fabric.parseElements(elements, function(instances, elements) {
- if (callback) {
- callback(instances, options, elements, descendants);
- }
- }, clone(options), reviver, parsingOptions);
- };
-
- var reFontDeclaration = new RegExp(
- '(normal|italic)?\\s*(normal|small-caps)?\\s*' +
- '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' +
- fabric.reNum +
- '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + fabric.reNum + '))?\\s+(.*)');
-
- extend(fabric, {
- /**
- * Parses a short font declaration, building adding its properties to a style object
- * @static
- * @function
- * @memberOf fabric
- * @param {String} value font declaration
- * @param {Object} oStyle definition
- */
- parseFontDeclaration: function(value, oStyle) {
- var match = value.match(reFontDeclaration);
-
- if (!match) {
- return;
- }
- var fontStyle = match[1],
- // font variant is not used
- // fontVariant = match[2],
- fontWeight = match[3],
- fontSize = match[4],
- lineHeight = match[5],
- fontFamily = match[6];
-
- if (fontStyle) {
- oStyle.fontStyle = fontStyle;
- }
- if (fontWeight) {
- oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
- }
- if (fontSize) {
- oStyle.fontSize = parseUnit(fontSize);
- }
- if (fontFamily) {
- oStyle.fontFamily = fontFamily;
- }
- if (lineHeight) {
- oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
- }
- },
-
- /**
- * Parses an SVG document, returning all of the gradient declarations found in it
- * @static
- * @function
- * @memberOf fabric
- * @param {SVGDocument} doc SVG document to parse
- * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
- */
- getGradientDefs: function(doc) {
- var tagArray = [
- 'linearGradient',
- 'radialGradient',
- 'svg:linearGradient',
- 'svg:radialGradient'],
- elList = _getMultipleNodes(doc, tagArray),
- el, j = 0, id, xlink,
- gradientDefs = { }, idsToXlinkMap = { };
-
- j = elList.length;
-
- while (j--) {
- el = elList[j];
- xlink = el.getAttribute('xlink:href');
- id = el.getAttribute('id');
- if (xlink) {
- idsToXlinkMap[id] = xlink.substr(1);
- }
- gradientDefs[id] = el;
- }
-
- for (id in idsToXlinkMap) {
- var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
- el = gradientDefs[id];
- while (el2.firstChild) {
- el.appendChild(el2.firstChild);
- }
- }
- return gradientDefs;
- },
-
- /**
- * Returns an object of attributes' name/value, given element and an array of attribute names;
- * Parses parent "g" nodes recursively upwards.
- * @static
- * @memberOf fabric
- * @param {DOMElement} element Element to parse
- * @param {Array} attributes Array of attributes to parse
- * @return {Object} object containing parsed attributes' names/values
- */
- parseAttributes: function(element, attributes, svgUid) {
-
- if (!element) {
- return;
- }
-
- var value,
- parentAttributes = { },
- fontSize;
-
- if (typeof svgUid === '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 && fabric.svgValidParentsRegEx.test(element.parentNode.nodeName)) {
- parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
- }
- fontSize = (parentAttributes && parentAttributes.fontSize ) ||
- element.getAttribute('font-size') || fabric.Text.DEFAULT_SVG_FONT_SIZE;
-
- var ownAttributes = attributes.reduce(function(memo, attr) {
- value = element.getAttribute(attr);
- if (value) { // eslint-disable-line
- memo[attr] = value;
- }
- return memo;
- }, { });
- // add values parsed from style, which take precedence over attributes
- // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
- ownAttributes = extend(ownAttributes,
- extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
-
- var normalizedAttr, normalizedValue, normalizedStyle = {};
- for (var attr in ownAttributes) {
- normalizedAttr = normalizeAttr(attr);
- normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize);
- normalizedStyle[normalizedAttr] = normalizedValue;
- }
- if (normalizedStyle && normalizedStyle.font) {
- fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle);
- }
- var mergedAttrs = extend(parentAttributes, normalizedStyle);
- return fabric.svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs);
- },
-
- /**
- * Transforms an array of svg elements to corresponding fabric.* instances
- * @static
- * @memberOf fabric
- * @param {Array} elements Array of elements to parse
- * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
- * @param {Object} [options] Options object
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- */
- parseElements: function(elements, callback, options, reviver, parsingOptions) {
- new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse();
- },
-
- /**
- * Parses "style" attribute, retuning an object with values
- * @static
- * @memberOf fabric
- * @param {SVGElement} element Element to parse
- * @return {Object} Objects with values parsed from style attribute of an element
- */
- parseStyleAttribute: function(element) {
- var oStyle = { },
- style = element.getAttribute('style');
-
- if (!style) {
- return oStyle;
- }
-
- if (typeof style === 'string') {
- parseStyleString(style, oStyle);
- }
- else {
- parseStyleObject(style, oStyle);
- }
-
- return oStyle;
- },
-
- /**
- * Parses "points" attribute, returning an array of values
- * @static
- * @memberOf fabric
- * @param {String} points points attribute string
- * @return {Array} array of points
- */
- parsePointsAttribute: function(points) {
-
- // points attribute is required and must not be empty
- if (!points) {
- return null;
- }
-
- // replace commas with whitespace and remove bookending whitespace
- points = points.replace(/,/g, ' ').trim();
-
- points = points.split(/\s+/);
- var parsedPoints = [], i, len;
-
- for (i = 0, len = points.length; i < len; i += 2) {
- parsedPoints.push({
- x: parseFloat(points[i]),
- y: parseFloat(points[i + 1])
- });
- }
-
- // odd number of points is an error
- // if (parsedPoints.length % 2 !== 0) {
- // return null;
- // }
-
- return parsedPoints;
- },
-
- /**
- * Returns CSS rules for a given SVG document
- * @static
- * @function
- * @memberOf fabric
- * @param {SVGDocument} doc SVG document to parse
- * @return {Object} CSS rules of this document
- */
- getCSSRules: function(doc) {
- var styles = doc.getElementsByTagName('style'), i, len,
- allRules = { }, rules;
-
- // very crude parsing of style contents
- 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;
-
- // remove comments
- styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
- if (styleContents.trim() === '') {
- continue;
- }
- 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 (i = 0, len = propertyValuePairs.length; i < len; i++) {
- var pair = propertyValuePairs[i].split(/\s*:\s*/),
- property = pair[0],
- value = pair[1];
- ruleObj[property] = value;
- }
- rule = match[1];
- rule.split(',').forEach(function(_rule) {
- _rule = _rule.replace(/^svg/i, '').trim();
- if (_rule === '') {
- return;
- }
- if (allRules[_rule]) {
- fabric.util.object.extend(allRules[_rule], ruleObj);
- }
- else {
- allRules[_rule] = fabric.util.object.clone(ruleObj);
- }
- });
- });
- }
- return allRules;
- },
-
- /**
- * Takes url corresponding to an SVG document, and parses it into a set of fabric objects.
- * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)
- * @memberOf fabric
- * @param {String} url
- * @param {Function} callback
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- * @param {Object} [options] Object containing options for parsing
- * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources
- */
- loadSVGFromURL: function(url, callback, reviver, options) {
-
- url = url.replace(/^\n\s*/, '').trim();
- new fabric.util.request(url, {
- method: 'get',
- onComplete: onComplete
- });
-
- function onComplete(r) {
-
- var xml = r.responseXML;
- if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
- xml = new ActiveXObject('Microsoft.XMLDOM');
- xml.async = 'false';
- //IE chokes on DOCTYPE
- xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
+ fabric.Color.fromRgb = function(color) {
+ return Color.fromSource(Color.sourceFromRgb(color));
+ };
+ fabric.Color.sourceFromRgb = function(color) {
+ var match = color.match(Color.reRGBa);
+ if (match) {
+ var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1), g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1), b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
+ return [ parseInt(r, 10), parseInt(g, 10), parseInt(b, 10), match[4] ? parseFloat(match[4]) : 1 ];
}
- if (!xml || !xml.documentElement) {
- callback && callback(null);
+ };
+ fabric.Color.fromRgba = Color.fromRgb;
+ fabric.Color.fromHsl = function(color) {
+ return Color.fromSource(Color.sourceFromHsl(color));
+ };
+ fabric.Color.sourceFromHsl = function(color) {
+ var match = color.match(Color.reHSLa);
+ if (!match) {
+ return;
}
+ var h = (parseFloat(match[1]) % 360 + 360) % 360 / 360, s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1), l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1), r, g, b;
+ if (s === 0) {
+ r = g = b = l;
+ } else {
+ var q = l <= .5 ? l * (s + 1) : l + s - l * s, p = l * 2 - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+ return [ Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), match[4] ? parseFloat(match[4]) : 1 ];
+ };
+ fabric.Color.fromHsla = Color.fromHsl;
+ fabric.Color.fromHex = function(color) {
+ return Color.fromSource(Color.sourceFromHex(color));
+ };
+ fabric.Color.sourceFromHex = function(color) {
+ if (color.match(Color.reHex)) {
+ var value = color.slice(color.indexOf("#") + 1), isShortNotation = value.length === 3 || value.length === 4, isRGBa = value.length === 8 || value.length === 4, r = isShortNotation ? value.charAt(0) + value.charAt(0) : value.substring(0, 2), g = isShortNotation ? value.charAt(1) + value.charAt(1) : value.substring(2, 4), b = isShortNotation ? value.charAt(2) + value.charAt(2) : value.substring(4, 6), a = isRGBa ? isShortNotation ? value.charAt(3) + value.charAt(3) : value.subs [...]
+ return [ parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), parseFloat((parseInt(a, 16) / 255).toFixed(2)) ];
+ }
+ };
+ fabric.Color.fromSource = function(source) {
+ var oColor = new Color();
+ oColor.setSource(source);
+ return oColor;
+ };
+})(typeof exports !== "undefined" ? exports : this);
- fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) {
- callback && callback(results, _options, elements, allElements);
- }, reviver, options);
- }
- },
-
- /**
- * Takes string corresponding to an SVG document, and parses it into a set of fabric objects
- * @memberOf fabric
- * @param {String} string
- * @param {Function} callback
- * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
- * @param {Object} [options] Object containing options for parsing
- * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources
- */
- loadSVGFromString: function(string, callback, reviver, options) {
- string = string.trim();
- var doc;
- if (typeof DOMParser !== 'undefined') {
- var parser = new DOMParser();
- if (parser && parser.parseFromString) {
- doc = parser.parseFromString(string, 'text/xml');
- }
- }
- else if (fabric.window.ActiveXObject) {
- doc = new ActiveXObject('Microsoft.XMLDOM');
- doc.async = 'false';
- // IE chokes on DOCTYPE
- doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
- }
-
- fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) {
- callback(results, _options, elements, allElements);
- }, reviver, options);
+(function() {
+ function getColorStop(el) {
+ var style = el.getAttribute("style"), offset = el.getAttribute("offset") || 0, color, colorAlpha, opacity, i;
+ offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
+ offset = offset < 0 ? 0 : offset > 1 ? 1 : offset;
+ if (style) {
+ var keyValuePairs = style.split(/\s*;\s*/);
+ if (keyValuePairs[keyValuePairs.length - 1] === "") {
+ keyValuePairs.pop();
+ }
+ for (i = keyValuePairs.length; i--; ) {
+ var split = keyValuePairs[i].split(/\s*:\s*/), key = split[0].trim(), value = split[1].trim();
+ if (key === "stop-color") {
+ color = value;
+ } else if (key === "stop-opacity") {
+ opacity = value;
+ }
+ }
+ }
+ if (!color) {
+ color = el.getAttribute("stop-color") || "rgb(0,0,0)";
+ }
+ if (!opacity) {
+ opacity = el.getAttribute("stop-opacity");
+ }
+ color = new fabric.Color(color);
+ colorAlpha = color.getAlpha();
+ opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
+ opacity *= colorAlpha;
+ return {
+ offset: offset,
+ color: color.toRgb(),
+ opacity: opacity
+ };
}
- });
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) {
- this.elements = elements;
- this.callback = callback;
- this.options = options;
- this.reviver = reviver;
- this.svgUid = (options && options.svgUid) || 0;
- this.parsingOptions = parsingOptions;
-};
-
-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 i = 0, len = this.elements.length; i < len; i++) {
- this.elements[i].setAttribute('svgUid', this.svgUid);
- (function(_obj, i) {
- setTimeout(function() {
- _obj.createObject(_obj.elements[i], i);
- }, 0);
- })(this, i);
- }
-};
-
-fabric.ElementsParser.prototype.createObject = function(el, index) {
- var klass = fabric[fabric.util.string.capitalize(el.tagName.replace('svg:', ''))];
- if (klass && klass.fromElement) {
- try {
- this._createObject(klass, el, index);
+ function getLinearCoords(el) {
+ return {
+ x1: el.getAttribute("x1") || 0,
+ y1: el.getAttribute("y1") || 0,
+ x2: el.getAttribute("x2") || "100%",
+ y2: el.getAttribute("y2") || 0
+ };
}
- catch (err) {
- fabric.log(err);
+ function getRadialCoords(el) {
+ return {
+ x1: el.getAttribute("fx") || el.getAttribute("cx") || "50%",
+ y1: el.getAttribute("fy") || el.getAttribute("cy") || "50%",
+ r1: 0,
+ x2: el.getAttribute("cx") || "50%",
+ y2: el.getAttribute("cy") || "50%",
+ r2: el.getAttribute("r") || "50%"
+ };
}
- }
- else {
- this.checkIfDone();
- }
-};
-
-fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
- 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);
+ var clone = fabric.util.object.clone;
+ fabric.Gradient = fabric.util.createClass({
+ offsetX: 0,
+ offsetY: 0,
+ initialize: function(options) {
+ options || (options = {});
+ var coords = {};
+ this.id = fabric.Object.__uid++;
+ this.type = options.type || "linear";
+ coords = {
+ x1: options.coords.x1 || 0,
+ y1: options.coords.y1 || 0,
+ x2: options.coords.x2 || 0,
+ y2: options.coords.y2 || 0
+ };
+ if (this.type === "radial") {
+ coords.r1 = options.coords.r1 || 0;
+ coords.r2 = options.coords.r2 || 0;
+ }
+ this.coords = coords;
+ this.colorStops = options.colorStops.slice();
+ if (options.gradientTransform) {
+ this.gradientTransform = options.gradientTransform;
+ }
+ this.offsetX = options.offsetX || this.offsetX;
+ this.offsetY = options.offsetY || this.offsetY;
+ },
+ addColorStop: function(colorStops) {
+ for (var position in colorStops) {
+ var color = new fabric.Color(colorStops[position]);
+ this.colorStops.push({
+ offset: parseFloat(position),
+ color: color.toRgb(),
+ opacity: color.getAlpha()
+ });
+ }
+ return this;
+ },
+ toObject: function(propertiesToInclude) {
+ var object = {
+ type: this.type,
+ coords: this.coords,
+ colorStops: this.colorStops,
+ offsetX: this.offsetX,
+ offsetY: this.offsetY,
+ gradientTransform: this.gradientTransform ? this.gradientTransform.concat() : this.gradientTransform
+ };
+ fabric.util.populateWithProperties(this, object, propertiesToInclude);
+ return object;
+ },
+ toSVG: function(object) {
+ var coords = clone(this.coords, true), i, len, markup, commonAttributes, colorStops = clone(this.colorStops, true), needsSwap = coords.r1 > coords.r2, offsetX = object.width / 2, offsetY = object.height / 2;
+ colorStops.sort(function(a, b) {
+ return a.offset - b.offset;
+ });
+ 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;
+ }
+ }
+ commonAttributes = 'id="SVGID_' + this.id + '" gradientUnits="userSpaceOnUse"';
+ if (this.gradientTransform) {
+ commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(" ") + ')" ';
+ }
+ if (this.type === "linear") {
+ markup = [ "<linearGradient ", commonAttributes, ' x1="', coords.x1, '" y1="', coords.y1, '" x2="', coords.x2, '" y2="', coords.y2, '">\n' ];
+ } else if (this.type === "radial") {
+ markup = [ "<radialGradient ", commonAttributes, ' cx="', needsSwap ? coords.x1 : coords.x2, '" cy="', needsSwap ? coords.y1 : coords.y2, '" r="', needsSwap ? coords.r1 : coords.r2, '" fx="', needsSwap ? coords.x2 : coords.x1, '" fy="', needsSwap ? coords.y2 : coords.y1, '">\n' ];
+ }
+ if (this.type === "radial") {
+ if (needsSwap) {
+ colorStops = colorStops.concat();
+ colorStops.reverse();
+ for (i = 0, len = colorStops.length; i < len; i++) {
+ colorStops[i].offset = 1 - colorStops[i].offset;
+ }
+ }
+ var minRadius = Math.min(coords.r1, coords.r2);
+ if (minRadius > 0) {
+ var maxRadius = Math.max(coords.r1, coords.r2), percentageShift = minRadius / maxRadius;
+ for (i = 0, len = colorStops.length; i < len; i++) {
+ colorStops[i].offset += percentageShift * (1 - colorStops[i].offset);
+ }
+ }
+ }
+ 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, typeof colorStop.opacity !== "undefined" ? ";stop-opacity: " + colorStop.opacity : ";", '"/>\n');
+ }
+ markup.push(this.type === "linear" ? "</linearGradient>\n" : "</radialGradient>\n");
+ return markup.join("");
+ },
+ toLive: function(ctx) {
+ var gradient, coords = fabric.util.object.clone(this.coords), i, len;
+ if (!this.type) {
+ return;
+ }
+ if (this.type === "linear") {
+ gradient = ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2);
+ } else if (this.type === "radial") {
+ gradient = ctx.createRadialGradient(coords.x1, coords.y1, coords.r1, coords.x2, coords.y2, coords.r2);
+ }
+ 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;
+ if (typeof opacity !== "undefined") {
+ color = new fabric.Color(color).setAlpha(opacity).toRgba();
+ }
+ gradient.addColorStop(offset, color);
+ }
+ return gradient;
+ }
+ });
+ fabric.util.object.extend(fabric.Gradient, {
+ fromElement: function(el, instance) {
+ var colorStopEls = el.getElementsByTagName("stop"), type, gradientUnits = el.getAttribute("gradientUnits") || "objectBoundingBox", gradientTransform = el.getAttribute("gradientTransform"), colorStops = [], coords, ellipseMatrix, i;
+ if (el.nodeName === "linearGradient" || el.nodeName === "LINEARGRADIENT") {
+ type = "linear";
+ } else {
+ type = "radial";
+ }
+ if (type === "linear") {
+ coords = getLinearCoords(el);
+ } else if (type === "radial") {
+ coords = getRadialCoords(el);
+ }
+ for (i = colorStopEls.length; i--; ) {
+ colorStops.push(getColorStop(colorStopEls[i]));
+ }
+ ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
+ var gradient = new fabric.Gradient({
+ type: type,
+ coords: coords,
+ colorStops: colorStops,
+ offsetX: -instance.left,
+ offsetY: -instance.top
+ });
+ if (gradientTransform || ellipseMatrix !== "") {
+ gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || "") + ellipseMatrix);
+ }
+ return gradient;
+ },
+ forObject: function(obj, options) {
+ options || (options = {});
+ _convertPercentUnitsToValues(obj, options.coords, "userSpaceOnUse");
+ return new fabric.Gradient(options);
+ }
+ });
+ function _convertPercentUnitsToValues(object, options, gradientUnits) {
+ var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = "";
+ for (var prop in options) {
+ if (options[prop] === "Infinity") {
+ options[prop] = 1;
+ } else if (options[prop] === "-Infinity") {
+ options[prop] = 0;
+ }
+ propValue = parseFloat(options[prop], 10);
+ if (typeof options[prop] === "string" && /^(\d+\.\d+)%|(\d+)%$/.test(options[prop])) {
+ multFactor = .01;
+ } else {
+ multFactor = 1;
+ }
+ if (prop === "x1" || prop === "x2" || prop === "r2") {
+ multFactor *= gradientUnits === "objectBoundingBox" ? object.width : 1;
+ addFactor = gradientUnits === "objectBoundingBox" ? object.left || 0 : 0;
+ } else if (prop === "y1" || prop === "y2") {
+ multFactor *= gradientUnits === "objectBoundingBox" ? object.height : 1;
+ addFactor = gradientUnits === "objectBoundingBox" ? object.top || 0 : 0;
+ }
+ options[prop] = propValue * multFactor + addFactor;
+ }
+ if (object.type === "ellipse" && options.r2 !== null && gradientUnits === "objectBoundingBox" && object.rx !== object.ry) {
+ var scaleFactor = object.ry / object.rx;
+ ellipseMatrix = " scale(1, " + scaleFactor + ")";
+ if (options.y1) {
+ options.y1 /= scaleFactor;
+ }
+ if (options.y2) {
+ options.y2 /= scaleFactor;
+ }
+ }
+ return ellipseMatrix;
}
- obj._removeTransformMatrix(_options);
- _this.reviver && _this.reviver(el, obj);
- _this.instances[index] = obj;
- _this.checkIfDone();
- };
-};
-
-fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
-
- var instanceFillValue = obj.get(property);
- if (!(/^url\(/).test(instanceFillValue)) {
- return;
- }
- var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
- if (fabric.gradientDefs[this.svgUid][gradientId]) {
- obj.set(property,
- fabric.Gradient.fromElement(fabric.gradientDefs[this.svgUid][gradientId], obj));
- }
-};
+})();
-fabric.ElementsParser.prototype.checkIfDone = function() {
- if (--this.numElements === 0) {
- this.instances = this.instances.filter(function(el) {
- // eslint-disable-next-line no-eq-null, eqeqeq
- return el != null;
+(function() {
+ "use strict";
+ var toFixed = fabric.util.toFixed;
+ fabric.Pattern = fabric.util.createClass({
+ repeat: "repeat",
+ offsetX: 0,
+ offsetY: 0,
+ crossOrigin: "",
+ patternTransform: null,
+ initialize: function(options, callback) {
+ options || (options = {});
+ this.id = fabric.Object.__uid++;
+ this.setOptions(options);
+ if (!options.source || options.source && typeof options.source !== "string") {
+ callback && callback(this);
+ return;
+ }
+ if (typeof fabric.util.getFunctionBody(options.source) !== "undefined") {
+ this.source = new Function(fabric.util.getFunctionBody(options.source));
+ callback && callback(this);
+ } else {
+ var _this = this;
+ this.source = fabric.util.createImage();
+ fabric.util.loadImage(options.source, function(img) {
+ _this.source = img;
+ callback && callback(_this);
+ }, null, this.crossOrigin);
+ }
+ },
+ toObject: function(propertiesToInclude) {
+ var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, source, object;
+ if (typeof this.source === "function") {
+ source = String(this.source);
+ } else if (typeof this.source.src === "string") {
+ source = this.source.src;
+ } else if (typeof this.source === "object" && this.source.toDataURL) {
+ source = this.source.toDataURL();
+ }
+ object = {
+ type: "pattern",
+ source: source,
+ repeat: this.repeat,
+ crossOrigin: this.crossOrigin,
+ offsetX: toFixed(this.offsetX, NUM_FRACTION_DIGITS),
+ offsetY: toFixed(this.offsetY, NUM_FRACTION_DIGITS),
+ patternTransform: this.patternTransform ? this.patternTransform.concat() : null
+ };
+ fabric.util.populateWithProperties(this, object, propertiesToInclude);
+ return object;
+ },
+ toSVG: function(object) {
+ var patternSource = typeof this.source === "function" ? this.source() : this.source, patternWidth = patternSource.width / object.width, patternHeight = patternSource.height / object.height, patternOffsetX = this.offsetX / object.width, patternOffsetY = this.offsetY / object.height, patternImgSrc = "";
+ if (this.repeat === "repeat-x" || this.repeat === "no-repeat") {
+ patternHeight = 1;
+ }
+ if (this.repeat === "repeat-y" || this.repeat === "no-repeat") {
+ patternWidth = 1;
+ }
+ if (patternSource.src) {
+ patternImgSrc = patternSource.src;
+ } else if (patternSource.toDataURL) {
+ patternImgSrc = patternSource.toDataURL();
+ }
+ return '<pattern id="SVGID_' + this.id + '" x="' + patternOffsetX + '" y="' + patternOffsetY + '" width="' + patternWidth + '" height="' + patternHeight + '">\n' + '<image x="0" y="0"' + ' width="' + patternSource.width + '" height="' + patternSource.height + '" xlink:href="' + patternImgSrc + '"></image>\n' + "</pattern>\n";
+ },
+ setOptions: function(options) {
+ for (var prop in options) {
+ this[prop] = options[prop];
+ }
+ },
+ toLive: function(ctx) {
+ var source = typeof this.source === "function" ? this.source() : this.source;
+ if (!source) {
+ return "";
+ }
+ if (typeof source.src !== "undefined") {
+ if (!source.complete) {
+ return "";
+ }
+ if (source.naturalWidth === 0 || source.naturalHeight === 0) {
+ return "";
+ }
+ }
+ return ctx.createPattern(source, this.repeat);
+ }
});
- this.callback(this.instances, this.elements);
- }
-};
-
+})();
(function(global) {
+ "use strict";
+ var fabric = global.fabric || (global.fabric = {}), toFixed = fabric.util.toFixed;
+ if (fabric.Shadow) {
+ fabric.warn("fabric.Shadow is already defined.");
+ return;
+ }
+ fabric.Shadow = fabric.util.createClass({
+ color: "rgb(0,0,0)",
+ blur: 0,
+ offsetX: 0,
+ offsetY: 0,
+ affectStroke: false,
+ includeDefaultValues: true,
+ initialize: function(options) {
+ if (typeof options === "string") {
+ options = this._parseShadow(options);
+ }
+ for (var prop in options) {
+ this[prop] = options[prop];
+ }
+ this.id = fabric.Object.__uid++;
+ },
+ _parseShadow: function(shadow) {
+ var shadowStr = shadow.trim(), offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [], color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, "") || "rgb(0,0,0)";
+ return {
+ color: color.trim(),
+ offsetX: parseInt(offsetsAndBlur[1], 10) || 0,
+ offsetY: parseInt(offsetsAndBlur[2], 10) || 0,
+ blur: parseInt(offsetsAndBlur[3], 10) || 0
+ };
+ },
+ toString: function() {
+ return [ this.offsetX, this.offsetY, this.blur, this.color ].join("px ");
+ },
+ toSVG: function(object) {
+ var fBoxX = 40, fBoxY = 40, NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, offset = fabric.util.rotateVector({
+ x: this.offsetX,
+ y: this.offsetY
+ }, fabric.util.degreesToRadians(-object.angle)), BLUR_BOX = 20;
+ if (object.width && object.height) {
+ fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
+ fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
+ }
+ if (object.flipX) {
+ offset.x *= -1;
+ }
+ if (object.flipY) {
+ offset.y *= -1;
+ }
+ return '<filter id="SVGID_' + this.id + '" y="-' + fBoxY + '%" height="' + (100 + 2 * fBoxY) + '%" ' + 'x="-' + fBoxX + '%" width="' + (100 + 2 * fBoxX) + '%" ' + ">\n" + '\t<feGaussianBlur in="SourceAlpha" stdDeviation="' + toFixed(this.blur ? this.blur / 2 : 0, NUM_FRACTION_DIGITS) + '"></feGaussianBlur>\n' + '\t<feOffset dx="' + toFixed(offset.x, NUM_FRACTION_DIGITS) + '" dy="' + toFixed(offset.y, NUM_FRACTION_DIGITS) + '" result="oBlur" ></feOffset>\n' + '\t<feFlood flood [...]
+ },
+ toObject: function() {
+ if (this.includeDefaultValues) {
+ return {
+ color: this.color,
+ blur: this.blur,
+ offsetX: this.offsetX,
+ offsetY: this.offsetY,
+ affectStroke: this.affectStroke
+ };
+ }
+ var obj = {}, proto = fabric.Shadow.prototype;
+ [ "color", "blur", "offsetX", "offsetY", "affectStroke" ].forEach(function(prop) {
+ if (this[prop] !== proto[prop]) {
+ obj[prop] = this[prop];
+ }
+ }, this);
+ return obj;
+ }
+ });
+ fabric.Shadow.reOffsetsAndBlur = /(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/;
+})(typeof exports !== "undefined" ? exports : this);
- 'use strict';
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Point) {
- fabric.warn('fabric.Point is already defined');
- return;
- }
-
- fabric.Point = Point;
-
- /**
- * Point class
- * @class fabric.Point
- * @memberOf fabric
- * @constructor
- * @param {Number} x
- * @param {Number} y
- * @return {fabric.Point} thisArg
- */
- function Point(x, y) {
- this.x = x;
- this.y = y;
- }
-
- Point.prototype = /** @lends fabric.Point.prototype */ {
-
- type: 'point',
-
- constructor: Point,
-
- /**
- * Adds another point to this one and returns another one
- * @param {fabric.Point} that
- * @return {fabric.Point} new Point instance with added values
- */
- add: function (that) {
- return new Point(this.x + that.x, this.y + that.y);
- },
-
- /**
- * Adds another point to this one
- * @param {fabric.Point} that
- * @return {fabric.Point} thisArg
- * @chainable
- */
- addEquals: function (that) {
- this.x += that.x;
- this.y += that.y;
- return this;
- },
-
- /**
- * Adds value to this point and returns a new one
- * @param {Number} scalar
- * @return {fabric.Point} new Point with added value
- */
- scalarAdd: function (scalar) {
- return new Point(this.x + scalar, this.y + scalar);
- },
-
- /**
- * Adds value to this point
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- * @chainable
- */
- scalarAddEquals: function (scalar) {
- this.x += scalar;
- this.y += scalar;
- return this;
- },
-
- /**
- * Subtracts another point from this point and returns a new one
- * @param {fabric.Point} that
- * @return {fabric.Point} new Point object with subtracted values
- */
- subtract: function (that) {
- return new Point(this.x - that.x, this.y - that.y);
- },
-
- /**
- * Subtracts another point from this point
- * @param {fabric.Point} that
- * @return {fabric.Point} thisArg
- * @chainable
- */
- subtractEquals: function (that) {
- this.x -= that.x;
- this.y -= that.y;
- return this;
- },
-
- /**
- * Subtracts value from this point and returns a new one
- * @param {Number} scalar
- * @return {fabric.Point}
- */
- scalarSubtract: function (scalar) {
- return new Point(this.x - scalar, this.y - scalar);
- },
-
- /**
- * Subtracts value from this point
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- * @chainable
- */
- scalarSubtractEquals: function (scalar) {
- this.x -= scalar;
- this.y -= scalar;
- return this;
- },
-
- /**
- * Multiplies this point by a value and returns a new one
- * TODO: rename in scalarMultiply in 2.0
- * @param {Number} scalar
- * @return {fabric.Point}
- */
- multiply: function (scalar) {
- return new Point(this.x * scalar, this.y * scalar);
- },
-
- /**
- * Multiplies this point by a value
- * TODO: rename in scalarMultiplyEquals in 2.0
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- * @chainable
- */
- multiplyEquals: function (scalar) {
- this.x *= scalar;
- this.y *= scalar;
- return this;
- },
-
- /**
- * Divides this point by a value and returns a new one
- * TODO: rename in scalarDivide in 2.0
- * @param {Number} scalar
- * @return {fabric.Point}
- */
- divide: function (scalar) {
- return new Point(this.x / scalar, this.y / scalar);
- },
-
- /**
- * Divides this point by a value
- * TODO: rename in scalarDivideEquals in 2.0
- * @param {Number} scalar
- * @return {fabric.Point} thisArg
- * @chainable
- */
- divideEquals: function (scalar) {
- this.x /= scalar;
- this.y /= scalar;
- return this;
- },
-
- /**
- * Returns true if this point is equal to another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- eq: function (that) {
- return (this.x === that.x && this.y === that.y);
- },
-
- /**
- * Returns true if this point is less than another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- lt: function (that) {
- return (this.x < that.x && this.y < that.y);
- },
-
- /**
- * Returns true if this point is less than or equal to another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- lte: function (that) {
- return (this.x <= that.x && this.y <= that.y);
- },
-
- /**
-
- * Returns true if this point is greater another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- gt: function (that) {
- return (this.x > that.x && this.y > that.y);
- },
+(function() {
+ "use strict";
+ if (fabric.StaticCanvas) {
+ fabric.warn("fabric.StaticCanvas is already defined.");
+ return;
+ }
+ var extend = fabric.util.object.extend, getElementOffset = fabric.util.getElementOffset, removeFromArray = fabric.util.removeFromArray, toFixed = fabric.util.toFixed, transformPoint = fabric.util.transformPoint, invertTransform = fabric.util.invertTransform, CANVAS_INIT_ERROR = new Error("Could not initialize `canvas` element");
+ fabric.StaticCanvas = fabric.util.createClass(fabric.CommonMethods, {
+ initialize: function(el, options) {
+ options || (options = {});
+ this.renderAndResetBound = this.renderAndReset.bind(this);
+ this.requestRenderAllBound = this.requestRenderAll.bind(this);
+ this._initStatic(el, options);
+ },
+ backgroundColor: "",
+ backgroundImage: null,
+ overlayColor: "",
+ overlayImage: null,
+ includeDefaultValues: true,
+ stateful: false,
+ renderOnAddRemove: true,
+ clipTo: null,
+ controlsAboveOverlay: false,
+ allowTouchScrolling: false,
+ imageSmoothingEnabled: true,
+ viewportTransform: fabric.iMatrix.concat(),
+ backgroundVpt: true,
+ overlayVpt: true,
+ onBeforeScaleRotate: function() {},
+ enableRetinaScaling: true,
+ vptCoords: {},
+ skipOffscreen: true,
+ _initStatic: function(el, options) {
+ var cb = this.requestRenderAllBound;
+ this._objects = [];
+ this._createLowerCanvas(el);
+ this._initOptions(options);
+ this._setImageSmoothing();
+ if (!this.interactive) {
+ this._initRetinaScaling();
+ }
+ if (options.overlayImage) {
+ this.setOverlayImage(options.overlayImage, cb);
+ }
+ if (options.backgroundImage) {
+ this.setBackgroundImage(options.backgroundImage, cb);
+ }
+ if (options.backgroundColor) {
+ this.setBackgroundColor(options.backgroundColor, cb);
+ }
+ if (options.overlayColor) {
+ this.setOverlayColor(options.overlayColor, cb);
+ }
+ this.calcOffset();
+ },
+ _isRetinaScaling: function() {
+ return fabric.devicePixelRatio !== 1 && this.enableRetinaScaling;
+ },
+ getRetinaScaling: function() {
+ return this._isRetinaScaling() ? fabric.devicePixelRatio : 1;
+ },
+ _initRetinaScaling: function() {
+ if (!this._isRetinaScaling()) {
+ return;
+ }
+ 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() {
+ this._offset = getElementOffset(this.lowerCanvasEl);
+ return this;
+ },
+ setOverlayImage: function(image, callback, options) {
+ return this.__setBgOverlayImage("overlayImage", image, callback, options);
+ },
+ setBackgroundImage: function(image, callback, options) {
+ return this.__setBgOverlayImage("backgroundImage", image, callback, options);
+ },
+ setOverlayColor: function(overlayColor, callback) {
+ return this.__setBgOverlayColor("overlayColor", overlayColor, callback);
+ },
+ setBackgroundColor: function(backgroundColor, callback) {
+ return this.__setBgOverlayColor("backgroundColor", backgroundColor, callback);
+ },
+ _setImageSmoothing: function() {
+ var ctx = this.getContext();
+ ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled || ctx.webkitImageSmoothingEnabled || ctx.mozImageSmoothingEnabled || ctx.msImageSmoothingEnabled || ctx.oImageSmoothingEnabled;
+ ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
+ },
+ __setBgOverlayImage: function(property, image, callback, options) {
+ if (typeof image === "string") {
+ fabric.util.loadImage(image, function(img) {
+ img && (this[property] = new fabric.Image(img, options));
+ callback && callback(img);
+ }, this, options && options.crossOrigin);
+ } else {
+ options && image.setOptions(options);
+ this[property] = image;
+ callback && callback(image);
+ }
+ return this;
+ },
+ __setBgOverlayColor: function(property, color, callback) {
+ this[property] = color;
+ this._initGradient(color, property);
+ this._initPattern(color, property, callback);
+ return this;
+ },
+ _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;
+ }
+ return element;
+ },
+ _initOptions: function(options) {
+ this._setOptions(options);
+ this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
+ this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
+ if (!this.lowerCanvasEl.style) {
+ return;
+ }
+ 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(canvasEl) {
+ if (canvasEl && canvasEl.getContext) {
+ this.lowerCanvasEl = canvasEl;
+ } else {
+ this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
+ }
+ fabric.util.addClass(this.lowerCanvasEl, "lower-canvas");
+ if (this.interactive) {
+ this._applyCanvasStyle(this.lowerCanvasEl);
+ }
+ this.contextContainer = this.lowerCanvasEl.getContext("2d");
+ },
+ getWidth: function() {
+ return this.width;
+ },
+ getHeight: function() {
+ return this.height;
+ },
+ setWidth: function(value, options) {
+ return this.setDimensions({
+ width: value
+ }, options);
+ },
+ setHeight: function(value, options) {
+ return this.setDimensions({
+ height: value
+ }, options);
+ },
+ setDimensions: function(dimensions, options) {
+ var cssValue;
+ options = options || {};
+ for (var prop in dimensions) {
+ cssValue = dimensions[prop];
+ if (!options.cssOnly) {
+ this._setBackstoreDimension(prop, dimensions[prop]);
+ cssValue += "px";
+ }
+ if (!options.backstoreOnly) {
+ this._setCssDimension(prop, cssValue);
+ }
+ }
+ if (this._isCurrentlyDrawing) {
+ this.freeDrawingBrush && this.freeDrawingBrush._setBrushStyles();
+ }
+ this._initRetinaScaling();
+ this._setImageSmoothing();
+ this.calcOffset();
+ if (!options.cssOnly) {
+ this.requestRenderAll();
+ }
+ return this;
+ },
+ _setBackstoreDimension: function(prop, value) {
+ this.lowerCanvasEl[prop] = value;
+ if (this.upperCanvasEl) {
+ this.upperCanvasEl[prop] = value;
+ }
+ if (this.cacheCanvasEl) {
+ this.cacheCanvasEl[prop] = value;
+ }
+ this[prop] = value;
+ return this;
+ },
+ _setCssDimension: function(prop, value) {
+ this.lowerCanvasEl.style[prop] = value;
+ if (this.upperCanvasEl) {
+ this.upperCanvasEl.style[prop] = value;
+ }
+ if (this.wrapperEl) {
+ this.wrapperEl.style[prop] = value;
+ }
+ return this;
+ },
+ getZoom: function() {
+ return this.viewportTransform[0];
+ },
+ setViewportTransform: function(vpt) {
+ var activeObject = this._activeObject, object, ignoreVpt = false, skipAbsolute = true, i, len;
+ this.viewportTransform = vpt;
+ for (i = 0, len = this._objects.length; i < len; i++) {
+ object = this._objects[i];
+ object.group || object.setCoords(ignoreVpt, skipAbsolute);
+ }
+ if (activeObject && activeObject.type === "activeSelection") {
+ activeObject.setCoords(ignoreVpt, skipAbsolute);
+ }
+ this.calcViewportBoundaries();
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ zoomToPoint: function(point, value) {
+ var before = point, vpt = this.viewportTransform.slice(0);
+ point = transformPoint(point, invertTransform(this.viewportTransform));
+ vpt[0] = value;
+ vpt[3] = value;
+ var after = transformPoint(point, vpt);
+ vpt[4] += before.x - after.x;
+ vpt[5] += before.y - after.y;
+ return this.setViewportTransform(vpt);
+ },
+ setZoom: function(value) {
+ this.zoomToPoint(new fabric.Point(0, 0), value);
+ return this;
+ },
+ absolutePan: function(point) {
+ var vpt = this.viewportTransform.slice(0);
+ vpt[4] = -point.x;
+ vpt[5] = -point.y;
+ return this.setViewportTransform(vpt);
+ },
+ relativePan: function(point) {
+ return this.absolutePan(new fabric.Point(-point.x - this.viewportTransform[4], -point.y - this.viewportTransform[5]));
+ },
+ getElement: function() {
+ return this.lowerCanvasEl;
+ },
+ _onObjectAdded: function(obj) {
+ this.stateful && obj.setupState();
+ obj._set("canvas", this);
+ obj.setCoords();
+ this.fire("object:added", {
+ target: obj
+ });
+ obj.fire("added");
+ },
+ _onObjectRemoved: function(obj) {
+ this.fire("object:removed", {
+ target: obj
+ });
+ obj.fire("removed");
+ delete obj.canvas;
+ },
+ clearContext: function(ctx) {
+ ctx.clearRect(0, 0, this.width, this.height);
+ return this;
+ },
+ getContext: function() {
+ return this.contextContainer;
+ },
+ clear: function() {
+ this._objects.length = 0;
+ this.backgroundImage = null;
+ this.overlayImage = null;
+ this.backgroundColor = "";
+ this.overlayColor = "";
+ if (this._hasITextHandlers) {
+ this.off("mouse:up", this._mouseUpITextHandler);
+ this._iTextInstances = null;
+ this._hasITextHandlers = false;
+ }
+ this.clearContext(this.contextContainer);
+ this.fire("canvas:cleared");
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ renderAll: function() {
+ var canvasToDrawOn = this.contextContainer;
+ this.renderCanvas(canvasToDrawOn, this._objects);
+ return this;
+ },
+ renderAndReset: function() {
+ this.isRendering = 0;
+ this.renderAll();
+ },
+ requestRenderAll: function() {
+ if (!this.isRendering) {
+ this.isRendering = fabric.util.requestAnimFrame(this.renderAndResetBound);
+ }
+ return this;
+ },
+ calcViewportBoundaries: function() {
+ 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);
+ points.tr = new fabric.Point(points.br.x, points.tl.y);
+ points.bl = new fabric.Point(points.tl.x, points.br.y);
+ this.vptCoords = points;
+ return points;
+ },
+ 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");
+ if (this.clipTo) {
+ fabric.util.clipContext(this, ctx);
+ }
+ this._renderBackground(ctx);
+ ctx.save();
+ 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) {
+ this.drawControls(ctx);
+ }
+ if (this.clipTo) {
+ ctx.restore();
+ }
+ this._renderOverlay(ctx);
+ if (this.controlsAboveOverlay && this.interactive) {
+ this.drawControls(ctx);
+ }
+ this.fire("after:render");
+ },
+ _renderObjects: function(ctx, objects) {
+ var i, len;
+ for (i = 0, len = objects.length; i < len; ++i) {
+ objects[i] && objects[i].render(ctx);
+ }
+ },
+ _renderBackgroundOrOverlay: function(ctx, property) {
+ var object = this[property + "Color"], v;
+ if (object) {
+ ctx.fillStyle = object.toLive ? object.toLive(ctx, this) : object;
+ ctx.fillRect(object.offsetX || 0, object.offsetY || 0, this.width, this.height);
+ }
+ object = this[property + "Image"];
+ if (object) {
+ if (this[property + "Vpt"]) {
+ v = this.viewportTransform;
+ ctx.save();
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+ object.render(ctx);
+ this[property + "Vpt"] && ctx.restore();
+ }
+ },
+ _renderBackground: function(ctx) {
+ this._renderBackgroundOrOverlay(ctx, "background");
+ },
+ _renderOverlay: function(ctx) {
+ this._renderBackgroundOrOverlay(ctx, "overlay");
+ },
+ getCenter: function() {
+ return {
+ top: this.height / 2,
+ left: this.width / 2
+ };
+ },
+ centerObjectH: function(object) {
+ return this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
+ },
+ centerObjectV: function(object) {
+ return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
+ },
+ centerObject: function(object) {
+ var center = this.getCenter();
+ return this._centerObject(object, new fabric.Point(center.left, center.top));
+ },
+ viewportCenterObject: function(object) {
+ var vpCenter = this.getVpCenter();
+ return this._centerObject(object, vpCenter);
+ },
+ viewportCenterObjectH: function(object) {
+ var vpCenter = this.getVpCenter();
+ this._centerObject(object, new fabric.Point(vpCenter.x, object.getCenterPoint().y));
+ return this;
+ },
+ viewportCenterObjectV: function(object) {
+ var vpCenter = this.getVpCenter();
+ return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, vpCenter.y));
+ },
+ getVpCenter: function() {
+ var center = this.getCenter(), iVpt = invertTransform(this.viewportTransform);
+ return transformPoint({
+ x: center.left,
+ y: center.top
+ }, iVpt);
+ },
+ _centerObject: function(object, center) {
+ object.setPositionByOrigin(center, "center", "center");
+ object.setCoords();
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ toDatalessJSON: function(propertiesToInclude) {
+ return this.toDatalessObject(propertiesToInclude);
+ },
+ toObject: function(propertiesToInclude) {
+ return this._toObjectMethod("toObject", propertiesToInclude);
+ },
+ toDatalessObject: function(propertiesToInclude) {
+ return this._toObjectMethod("toDatalessObject", propertiesToInclude);
+ },
+ _toObjectMethod: function(methodName, propertiesToInclude) {
+ var data = {
+ version: fabric.version,
+ objects: this._toObjects(methodName, propertiesToInclude)
+ };
+ extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
+ fabric.util.populateWithProperties(this, data, propertiesToInclude);
+ return data;
+ },
+ _toObjects: function(methodName, propertiesToInclude) {
+ return this.getObjects().filter(function(object) {
+ return !object.excludeFromExport;
+ }).map(function(instance) {
+ return this._toObject(instance, methodName, propertiesToInclude);
+ }, this);
+ },
+ _toObject: function(instance, methodName, propertiesToInclude) {
+ var originalValue;
+ if (!this.includeDefaultValues) {
+ originalValue = instance.includeDefaultValues;
+ instance.includeDefaultValues = false;
+ }
+ var object = instance[methodName](propertiesToInclude);
+ if (!this.includeDefaultValues) {
+ instance.includeDefaultValues = originalValue;
+ }
+ return object;
+ },
+ __serializeBgOverlay: function(methodName, propertiesToInclude) {
+ var data = {}, bgImage = this.backgroundImage, overlay = this.overlayImage;
+ if (this.backgroundColor) {
+ data.background = this.backgroundColor.toObject ? this.backgroundColor.toObject(propertiesToInclude) : this.backgroundColor;
+ }
+ if (this.overlayColor) {
+ data.overlay = this.overlayColor.toObject ? this.overlayColor.toObject(propertiesToInclude) : this.overlayColor;
+ }
+ if (bgImage && !bgImage.excludeFromExport) {
+ data.backgroundImage = this._toObject(bgImage, methodName, propertiesToInclude);
+ }
+ if (overlay && !overlay.excludeFromExport) {
+ data.overlayImage = this._toObject(overlay, methodName, propertiesToInclude);
+ }
+ return data;
+ },
+ svgViewportTransformation: true,
+ toSVG: function(options, reviver) {
+ options || (options = {});
+ var markup = [];
+ this._setSVGPreamble(markup, options);
+ this._setSVGHeader(markup, options);
+ this._setSVGBgOverlayColor(markup, "backgroundColor");
+ this._setSVGBgOverlayImage(markup, "backgroundImage", reviver);
+ this._setSVGObjects(markup, reviver);
+ this._setSVGBgOverlayColor(markup, "overlayColor");
+ this._setSVGBgOverlayImage(markup, "overlayImage", reviver);
+ markup.push("</svg>");
+ return markup.join("");
+ },
+ _setSVGPreamble: function(markup, options) {
+ if (options.suppressPreamble) {
+ return;
+ }
+ 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');
+ },
+ _setSVGHeader: function(markup, options) {
+ var width = options.width || this.width, height = options.height || this.height, vpt, viewBox = 'viewBox="0 0 ' + this.width + " " + this.height + '" ', NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
+ if (options.viewBox) {
+ viewBox = 'viewBox="' + options.viewBox.x + " " + options.viewBox.y + " " + options.viewBox.width + " " + options.viewBox.height + '" ';
+ } else {
+ if (this.svgViewportTransformation) {
+ vpt = this.viewportTransform;
+ viewBox = 'viewBox="' + toFixed(-vpt[4] / vpt[0], NUM_FRACTION_DIGITS) + " " + toFixed(-vpt[5] / vpt[3], NUM_FRACTION_DIGITS) + " " + toFixed(this.width / vpt[0], NUM_FRACTION_DIGITS) + " " + toFixed(this.height / vpt[3], NUM_FRACTION_DIGITS) + '" ';
+ }
+ }
+ 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', "<desc>Created with Fabric.js ", fabric.version, "</desc>\n", "<defs>\n", this.createSVGFontFacesMarkup(), this.createSVGRefElementsMarkup(), "</defs>\n");
+ },
+ createSVGRefElementsMarkup: function() {
+ var _this = this, markup = [ "backgroundColor", "overlayColor" ].map(function(prop) {
+ var fill = _this[prop];
+ if (fill && fill.toLive) {
+ return fill.toSVG(_this, false);
+ }
+ });
+ return markup.join("");
+ },
+ createSVGFontFacesMarkup: function() {
+ var markup = "", fontList = {}, obj, fontFamily, style, row, rowIndex, _char, charIndex, i, len, fontPaths = fabric.fontPaths, objects = this.getObjects();
+ 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]) {
+ continue;
+ }
+ fontList[fontFamily] = true;
+ if (!obj.styles) {
+ continue;
+ }
+ style = obj.styles;
+ for (rowIndex in style) {
+ row = style[rowIndex];
+ for (charIndex in row) {
+ _char = row[charIndex];
+ fontFamily = _char.fontFamily;
+ if (!fontList[fontFamily] && fontPaths[fontFamily]) {
+ fontList[fontFamily] = true;
+ }
+ }
+ }
+ }
+ for (var j in fontList) {
+ markup += [ "\t\t@font-face {\n", "\t\t\tfont-family: '", j, "';\n", "\t\t\tsrc: url('", fontPaths[j], "');\n", "\t\t}\n" ].join("");
+ }
+ if (markup) {
+ markup = [ '\t<style type="text/css">', "<![CDATA[\n", markup, "]]>", "</style>\n" ].join("");
+ }
+ return markup;
+ },
+ _setSVGObjects: function(markup, reviver) {
+ var instance, i, len, objects = this.getObjects();
+ for (i = 0, len = objects.length; i < len; i++) {
+ instance = objects[i];
+ if (instance.excludeFromExport) {
+ continue;
+ }
+ this._setSVGObject(markup, instance, reviver);
+ }
+ },
+ _setSVGObject: function(markup, instance, reviver) {
+ markup.push(instance.toSVG(reviver));
+ },
+ _setSVGBgOverlayImage: function(markup, property, reviver) {
+ if (this[property] && this[property].toSVG) {
+ markup.push(this[property].toSVG(reviver));
+ }
+ },
+ _setSVGBgOverlayColor: function(markup, property) {
+ var filler = this[property], vpt = this.viewportTransform, finalWidth = this.width / vpt[0], finalHeight = this.height / vpt[3];
+ if (!filler) {
+ return;
+ }
+ if (filler.toLive) {
+ var repeat = filler.repeat;
+ markup.push('<rect transform="translate(', finalWidth / 2, ",", finalHeight / 2, ')"', ' x="', filler.offsetX - finalWidth / 2, '" y="', filler.offsetY - finalHeight / 2, '" ', 'width="', repeat === "repeat-y" || repeat === "no-repeat" ? filler.source.width : finalWidth, '" height="', repeat === "repeat-x" || repeat === "no-repeat" ? filler.source.height : finalHeight, '" fill="url(#SVGID_' + filler.id + ')"', "></rect>\n");
+ } else {
+ markup.push('<rect x="0" y="0" width="100%" height="100%" ', 'fill="', this[property], '"', "></rect>\n");
+ }
+ },
+ sendToBack: function(object) {
+ if (!object) {
+ return this;
+ }
+ var activeSelection = this._activeObject, i, obj, objs;
+ if (object === activeSelection && object.type === "activeSelection") {
+ objs = activeSelection._objects;
+ for (i = objs.length; i--; ) {
+ obj = objs[i];
+ removeFromArray(this._objects, obj);
+ this._objects.unshift(obj);
+ }
+ } else {
+ removeFromArray(this._objects, object);
+ this._objects.unshift(object);
+ }
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ bringToFront: function(object) {
+ if (!object) {
+ return this;
+ }
+ var activeSelection = this._activeObject, i, obj, objs;
+ if (object === activeSelection && object.type === "activeSelection") {
+ objs = activeSelection._objects;
+ for (i = 0; i < objs.length; i++) {
+ obj = objs[i];
+ removeFromArray(this._objects, obj);
+ this._objects.push(obj);
+ }
+ } else {
+ removeFromArray(this._objects, object);
+ this._objects.push(object);
+ }
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ sendBackwards: function(object, intersecting) {
+ if (!object) {
+ return this;
+ }
+ var activeSelection = this._activeObject, i, obj, idx, newIdx, objs, objsMoved = 0;
+ if (object === activeSelection && object.type === "activeSelection") {
+ objs = activeSelection._objects;
+ for (i = 0; i < objs.length; i++) {
+ obj = objs[i];
+ idx = this._objects.indexOf(obj);
+ if (idx > 0 + objsMoved) {
+ newIdx = idx - 1;
+ removeFromArray(this._objects, obj);
+ this._objects.splice(newIdx, 0, obj);
+ }
+ objsMoved++;
+ }
+ } else {
+ idx = this._objects.indexOf(object);
+ if (idx !== 0) {
+ newIdx = this._findNewLowerIndex(object, idx, intersecting);
+ removeFromArray(this._objects, object);
+ this._objects.splice(newIdx, 0, object);
+ }
+ }
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ _findNewLowerIndex: function(object, idx, intersecting) {
+ var newIdx, i;
+ if (intersecting) {
+ newIdx = idx;
+ for (i = idx - 1; i >= 0; --i) {
+ var isIntersecting = object.intersectsWithObject(this._objects[i]) || object.isContainedWithinObject(this._objects[i]) || this._objects[i].isContainedWithinObject(object);
+ if (isIntersecting) {
+ newIdx = i;
+ break;
+ }
+ }
+ } else {
+ newIdx = idx - 1;
+ }
+ return newIdx;
+ },
+ bringForward: function(object, intersecting) {
+ if (!object) {
+ return this;
+ }
+ var activeSelection = this._activeObject, i, obj, idx, newIdx, objs, objsMoved = 0;
+ if (object === activeSelection && object.type === "activeSelection") {
+ objs = activeSelection._objects;
+ for (i = objs.length; i--; ) {
+ obj = objs[i];
+ idx = this._objects.indexOf(obj);
+ if (idx < this._objects.length - 1 - objsMoved) {
+ newIdx = idx + 1;
+ removeFromArray(this._objects, obj);
+ this._objects.splice(newIdx, 0, obj);
+ }
+ objsMoved++;
+ }
+ } else {
+ idx = this._objects.indexOf(object);
+ if (idx !== this._objects.length - 1) {
+ newIdx = this._findNewUpperIndex(object, idx, intersecting);
+ removeFromArray(this._objects, object);
+ this._objects.splice(newIdx, 0, object);
+ }
+ }
+ this.renderOnAddRemove && this.requestRenderAll();
+ return this;
+ },
+ _findNewUpperIndex: function(object, idx, intersecting) {
+ var newIdx, i, len;
+ if (intersecting) {
+ newIdx = idx;
+ for (i = idx + 1, len = this._objects.length; i < len; ++i) {
+ var isIntersecting = object.intersectsWithObject(this._objects[i]) || object.isContainedWithinObject(this._objects[i]) || this._objects[i].isContainedWithinObject(object);
+ if (isIntersecting) {
+ newIdx = i;
+ break;
+ }
+ }
+ } else {
+ newIdx = idx + 1;
+ }
+ return newIdx;
+ },
+ moveTo: function(object, index) {
+ removeFromArray(this._objects, object);
+ this._objects.splice(index, 0, object);
+ return this.renderOnAddRemove && this.requestRenderAll();
+ },
+ dispose: function() {
+ if (this.isRendering) {
+ fabric.util.cancelAnimFrame(this.isRendering);
+ this.isRendering = 0;
+ }
+ this.forEachObject(function(object) {
+ object.dispose && object.dispose();
+ });
+ this._objects = [];
+ this.backgroundImage = null;
+ this.overlayImage = null;
+ this._iTextInstances = null;
+ this.lowerCanvasEl = null;
+ this.contextContainer = null;
+ return this;
+ },
+ toString: function() {
+ return "#<fabric.Canvas (" + this.complexity() + "): " + "{ objects: " + this.getObjects().length + " }>";
+ }
+ });
+ extend(fabric.StaticCanvas.prototype, fabric.Observable);
+ extend(fabric.StaticCanvas.prototype, fabric.Collection);
+ extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter);
+ extend(fabric.StaticCanvas, {
+ EMPTY_JSON: '{"objects": [], "background": "white"}',
+ supports: function(methodName) {
+ var el = fabric.util.createCanvasElement();
+ if (!el || !el.getContext) {
+ return null;
+ }
+ var ctx = el.getContext("2d");
+ if (!ctx) {
+ return null;
+ }
+ switch (methodName) {
+ case "getImageData":
+ return typeof ctx.getImageData !== "undefined";
- /**
- * Returns true if this point is greater than or equal to another one
- * @param {fabric.Point} that
- * @return {Boolean}
- */
- gte: function (that) {
- return (this.x >= that.x && this.y >= that.y);
- },
+ case "setLineDash":
+ return typeof ctx.setLineDash !== "undefined";
- /**
- * Returns new point which is the result of linear interpolation with this one and another one
- * @param {fabric.Point} that
- * @param {Number} t , position of interpolation, between 0 and 1 default 0.5
- * @return {fabric.Point}
- */
- lerp: function (that, t) {
- if (typeof t === 'undefined') {
- t = 0.5;
- }
- t = Math.max(Math.min(1, t), 0);
- return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
- },
+ case "toDataURL":
+ return typeof el.toDataURL !== "undefined";
- /**
- * Returns distance from this point and another one
- * @param {fabric.Point} that
- * @return {Number}
- */
- distanceFrom: function (that) {
- var dx = this.x - that.x,
- dy = this.y - that.y;
- return Math.sqrt(dx * dx + dy * dy);
- },
+ case "toDataURLWithQuality":
+ try {
+ el.toDataURL("image/jpeg", 0);
+ return true;
+ } catch (e) {}
+ return false;
- /**
- * Returns the point between this point and another one
- * @param {fabric.Point} that
- * @return {fabric.Point}
- */
- midPointFrom: function (that) {
- return this.lerp(that);
- },
+ default:
+ return null;
+ }
+ }
+ });
+ fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject;
+ if (fabric.isLikelyNode) {
+ fabric.StaticCanvas.prototype.createPNGStream = function() {
+ var impl = fabric.util.getNodeCanvas(this.lowerCanvasEl);
+ return impl && impl.createPNGStream();
+ };
+ fabric.StaticCanvas.prototype.createJPEGStream = function(opts) {
+ var impl = fabric.util.getNodeCanvas(this.lowerCanvasEl);
+ return impl && impl.createJPEGStream(opts);
+ };
+ }
+})();
- /**
- * Returns a new point which is the min of this and another one
- * @param {fabric.Point} that
- * @return {fabric.Point}
- */
- min: function (that) {
- return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
+fabric.BaseBrush = fabric.util.createClass({
+ color: "rgb(0, 0, 0)",
+ width: 1,
+ shadow: null,
+ strokeLineCap: "round",
+ strokeLineJoin: "round",
+ strokeMiterLimit: 10,
+ strokeDashArray: null,
+ setShadow: function(options) {
+ this.shadow = new fabric.Shadow(options);
+ return this;
},
-
- /**
- * Returns a new point which is the max of this and another one
- * @param {fabric.Point} that
- * @return {fabric.Point}
- */
- max: function (that) {
- return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
+ _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);
+ }
},
-
- /**
- * Returns string representation of this point
- * @return {String}
- */
- toString: function () {
- return this.x + ',' + this.y;
+ _saveAndTransform: function(ctx) {
+ var v = this.canvas.viewportTransform;
+ ctx.save();
+ ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
},
-
- /**
- * Sets x/y of this point
- * @param {Number} x
- * @param {Number} y
- * @chainable
- */
- setXY: function (x, y) {
- this.x = x;
- this.y = y;
- return this;
+ _setShadow: function() {
+ if (!this.shadow) {
+ return;
+ }
+ var ctx = this.canvas.contextTop, zoom = this.canvas.getZoom();
+ ctx.shadowColor = this.shadow.color;
+ ctx.shadowBlur = this.shadow.blur * zoom;
+ ctx.shadowOffsetX = this.shadow.offsetX * zoom;
+ ctx.shadowOffsetY = this.shadow.offsetY * zoom;
},
+ _resetShadow: function() {
+ var ctx = this.canvas.contextTop;
+ ctx.shadowColor = "";
+ ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
+ }
+});
- /**
- * Sets x of this point
- * @param {Number} x
- * @chainable
- */
- setX: function (x) {
- this.x = x;
- return this;
- },
+(function() {
+ fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, {
+ initialize: function(canvas) {
+ this.canvas = canvas;
+ this._points = [];
+ },
+ _drawSegment: function(ctx, p1, p2) {
+ var midPoint = p1.midPointFrom(p2);
+ ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
+ return midPoint;
+ },
+ onMouseDown: function(pointer) {
+ this._prepareForDrawing(pointer);
+ this._captureDrawingPath(pointer);
+ this._render();
+ },
+ onMouseMove: function(pointer) {
+ if (this._captureDrawingPath(pointer) && this._points.length > 1) {
+ var points = this._points, length = points.length, ctx = this.canvas.contextTop;
+ this._saveAndTransform(ctx);
+ if (this.oldEnd) {
+ ctx.beginPath();
+ ctx.moveTo(this.oldEnd.x, this.oldEnd.y);
+ }
+ this.oldEnd = this._drawSegment(ctx, points[length - 2], points[length - 1], true);
+ ctx.stroke();
+ ctx.restore();
+ }
+ },
+ onMouseUp: function() {
+ this.oldEnd = undefined;
+ this._finalizeAndAddPath();
+ },
+ _prepareForDrawing: function(pointer) {
+ var p = new fabric.Point(pointer.x, pointer.y);
+ this._reset();
+ this._addPoint(p);
+ this.canvas.contextTop.moveTo(p.x, p.y);
+ },
+ _addPoint: function(point) {
+ if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) {
+ return false;
+ }
+ this._points.push(point);
+ return true;
+ },
+ _reset: function() {
+ this._points.length = 0;
+ this._setBrushStyles();
+ this._setShadow();
+ },
+ _captureDrawingPath: function(pointer) {
+ var pointerPoint = new fabric.Point(pointer.x, pointer.y);
+ return this._addPoint(pointerPoint);
+ },
+ _render: function() {
+ var ctx = this.canvas.contextTop, i, len, p1 = this._points[0], p2 = this._points[1];
+ this._saveAndTransform(ctx);
+ ctx.beginPath();
+ if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
+ var width = this.width / 1e3;
+ p1 = new fabric.Point(p1.x, p1.y);
+ p2 = new fabric.Point(p2.x, p2.y);
+ p1.x -= width;
+ p2.x += width;
+ }
+ ctx.moveTo(p1.x, p1.y);
+ for (i = 1, len = this._points.length; i < len; i++) {
+ this._drawSegment(ctx, p1, p2);
+ p1 = this._points[i];
+ p2 = this._points[i + 1];
+ }
+ ctx.lineTo(p1.x, p1.y);
+ ctx.stroke();
+ ctx.restore();
+ },
+ convertPointsToSVGPath: function(points) {
+ var path = [], i, width = this.width / 1e3, p1 = new fabric.Point(points[0].x, points[0].y), p2 = new fabric.Point(points[1].x, points[1].y), len = points.length, multSignX = 1, multSignY = 1, manyPoints = len > 2;
+ 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);
+ path.push("Q ", p1.x, " ", p1.y, " ", midPoint.x, " ", midPoint.y, " ");
+ }
+ p1 = points[i];
+ if (i + 1 < points.length) {
+ p2 = points[i + 1];
+ }
+ }
+ 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;
+ },
+ createPath: function(pathData) {
+ var path = new fabric.Path(pathData, {
+ fill: null,
+ stroke: this.color,
+ strokeWidth: this.width,
+ strokeLineCap: this.strokeLineCap,
+ strokeMiterLimit: this.strokeMiterLimit,
+ strokeLineJoin: this.strokeLineJoin,
+ strokeDashArray: this.strokeDashArray
+ });
+ var position = new fabric.Point(path.left + path.width / 2, path.top + path.height / 2);
+ position = path.translateToGivenOrigin(position, "center", "center", path.originX, path.originY);
+ path.top = position.y;
+ path.left = position.x;
+ if (this.shadow) {
+ this.shadow.affectStroke = true;
+ path.setShadow(this.shadow);
+ }
+ return path;
+ },
+ _finalizeAndAddPath: function() {
+ var ctx = this.canvas.contextTop;
+ ctx.closePath();
+ var pathData = this.convertPointsToSVGPath(this._points).join("");
+ if (pathData === "M 0 0 Q 0 0 0 0 L 0 0") {
+ 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._resetShadow();
+ this.canvas.fire("path:created", {
+ path: path
+ });
+ }
+ });
+})();
- /**
- * Sets y of this point
- * @param {Number} y
- * @chainable
- */
- setY: function (y) {
- this.y = y;
- return this;
+fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, {
+ width: 10,
+ initialize: function(canvas) {
+ this.canvas = canvas;
+ this.points = [];
},
-
- /**
- * Sets x/y of this point from another point
- * @param {fabric.Point} that
- * @chainable
- */
- setFromPoint: function (that) {
- this.x = that.x;
- this.y = that.y;
- return this;
+ drawDot: function(pointer) {
+ var point = this.addPoint(pointer), ctx = this.canvas.contextTop;
+ this._saveAndTransform(ctx);
+ ctx.fillStyle = point.fill;
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
+ ctx.closePath();
+ ctx.fill();
+ ctx.restore();
},
-
- /**
- * Swaps x/y of this point and another point
- * @param {fabric.Point} that
- */
- swap: function (that) {
- var x = this.x,
- y = this.y;
- this.x = that.x;
- this.y = that.y;
- that.x = x;
- that.y = y;
+ onMouseDown: function(pointer) {
+ this.points.length = 0;
+ this.canvas.clearContext(this.canvas.contextTop);
+ this._setShadow();
+ this.drawDot(pointer);
},
-
- /**
- * return a cloned instance of the point
- * @return {fabric.Point}
- */
- clone: function () {
- return new Point(this.x, this.y);
- }
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
- 'use strict';
-
- /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Intersection) {
- fabric.warn('fabric.Intersection is already defined');
- return;
- }
-
- /**
- * Intersection class
- * @class fabric.Intersection
- * @memberOf fabric
- * @constructor
- */
- function Intersection(status) {
- this.status = status;
- this.points = [];
- }
-
- fabric.Intersection = Intersection;
-
- fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
-
- constructor: Intersection,
-
- /**
- * Appends a point to intersection
- * @param {fabric.Point} point
- * @return {fabric.Intersection} thisArg
- * @chainable
- */
- appendPoint: function (point) {
- this.points.push(point);
- return this;
+ _render: function() {
+ var ctx = this.canvas.contextTop, i, len, points = this.points, point;
+ this._saveAndTransform(ctx);
+ for (i = 0, len = points.length; i < len; i++) {
+ point = points[i];
+ ctx.fillStyle = point.fill;
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
+ ctx.closePath();
+ ctx.fill();
+ }
+ ctx.restore();
},
-
- /**
- * Appends points to intersection
- * @param {Array} points
- * @return {fabric.Intersection} thisArg
- * @chainable
- */
- appendPoints: function (points) {
- this.points = this.points.concat(points);
- return this;
- }
- };
-
- /**
- * Checks if one line intersects another
- * TODO: rename in intersectSegmentSegment
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {fabric.Point} b1
- * @param {fabric.Point} b2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
- var result,
- uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
- ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
- uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
- if (uB !== 0) {
- var ua = uaT / uB,
- ub = ubT / uB;
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- result = new Intersection('Intersection');
- result.appendPoint(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
- }
- else {
- result = new Intersection();
- }
- }
- else {
- if (uaT === 0 || ubT === 0) {
- result = new Intersection('Coincident');
- }
- else {
- result = new Intersection('Parallel');
- }
+ onMouseMove: function(pointer) {
+ this.drawDot(pointer);
+ },
+ onMouseUp: function() {
+ var originalRenderOnAddRemove = this.canvas.renderOnAddRemove, i, len;
+ this.canvas.renderOnAddRemove = false;
+ var circles = [];
+ for (i = 0, len = this.points.length; i < len; i++) {
+ var point = this.points[i], circle = new fabric.Circle({
+ radius: point.radius,
+ left: point.x,
+ top: point.y,
+ originX: "center",
+ originY: "center",
+ fill: point.fill
+ });
+ this.shadow && circle.setShadow(this.shadow);
+ circles.push(circle);
+ }
+ var group = new fabric.Group(circles, {
+ originX: "center",
+ originY: "center"
+ });
+ group.canvas = this.canvas;
+ this.canvas.add(group);
+ this.canvas.fire("path:created", {
+ path: group
+ });
+ this.canvas.clearContext(this.canvas.contextTop);
+ this._resetShadow();
+ this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
+ this.canvas.requestRenderAll();
+ },
+ addPoint: function(pointer) {
+ var pointerPoint = new fabric.Point(pointer.x, pointer.y), circleRadius = fabric.util.getRandomInt(Math.max(0, this.width - 20), this.width + 20) / 2, circleColor = new fabric.Color(this.color).setAlpha(fabric.util.getRandomInt(0, 100) / 100).toRgba();
+ pointerPoint.radius = circleRadius;
+ pointerPoint.fill = circleColor;
+ this.points.push(pointerPoint);
+ return pointerPoint;
}
- return result;
- };
-
- /**
- * Checks if line intersects polygon
- * TODO: rename in intersectSegmentPolygon
- * fix detection of coincident
- * @static
- * @param {fabric.Point} a1
- * @param {fabric.Point} a2
- * @param {Array} points
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectLinePolygon = function(a1, a2, points) {
- var result = new Intersection(),
- length = points.length,
- b1, b2, inter, i;
-
- for (i = 0; i < length; i++) {
- b1 = points[i];
- b2 = points[(i + 1) % length];
- inter = Intersection.intersectLineLine(a1, a2, b1, b2);
+});
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = 'Intersection';
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects another polygon
- * @static
- * @param {Array} points1
- * @param {Array} points2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
- var result = new Intersection(),
- length = points1.length, i;
-
- for (i = 0; i < length; i++) {
- var a1 = points1[i],
- a2 = points1[(i + 1) % length],
- inter = Intersection.intersectLinePolygon(a1, a2, points2);
-
- result.appendPoints(inter.points);
- }
- if (result.points.length > 0) {
- result.status = 'Intersection';
- }
- return result;
- };
-
- /**
- * Checks if polygon intersects rectangle
- * @static
- * @param {Array} points
- * @param {fabric.Point} r1
- * @param {fabric.Point} r2
- * @return {fabric.Intersection}
- */
- fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
- var min = r1.min(r2),
- max = r1.max(r2),
- topRight = new fabric.Point(max.x, min.y),
- bottomLeft = new fabric.Point(min.x, max.y),
- inter1 = Intersection.intersectLinePolygon(min, topRight, points),
- inter2 = Intersection.intersectLinePolygon(topRight, max, points),
- inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
- inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
- result = new Intersection();
-
- result.appendPoints(inter1.points);
- result.appendPoints(inter2.points);
- result.appendPoints(inter3.points);
- result.appendPoints(inter4.points);
-
- if (result.points.length > 0) {
- result.status = 'Intersection';
- }
- return result;
- };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
- 'use strict';
-
- var fabric = global.fabric || (global.fabric = { });
-
- if (fabric.Color) {
- fabric.warn('fabric.Color is already defined.');
- return;
- }
-
- /**
- * Color class
- * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
- * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
- *
- * @class fabric.Color
- * @param {String} color optional in hex or rgb(a) or hsl format or from known color list
- * @return {fabric.Color} thisArg
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
- */
- function Color(color) {
- if (!color) {
- this.setSource([0, 0, 0, 1]);
- }
- else {
- this._tryParsingColor(color);
- }
- }
-
- fabric.Color = Color;
-
- fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
-
- /**
- * @private
- * @param {String|Array} color Color value to parse
- */
- _tryParsingColor: function(color) {
- var source;
-
- if (color in Color.colorNameMap) {
- color = Color.colorNameMap[color];
- }
-
- if (color === 'transparent') {
- source = [255, 255, 255, 0];
- }
-
- if (!source) {
- source = Color.sourceFromHex(color);
- }
- if (!source) {
- source = Color.sourceFromRgb(color);
- }
- if (!source) {
- source = Color.sourceFromHsl(color);
- }
- if (!source) {
- //if color is not recognize let's make black as canvas does
- source = [0, 0, 0, 1];
- }
- if (source) {
- this.setSource(source);
- }
- },
-
- /**
- * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
- * @private
- * @param {Number} r Red color value
- * @param {Number} g Green color value
- * @param {Number} b Blue color value
- * @return {Array} Hsl color
- */
- _rgbToHsl: function(r, g, b) {
- r /= 255; g /= 255; b /= 255;
-
- var h, s, l,
- max = fabric.util.array.max([r, g, b]),
- min = fabric.util.array.min([r, g, b]);
-
- l = (max + min) / 2;
-
- if (max === min) {
- h = s = 0; // achromatic
- }
- else {
- var d = max - min;
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
- switch (max) {
- case r:
- h = (g - b) / d + (g < b ? 6 : 0);
- break;
- case g:
- h = (b - r) / d + 2;
- break;
- case b:
- h = (r - g) / d + 4;
- break;
- }
- h /= 6;
- }
-
- return [
- Math.round(h * 360),
- Math.round(s * 100),
- Math.round(l * 100)
- ];
- },
-
- /**
- * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
- * @return {Array}
- */
- getSource: function() {
- return this._source;
- },
-
- /**
- * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
- * @param {Array} source
- */
- setSource: function(source) {
- this._source = source;
- },
-
- /**
- * Returns color representation in RGB format
- * @return {String} ex: rgb(0-255,0-255,0-255)
- */
- toRgb: function() {
- var source = this.getSource();
- return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
- },
-
- /**
- * Returns color representation in RGBA format
- * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
- */
- toRgba: function() {
- var source = this.getSource();
- return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
- },
-
- /**
- * Returns color representation in HSL format
- * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
- */
- toHsl: function() {
- var source = this.getSource(),
- hsl = this._rgbToHsl(source[0], source[1], source[2]);
-
- return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
- },
-
- /**
- * Returns color representation in HSLA format
- * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
- */
- toHsla: function() {
- var source = this.getSource(),
- hsl = this._rgbToHsl(source[0], source[1], source[2]);
-
- return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
- },
-
- /**
- * Returns color representation in HEX format
- * @return {String} ex: FF5555
- */
- toHex: function() {
- var source = this.getSource(), r, g, b;
-
- r = source[0].toString(16);
- r = (r.length === 1) ? ('0' + r) : r;
-
- g = source[1].toString(16);
- g = (g.length === 1) ? ('0' + g) : g;
-
- b = source[2].toString(16);
- b = (b.length === 1) ? ('0' + b) : b;
-
- return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
- },
-
- /**
- * Returns color representation in HEXA format
- * @return {String} ex: FF5555CC
- */
- toHexa: function() {
- var source = this.getSource(), a;
-
- a = Math.round(source[3] * 255);
- a = a.toString(16);
- a = (a.length === 1) ? ('0' + a) : a;
-
- return this.toHex() + a.toUpperCase();
- },
-
- /**
- * Gets value of alpha channel for this color
- * @return {Number} 0-1
- */
- getAlpha: function() {
- return this.getSource()[3];
- },
-
- /**
- * Sets value of alpha channel for this color
- * @param {Number} alpha Alpha value 0-1
- * @return {fabric.Color} thisArg
- */
- setAlpha: function(alpha) {
- var source = this.getSource();
- source[3] = alpha;
- this.setSource(source);
- return this;
+fabric.SprayBrush = fabric.util.createClass(fabric.BaseBrush, {
+ width: 10,
+ density: 20,
+ dotWidth: 1,
+ dotWidthVariance: 1,
+ randomOpacity: false,
+ optimizeOverlapping: true,
+ initialize: function(canvas) {
+ this.canvas = canvas;
+ this.sprayChunks = [];
},
-
- /**
- * Transforms color to its grayscale representation
- * @return {fabric.Color} thisArg
- */
- toGrayscale: function() {
- var source = this.getSource(),
- average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
- currentAlpha = source[3];
- this.setSource([average, average, average, currentAlpha]);
- return this;
+ onMouseDown: function(pointer) {
+ this.sprayChunks.length = 0;
+ this.canvas.clearContext(this.canvas.contextTop);
+ this._setShadow();
+ this.addSprayChunk(pointer);
+ this.render(this.sprayChunkPoints);
},
-
- /**
- * Transforms color to its black and white representation
- * @param {Number} threshold
- * @return {fabric.Color} thisArg
- */
- toBlackWhite: function(threshold) {
- var source = this.getSource(),
- average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
- currentAlpha = source[3];
-
- threshold = threshold || 127;
-
- average = (Number(average) < Number(threshold)) ? 0 : 255;
- this.setSource([average, average, average, currentAlpha]);
- return this;
+ onMouseMove: function(pointer) {
+ this.addSprayChunk(pointer);
+ this.render(this.sprayChunkPoints);
},
-
- /**
- * Overlays color with another color
- * @param {String|fabric.Color} otherColor
- * @return {fabric.Color} thisArg
- */
- overlayWith: function(otherColor) {
- if (!(otherColor instanceof Color)) {
- otherColor = new Color(otherColor);
- }
-
- var result = [],
- alpha = this.getAlpha(),
- otherAlpha = 0.5,
- source = this.getSource(),
- otherSource = otherColor.getSource(), i;
-
- for (i = 0; i < 3; i++) {
- result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
- }
-
- result[3] = alpha;
- this.setSource(result);
- return this;
- }
- };
-
- /**
- * Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgba(255, 100, 10, 0.5), rgba( 255 , 100 , 10 , 0.5 ), rgb(1,1,1), rgba(100%, 60%, 10%, 0.5))
- * @static
- * @field
- * @memberOf fabric.Color
- */
- // 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*)?\)$/;
-
- /**
- * Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
- * @static
- * @field
- * @memberOf fabric.Color
- */
- fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
-
- /**
- * Regex matching color in HEX format (ex: #FF5544CC, #FF5555, 010155, aff)
- * @static
- * @field
- * @memberOf fabric.Color
- */
- fabric.Color.reHex = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
-
- /**
- * Map of the 148 color names with HEX code
- * @static
- * @field
- * @memberOf fabric.Color
- * @see: https://www.w3.org/TR/css3-color/#svg-color
- */
- fabric.Color.colorNameMap = {
- 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'
- };
-
- /**
- * @private
- * @param {Number} p
- * @param {Number} q
- * @param {Number} t
- * @return {Number}
- */
- function hue2rgb(p, q, t) {
- if (t < 0) {
- t += 1;
- }
- if (t > 1) {
- t -= 1;
- }
- if (t < 1 / 6) {
- return p + (q - p) * 6 * t;
- }
- if (t < 1 / 2) {
- return q;
- }
- if (t < 2 / 3) {
- return p + (q - p) * (2 / 3 - t) * 6;
- }
- return p;
- }
-
- /**
- * Returns new color object, when given a color in RGB format
- * @memberOf fabric.Color
- * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
- * @return {fabric.Color}
- */
- fabric.Color.fromRgb = function(color) {
- return Color.fromSource(Color.sourceFromRgb(color));
- };
-
- /**
- * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
- * @memberOf fabric.Color
- * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
- * @return {Array} source
- */
- fabric.Color.sourceFromRgb = function(color) {
- var match = color.match(Color.reRGBa);
- if (match) {
- var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
- g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
- b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
-
- return [
- parseInt(r, 10),
- parseInt(g, 10),
- parseInt(b, 10),
... 32178 lines suppressed ...
--
To stop receiving notification emails like this one, please contact
solomax@apache.org.