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/08/07 06:19:28 UTC

[openmeetings] branch master updated: [OPENMEETINGS-1897] fabricjs is updated

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7a46c9b  [OPENMEETINGS-1897] fabricjs is updated
7a46c9b is described below

commit 7a46c9b73269b964f5bbfa2309d9a5816c640849
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Tue Aug 7 13:18:57 2018 +0700

    [OPENMEETINGS-1897] fabricjs is updated
---
 .../org/apache/openmeetings/web/room/wb/fabric.js  | 41929 +++++++++++++------
 1 file changed, 28328 insertions(+), 13601 deletions(-)

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 d60c339..aef061d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/fabric.js
@@ -1,14024 +1,28751 @@
 /* Licensed MIT https://github.com/kangax/fabric.js/blob/master/LICENSE */
+/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
+/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
 
-var fabric = fabric || {
-    version: "2.2.4"
-};
-
-if (typeof exports !== "undefined") {
-    exports.fabric = fabric;
-} else if (typeof define === "function" && define.amd) {
-    define([], function() {
-        return fabric;
-    });
+var fabric = fabric || { version: '2.3.4' };
+if (typeof exports !== 'undefined') {
+  exports.fabric = fabric;
 }
-
-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;
+/* _AMD_START_ */
+else if (typeof define === 'function' && define.amd) {
+  define([], function() { return fabric; });
+}
+/* _AMD_END_ */
+if (typeof document !== 'undefined' && typeof window !== 'undefined') {
+  fabric.document = document;
+  fabric.window = window;
+}
+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;
 }
 
-fabric.isTouchSupported = "ontouchstart" in fabric.window;
+/**
+ * True when in environment that supports touch events
+ * @type boolean
+ */
+fabric.isTouchSupported = 'ontouchstart' in fabric.window;
 
-fabric.isLikelyNode = typeof Buffer !== "undefined" && typeof window === "undefined";
+/**
+ * True when in environment that's probably Node.js
+ * @type boolean
+ */
+fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
+                      typeof window === 'undefined';
 
-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_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_ */
 
+/**
+ * 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';
 
-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.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;
 
-fabric.charWidthsCache = {};
+/**
+ * Cache Object for widths of chars in text rendering.
+ */
+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;
 
-fabric.devicePixelRatio = fabric.window.devicePixelRatio || fabric.window.webkitDevicePixelRatio || fabric.window.mozDevicePixelRatio || 1;
-
+/**
+ * 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.browserShadowBlurConstant = 1;
 
+/**
+ * This object contains the result of arc to beizer conversion for faster retrieving if the same arc needs to be converted again.
+ * It was an internal variable, is accessible since version 2.3.4
+ */
+fabric.arcToSegmentsCache = { };
+
+/**
+ * This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it.
+ * It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing
+ * you do not get any speed benefit and you get a big object in memory.
+ * The object was a private variable before, while now is appended to the lib so that you have access to it and you
+ * can eventually clear it.
+ * It was an internal variable, is accessible since version 2.3.4
+ */
+fabric.boundsOfCurveCache = { };
+
+/**
+ * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better
+ * @default true
+ */
+fabric.cachesBoundsOfCurve = true;
+
 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") {
-    window.fabric = fabric;
+
+if (typeof document !== 'undefined' && typeof window !== 'undefined') {
+  // ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)
+  window.fabric = fabric;
 }
 
+
 (function() {
-    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);
-        }
+
+  /**
+   * @private
+   * @param {String} eventName
+   * @param {Function} handler
+   */
+  function _removeEventListener(eventName, handler) {
+    if (!this.__eventListeners[eventName]) {
+      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 eventListener = this.__eventListeners[eventName];
+    if (handler) {
+      eventListener[eventListener.indexOf(handler)] = false;
     }
-    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;
+    else {
+      fabric.util.array.fill(eventListener, false);
     }
-    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;
+  }
+
+  /**
+   * 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 = { };
     }
-    fabric.Observable = {
-        observe: observe,
-        stopObserving: stopObserving,
-        fire: fire,
-        on: observe,
-        off: stopObserving,
-        trigger: fire
-    };
+    // 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);
+    }
+    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;
+    }
+
+    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;
+  }
+
+  /**
+   * @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
+  };
 })();
 
+
+/**
+ * @namespace fabric.Collection
+ */
 fabric.Collection = {
-    _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]);
-            }
-        }
-        this.renderOnAddRemove && this.requestRenderAll();
-        return this;
+
+  _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,
+      PiBy2 = Math.PI / 2;
+
+  /**
+   * @namespace fabric.util
+   */
+  fabric.util = {
+
+    /**
+     * Calculate the cos of an angle, avoiding returning floats for known results
+     * @static
+     * @memberOf fabric.util
+     * @param {Number} angle the angle in radians or in degree
+     * @return {Number}
+     */
+    cos: function(angle) {
+      if (angle === 0) { return 1; }
+      if (angle < 0) {
+        // cos(a) = cos(-a)
+        angle = -angle;
+      }
+      var angleSlice = angle / PiBy2;
+      switch (angleSlice) {
+        case 1: case 3: return 0;
+        case 2: return -1;
+      }
+      return Math.cos(angle);
     },
-    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;
+
+    /**
+     * Calculate the sin of an angle, avoiding returning floats for known results
+     * @static
+     * @memberOf fabric.util
+     * @param {Number} angle the angle in radians or in degree
+     * @return {Number}
+     */
+    sin: function(angle) {
+      if (angle === 0) { return 0; }
+      var angleSlice = angle / PiBy2, sign = 1;
+      if (angle < 0) {
+        // sin(-a) = -sin(a)
+        sign = -1;
+      }
+      switch (angleSlice) {
+        case 1: return sign;
+        case 2: return 0;
+        case 3: return -sign;
+      }
+      return Math.sin(angle);
     },
-    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;
+
+    /**
+     * 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;
     },
-    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 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;
     },
-    getObjects: function(type) {
-        if (typeof type === "undefined") {
-            return this._objects;
-        }
-        return this._objects.filter(function(o) {
-            return o.type === type;
-        });
+
+    /**
+     * 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;
     },
-    item: function(index) {
-        return this.getObjects()[index];
+
+    /**
+     * 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;
     },
-    isEmpty: function() {
-        return this.getObjects().length === 0;
+
+    /**
+     * 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);
     },
-    size: function() {
-        return this.getObjects().length;
+
+    /**
+     * 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 = 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
+      };
     },
-    contains: function(object) {
-        return this.getObjects().indexOf(object) > -1;
+
+    /**
+     * 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]
+      );
     },
-    complexity: function() {
-        return this.getObjects().reduce(function(memo, current) {
-            memo += current.complexity ? current.complexity() : 0;
-            return memo;
-        }, 0);
-    }
-};
 
-fabric.CommonMethods = {
-    _setOptions: function(options) {
-        for (var prop in options) {
-            this.set(prop, options[prop]);
-        }
+    /**
+     * 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
+      };
     },
-    _initGradient: function(filler, property) {
-        if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
-            this.set(property, new fabric.Gradient(filler));
-        }
+
+    /**
+     * 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;
     },
-    _initPattern: function(filler, property, callback) {
-        if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
-            this.set(property, new fabric.Pattern(filler, callback));
-        } else {
-            callback && callback();
-        }
+
+    /**
+     * 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));
     },
-    _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);
-        }
+
+    /**
+     * 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;
     },
-    _setObject: function(obj) {
-        for (var prop in obj) {
-            this._set(prop, obj[prop]);
+
+    /**
+     * 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);
         }
+      }
+
+      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();
+        });
+      });
     },
-    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);
-            }
+
+    /**
+     * 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);
         }
-        return this;
+      }
+
+      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();
+        }
+      });
     },
-    _set: function(key, value) {
-        this[key] = value;
+
+    /**
+     * 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;
     },
-    toggle: function(property) {
-        var value = this.get(property);
-        if (typeof value === "boolean") {
-            this.set(property, !value);
-        }
-        return this;
+
+    /**
+     * 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]];
+          }
+        }
+      }
     },
-    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, 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;
+    /**
+     * 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;
 
-              case 2:
-                return 0;
+      ctx.save();
+      ctx.translate(x, y);
+      ctx.moveTo(0, 0);
+      ctx.rotate(rot);
 
-              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;
+      x = 0;
+      while (len > x) {
+        x += da[di++ % dc];
+        if (x > len) {
+          x = len;
+        }
+        ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
+        draw = !draw;
+      }
 
-              case "cm":
-                return number * fabric.DPI / 2.54;
+      ctx.restore();
+    },
 
-              case "in":
-                return number * fabric.DPI;
+    /**
+     * Creates canvas element
+     * @static
+     * @memberOf fabric.util
+     * @return {CanvasElement} initialized canvas element
+     */
+    createCanvasElement: function() {
+      return fabric.document.createElement('canvas');
+    },
 
-              case "pt":
-                return number * fabric.DPI / 72;
+    /**
+     * Creates image element (works on client and node)
+     * @static
+     * @memberOf fabric.util
+     * @return {HTMLImageElement} HTML image element
+     */
+    createImage: function() {
+      return fabric.document.createElement('img');
+    },
 
-              case "pc":
-                return number * fabric.DPI / 72 * 12;
+    /**
+     * @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();
+    },
 
-              case "em":
-                return number * fontSize;
+    /**
+     * 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]
+      ];
+    },
 
-              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);
+    /**
+     * 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]
+      };
+    },
 
-(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]);
+    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);
+    },
+
+    /**
+     * reset an object transform state to neutral. Top and left are not accounted for
+     * @static
+     * @memberOf fabric.util
+     * @param  {fabric.Object} target object to transform
+     */
+    resetObjectTransform: function (target) {
+      target.scaleX = 1;
+      target.scaleY = 1;
+      target.skewX = 0;
+      target.skewY = 0;
+      target.flipX = false;
+      target.flipY = false;
+      target.rotate(0);
+    },
+
+    /**
+     * Extract Object transform values
+     * @static
+     * @memberOf fabric.util
+     * @param  {fabric.Object} target object to read from
+     * @return {Object} Components of transform
+     */
+    saveObjectTransform: function (target) {
+      return {
+        scaleX: target.scaleX,
+        scaleY: target.scaleY,
+        skewX: target.skewX,
+        skewY: target.skewY,
+        angle: target.angle,
+        left: target.left,
+        flipX: target.flipX,
+        flipY: target.flipY,
+        top: target.top
+      };
+    },
+
+    /**
+     * 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];
+    },
+
+    /**
+     * 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;
         }
-    };
-    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);
-            }
+        else {
+          x = 0;
         }
-        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;
-})();
+        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;
+
+      // 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 the given font family or all the cache if no
+     * fontFamily is specified.
+     * Use it if you know you are loading fonts in a lazy way and you are not waiting
+     * for custom fonts to load properly when adding text objects to the canvas.
+     * If a text object is added when its own font is not loaded yet, you will get wrong
+     * measurement and so wrong bounding boxes.
+     * After the font cache is cleared, either change the textObject text content or call
+     * initDimensions() to trigger a recalculation
+     * @memberOf fabric.util
+     * @param {String} [fontFamily] font family to clear
+     */
+    clearFabricFontCache: function(fontFamily) {
+      fontFamily = (fontFamily || '').toLowerCase();
+      if (!fontFamily) {
+        fabric.charWidthsCache = { };
+      }
+      else if (fabric.charWidthsCache[fontFamily]) {
+        delete fabric.charWidthsCache[fontFamily];
+      }
+    },
+
+    /**
+     * Given current aspect ratio, determines the max width and height that can
+     * respect the total allowed area for the cache.
+     * @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 };
+    },
+
+    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);
+
 
 (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;
+
+  var _join = Array.prototype.join;
+
+  /* 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 (fabric.arcToSegmentsCache[argsString]) {
+      return fabric.arcToSegmentsCache[argsString];
     }
-    function max(array, byProperty) {
-        return find(array, byProperty, function(value1, value2) {
-            return value1 >= value2;
-        });
+
+    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 * 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;
+
+    if (pl < 0) {
+      var s = Math.sqrt(1 - pl / (rx2 * ry2));
+      rx *= s;
+      ry *= s;
     }
-    function min(array, byProperty) {
-        return find(array, byProperty, function(value1, value2) {
-            return value1 < value2;
-        });
+    else {
+      root = (large === sweep ? -1.0 : 1.0) *
+              Math.sqrt( pl / (rx2 * py2 + ry2 * px2));
     }
-    function fill(array, value) {
-        var k = array.length;
-        while (k--) {
-            array[k] = value;
-        }
-        return array;
+
+    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;
     }
-    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;
+    else if (sweep === 1 && dtheta < 0) {
+      dtheta += 2 * PI;
     }
-    fabric.util.array = {
-        fill: fill,
-        invoke: invoke,
-        min: min,
-        max: max
-    };
-})();
 
-(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;
+    // 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;
+    }
+    fabric.arcToSegmentsCache[argsString] = result;
+    return result;
+  }
+
+  function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
+    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 * costh3);
+
+    return [
+      cp1X, cp1Y,
+      cp2X, cp2Y,
+      toX, toY
+    ];
+  }
+
+  /*
+   * 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 clone(object, deep) {
-        return extend({}, object, deep);
+    else {
+      return 2 * Math.PI - (ta - tb);
     }
-    fabric.util.object = {
-        extend: extend,
-        clone: clone
-    };
-    fabric.util.object.extend(fabric.util, fabric.Observable);
+  }
+
+  /**
+   * 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]);
+    }
+  };
+
+  /**
+   * 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];
+    }
+    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;
+    if (fabric.cachesBoundsOfCurve) {
+      argsString = _join.call(arguments);
+      if (fabric.boundsOfCurveCache[argsString]) {
+        return fabric.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])
+      }
+    ];
+    if (fabric.cachesBoundsOfCurve) {
+      fabric.boundsOfCurveCache[argsString] = result;
+    }
+    return result;
+  }
+
+  fabric.util.getBoundsOfCurve = getBoundsOfCurve;
+
 })();
 
+
 (function() {
-    function camelize(string) {
-        return string.replace(/-+(.)?/g, function(match, character) {
-            return character ? character.toUpperCase() : "";
-        });
+
+  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 capitalize(string, firstLetterOnly) {
-        return string.charAt(0).toUpperCase() + (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
+    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 escapeXml(string) {
-        return string.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+    return array;
+  }
+
+  /**
+   * @private
+   */
+  function find(array, byProperty, condition) {
+    if (!array || array.length === 0) {
+      return;
     }
-    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;
+
+    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 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";
+    else {
+      while (i--) {
+        if (condition(array[i], result)) {
+          result = array[i];
         }
-        return false;
+      }
     }
-    fabric.util.string = {
-        camelize: camelize,
-        capitalize: capitalize,
-        escapeXml: escapeXml,
-        graphemeSplit: graphemeSplit
-    };
+    return result;
+  }
+
+  /**
+   * @namespace fabric.util.array
+   */
+  fabric.util.array = {
+    fill: fill,
+    invoke: invoke,
+    min: min,
+    max: max
+  };
+
 })();
 
+
 (function() {
-    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) {
+  /**
+   * 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') {
         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;
-                }
-                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);
+          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;
+      }
     }
-    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;
+    else {
+      for (var property in source) {
+        destination[property] = source[property];
+      }
     }
-    fabric.util.createClass = createClass;
+    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);
 })();
 
+
 (function() {
-    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;
-    }
-    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);
-        };
+
+  /**
+   * 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);
     }
-    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);
-                }
-            }
-        };
+    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 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);
-                    }
-                }
-            }
-        };
+    if (code < 0xD800 || code > 0xDFFF) {
+      return str.charAt(i);
     }
-    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
-        };
+
+    // 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 pointerX = function(event) {
-        return event.clientX;
-    }, pointerY = function(event) {
-        return event.clientY;
-    };
-    function _getPointer(event, pageProp, clientProp) {
-        var touchProp = event.type === "touchend" ? "changedTouches" : "touches";
-        var pointer, eventTouchProp = event[touchProp];
-        if (eventTouchProp && eventTouchProp[0]) {
-            pointer = eventTouchProp[0][clientProp];
-        }
-        if (typeof pointer === "undefined") {
-            pointer = event[clientProp];
-        }
-        return pointer;
+    // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
+    if (i === 0) {
+      throw 'Low surrogate without preceding high surrogate';
     }
-    if (fabric.isTouchSupported) {
-        pointerX = function(event) {
-            return _getPointer(event, "pageX", "clientX");
-        };
-        pointerY = function(event) {
-            return _getPointer(event, "pageY", "clientY");
-        };
+    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';
     }
-    fabric.util.getPointer = getPointer;
+    // 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
+  };
 })();
 
+
 (function() {
-    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];
-            }
+
+  var slice = Array.prototype.slice, emptyFunction = function() { },
+
+      IS_DONTENUM_BUGGY = (function() {
+        for (var p in { toString: 1 }) {
+          if (p === 'toString') {
+            return false;
+          }
         }
-        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;
+        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;
             }
-            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 + ")";
+            if (source.valueOf !== Object.prototype.valueOf) {
+              klass.prototype.valueOf = source.valueOf;
             }
-            return element;
-        };
+          }
+        }
+      };
+
+  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;
     }
-    fabric.util.setStyle = setStyle;
-})();
 
-(function() {
-    var _slice = Array.prototype.slice;
-    function getById(id) {
-        return typeof id === "string" ? fabric.document.getElementById(id) : id;
+    if (!parentMethod) {
+      return console.log('tried to callSuper ' + methodName + ', method not found in prototype chain', this);
     }
-    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;
-        };
+
+    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();
     }
-    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;
+    function klass() {
+      this.initialize.apply(this, arguments);
     }
-    function addClass(element, className) {
-        if (element && (" " + element.className + " ").indexOf(" " + className + " ") === -1) {
-            element.className += (element.className ? " " : "") + className;
-        }
+
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      Subclass.prototype = parent.prototype;
+      klass.prototype = new Subclass();
+      parent.subclasses.push(klass);
     }
-    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;
+    for (var i = 0, length = properties.length; i < length; i++) {
+      addMethods(klass, properties[i], parent);
     }
-    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
-        };
+    if (!klass.prototype.initialize) {
+      klass.prototype.initialize = emptyFunction;
     }
-    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
-        };
+    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;
+      }
     }
-    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 true;
+  }
+
+  /** @ignore */
+  var getElement,
+      setElement,
+      getUniqueId = (function () {
+        var uid = 0;
+        return function (element) {
+          return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
         };
+      })();
+
+  (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';
+    var pointer, eventTouchProp = event[touchProp];
+
+    if (eventTouchProp && eventTouchProp[0]) {
+      pointer = eventTouchProp[0][clientProp];
     }
-    (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;
+
+    if (typeof pointer === 'undefined') {
+      pointer = event[clientProp];
+    }
+
+    return pointer;
+  }
+
+  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;
+    }
+    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 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() {
-    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;
-            }
+
+  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
         };
-        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;
+
+    // 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;
+      }
     }
-    fabric.util.request = request;
-})();
 
-fabric.log = function() {};
+    return { left: left, top: top };
+  }
 
-fabric.warn = 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'
+        };
 
-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);
-            };
-        }
-    });
-}
+    if (!doc) {
+      return offset;
+    }
 
-(function() {
-    function noop() {
-        return false;
+    for (var attr in offsetAttributes) {
+      offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
     }
-    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);
-        });
+
+    docElem = doc.documentElement;
+    if ( typeof element.getBoundingClientRect !== 'undefined' ) {
+      box = element.getBoundingClientRect();
     }
-    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);
+
+    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 _cancelAnimFrame = fabric.window.cancelAnimationFrame || fabric.window.clearTimeout;
-    function requestAnimFrame() {
-        return _requestAnimFrame.apply(fabric.window, arguments);
+  }
+  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;
+    }
+
+    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);
+    }
+
+    fabric.util.getScript = getScript;
+  })();
+
+  function getNodeCanvas(element) {
+    var impl = fabric.jsdomImplForWrapper(element);
+    return impl._canvas || impl._image;
+  };
+
+  function cleanUpJsdomNode(element) {
+    if (!fabric.isLikelyNode) {
+      return;
     }
-    function cancelAnimFrame() {
-        return _cancelAnimFrame.apply(fabric.window, arguments);
+    var impl = fabric.jsdomImplForWrapper(element);
+    if (impl) {
+      impl._image = null;
+      impl._canvas = null;
+      // unsure if necessary
+      impl._currentSrc = null;
+      impl._attributes = null;
+      impl._classList = null;
     }
-    fabric.util.animate = animate;
-    fabric.util.requestAnimFrame = requestAnimFrame;
-    fabric.util.cancelAnimFrame = cancelAnimFrame;
+  }
+
+  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.cleanUpJsdomNode = cleanUpJsdomNode;
+
 })();
 
+
 (function() {
-    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);
-            }
-        }));
+
+  function addParamToUrl(url, param) {
+    return url + (/\?/.test(url) ? '&' : '?') + param;
+  }
+
+  var makeXHR = (function() {
+    var factories = [
+      function() { return new fabric.window.XMLHttpRequest(); },
+      function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
+      function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
+      function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); }
+    ];
+    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);
+      }
     }
-    fabric.util.animateColor = animateColor;
+
+    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);
+      };
+    }
+  });
+}
+/* eslint-enable */
+
+
 (function() {
-    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
-        };
+
+  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;
+
+})();
+
+
+(function() {
+
+  function normalize(a, c, p, s) {
+    if (a < Math.abs(c)) {
+      a = c;
+      s = p / 4;
     }
-    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);
+    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 easeOutCubic(t, b, c, d) {
-        return c * ((t = t / d - 1) * t * t + 1) + b;
+    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 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 + 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 easeInQuart(t, b, c, d) {
-        return c * (t /= d) * t * t * t + 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 easeOutQuart(t, b, c, d) {
-        return -c * ((t = t / d - 1) * t * t * t - 1) + 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 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;
+    if (t === d) {
+      return b + c;
     }
-    function easeInQuint(t, b, c, d) {
-        return c * (t /= d) * t * t * t * t + b;
+    t /= d / 2;
+    if (t < 1) {
+      return c / 2 * Math.pow(2, 10 * (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.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 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;
+    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 easeInSine(t, b, c, d) {
-        return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
+    t /= d;
+    if (t === 1) {
+      return b + c;
     }
-    function easeOutSine(t, b, c, d) {
-        return c * Math.sin(t / d * (Math.PI / 2)) + b;
+    if (!p) {
+      p = d * 0.3;
     }
-    function easeInOutSine(t, b, c, d) {
-        return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + 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 easeInExpo(t, b, c, d) {
-        return t === 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
+    t /= d;
+    if (t === 1) {
+      return b + c;
     }
-    function easeOutExpo(t, b, c, d) {
-        return t === d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
+    if (!p) {
+      p = d * 0.3;
     }
-    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;
+    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 easeInCirc(t, b, c, d) {
-        return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
+    t /= d / 2;
+    if (t === 2) {
+      return b + c;
     }
-    function easeOutCirc(t, b, c, d) {
-        return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
+    if (!p) {
+      p = d * (0.3 * 1.5);
     }
-    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;
+    var opts = normalize(a, c, p, s);
+    if (t < 1) {
+      return -0.5 * elastic(opts, t, d) + b;
     }
-    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 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 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 /= 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 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;
+    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 easeInBack(t, b, c, d, s) {
-        if (s === undefined) {
-            s = 1.70158;
-        }
-        return c * (t /= d) * t * ((s + 1) * t - s) + b;
+    t /= d / 2;
+    if (t < 1) {
+      return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + 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;
+    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 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 / 2.75)) {
+      return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
     }
-    function easeInBounce(t, b, c, d) {
-        return c - easeOutBounce(d - t, 0, c, d) + b;
+    else if (t < (2.5 / 2.75)) {
+      return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + 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;
-        }
+    else {
+      return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + 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;
+  }
+
+  /**
+   * 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;
     }
-    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
-    };
+    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
+  };
+
 })();
 
+
 (function(global) {
-    "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;
+
+  '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',
+        '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) {
+    // transform attribute names
+    if (attr in attributesMap) {
+      return attributesMap[attr];
     }
-    function getSvgRegex(arr) {
-        return new RegExp("^(" + arr.join("|") + ")\\b", "i");
+    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 _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 === 'strokeDashArray') {
+      if (value === 'none') {
+        value = null;
+      }
+      else {
+        value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
+          return parseFloat(n);
         });
+      }
     }
-    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 === 'transformMatrix') {
+      if (parentAttributes && parentAttributes.transformMatrix) {
+        value = multiplyTransformMatrices(
+          parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
+      }
+      else {
+        value = fabric.parseTransformAttribute(value);
+      }
     }
-    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 === 'visible') {
+      value = value !== 'none' && value !== 'hidden';
+      // display=none on parent element always takes precedence over child element
+      if (parentAttributes && parentAttributes.visible === false) {
+        value = false;
+      }
     }
-    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 if (attr === 'opacity') {
+      value = parseFloat(value);
+      if (parentAttributes && typeof parentAttributes.opacity !== 'undefined') {
+        value *= parentAttributes.opacity;
+      }
     }
-    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;
+    else if (attr === 'textAnchor' /* text-anchor */) {
+      value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
     }
-    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;
-            }
-        }
+    else if (attr === 'charSpacing') {
+      // parseUnit returns px and we convert it to em
+      parsed = parseUnit(value, fontSize) / fontSize * 1000;
     }
-    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++;
-            }
-        }
+    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';
+      }
     }
-    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;
+    else {
+      parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
     }
-    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);
+
+    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;
         }
-    });
-})(typeof exports !== "undefined" ? exports : this);
+        attributes[attr] = fabric.Object.prototype[attr];
+      }
 
-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;
-};
+      if (attributes[attr].indexOf('url(') === 0) {
+        continue;
+      }
 
-fabric.ElementsParser.prototype.parse = function() {
-    this.instances = new Array(this.elements.length);
-    this.numElements = this.elements.length;
-    this.createObjects();
-};
+      var color = new fabric.Color(attributes[attr]);
+      attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
+    }
+    return attributes;
+  }
 
-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);
+  /**
+   * @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));
     }
-};
+    return nodeArray;
+  }
 
-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();
+  /**
+   * 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 = 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);
     }
-};
 
-fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
-    klass.fromElement(el, this.createCallback(index, el), this.options);
-};
+    function scaleMatrix(matrix, args) {
+      var multiplierX = args[0],
+          multiplierY = (args.length === 2) ? args[1] : args[0];
 
-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();
+      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];
+      }
+    }
+
+    // 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;
+        }
+
+        // snapshot current matrix into matrices array
+        matrices.push(matrix.concat());
+        // reset
+        matrix = iMatrix.concat();
+      });
+
+      var combinedMatrix = matrices[0];
+      while (matrices.length > 1) {
+        matrices.shift();
+        combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
+      }
+      return combinedMatrix;
     };
-};
+  })();
 
-fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
-    var instanceFillValue = obj[property];
-    if (!/^url\(/.test(instanceFillValue)) {
-        return;
+  /**
+   * @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;
     }
-    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));
+  }
+
+  /**
+   * @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];
+        }
+      }
     }
-};
+    return styles;
+  }
 
-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);
+  /**
+   * @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);
     }
-};
+    return firstMatching && parentMatching && (selectors.length === 0);
+  }
 
-(function(global) {
-    "use strict";
-    var fabric = global.fabric || (global.fabric = {});
-    if (fabric.Point) {
-        fabric.warn("fabric.Point is already defined");
-        return;
+  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.Point = Point;
-    function Point(x, y) {
-        this.x = x;
-        this.y = y;
+    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, '');
     }
-    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);
+    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;
+  }
 
-(function(global) {
-    "use strict";
-    var fabric = global.fabric || (global.fabric = {});
-    if (fabric.Intersection) {
-        fabric.warn("fabric.Intersection is already defined");
-        return;
+  /**
+   * @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 Intersection(status) {
-        this.status = status;
-        this.points = [];
+    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;
+      }
     }
-    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;
-        }
-    };
-    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");
-            }
+  }
+
+  /**
+   * @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;
         }
-        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);
+
+        if (attr.nodeName === 'transform') {
+          currentTrans = attr.nodeValue + ' ' + currentTrans;
         }
-        if (result.points.length > 0) {
-            result.status = "Intersection";
+        else {
+          el2.setAttribute(attr.nodeName, attr.nodeValue);
         }
-        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);
+      }
 
-(function(global) {
-    "use strict";
-    var fabric = global.fabric || (global.fabric = {});
-    if (fabric.Color) {
-        fabric.warn("fabric.Color is already defined.");
-        return;
-    }
-    function Color(color) {
-        if (!color) {
-            this.setSource([ 0, 0, 0, 1 ]);
-        } else {
-            this._tryParsingColor(color);
-        }
+      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++;
+      }
     }
-    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;
+  }
+
+  // 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 = '', widthDiff = 0, heightDiff = 0;
+
+    parsedDim.width = 0;
+    parsedDim.height = 0;
+    parsedDim.toBeParsed = toBeParsed;
+
+    if (toBeParsed) {
+      return parsedDim;
     }
-    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 ];
-        }
-    };
-    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);
 
-(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
-        };
+    if (missingViewBox) {
+      parsedDim.width = parseUnit(widthAttr);
+      parsedDim.height = parseUnit(heightAttr);
+      return parsedDim;
     }
-    function getLinearCoords(el) {
-        return {
-            x1: el.getAttribute("x1") || 0,
-            y1: el.getAttribute("y1") || 0,
-            x2: el.getAttribute("x2") || "100%",
-            y2: el.getAttribute("y2") || 0
-        };
+
+    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;
     }
-    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 {
+      parsedDim.width = viewBoxWidth;
+      parsedDim.height = viewBoxHeight;
     }
-    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);
-        }
+
+    // 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
+      if (preserveAspectRatio.meetOrSlice === 'meet') {
+        scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX);
+        // calculate additional translation to move the viewbox
+      }
+      if (preserveAspectRatio.meetOrSlice === 'slice') {
+        scaleY = scaleX = (scaleX > scaleY ? scaleX : scaleY);
+        // calculate additional translation to move the viewbox
+      }
+      widthDiff = parsedDim.width - viewBoxWidth * scaleX;
+      heightDiff = parsedDim.height - viewBoxHeight * scaleX;
+      if (preserveAspectRatio.alignX === 'Mid') {
+        widthDiff /= 2;
+      }
+      if (preserveAspectRatio.alignY === 'Mid') {
+        heightDiff /= 2;
+      }
+      if (preserveAspectRatio.alignX === 'Min') {
+        widthDiff = 0;
+      }
+      if (preserveAspectRatio.alignY === 'Min') {
+        heightDiff = 0;
+      }
+    }
+
+    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 + widthDiff) + ' ' +
+                  (minY * scaleY + heightDiff) + ') ';
+
+    if (element.nodeName === 'svg') {
+      el = element.ownerDocument.createElement('g');
+      // element.firstChild != null
+      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;
+  }
+
+  /**
+   * 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
     });
-    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 (!elements || (elements && !elements.length)) {
+      callback && callback([], {});
+      return;
+    }
+
+    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, ''));
         }
-        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;
-            }
+        if (!xml || !xml.documentElement) {
+          callback && callback(null);
         }
-        return ellipseMatrix;
+
+        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() {
-    "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);
-        }
-    });
-})();
+})(typeof exports !== 'undefined' ? exports : this);
 
-(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.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;
+};
+
+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);
     }
-    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, color = new fabric.Color(this.color);
-            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);
+    catch (err) {
+      fabric.log(err);
+    }
+  }
+  else {
+    this.checkIfDone();
+  }
+};
 
-(function() {
-    "use strict";
-    if (fabric.StaticCanvas) {
-        fabric.warn("fabric.StaticCanvas is already defined.");
-        return;
+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 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";
-                    this.hasLostContext = true;
-                }
-                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 + " }>";
-        }
+    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[property];
+  if (!(/^url\(/).test(instanceFillValue)) {
+    return;
+  }
+  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));
+  }
+};
+
+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;
     });
-    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";
+    this.callback(this.instances, this.elements);
+  }
+};
 
-              case "setLineDash":
-                return typeof ctx.setLineDash !== "undefined";
 
-              case "toDataURL":
-                return typeof el.toDataURL !== "undefined";
+(function(global) {
 
-              case "toDataURLWithQuality":
-                try {
-                    el.toDataURL("image/jpeg", 0);
-                    return true;
-                } catch (e) {}
-                return false;
+  'use strict';
 
-              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);
-        };
-    }
-})();
+  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
 
-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;
+  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);
     },
-    _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 (fabric.StaticCanvas.supports("setLineDash")) {
-            ctx.setLineDash(this.strokeDashArray || []);
-        }
+
+    /**
+     * 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;
     },
-    _saveAndTransform: function(ctx) {
-        var v = this.canvas.viewportTransform;
-        ctx.save();
-        ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
+
+    /**
+     * 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);
     },
-    _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;
+
+    /**
+     * Adds value to this point
+     * @param {Number} scalar
+     * @return {fabric.Point} thisArg
+     * @chainable
+     */
+    scalarAddEquals: function (scalar) {
+      this.x += scalar;
+      this.y += scalar;
+      return this;
     },
-    _resetShadow: function() {
-        var ctx = this.canvas.contextTop;
-        ctx.shadowColor = "";
-        ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
-    }
-});
 
-(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) {
-                if (this.needsFullRender) {
-                    this.canvas.clearContext(this.canvas.contextTop);
-                    this._render();
-                } else {
-                    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();
-            var color = new fabric.Color(this.color);
-            this.needsFullRender = color.getAlpha() < 1;
-            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
-            });
-        }
-    });
-})();
+    /**
+     * 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);
+    },
 
-fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, {
-    width: 10,
-    initialize: function(canvas) {
-        this.canvas = canvas;
-        this.points = [];
+    /**
+     * 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;
     },
-    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();
+
+    /**
+     * 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);
     },
-    onMouseDown: function(pointer) {
-        this.points.length = 0;
-        this.canvas.clearContext(this.canvas.contextTop);
-        this._setShadow();
-        this.drawDot(pointer);
+
+    /**
+     * Subtracts value from this point
+     * @param {Number} scalar
+     * @return {fabric.Point} thisArg
+     * @chainable
+     */
+    scalarSubtractEquals: function (scalar) {
+      this.x -= scalar;
+      this.y -= scalar;
+      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();
+
+    /**
+     * 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);
     },
-    onMouseMove: function(pointer) {
-        this.drawDot(pointer);
+
+    /**
+     * 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;
     },
-    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();
+
+    /**
+     * 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);
     },
-    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;
-    }
-});
 
-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 = [];
+    /**
+     * 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;
     },
-    onMouseDown: function(pointer) {
-        this.sprayChunks.length = 0;
-        this.canvas.clearContext(this.canvas.contextTop);
-        this._setShadow();
-        this.addSprayChunk(pointer);
-        this.render(this.sprayChunkPoints);
+
+    /**
+     * 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);
     },
-    onMouseMove: function(pointer) {
-        this.addSprayChunk(pointer);
-        this.render(this.sprayChunkPoints);
+
+    /**
+     * 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);
     },
-    onMouseUp: function() {
-        var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
-        this.canvas.renderOnAddRemove = false;
-        var rects = [];
-        for (var i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
-            var sprayChunk = this.sprayChunks[i];
-            for (var j = 0, jlen = sprayChunk.length; j < jlen; j++) {
-                var rect = new fabric.Rect({
-                    width: sprayChunk[j].width,
-                    height: sprayChunk[j].width,
-                    left: sprayChunk[j].x + 1,
-                    top: sprayChunk[j].y + 1,
-                    originX: "center",
-                    originY: "center",
-                    fill: this.color
-                });
-                rects.push(rect);
-            }
-        }
-        if (this.optimizeOverlapping) {
-            rects = this._getOptimizedRects(rects);
-        }
-        var group = new fabric.Group(rects, {
-            originX: "center",
-            originY: "center"
-        });
-        this.shadow && group.setShadow(this.shadow);
-        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();
+
+    /**
+     * 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);
     },
-    _getOptimizedRects: function(rects) {
-        var uniqueRects = {}, key, i, len;
-        for (i = 0, len = rects.length; i < len; i++) {
-            key = rects[i].left + "" + rects[i].top;
-            if (!uniqueRects[key]) {
-                uniqueRects[key] = rects[i];
-            }
-        }
-        var uniqueRectsArray = [];
-        for (key in uniqueRects) {
-            uniqueRectsArray.push(uniqueRects[key]);
-        }
-        return uniqueRectsArray;
+
+    /**
+
+     * 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);
     },
-    render: function(sprayChunk) {
-        var ctx = this.canvas.contextTop, i, len;
-        ctx.fillStyle = this.color;
-        this._saveAndTransform(ctx);
-        for (i = 0, len = sprayChunk.length; i < len; i++) {
-            var point = sprayChunk[i];
-            if (typeof point.opacity !== "undefined") {
-                ctx.globalAlpha = point.opacity;
-            }
-            ctx.fillRect(point.x, point.y, point.width, point.width);
-        }
-        ctx.restore();
+
+    /**
+     * 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);
     },
-    _render: function() {
-        var ctx = this.canvas.contextTop, i, ilen;
-        ctx.fillStyle = this.color;
-        this._saveAndTransform(ctx);
-        for (i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
-            this.render(this.sprayChunks[i]);
-        }
-        ctx.restore();
+
+    /**
+     * 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);
     },
-    addSprayChunk: function(pointer) {
-        this.sprayChunkPoints = [];
-        var x, y, width, radius = this.width / 2, i;
-        for (i = 0; i < this.density; i++) {
-            x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius);
-            y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius);
-            if (this.dotWidthVariance) {
-                width = fabric.util.getRandomInt(Math.max(1, this.dotWidth - this.dotWidthVariance), this.dotWidth + this.dotWidthVariance);
-            } else {
-                width = this.dotWidth;
-            }
-            var point = new fabric.Point(x, y);
-            point.width = width;
-            if (this.randomOpacity) {
-                point.opacity = fabric.util.getRandomInt(0, 100) / 100;
-            }
-            this.sprayChunkPoints.push(point);
-        }
-        this.sprayChunks.push(this.sprayChunkPoints);
-    }
-});
 
-fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, {
-    getPatternSrc: function() {
-        var dotWidth = 20, dotDistance = 5, patternCanvas = fabric.document.createElement("canvas"), patternCtx = patternCanvas.getContext("2d");
-        patternCanvas.width = patternCanvas.height = dotWidth + dotDistance;
-        patternCtx.fillStyle = this.color;
-        patternCtx.beginPath();
-        patternCtx.arc(dotWidth / 2, dotWidth / 2, dotWidth / 2, 0, Math.PI * 2, false);
-        patternCtx.closePath();
-        patternCtx.fill();
-        return patternCanvas;
+    /**
+     * 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);
     },
-    getPatternSrcFunction: function() {
-        return String(this.getPatternSrc).replace("this.color", '"' + this.color + '"');
+
+    /**
+     * Returns the point between this point and another one
+     * @param {fabric.Point} that
+     * @return {fabric.Point}
+     */
+    midPointFrom: function (that) {
+      return this.lerp(that);
     },
-    getPattern: function() {
-        return this.canvas.contextTop.createPattern(this.source || this.getPatternSrc(), "repeat");
+
+    /**
+     * 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));
     },
-    _setBrushStyles: function() {
-        this.callSuper("_setBrushStyles");
-        this.canvas.contextTop.strokeStyle = this.getPattern();
+
+    /**
+     * 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));
     },
-    createPath: function(pathData) {
-        var path = this.callSuper("createPath", pathData), topLeft = path._getLeftTopCoords().scalarAdd(path.strokeWidth / 2);
-        path.stroke = new fabric.Pattern({
-            source: this.source || this.getPatternSrcFunction(),
-            offsetX: -topLeft.x,
-            offsetY: -topLeft.y
-        });
-        return path;
-    }
-});
 
-(function() {
-    var getPointer = fabric.util.getPointer, degreesToRadians = fabric.util.degreesToRadians, radiansToDegrees = fabric.util.radiansToDegrees, atan2 = Math.atan2, abs = Math.abs, supportLineDash = fabric.StaticCanvas.supports("setLineDash"), STROKE_OFFSET = .5;
-    fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, {
-        initialize: function(el, options) {
-            options || (options = {});
-            this.renderAndResetBound = this.renderAndReset.bind(this);
-            this._initStatic(el, options);
-            this._initInteractive();
-            this._createCacheCanvas();
-        },
-        uniScaleTransform: false,
-        uniScaleKey: "shiftKey",
-        centeredScaling: false,
-        centeredRotation: false,
-        centeredKey: "altKey",
-        altActionKey: "shiftKey",
-        interactive: true,
-        selection: true,
-        selectionKey: "shiftKey",
-        altSelectionKey: null,
-        selectionColor: "rgba(100, 100, 255, 0.3)",
-        selectionDashArray: [],
-        selectionBorderColor: "rgba(255, 255, 255, 0.3)",
-        selectionLineWidth: 1,
-        selectionFullyContained: false,
-        hoverCursor: "move",
-        moveCursor: "move",
-        defaultCursor: "default",
-        freeDrawingCursor: "crosshair",
-        rotationCursor: "crosshair",
-        notAllowedCursor: "not-allowed",
-        containerClass: "canvas-container",
-        perPixelTargetFind: false,
-        targetFindTolerance: 0,
-        skipTargetFind: false,
-        isDrawingMode: false,
-        preserveObjectStacking: false,
-        snapAngle: 0,
-        snapThreshold: null,
-        stopContextMenu: false,
-        fireRightClick: false,
-        fireMiddleClick: false,
-        _initInteractive: function() {
-            this._currentTransform = null;
-            this._groupSelector = null;
-            this._initWrapperElement();
-            this._createUpperCanvas();
-            this._initEventListeners();
-            this._initRetinaScaling();
-            this.freeDrawingBrush = fabric.PencilBrush && new fabric.PencilBrush(this);
-            this.calcOffset();
-        },
-        _chooseObjectsToRender: function() {
-            var activeObjects = this.getActiveObjects(), object, objsToRender, activeGroupObjects;
-            if (activeObjects.length > 0 && !this.preserveObjectStacking) {
-                objsToRender = [];
-                activeGroupObjects = [];
-                for (var i = 0, length = this._objects.length; i < length; i++) {
-                    object = this._objects[i];
-                    if (activeObjects.indexOf(object) === -1) {
-                        objsToRender.push(object);
-                    } else {
-                        activeGroupObjects.push(object);
-                    }
-                }
-                if (activeObjects.length > 1) {
-                    this._activeObject._objects = activeGroupObjects;
-                }
-                objsToRender.push.apply(objsToRender, activeGroupObjects);
-            } else {
-                objsToRender = this._objects;
-            }
-            return objsToRender;
-        },
-        renderAll: function() {
-            if (this.contextTopDirty && !this._groupSelector && !this.isDrawingMode) {
-                this.clearContext(this.contextTop);
-                this.contextTopDirty = false;
-            }
-            if (this.hasLostContext) {
-                this.renderTopLayer(this.contextTop);
-            }
-            var canvasToDrawOn = this.contextContainer;
-            this.renderCanvas(canvasToDrawOn, this._chooseObjectsToRender());
-            return this;
-        },
-        renderTopLayer: function(ctx) {
-            if (this.isDrawingMode && this._isCurrentlyDrawing) {
-                this.freeDrawingBrush && this.freeDrawingBrush._render();
-            }
-            if (this.selection && this._groupSelector) {
-                this._drawSelection(ctx);
-            }
-        },
-        renderTop: function() {
-            var ctx = this.contextTop;
-            this.clearContext(ctx);
-            this.renderTopLayer(ctx);
-            this.fire("after:render");
-            this.contextTopDirty = true;
-            return this;
-        },
-        _resetCurrentTransform: function() {
-            var t = this._currentTransform;
-            t.target.set({
-                scaleX: t.original.scaleX,
-                scaleY: t.original.scaleY,
-                skewX: t.original.skewX,
-                skewY: t.original.skewY,
-                left: t.original.left,
-                top: t.original.top
-            });
-            if (this._shouldCenterTransform(t.target)) {
-                if (t.originX !== "center") {
-                    if (t.originX === "right") {
-                        t.mouseXSign = -1;
-                    } else {
-                        t.mouseXSign = 1;
-                    }
-                }
-                if (t.originY !== "center") {
-                    if (t.originY === "bottom") {
-                        t.mouseYSign = -1;
-                    } else {
-                        t.mouseYSign = 1;
-                    }
-                }
-                t.originX = "center";
-                t.originY = "center";
-            } else {
-                t.originX = t.original.originX;
-                t.originY = t.original.originY;
-            }
-        },
-        containsPoint: function(e, target, point) {
-            var ignoreZoom = true, pointer = point || this.getPointer(e, ignoreZoom), xy;
-            if (target.group && target.group === this._activeObject && target.group.type === "activeSelection") {
-                xy = this._normalizePointer(target.group, pointer);
-            } else {
-                xy = {
-                    x: pointer.x,
-                    y: pointer.y
-                };
-            }
-            return target.containsPoint(xy) || target._findTargetCorner(pointer);
-        },
-        _normalizePointer: function(object, pointer) {
-            var m = object.calcTransformMatrix(), invertedM = fabric.util.invertTransform(m), vptPointer = this.restorePointerVpt(pointer);
-            return fabric.util.transformPoint(vptPointer, invertedM);
-        },
-        isTargetTransparent: function(target, x, y) {
-            var ctx = this.contextCache, originalColor = target.selectionBackgroundColor, v = this.viewportTransform;
-            target.selectionBackgroundColor = "";
-            this.clearContext(ctx);
-            ctx.save();
-            ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
-            target.render(ctx);
-            ctx.restore();
-            target === this._activeObject && target._renderControls(ctx, {
-                hasBorders: false,
-                transparentCorners: false
-            }, {
-                hasBorders: false
-            });
-            target.selectionBackgroundColor = originalColor;
-            var isTransparent = fabric.util.isTransparent(ctx, x, y, this.targetFindTolerance);
-            return isTransparent;
-        },
-        _isSelectionKeyPressed: function(e) {
-            var selectionKeyPressed = false;
-            if (Object.prototype.toString.call(this.selectionKey) === "[object Array]") {
-                selectionKeyPressed = !!this.selectionKey.find(function(key) {
-                    return e[key] === true;
-                });
-            } else {
-                selectionKeyPressed = e[this.selectionKey];
-            }
-            return selectionKeyPressed;
-        },
-        _shouldClearSelection: function(e, target) {
-            var activeObjects = this.getActiveObjects(), activeObject = this._activeObject;
-            return !target || target && activeObject && activeObjects.length > 1 && activeObjects.indexOf(target) === -1 && activeObject !== target && !this._isSelectionKeyPressed(e) || target && !target.evented || target && !target.selectable && activeObject && activeObject !== target;
-        },
-        _shouldCenterTransform: function(target) {
-            if (!target) {
-                return;
-            }
-            var t = this._currentTransform, centerTransform;
-            if (t.action === "scale" || t.action === "scaleX" || t.action === "scaleY") {
-                centerTransform = this.centeredScaling || target.centeredScaling;
-            } else if (t.action === "rotate") {
-                centerTransform = this.centeredRotation || target.centeredRotation;
-            }
-            return centerTransform ? !t.altKey : t.altKey;
-        },
-        _getOriginFromCorner: function(target, corner) {
-            var origin = {
-                x: target.originX,
-                y: target.originY
-            };
-            if (corner === "ml" || corner === "tl" || corner === "bl") {
-                origin.x = "right";
-            } else if (corner === "mr" || corner === "tr" || corner === "br") {
-                origin.x = "left";
-            }
-            if (corner === "tl" || corner === "mt" || corner === "tr") {
-                origin.y = "bottom";
-            } else if (corner === "bl" || corner === "mb" || corner === "br") {
-                origin.y = "top";
-            }
-            return origin;
-        },
-        _getActionFromCorner: function(target, corner, e) {
-            if (!corner) {
-                return "drag";
-            }
-            switch (corner) {
-              case "mtr":
-                return "rotate";
+    /**
+     * Returns string representation of this point
+     * @return {String}
+     */
+    toString: function () {
+      return this.x + ',' + this.y;
+    },
 
-              case "ml":
-              case "mr":
-                return e[this.altActionKey] ? "skewY" : "scaleX";
+    /**
+     * 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;
+    },
 
-              case "mt":
-              case "mb":
-                return e[this.altActionKey] ? "skewX" : "scaleY";
+    /**
+     * Sets x of this point
+     * @param {Number} x
+     * @chainable
+     */
+    setX: function (x) {
+      this.x = x;
+      return this;
+    },
 
-              default:
-                return "scale";
-            }
-        },
-        _setupCurrentTransform: function(e, target) {
-            if (!target) {
-                return;
-            }
-            var pointer = this.getPointer(e), corner = target._findTargetCorner(this.getPointer(e, true)), action = this._getActionFromCorner(target, corner, e), origin = this._getOriginFromCorner(target, corner);
-            this._currentTransform = {
-                target: target,
-                action: action,
-                corner: corner,
-                scaleX: target.scaleX,
-                scaleY: target.scaleY,
-                skewX: target.skewX,
-                skewY: target.skewY,
-                offsetX: pointer.x - target.left,
-                offsetY: pointer.y - target.top,
-                originX: origin.x,
-                originY: origin.y,
-                ex: pointer.x,
-                ey: pointer.y,
-                lastX: pointer.x,
-                lastY: pointer.y,
-                theta: degreesToRadians(target.angle),
-                width: target.width * target.scaleX,
-                mouseXSign: 1,
-                mouseYSign: 1,
-                shiftKey: e.shiftKey,
-                altKey: e[this.centeredKey]
-            };
-            this._currentTransform.original = {
-                left: target.left,
-                top: target.top,
-                scaleX: target.scaleX,
-                scaleY: target.scaleY,
-                skewX: target.skewX,
-                skewY: target.skewY,
-                originX: origin.x,
-                originY: origin.y
-            };
-            this._resetCurrentTransform();
-        },
-        _translateObject: function(x, y) {
-            var transform = this._currentTransform, target = transform.target, newLeft = x - transform.offsetX, newTop = y - transform.offsetY, moveX = !target.get("lockMovementX") && target.left !== newLeft, moveY = !target.get("lockMovementY") && target.top !== newTop;
-            moveX && target.set("left", newLeft);
-            moveY && target.set("top", newTop);
-            return moveX || moveY;
-        },
-        _changeSkewTransformOrigin: function(mouseMove, t, by) {
-            var property = "originX", origins = {
-                0: "center"
-            }, skew = t.target.skewX, originA = "left", originB = "right", corner = t.corner === "mt" || t.corner === "ml" ? 1 : -1, flipSign = 1;
-            mouseMove = mouseMove > 0 ? 1 : -1;
-            if (by === "y") {
-                skew = t.target.skewY;
-                originA = "top";
-                originB = "bottom";
-                property = "originY";
-            }
-            origins[-1] = originA;
-            origins[1] = originB;
-            t.target.flipX && (flipSign *= -1);
-            t.target.flipY && (flipSign *= -1);
-            if (skew === 0) {
-                t.skewSign = -corner * mouseMove * flipSign;
-                t[property] = origins[-mouseMove];
-            } else {
-                skew = skew > 0 ? 1 : -1;
-                t.skewSign = skew;
-                t[property] = origins[skew * corner * flipSign];
-            }
-        },
-        _skewObject: function(x, y, by) {
-            var t = this._currentTransform, target = t.target, skewed = false, lockSkewingX = target.get("lockSkewingX"), lockSkewingY = target.get("lockSkewingY");
-            if (lockSkewingX && by === "x" || lockSkewingY && by === "y") {
-                return false;
-            }
-            var center = target.getCenterPoint(), actualMouseByCenter = target.toLocalPoint(new fabric.Point(x, y), "center", "center")[by], lastMouseByCenter = target.toLocalPoint(new fabric.Point(t.lastX, t.lastY), "center", "center")[by], actualMouseByOrigin, constraintPosition, dim = target._getTransformedDimensions();
-            this._changeSkewTransformOrigin(actualMouseByCenter - lastMouseByCenter, t, by);
-            actualMouseByOrigin = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY)[by];
-            constraintPosition = target.translateToOriginPoint(center, t.originX, t.originY);
-            skewed = this._setObjectSkew(actualMouseByOrigin, t, by, dim);
-            t.lastX = x;
-            t.lastY = y;
-            target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
-            return skewed;
-        },
-        _setObjectSkew: function(localMouse, transform, by, _dim) {
-            var target = transform.target, newValue, skewed = false, skewSign = transform.skewSign, newDim, dimNoSkew, otherBy, _otherBy, _by, newDimMouse, skewX, skewY;
-            if (by === "x") {
-                otherBy = "y";
-                _otherBy = "Y";
-                _by = "X";
-                skewX = 0;
-                skewY = target.skewY;
-            } else {
-                otherBy = "x";
-                _otherBy = "X";
-                _by = "Y";
-                skewX = target.skewX;
-                skewY = 0;
-            }
-            dimNoSkew = target._getTransformedDimensions(skewX, skewY);
-            newDimMouse = 2 * Math.abs(localMouse) - dimNoSkew[by];
-            if (newDimMouse <= 2) {
-                newValue = 0;
-            } else {
-                newValue = skewSign * Math.atan(newDimMouse / target["scale" + _by] / (dimNoSkew[otherBy] / target["scale" + _otherBy]));
-                newValue = fabric.util.radiansToDegrees(newValue);
-            }
-            skewed = target["skew" + _by] !== newValue;
-            target.set("skew" + _by, newValue);
-            if (target["skew" + _otherBy] !== 0) {
-                newDim = target._getTransformedDimensions();
-                newValue = _dim[otherBy] / newDim[otherBy] * target["scale" + _otherBy];
-                target.set("scale" + _otherBy, newValue);
-            }
-            return skewed;
-        },
-        _scaleObject: function(x, y, by) {
-            var t = this._currentTransform, target = t.target, lockScalingX = target.lockScalingX, lockScalingY = target.lockScalingY, lockScalingFlip = target.lockScalingFlip;
-            if (lockScalingX && lockScalingY) {
-                return false;
-            }
-            var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY), localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY), dim = target._getTransformedDimensions(), scaled = false;
-            this._setLocalMouse(localMouse, t);
-            scaled = this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by, lockScalingFlip, dim);
-            target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
-            return scaled;
-        },
-        _setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
-            var target = transform.target, forbidScalingX = false, forbidScalingY = false, scaled = false, changeX, changeY, scaleX, scaleY;
-            scaleX = localMouse.x * target.scaleX / _dim.x;
-            scaleY = localMouse.y * target.scaleY / _dim.y;
-            changeX = target.scaleX !== scaleX;
-            changeY = target.scaleY !== scaleY;
-            if (lockScalingFlip && scaleX <= 0 && scaleX < target.scaleX) {
-                forbidScalingX = true;
-                localMouse.x = 0;
-            }
-            if (lockScalingFlip && scaleY <= 0 && scaleY < target.scaleY) {
-                forbidScalingY = true;
-                localMouse.y = 0;
-            }
-            if (by === "equally" && !lockScalingX && !lockScalingY) {
-                scaled = this._scaleObjectEqually(localMouse, target, transform, _dim);
-            } else if (!by) {
-                forbidScalingX || lockScalingX || target.set("scaleX", scaleX) && (scaled = scaled || changeX);
-                forbidScalingY || lockScalingY || target.set("scaleY", scaleY) && (scaled = scaled || changeY);
-            } else if (by === "x" && !target.get("lockUniScaling")) {
-                forbidScalingX || lockScalingX || target.set("scaleX", scaleX) && (scaled = scaled || changeX);
-            } else if (by === "y" && !target.get("lockUniScaling")) {
-                forbidScalingY || lockScalingY || target.set("scaleY", scaleY) && (scaled = scaled || changeY);
-            }
-            transform.newScaleX = scaleX;
-            transform.newScaleY = scaleY;
-            forbidScalingX || forbidScalingY || this._flipObject(transform, by);
-            return scaled;
-        },
-        _scaleObjectEqually: function(localMouse, target, transform, _dim) {
-            var dist = localMouse.y + localMouse.x, lastDist = _dim.y * transform.original.scaleY / target.scaleY + _dim.x * transform.original.scaleX / target.scaleX, scaled, signX = localMouse.x < 0 ? -1 : 1, signY = localMouse.y < 0 ? -1 : 1;
-            transform.newScaleX = signX * Math.abs(transform.original.scaleX * dist / lastDist);
-            transform.newScaleY = signY * Math.abs(transform.original.scaleY * dist / lastDist);
-            scaled = transform.newScaleX !== target.scaleX || transform.newScaleY !== target.scaleY;
-            target.set("scaleX", transform.newScaleX);
-            target.set("scaleY", transform.newScaleY);
-            return scaled;
-        },
-        _flipObject: function(transform, by) {
-            if (transform.newScaleX < 0 && by !== "y") {
-                if (transform.originX === "left") {
-                    transform.originX = "right";
-                } else if (transform.originX === "right") {
-                    transform.originX = "left";
-                }
-            }
-            if (transform.newScaleY < 0 && by !== "x") {
-                if (transform.originY === "top") {
-                    transform.originY = "bottom";
-                } else if (transform.originY === "bottom") {
-                    transform.originY = "top";
-                }
-            }
-        },
-        _setLocalMouse: function(localMouse, t) {
-            var target = t.target, zoom = this.getZoom(), padding = target.padding / zoom;
-            if (t.originX === "right") {
-                localMouse.x *= -1;
-            } else if (t.originX === "center") {
-                localMouse.x *= t.mouseXSign * 2;
-                if (localMouse.x < 0) {
-                    t.mouseXSign = -t.mouseXSign;
-                }
-            }
-            if (t.originY === "bottom") {
-                localMouse.y *= -1;
-            } else if (t.originY === "center") {
-                localMouse.y *= t.mouseYSign * 2;
-                if (localMouse.y < 0) {
-                    t.mouseYSign = -t.mouseYSign;
-                }
-            }
-            if (abs(localMouse.x) > padding) {
-                if (localMouse.x < 0) {
-                    localMouse.x += padding;
-                } else {
-                    localMouse.x -= padding;
-                }
-            } else {
-                localMouse.x = 0;
-            }
-            if (abs(localMouse.y) > padding) {
-                if (localMouse.y < 0) {
-                    localMouse.y += padding;
-                } else {
-                    localMouse.y -= padding;
-                }
-            } else {
-                localMouse.y = 0;
-            }
-        },
-        _rotateObject: function(x, y) {
-            var t = this._currentTransform, target = t.target, constraintPosition, constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
-            if (target.lockRotation) {
-                return false;
-            }
-            var lastAngle = atan2(t.ey - constraintPosition.y, t.ex - constraintPosition.x), curAngle = atan2(y - constraintPosition.y, x - constraintPosition.x), angle = radiansToDegrees(curAngle - lastAngle + t.theta), hasRotated = true;
-            if (target.snapAngle > 0) {
-                var snapAngle = target.snapAngle, snapThreshold = target.snapThreshold || snapAngle, rightAngleLocked = Math.ceil(angle / snapAngle) * snapAngle, leftAngleLocked = Math.floor(angle / snapAngle) * snapAngle;
-                if (Math.abs(angle - leftAngleLocked) < snapThreshold) {
-                    angle = leftAngleLocked;
-                } else if (Math.abs(angle - rightAngleLocked) < snapThreshold) {
-                    angle = rightAngleLocked;
-                }
-            }
-            if (angle < 0) {
-                angle = 360 + angle;
-            }
-            angle %= 360;
-            if (target.angle === angle) {
-                hasRotated = false;
-            } else {
-                target.angle = angle;
-                target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
-            }
-            return hasRotated;
-        },
-        setCursor: function(value) {
-            this.upperCanvasEl.style.cursor = value;
-        },
-        _resetObjectTransform: function(target) {
-            target.scaleX = 1;
-            target.scaleY = 1;
-            target.skewX = 0;
-            target.skewY = 0;
-            target.rotate(0);
-        },
-        _drawSelection: function(ctx) {
-            var groupSelector = this._groupSelector, left = groupSelector.left, top = groupSelector.top, aleft = abs(left), atop = abs(top);
-            if (this.selectionColor) {
-                ctx.fillStyle = this.selectionColor;
-                ctx.fillRect(groupSelector.ex - (left > 0 ? 0 : -left), groupSelector.ey - (top > 0 ? 0 : -top), aleft, atop);
-            }
-            if (!this.selectionLineWidth || !this.selectionBorderColor) {
-                return;
-            }
-            ctx.lineWidth = this.selectionLineWidth;
-            ctx.strokeStyle = this.selectionBorderColor;
-            if (this.selectionDashArray.length > 1 && !supportLineDash) {
-                var px = groupSelector.ex + STROKE_OFFSET - (left > 0 ? 0 : aleft), py = groupSelector.ey + STROKE_OFFSET - (top > 0 ? 0 : atop);
-                ctx.beginPath();
-                fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
-                fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
-                fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
-                fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
-                ctx.closePath();
-                ctx.stroke();
-            } else {
-                fabric.Object.prototype._setLineDash.call(this, ctx, this.selectionDashArray);
-                ctx.strokeRect(groupSelector.ex + STROKE_OFFSET - (left > 0 ? 0 : aleft), groupSelector.ey + STROKE_OFFSET - (top > 0 ? 0 : atop), aleft, atop);
-            }
-        },
-        findTarget: function(e, skipGroup) {
-            if (this.skipTargetFind) {
-                return;
-            }
-            var ignoreZoom = true, pointer = this.getPointer(e, ignoreZoom), activeObject = this._activeObject, aObjects = this.getActiveObjects(), activeTarget, activeTargetSubs;
-            this.targets = [];
-            if (aObjects.length > 1 && !skipGroup && activeObject === this._searchPossibleTargets([ activeObject ], pointer)) {
-                return activeObject;
-            }
-            if (aObjects.length === 1 && activeObject._findTargetCorner(pointer)) {
-                return activeObject;
-            }
-            if (aObjects.length === 1 && activeObject === this._searchPossibleTargets([ activeObject ], pointer)) {
-                if (!this.preserveObjectStacking) {
-                    return activeObject;
-                } else {
-                    activeTarget = activeObject;
-                    activeTargetSubs = this.targets;
-                    this.targets = [];
-                }
-            }
-            var target = this._searchPossibleTargets(this._objects, pointer);
-            if (e[this.altSelectionKey] && target && activeTarget && target !== activeTarget) {
-                target = activeTarget;
-                this.targets = activeTargetSubs;
-            }
-            return target;
-        },
-        _checkTarget: function(pointer, obj) {
-            if (obj && obj.visible && obj.evented && this.containsPoint(null, obj, pointer)) {
-                if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
-                    var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
-                    if (!isTransparent) {
-                        return true;
-                    }
-                } else {
-                    return true;
-                }
-            }
-        },
-        _searchPossibleTargets: function(objects, pointer) {
-            var target, i = objects.length, normalizedPointer, subTarget;
-            while (i--) {
-                if (this._checkTarget(pointer, objects[i])) {
-                    target = objects[i];
-                    if (target.subTargetCheck && target instanceof fabric.Group) {
-                        normalizedPointer = this._normalizePointer(target, pointer);
-                        subTarget = this._searchPossibleTargets(target._objects, normalizedPointer);
-                        subTarget && this.targets.push(subTarget);
-                    }
-                    break;
-                }
-            }
-            return target;
-        },
-        restorePointerVpt: function(pointer) {
-            return fabric.util.transformPoint(pointer, fabric.util.invertTransform(this.viewportTransform));
-        },
-        getPointer: function(e, ignoreZoom, upperCanvasEl) {
-            if (!upperCanvasEl) {
-                upperCanvasEl = this.upperCanvasEl;
-            }
-            var pointer = getPointer(e), bounds = upperCanvasEl.getBoundingClientRect(), boundsWidth = bounds.width || 0, boundsHeight = bounds.height || 0, cssScale;
-            if (!boundsWidth || !boundsHeight) {
-                if ("top" in bounds && "bottom" in bounds) {
-                    boundsHeight = Math.abs(bounds.top - bounds.bottom);
-                }
-                if ("right" in bounds && "left" in bounds) {
-                    boundsWidth = Math.abs(bounds.right - bounds.left);
-                }
-            }
-            this.calcOffset();
-            pointer.x = pointer.x - this._offset.left;
-            pointer.y = pointer.y - this._offset.top;
-            if (!ignoreZoom) {
-                pointer = this.restorePointerVpt(pointer);
-            }
-            if (boundsWidth === 0 || boundsHeight === 0) {
-                cssScale = {
-                    width: 1,
-                    height: 1
-                };
-            } else {
-                cssScale = {
-                    width: upperCanvasEl.width / boundsWidth,
-                    height: upperCanvasEl.height / boundsHeight
-                };
-            }
-            return {
-                x: pointer.x * cssScale.width,
-                y: pointer.y * cssScale.height
-            };
-        },
-        _createUpperCanvas: function() {
-            var lowerCanvasClass = this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/, "");
-            if (this.upperCanvasEl) {
-                this.upperCanvasEl.className = "";
-            } else {
-                this.upperCanvasEl = this._createCanvasElement();
-            }
-            fabric.util.addClass(this.upperCanvasEl, "upper-canvas " + lowerCanvasClass);
-            this.wrapperEl.appendChild(this.upperCanvasEl);
-            this._copyCanvasStyle(this.lowerCanvasEl, this.upperCanvasEl);
-            this._applyCanvasStyle(this.upperCanvasEl);
-            this.contextTop = this.upperCanvasEl.getContext("2d");
-        },
-        _createCacheCanvas: function() {
-            this.cacheCanvasEl = this._createCanvasElement();
-            this.cacheCanvasEl.setAttribute("width", this.width);
-            this.cacheCanvasEl.setAttribute("height", this.height);
-            this.contextCache = this.cacheCanvasEl.getContext("2d");
-        },
-        _initWrapperElement: function() {
-            this.wrapperEl = fabric.util.wrapElement(this.lowerCanvasEl, "div", {
-                class: this.containerClass
-            });
-            fabric.util.setStyle(this.wrapperEl, {
-                width: this.width + "px",
-                height: this.height + "px",
-                position: "relative"
-            });
-            fabric.util.makeElementUnselectable(this.wrapperEl);
-        },
-        _applyCanvasStyle: function(element) {
-            var width = this.width || element.width, height = this.height || element.height;
-            fabric.util.setStyle(element, {
-                position: "absolute",
-                width: width + "px",
-                height: height + "px",
-                left: 0,
-                top: 0,
-                "touch-action": this.allowTouchScrolling ? "manipulation" : "none"
-            });
-            element.width = width;
-            element.height = height;
-            fabric.util.makeElementUnselectable(element);
-        },
-        _copyCanvasStyle: function(fromEl, toEl) {
-            toEl.style.cssText = fromEl.style.cssText;
-        },
-        getSelectionContext: function() {
-            return this.contextTop;
-        },
-        getSelectionElement: function() {
-            return this.upperCanvasEl;
-        },
-        getActiveObject: function() {
-            return this._activeObject;
-        },
-        getActiveObjects: function() {
-            var active = this._activeObject;
-            if (active) {
-                if (active.type === "activeSelection" && active._objects) {
-                    return active._objects.slice(0);
-                } else {
-                    return [ active ];
-                }
-            }
-            return [];
-        },
-        _onObjectRemoved: function(obj) {
-            if (obj === this._activeObject) {
-                this.fire("before:selection:cleared", {
-                    target: obj
-                });
-                this._discardActiveObject();
-                this.fire("selection:cleared", {
-                    target: obj
-                });
-                obj.fire("deselected");
-            }
-            if (this._hoveredTarget === obj) {
-                this._hoveredTarget = null;
-            }
-            this.callSuper("_onObjectRemoved", obj);
-        },
-        _fireSelectionEvents: function(oldObjects, e) {
-            var somethingChanged = false, objects = this.getActiveObjects(), added = [], removed = [], opt = {
-                e: e
-            };
-            oldObjects.forEach(function(oldObject) {
-                if (objects.indexOf(oldObject) === -1) {
-                    somethingChanged = true;
-                    oldObject.fire("deselected", opt);
-                    removed.push(oldObject);
-                }
-            });
-            objects.forEach(function(object) {
-                if (oldObjects.indexOf(object) === -1) {
-                    somethingChanged = true;
-                    object.fire("selected", opt);
-                    added.push(object);
-                }
-            });
-            if (oldObjects.length > 0 && objects.length > 0) {
-                opt.selected = added;
-                opt.deselected = removed;
-                opt.updated = added[0] || removed[0];
-                opt.target = this._activeObject;
-                somethingChanged && this.fire("selection:updated", opt);
-            } else if (objects.length > 0) {
-                if (objects.length === 1) {
-                    opt.target = added[0];
-                    this.fire("object:selected", opt);
-                }
-                opt.selected = added;
-                opt.target = this._activeObject;
-                this.fire("selection:created", opt);
-            } else if (oldObjects.length > 0) {
-                opt.deselected = removed;
-                this.fire("selection:cleared", opt);
-            }
-        },
-        setActiveObject: function(object, e) {
-            var currentActives = this.getActiveObjects();
-            this._setActiveObject(object, e);
-            this._fireSelectionEvents(currentActives, e);
-            return this;
-        },
-        _setActiveObject: function(object, e) {
-            if (this._activeObject === object) {
-                return false;
-            }
-            if (!this._discardActiveObject(e, object)) {
-                return false;
-            }
-            if (object.onSelect({
-                e: e
-            })) {
-                return false;
-            }
-            this._activeObject = object;
-            return true;
-        },
-        _discardActiveObject: function(e, object) {
-            var obj = this._activeObject;
-            if (obj) {
-                if (obj.onDeselect({
-                    e: e,
-                    object: object
-                })) {
-                    return false;
-                }
-                this._activeObject = null;
-            }
-            return true;
-        },
-        discardActiveObject: function(e) {
-            var currentActives = this.getActiveObjects();
-            if (currentActives.length) {
-                this.fire("before:selection:cleared", {
-                    target: currentActives[0],
-                    e: e
-                });
-            }
-            this._discardActiveObject(e);
-            this._fireSelectionEvents(currentActives, e);
-            return this;
-        },
-        dispose: function() {
-            var wrapper = this.wrapperEl;
-            this.removeListeners();
-            wrapper.removeChild(this.upperCanvasEl);
-            wrapper.removeChild(this.lowerCanvasEl);
-            this.upperCanvasEl = null;
-            this.cacheCanvasEl = null;
-            this.contextCache = null;
-            this.contextTop = null;
-            if (wrapper.parentNode) {
-                wrapper.parentNode.replaceChild(this.lowerCanvasEl, this.wrapperEl);
-            }
-            delete this.wrapperEl;
-            fabric.StaticCanvas.prototype.dispose.call(this);
-            return this;
-        },
-        clear: function() {
-            this.discardActiveObject();
-            this.clearContext(this.contextTop);
-            return this.callSuper("clear");
-        },
-        drawControls: function(ctx) {
-            var activeObject = this._activeObject;
-            if (activeObject) {
-                activeObject._renderControls(ctx);
-            }
-        },
-        _toObject: function(instance, methodName, propertiesToInclude) {
-            var originalProperties = this._realizeGroupTransformOnObject(instance), object = this.callSuper("_toObject", instance, methodName, propertiesToInclude);
-            this._unwindGroupTransformOnObject(instance, originalProperties);
-            return object;
-        },
-        _realizeGroupTransformOnObject: function(instance) {
-            if (instance.group && instance.group.type === "activeSelection" && this._activeObject === instance.group) {
-                var layoutProps = [ "angle", "flipX", "flipY", "left", "scaleX", "scaleY", "skewX", "skewY", "top" ];
-                var originalValues = {};
-                layoutProps.forEach(function(prop) {
-                    originalValues[prop] = instance[prop];
-                });
-                this._activeObject.realizeTransform(instance);
-                return originalValues;
-            } else {
-                return null;
-            }
-        },
-        _unwindGroupTransformOnObject: function(instance, originalValues) {
-            if (originalValues) {
-                instance.set(originalValues);
-            }
-        },
-        _setSVGObject: function(markup, instance, reviver) {
-            var originalProperties = this._realizeGroupTransformOnObject(instance);
-            this.callSuper("_setSVGObject", markup, instance, reviver);
-            this._unwindGroupTransformOnObject(instance, originalProperties);
-        }
-    });
-    for (var prop in fabric.StaticCanvas) {
-        if (prop !== "prototype") {
-            fabric.Canvas[prop] = fabric.StaticCanvas[prop];
-        }
+    /**
+     * Sets y of this point
+     * @param {Number} y
... 32370 lines suppressed ...