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:15:57 UTC

[openmeetings] branch 4.0.x updated: [OPENMEETINGS-1848] libraries are updated

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch 4.0.x
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/4.0.x by this push:
     new aa23462  [OPENMEETINGS-1848] libraries are updated
aa23462 is described below

commit aa2346251c65c8252b9ff6b6399c5b3c52760199
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                          |     6 +
 .../org/apache/openmeetings/web/room/wb/MathJax.js |    88 +-
 .../org/apache/openmeetings/web/room/wb/fabric.js  | 41330 ++++++-------------
 pom.xml                                            |    20 +-
 7 files changed, 13647 insertions(+), 27805 deletions(-)

diff --git a/openmeetings-core/pom.xml b/openmeetings-core/pom.xml
index 4d37c67..6ba15e0 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 efec32e..79f164b 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 b39ecd6..24c8fc7 100644
--- a/openmeetings-server/pom.xml
+++ b/openmeetings-server/pom.xml
@@ -117,7 +117,7 @@
 			<id>prepare-red5-server</id>
 			<activation>
 				<file>
-					<missing>red5-server/red5-server-1.0.10-M6.tar.gz</missing> <!-- TODO should be in-sync with global property -->
+					<missing>red5-server/red5-server-1.0.10-M7.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 10146b1..f599807 100644
--- a/openmeetings-util/pom.xml
+++ b/openmeetings-util/pom.xml
@@ -128,5 +128,11 @@
 			<artifactId>tika-parsers</artifactId>
 			<version>1.17</version>
 		</dependency>
+		<dependency>
+			<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, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
     }
-
-    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, '&amp;')
-      .replace(/"/g, '&quot;')
-      .replace(/'/g, '&apos;')
-      .replace(/</g, '&lt;')
-      .replace(/>/g, '&gt;');
-  }
-
-  /**
-   * 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);
-
... 32184 lines suppressed ...

-- 
To stop receiving notification emails like this one, please contact
solomax@apache.org.